Skip to content

Commit

Permalink
Merge pull request #120 from rabbitholegg/mmackz-woofi-swap
Browse files Browse the repository at this point in the history
feat(woofi): add support for WooFi swap plugin
  • Loading branch information
Quazia authored Dec 12, 2023
2 parents 064919f + bd1c106 commit 0286954
Show file tree
Hide file tree
Showing 17 changed files with 585 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/tender-parents-work.md
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
1 change: 1 addition & 0 deletions packages/registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@rabbitholegg/questdk-plugin-traderjoe": "workspace:*",
"@rabbitholegg/questdk-plugin-zora": "workspace:*",
"@rabbitholegg/questdk-plugin-synapse": "workspace:*",
"@rabbitholegg/questdk-plugin-woofi": "workspace:*",
"viem": "^1.16.6"
}
}
2 changes: 2 additions & 0 deletions packages/registry/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { Zora } from '@rabbitholegg/questdk-plugin-zora'
import { Balancer } from '@rabbitholegg/questdk-plugin-balancer'
import { TraderJoe } from '@rabbitholegg/questdk-plugin-traderjoe'
import { Synapse } from '@rabbitholegg/questdk-plugin-synapse'
import { WooFi } from '@rabbitholegg/questdk-plugin-woofi'
import { ENTRYPOINT } from './contract-addresses'

export const plugins: Record<string, IActionPlugin> = {
Expand All @@ -57,6 +58,7 @@ export const plugins: Record<string, IActionPlugin> = {
[Balancer.pluginId]: Balancer,
[TraderJoe.pluginId]: TraderJoe,
[Synapse.pluginId]: Synapse,
[WooFi.pluginId]: WooFi,
}

export const getPlugin = (pluginId: string) => {
Expand Down
Empty file added packages/woofi/CHANGELOG.md
Empty file.
23 changes: 23 additions & 0 deletions packages/woofi/README.md
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
49 changes: 49 additions & 0 deletions packages/woofi/package.json
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"
}
}
61 changes: 61 additions & 0 deletions packages/woofi/src/WooFi.test.ts
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
})
})
})
})
})
61 changes: 61 additions & 0 deletions packages/woofi/src/WooFi.ts
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
}
38 changes: 38 additions & 0 deletions packages/woofi/src/abi.ts
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',
},
]
11 changes: 11 additions & 0 deletions packages/woofi/src/chain-ids.ts
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,
]
61 changes: 61 additions & 0 deletions packages/woofi/src/contract-addresses.ts
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
],
}
19 changes: 19 additions & 0 deletions packages/woofi/src/index.ts
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(),
}
Loading

0 comments on commit 0286954

Please sign in to comment.