-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #120 from rabbitholegg/mmackz-woofi-swap
feat(woofi): add support for WooFi swap plugin
- Loading branch information
Showing
17 changed files
with
585 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@rabbitholegg/questdk-plugin-registry": minor | ||
"@rabbitholegg/questdk-plugin-woofi": minor | ||
--- | ||
|
||
add woofi swap action plugin support |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# WooFi Plugin | ||
|
||
## Overview | ||
WOOFi is a cross-chain DEX where anyone can swap, stake, and earn crypto and trade perpetual futures across every major blockchain. | ||
|
||
### Swap Action | ||
|
||
#### Features | ||
- **Network Support:** Compatible with most major L2 networks. | ||
|
||
#### Limitations | ||
- There should be no limitations with the current swap plugin. Everything should work as expected. | ||
- Because of how the amountOut is calculated (minAmountOut), it may be lower then expected if the user has a high slippage setting. | ||
|
||
#### Sample Transactions | ||
|
||
There are 2 types of swaps we see on the WooRouter contracts | ||
- [Swap](https://optimistic.etherscan.io/tx/0xc6cb35161a46072e1167be1677f9d9ef2a7773f0de6499f79e8a95b366b0ee46) | ||
- [External Swap](https://optimistic.etherscan.io/tx/0xda72f698538b34bf2838225f1d95ac6067f15c207a311a6f7dde967d4469576c) | ||
|
||
### Stake Action | ||
|
||
- Coming Soon |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"name": "@rabbitholegg/questdk-plugin-woofi", | ||
"version": "1.0.0-alpha.7", | ||
"type": "module", | ||
"exports": { | ||
"require": "./dist/cjs/index.js", | ||
"import": "./dist/esm/index.js", | ||
"types": "./dist/types/index.d.ts" | ||
}, | ||
"main": "./dist/cjs/index.js", | ||
"module": "./dist/esm/index.js", | ||
"packageManager": "pnpm@8.3.1", | ||
"description": "", | ||
"scripts": { | ||
"bench": "vitest bench", | ||
"bench:ci": "CI=true vitest bench", | ||
"build": "pnpm run clean && pnpm run build:cjs && pnpm run build:esm && pnpm run build:types", | ||
"build:cjs": "tsc --project tsconfig.build.json --module commonjs --outDir ./dist/cjs --removeComments --verbatimModuleSyntax false && echo > ./dist/cjs/package.json '{\"type\":\"commonjs\"}'", | ||
"build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir ./dist/esm && echo > ./dist/esm/package.json '{\"type\":\"module\",\"sideEffects\":false}'", | ||
"build:types": "tsc --project tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap", | ||
"clean": "rimraf dist", | ||
"format": "rome format . --write", | ||
"lint": "rome check .", | ||
"lint:fix": "pnpm lint --apply", | ||
"test": "vitest dev", | ||
"test:cov": "vitest dev --coverage", | ||
"test:ci": "CI=true vitest --coverage", | ||
"test:ui": "vitest dev --ui" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"types": "./dist/types/index.d.ts", | ||
"typings": "./dist/types/index.d.ts", | ||
"devDependencies": { | ||
"@types/node": "^20.8.7", | ||
"@vitest/coverage-v8": "^0.33.0", | ||
"rimraf": "^5.0.5", | ||
"rome": "^12.1.3", | ||
"ts-node": "^10.9.1", | ||
"tsconfig": "workspace:*", | ||
"typescript": "^5.2.2", | ||
"vitest": "^0.33.0" | ||
}, | ||
"dependencies": { | ||
"@rabbitholegg/questdk": "2.0.0-alpha.28", | ||
"viem": "^1.16.6" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { apply } from '@rabbitholegg/questdk/filter' | ||
import { describe, expect, test } from 'vitest' | ||
import { swap, getSupportedTokenAddresses } from './WooFi' | ||
import { CHAIN_ID_ARRAY } from './chain-ids' | ||
import { passingTestCases, failingTestCases } from './test-transactions' | ||
import { SWAP_ABI } from './abi' | ||
|
||
describe('Given the WooFi plugin', () => { | ||
describe('When handling the swap action', () => { | ||
describe('should return a valid action filter', () => { | ||
test('when swapping on woofi', async () => { | ||
const { params } = passingTestCases[0] | ||
const filter = await swap(params) | ||
expect(filter).to.deep.equal({ | ||
chainId: 42161, | ||
to: '0x9aEd3A8896A85FE9a8CAc52C9B402D092B629a30', | ||
input: { | ||
$abi: SWAP_ABI, | ||
fromToken: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', | ||
toToken: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', | ||
fromAmount: { | ||
$gte: '222000000000000000', | ||
}, | ||
minToAmount: { | ||
$gte: '500000000', | ||
}, | ||
to: '0x9a67df384ec63f6cf960ef7e33287ea61491e415', | ||
}, | ||
}) | ||
}) | ||
}) | ||
describe('should pass filter with valid transactions', () => { | ||
passingTestCases.forEach((testCase) => { | ||
const { description, transaction, params } = testCase | ||
test(description, async () => { | ||
const filter = await swap(params) | ||
expect(apply(transaction, filter)).to.be.true | ||
}) | ||
}) | ||
}) | ||
|
||
describe('should not pass filter with invalid transactions', () => { | ||
failingTestCases.forEach((testCase) => { | ||
const { description, transaction, params } = testCase | ||
test(description, async () => { | ||
const filter = await swap(params) | ||
expect(apply(transaction, filter)).to.be.false | ||
}) | ||
}) | ||
}) | ||
|
||
describe('should return a valid token list', () => { | ||
CHAIN_ID_ARRAY.forEach((chainId) => { | ||
test(`for chainId ${chainId}`, async () => { | ||
const tokenList = await getSupportedTokenAddresses(chainId) | ||
expect(tokenList).to.not.be.empty | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { | ||
type TransactionFilter, | ||
type SwapActionParams, | ||
compressJson, | ||
} from '@rabbitholegg/questdk' | ||
import { type Address, zeroAddress } from 'viem' | ||
import { SWAP_ABI } from './abi' | ||
import { CHAIN_ID_ARRAY } from './chain-ids' | ||
import { CHAIN_TO_TOKENS, CHAIN_TO_ROUTER } from './contract-addresses' | ||
|
||
export const swap = async ( | ||
swap: SwapActionParams, | ||
): Promise<TransactionFilter> => { | ||
const { | ||
chainId, | ||
contractAddress, | ||
tokenIn, | ||
tokenOut, | ||
amountIn, | ||
amountOut, | ||
recipient, | ||
} = swap | ||
|
||
const swapContract = contractAddress ?? CHAIN_TO_ROUTER[chainId] | ||
|
||
if (swapContract === undefined) { | ||
throw new Error('contract address is not valid') | ||
} | ||
|
||
const fromToken = | ||
tokenIn === zeroAddress | ||
? '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' | ||
: tokenIn | ||
const toToken = | ||
tokenOut === zeroAddress | ||
? '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' | ||
: tokenOut | ||
|
||
return compressJson({ | ||
chainId, | ||
to: swapContract, | ||
input: { | ||
$abi: SWAP_ABI, | ||
fromToken, | ||
toToken, | ||
fromAmount: amountIn, | ||
minToAmount: amountOut, | ||
to: recipient, | ||
}, | ||
}) | ||
} | ||
|
||
export const getSupportedTokenAddresses = async ( | ||
_chainId: number, | ||
): Promise<Address[]> => { | ||
return CHAIN_TO_TOKENS[_chainId] ?? [] | ||
} | ||
|
||
export const getSupportedChainIds = async (): Promise<number[]> => { | ||
return CHAIN_ID_ARRAY | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { type Abi } from 'viem' | ||
|
||
export const SWAP_ABI: Abi = [ | ||
{ | ||
inputs: [ | ||
{ internalType: 'address', name: 'approveTarget', type: 'address' }, | ||
{ internalType: 'address', name: 'swapTarget', type: 'address' }, | ||
{ internalType: 'address', name: 'fromToken', type: 'address' }, | ||
{ internalType: 'address', name: 'toToken', type: 'address' }, | ||
{ internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'minToAmount', type: 'uint256' }, | ||
{ internalType: 'address payable', name: 'to', type: 'address' }, | ||
{ internalType: 'bytes', name: 'data', type: 'bytes' }, | ||
], | ||
name: 'externalSwap', | ||
outputs: [ | ||
{ internalType: 'uint256', name: 'realToAmount', type: 'uint256' }, | ||
], | ||
stateMutability: 'payable', | ||
type: 'function', | ||
}, | ||
{ | ||
inputs: [ | ||
{ internalType: 'address', name: 'fromToken', type: 'address' }, | ||
{ internalType: 'address', name: 'toToken', type: 'address' }, | ||
{ internalType: 'uint256', name: 'fromAmount', type: 'uint256' }, | ||
{ internalType: 'uint256', name: 'minToAmount', type: 'uint256' }, | ||
{ internalType: 'address payable', name: 'to', type: 'address' }, | ||
{ internalType: 'address', name: 'rebateTo', type: 'address' }, | ||
], | ||
name: 'swap', | ||
outputs: [ | ||
{ internalType: 'uint256', name: 'realToAmount', type: 'uint256' }, | ||
], | ||
stateMutability: 'payable', | ||
type: 'function', | ||
}, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Chains } from './utils' | ||
|
||
export const CHAIN_ID_ARRAY = [ | ||
Chains.ETHEREUM, | ||
Chains.OPTIMISM, | ||
Chains.POLYGON_POS, | ||
Chains.ZKSYNC_ERA, | ||
Chains.BASE, | ||
Chains.ARBITRUM_ONE, | ||
Chains.AVALANCHE, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { type Address, zeroAddress as NATIVE_TOKEN } from 'viem' | ||
import { Chains } from './utils' | ||
|
||
export const CHAIN_TO_ROUTER: { [chainId: number]: Address | undefined } = { | ||
[Chains.ETHEREUM]: '0x044c08639bD59BEB4F6ec52c0da6CD47283534E8', | ||
[Chains.OPTIMISM]: '0xEAf1Ac8E89EA0aE13E0f03634A4FF23502527024', | ||
[Chains.POLYGON_POS]: '0x817Eb46D60762442Da3D931Ff51a30334CA39B74', | ||
[Chains.ZKSYNC_ERA]: '0xfd505702b37Ae9b626952Eb2DD736d9045876417', | ||
[Chains.BASE]: '0x27425e9FB6A9A625E8484CFD9620851D1Fa322E5', | ||
[Chains.ARBITRUM_ONE]: '0x9aEd3A8896A85FE9a8CAc52C9B402D092B629a30', | ||
[Chains.AVALANCHE]: '0xC22FBb3133dF781E6C25ea6acebe2D2Bb8CeA2f9', | ||
} | ||
|
||
export const CHAIN_TO_TOKENS: { [_chainId: number]: Address[] } = { | ||
[Chains.ETHEREUM]: [ | ||
NATIVE_TOKEN, // ETH | ||
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC | ||
'0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT | ||
], | ||
[Chains.OPTIMISM]: [ | ||
NATIVE_TOKEN, // ETH | ||
'0x4200000000000000000000000000000000000042', // OP | ||
'0x7F5c764cBc14f9669B88837ca1490cCa17c31607', // USDC.e | ||
'0x68f180fcCe6836688e9084f035309E29Bf0A2095', // WBTC | ||
'0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', // USDT | ||
], | ||
[Chains.POLYGON_POS]: [ | ||
NATIVE_TOKEN, // MATIC | ||
'0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', // WETH | ||
'0x1B815d120B3eF02039Ee11dC2d33DE7aA4a8C603', // WOO | ||
'0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // USDC.e | ||
'0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6', // WBTC | ||
'0xc2132D05D31c914a87C6611C10748AEb04B58e8F', // USDT | ||
], | ||
[Chains.ZKSYNC_ERA]: [ | ||
NATIVE_TOKEN, // ETH | ||
'0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4', // USDC | ||
'0x493257fD37EDB34451f62EDf8D2a0C418852bA4C', // USDT | ||
], | ||
[Chains.BASE]: [ | ||
NATIVE_TOKEN, // ETH | ||
'0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', // USDbC | ||
], | ||
[Chains.ARBITRUM_ONE]: [ | ||
NATIVE_TOKEN, // ETH | ||
'0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC | ||
'0xcAFcD85D8ca7Ad1e1C6F82F651fA15E33AEfD07b', // WOO | ||
'0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', // WBTC | ||
'0x912CE59144191C1204E64559FE8253a0e49E6548', // ARB | ||
'0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', // USDT | ||
'0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', // USDC.e | ||
], | ||
[Chains.AVALANCHE]: [ | ||
NATIVE_TOKEN, // AVAX | ||
'0x152b9d0FdC40C096757F570A51E494bd4b943E50', // BTC.b | ||
'0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', // WETH.e | ||
'0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', // USDC | ||
'0xaBC9547B534519fF73921b1FBA6E672b5f58D083', // WOO.e | ||
'0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', // USDT | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { | ||
type IActionPlugin, | ||
PluginActionNotImplementedError, | ||
} from '@rabbitholegg/questdk' | ||
|
||
import { | ||
swap, | ||
getSupportedChainIds, | ||
getSupportedTokenAddresses, | ||
} from './WooFi.js' | ||
|
||
export const WooFi: IActionPlugin = { | ||
pluginId: 'woofi', | ||
getSupportedTokenAddresses, | ||
getSupportedChainIds, | ||
swap, | ||
bridge: async () => new PluginActionNotImplementedError(), | ||
mint: async () => new PluginActionNotImplementedError(), | ||
} |
Oops, something went wrong.