From 477c9177d935a2f7ec3acc015478a704945c9638 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Thu, 28 Dec 2023 22:34:59 -0800 Subject: [PATCH 01/21] feat(camelot): add ABI for V3 swaps --- packages/camelot/src/abi.ts | 137 +++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/packages/camelot/src/abi.ts b/packages/camelot/src/abi.ts index d9f631aa5..d3ded995d 100644 --- a/packages/camelot/src/abi.ts +++ b/packages/camelot/src/abi.ts @@ -1,4 +1,4 @@ -export const CAMELOT_ABI = [ +export const CAMELOT_V2_ABI = [ { inputs: [ { @@ -110,6 +110,141 @@ export const CAMELOT_ABI = [ }, ] +export const CAMELOT_V3_EXACT_INPUT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { + internalType: 'uint256', + name: 'amountOutMinimum', + type: 'uint256', + }, + ], + internalType: 'struct ISwapRouter.ExactInputParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'exactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { + internalType: 'uint256', + name: 'amountOutMinimum', + type: 'uint256', + }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' }, + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'exactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { + internalType: 'uint256', + name: 'amountOutMinimum', + type: 'uint256', + }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' }, + ], + internalType: 'struct ISwapRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'exactInputSingleSupportingFeeOnTransferTokens', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, +] + +export const CAMELOT_V3_EXACT_OUTPUT_ABI = [ + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + ], + internalType: 'struct ISwapRouter.ExactOutputParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'exactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'amountInMaximum', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtPrice', type: 'uint160' }, + ], + internalType: 'struct ISwapRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'exactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function', + }, +] + +export const V3_MULTICALL_ABI = [ + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function', + }, +] + export const PARASWAP_ABI = [ { inputs: [ From 2cf3d048d1dad28bbab4afff562d890f705b4daf Mon Sep 17 00:00:00 2001 From: Mmackz Date: Thu, 28 Dec 2023 22:35:24 -0800 Subject: [PATCH 02/21] feat(camelot): add contract address for V3 swap --- packages/camelot/src/contract-addresses.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/camelot/src/contract-addresses.ts b/packages/camelot/src/contract-addresses.ts index 4c8aa863a..af80757a4 100644 --- a/packages/camelot/src/contract-addresses.ts +++ b/packages/camelot/src/contract-addresses.ts @@ -17,5 +17,6 @@ export const DEFAULT_TOKEN_LIST: Address[] = [ ] export const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' -export const CAMELOT_ROUTER = '0xc873fEcbd354f5A56E00E710B90EF4201db2448d' +export const CAMELOT_V2_ROUTER = '0xc873fEcbd354f5A56E00E710B90EF4201db2448d' +export const CAMELOT_V3_ROUTER = '0x1f721e2e82f6676fce4ea07a5958cf098d339e18' export const PARASWAP_ROUTER = '0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57' From c554698a290f80bb79ab3a5f74265ebeae158db2 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 10:32:41 -0800 Subject: [PATCH 03/21] refactor(camelot): remove multicall abi --- packages/camelot/src/abi.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/camelot/src/abi.ts b/packages/camelot/src/abi.ts index d3ded995d..72772f80b 100644 --- a/packages/camelot/src/abi.ts +++ b/packages/camelot/src/abi.ts @@ -235,16 +235,6 @@ export const CAMELOT_V3_EXACT_OUTPUT_ABI = [ }, ] -export const V3_MULTICALL_ABI = [ - { - inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], - name: 'multicall', - outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], - stateMutability: 'payable', - type: 'function', - }, -] - export const PARASWAP_ABI = [ { inputs: [ From 13ba48759851332f38cfc202db7ba7697899bb34 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 10:33:19 -0800 Subject: [PATCH 04/21] refactor(camelot): rename variable --- packages/camelot/src/contract-addresses.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camelot/src/contract-addresses.ts b/packages/camelot/src/contract-addresses.ts index af80757a4..0c50303b9 100644 --- a/packages/camelot/src/contract-addresses.ts +++ b/packages/camelot/src/contract-addresses.ts @@ -16,7 +16,7 @@ export const DEFAULT_TOKEN_LIST: Address[] = [ Tokens.SIZE, // '0x939727d85d99d0ac339bf1b76dfe30ca27c19067' - SIZE ] -export const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' +export const INTERNAL_ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' export const CAMELOT_V2_ROUTER = '0xc873fEcbd354f5A56E00E710B90EF4201db2448d' export const CAMELOT_V3_ROUTER = '0x1f721e2e82f6676fce4ea07a5958cf098d339e18' export const PARASWAP_ROUTER = '0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57' From d7dd0511aaf3715e43d3635f6a3f3532c0edea65 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 10:33:59 -0800 Subject: [PATCH 05/21] refactor(camelot): use V3 path query function --- packages/camelot/src/utils.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/camelot/src/utils.ts b/packages/camelot/src/utils.ts index 147f068ff..1629a7a6d 100644 --- a/packages/camelot/src/utils.ts +++ b/packages/camelot/src/utils.ts @@ -61,7 +61,7 @@ export function createTestCase( } } -export const buildPathQuery = (tokenIn?: string, tokenOut?: string) => { +export const buildV2PathQuery = (tokenIn?: string, tokenOut?: string) => { // v2 paths are formatted as [, ] const conditions: FilterOperator[] = [] @@ -77,3 +77,22 @@ export const buildPathQuery = (tokenIn?: string, tokenOut?: string) => { $and: conditions, } } + +export const buildV3PathQuery = (tokenIn?: string, tokenOut?: string) => { + // v3 paths are formatted as 0x + + const conditions: FilterOperator[] = [] + + if (tokenIn) { + conditions.push({ $regex: `^${tokenIn.toLowerCase()}` }) + } + + if (tokenOut) { + // Chop the 0x prefix before comparing + conditions.push({ $regex: `${tokenOut.slice(2).toLowerCase()}$` }) + } + + return { + $and: conditions, + } +} From 57883b357ec256fdd34a74a03aa3e04a888aa912 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 10:34:49 -0800 Subject: [PATCH 06/21] test(camelot): add test transactions for V3 --- packages/camelot/src/test-transactions.ts | 44 +++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/camelot/src/test-transactions.ts b/packages/camelot/src/test-transactions.ts index 4e94b4811..73e405d0e 100644 --- a/packages/camelot/src/test-transactions.ts +++ b/packages/camelot/src/test-transactions.ts @@ -1,5 +1,5 @@ import { ARBITRUM_CHAIN_ID } from './chain-ids' -import { CAMELOT_ROUTER } from './contract-addresses' +import { CAMELOT_V2_ROUTER } from './contract-addresses' import { parseEther, parseUnits } from 'viem' import { Tokens, type TestParams } from './utils' import { @@ -19,7 +19,7 @@ export const V2_SWAP_ETH: TestParams = { }, params: { chainId: ARBITRUM_CHAIN_ID, - contractAddress: CAMELOT_ROUTER, + contractAddress: CAMELOT_V2_ROUTER, tokenIn: Tokens.ETH, tokenOut: '0xBfbCFe8873fE28Dfa25f1099282b088D52bbAD9C', // EQB Token amountIn: GreaterThanOrEqual(parseEther('0.00055')), @@ -47,6 +47,46 @@ export const V2_SWAP_TOKENS: TestParams = { }, } +export const V3_SWAP_ETH: TestParams = { + transaction: { + chainId: 42161, + from: '0x865c301c46d64de5c9b124ec1a97ef1efc1bcbd1', + hash: '0x82e2521430a41da6a57aa1ae4b5007caf0e5567ff6e66c340bf474af628e97e9', + input: + '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4bc65118800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000865c301c46d64de5c9b124ec1a97ef1efc1bcbd100000000000000000000000000000000000000000000000000000000658e4f4600000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000023a42c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + to: '0x1f721e2e82f6676fce4ea07a5958cf098d339e18', + value: '1000000000000000', + }, + params: { + chainId: ARBITRUM_CHAIN_ID, + tokenIn: Tokens.ETH, + tokenOut: Tokens.USDCE, + amountIn: GreaterThanOrEqual(parseEther('0.001')), + amountOut: GreaterThanOrEqual(parseUnits('2', 6)), + recipient: '0x865c301c46d64de5c9b124ec1a97ef1efc1bcbd1', + }, +} + +export const V3_TOKEN_TO_ETH: TestParams = { + transaction: { + chainId: 42161, + from: '0x537c820e296026443a2c4757d3a74bb7c789c904', + hash: '0x0a4514aa70fb9984a418756ca5efd325108006b94f9ab0a13686ec610c5bab09', + input: + '0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000124c04b8d59000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000658a92db0000000000000000000000000000000000000000000000000000000001312d0000000000000000000000000000000000000000000000000000196679dcad4b3d000000000000000000000000000000000000000000000000000000000000003cfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9ff970a61a04b1ca14834a43f5de4533ebddb5cc882af49447d8a07e3bd95bd0d56f35241523fbab10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004469bc35b200000000000000000000000000000000000000000000000000196679dcad4b3d000000000000000000000000537c820e296026443a2c4757d3a74bb7c789c90400000000000000000000000000000000000000000000000000000000', + to: '0x1f721e2e82f6676fce4ea07a5958cf098d339e18', + value: '0', + }, + params: { + chainId: ARBITRUM_CHAIN_ID, + tokenIn: Tokens.USDT, + tokenOut: Tokens.ETH, + amountIn: GreaterThanOrEqual(parseUnits('20', 6)), + amountOut: GreaterThanOrEqual(parseEther('0.0071495')), + recipient: '0x537c820e296026443a2c4757d3a74bb7c789c904', + }, +} + export const PARASWAP_MULTISWAP: TestParams = { transaction: { chainId: 42161, From cdb684db854f4bd7e954c9b421529b7e89173db2 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 10:36:48 -0800 Subject: [PATCH 07/21] feat(camelot): add V3 swap filter --- packages/camelot/src/Camelot.ts | 168 +++++++++++++++++++------------- 1 file changed, 102 insertions(+), 66 deletions(-) diff --git a/packages/camelot/src/Camelot.ts b/packages/camelot/src/Camelot.ts index ed94646eb..5bd32e3b0 100644 --- a/packages/camelot/src/Camelot.ts +++ b/packages/camelot/src/Camelot.ts @@ -6,105 +6,141 @@ import { import { type Address } from 'viem' import { CHAIN_ID_ARRAY, ARBITRUM_CHAIN_ID } from './chain-ids' import { DEFAULT_TOKEN_LIST } from './contract-addresses' -import { buildPathQuery, Tokens } from './utils' -import { CAMELOT_ABI, PARASWAP_ABI } from './abi' +import { buildV2PathQuery, buildV3PathQuery, Tokens } from './utils' import { - CAMELOT_ROUTER, + CAMELOT_V2_ABI, + CAMELOT_V3_EXACT_INPUT_ABI, + CAMELOT_V3_EXACT_OUTPUT_ABI, + PARASWAP_ABI, +} from './abi' +import { + CAMELOT_V2_ROUTER, + CAMELOT_V3_ROUTER, PARASWAP_ROUTER, - ETH_ADDRESS, + INTERNAL_ETH_ADDRESS, } from './contract-addresses' -const isValidContractAddress = (address: Address) => { - return ( - address?.toLowerCase() === CAMELOT_ROUTER.toLowerCase() || - address?.toLowerCase() === PARASWAP_ROUTER.toLowerCase() - ) -} - export const swap = async ( swap: SwapActionParams, ): Promise => { - const { - chainId, - contractAddress, - tokenIn, - tokenOut, - amountIn, - amountOut, - recipient, - } = swap + const { chainId, tokenIn, tokenOut, amountIn, amountOut, recipient } = swap const ethUsedIn = tokenIn === Tokens.ETH const ethUsedOut = tokenOut === Tokens.ETH - if (contractAddress && !isValidContractAddress(contractAddress)) { - throw new Error('Invalid Contract Address') - } + const tokenInOrEth = ethUsedIn ? INTERNAL_ETH_ADDRESS : tokenIn + const tokenOutOrEth = ethUsedOut ? INTERNAL_ETH_ADDRESS : tokenOut + const tokenInOrWeth = ethUsedIn ? Tokens.WETH : tokenIn + const tokenOutOrWeth = ethUsedOut ? Tokens.WETH : tokenOut + + console.log(buildV3PathQuery(tokenInOrWeth, tokenOutOrWeth)) return compressJson({ chainId: chainId, - to: { $or: [CAMELOT_ROUTER.toLowerCase(), PARASWAP_ROUTER.toLowerCase()] }, + from: recipient, + to: { + $or: [ + CAMELOT_V2_ROUTER.toLowerCase(), + CAMELOT_V3_ROUTER.toLowerCase(), + PARASWAP_ROUTER.toLowerCase(), + ], + }, value: ethUsedIn ? amountIn : undefined, input: { - $abi: [...CAMELOT_ABI, ...PARASWAP_ABI], $or: [ { // camelotV2 swap - to: recipient, - path: buildPathQuery(ethUsedIn ? Tokens.WETH : tokenIn, tokenOut), + $abi: CAMELOT_V2_ABI, + path: buildV2PathQuery(tokenInOrWeth, tokenOut), amountOutMin: amountOut, amountIn: ethUsedIn ? undefined : amountIn, }, { - // simpleswap, directUniV3Swap, directCurveSwap - data: { - fromToken: ethUsedIn ? ETH_ADDRESS : tokenIn, - fromAmount: amountIn, - toAmount: amountOut, - toToken: ethUsedOut ? ETH_ADDRESS : tokenOut, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - { - // multiswap - data: { - fromToken: ethUsedIn ? ETH_ADDRESS : tokenIn, - fromAmount: amountIn, - toAmount: amountOut, - path: { - $last: { - to: ethUsedOut ? ETH_ADDRESS : tokenOut, + // camelotV3 swap + $or: [ + { + $abiAbstract: CAMELOT_V3_EXACT_OUTPUT_ABI, + params: { + tokenIn: tokenInOrWeth, + tokenOut: tokenOutOrWeth, + amountInMaximum: amountIn, + amountOut, + }, + }, + { + $abiAbstract: CAMELOT_V3_EXACT_INPUT_ABI, + params: { + $or: [ + { + tokenIn: tokenInOrWeth, + tokenOut: tokenOutOrWeth, + }, + { + path: buildV3PathQuery(tokenInOrWeth, tokenOutOrWeth), + }, + ], + amountIn, + amountOutMinimum: amountOut, }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, + ], }, { - // megaswap - data: { - fromToken: ethUsedIn ? ETH_ADDRESS : tokenIn, - fromAmount: amountIn, - toAmount: amountOut, - path: { - $last: { + // paraswap + $abi: PARASWAP_ABI, + $or: [ + { + // simpleswap, directUniV3Swap, directCurveSwap + data: { + fromToken: tokenInOrEth, + fromAmount: amountIn, + toAmount: amountOut, + toToken: tokenOutOrEth, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + }, + }, + { + // multiswap + data: { + fromToken: tokenInOrEth, + fromAmount: amountIn, + toAmount: amountOut, path: { $last: { - to: ethUsedOut ? ETH_ADDRESS : tokenOut, + to: tokenOutOrEth, }, }, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - { - // directBalancerV2 - data: { - assets: buildPathQuery(tokenIn, tokenOut), - fromAmount: amountIn, - toAmount: amountOut, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, + { + // megaswap + data: { + fromToken: tokenInOrEth, + fromAmount: amountIn, + toAmount: amountOut, + path: { + $last: { + path: { + $last: { + to: tokenOutOrEth, + }, + }, + }, + }, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + }, + }, + { + // directBalancerV2 + data: { + assets: buildV2PathQuery(tokenIn, tokenOut), + fromAmount: amountIn, + toAmount: amountOut, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + }, + }, + ], }, ], }, From 2c9416eb4c349e53fab66de3d7d33825f324b880 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 12:07:43 -0800 Subject: [PATCH 08/21] test(camelot): fix action filter test --- packages/camelot/src/Camelot.test.ts | 306 ++++++++++++--------------- 1 file changed, 138 insertions(+), 168 deletions(-) diff --git a/packages/camelot/src/Camelot.test.ts b/packages/camelot/src/Camelot.test.ts index 65a5727e3..7f3a69e0a 100644 --- a/packages/camelot/src/Camelot.test.ts +++ b/packages/camelot/src/Camelot.test.ts @@ -1,16 +1,21 @@ import { GreaterThanOrEqual, apply } from '@rabbitholegg/questdk/filter' import { describe, expect, test } from 'vitest' import { - CAMELOT_ROUTER, + CAMELOT_V2_ROUTER, + CAMELOT_V3_ROUTER, DEFAULT_TOKEN_LIST, - ETH_ADDRESS, PARASWAP_ROUTER, } from './contract-addresses' +import { + CAMELOT_V2_ABI, + CAMELOT_V3_EXACT_INPUT_ABI, + CAMELOT_V3_EXACT_OUTPUT_ABI, + PARASWAP_ABI, +} from './abi' import { ARBITRUM_CHAIN_ID } from './chain-ids' import { parseEther, getAddress } from 'viem' import { swap } from './Camelot' import { Tokens } from './utils' -import { CAMELOT_ABI, PARASWAP_ABI } from './abi' import { failingTestCases, passingTestCases } from './test-setup' describe('Given the camelot plugin', () => { @@ -18,7 +23,6 @@ describe('Given the camelot plugin', () => { test('for a swap using ERC-20 token as tokenIn', async () => { const filter = await swap({ chainId: ARBITRUM_CHAIN_ID, - contractAddress: CAMELOT_ROUTER, tokenIn: Tokens.USDT, tokenOut: Tokens.WETH, amountIn: GreaterThanOrEqual(1000000n), @@ -28,12 +32,16 @@ describe('Given the camelot plugin', () => { expect(filter).to.deep.equal({ chainId: 42161, to: { - $or: [CAMELOT_ROUTER.toLowerCase(), PARASWAP_ROUTER.toLowerCase()], + $or: [ + CAMELOT_V2_ROUTER.toLowerCase(), + CAMELOT_V3_ROUTER.toLowerCase(), + PARASWAP_ROUTER.toLowerCase(), + ], }, input: { - $abi: [...CAMELOT_ABI, ...PARASWAP_ABI], $or: [ { + $abi: CAMELOT_V2_ABI, path: { $and: [ { @@ -52,181 +60,154 @@ describe('Given the camelot plugin', () => { }, }, { - data: { - fromToken: Tokens.USDT, - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', - }, - toToken: Tokens.WETH, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - { - data: { - fromToken: Tokens.USDT, - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', + $or: [ + { + $abiAbstract: CAMELOT_V3_EXACT_OUTPUT_ABI, + params: { + $or: [ + { + tokenIn: Tokens.USDT, + tokenOut: Tokens.WETH, + }, + { + path: { + $and: [ + { + $regex: + '^0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + }, + { + $regex: + '82af49447d8a07e3bd95bd0d56f35241523fbab1$', + }, + ], + }, + }, + ], + amountInMaximum: { + $gte: '1000000', + }, + amountOut: { + $gte: '500000000000000', + }, + }, }, - path: { - $last: { - to: Tokens.WETH, + { + $abiAbstract: CAMELOT_V3_EXACT_INPUT_ABI, + params: { + $or: [ + { + tokenIn: Tokens.USDT, + tokenOut: Tokens.WETH, + }, + { + path: { + $and: [ + { + $regex: + '^0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + }, + { + $regex: + '82af49447d8a07e3bd95bd0d56f35241523fbab1$', + }, + ], + }, + }, + ], + amountIn: { + $gte: '1000000', + }, + amountOutMinimum: { + $gte: '500000000000000', + }, }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, + ], }, { - data: { - fromToken: Tokens.USDT, - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', + $abi: PARASWAP_ABI, + $or: [ + { + data: { + fromToken: Tokens.USDT, + fromAmount: { + $gte: '1000000', + }, + toAmount: { + $gte: '500000000000000', + }, + toToken: Tokens.WETH, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + }, }, - path: { - $last: { + { + data: { + fromToken: Tokens.USDT, + fromAmount: { + $gte: '1000000', + }, + toAmount: { + $gte: '500000000000000', + }, path: { $last: { to: Tokens.WETH, }, }, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - { - data: { - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', - }, - assets: { - $and: [{ $first: Tokens.USDT }, { $last: Tokens.WETH }], - }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - ], - }, - }) - }) - test('for a swap using ETH as tokenIn', async () => { - const filter = await swap({ - chainId: ARBITRUM_CHAIN_ID, - contractAddress: CAMELOT_ROUTER, - tokenIn: Tokens.ETH, - tokenOut: Tokens.USDT, - amountIn: GreaterThanOrEqual(1000000n), - amountOut: GreaterThanOrEqual(parseEther('0.0005')), - recipient: '0x67ef327038b25ff762a0606bc92c4a0a6e767048', - }) - expect(filter).to.deep.equal({ - chainId: 42161, - to: { - $or: [CAMELOT_ROUTER.toLowerCase(), PARASWAP_ROUTER.toLowerCase()], - }, - value: { - $gte: '1000000', - }, - input: { - $abi: [...CAMELOT_ABI, ...PARASWAP_ABI], - $or: [ - { - to: '0x67ef327038b25ff762a0606bc92c4a0a6e767048', - path: { - $and: [ - { - $first: Tokens.WETH, - }, - { - $last: Tokens.USDT, - }, - ], - }, - amountOutMin: { - $gte: '500000000000000', - }, - }, - { - data: { - fromToken: ETH_ADDRESS, - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', - }, - toToken: Tokens.USDT, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - { - data: { - fromToken: ETH_ADDRESS, - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', - }, - path: { - $last: { - to: Tokens.USDT, - }, - }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - { - data: { - fromToken: ETH_ADDRESS, - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', - }, - path: { - $last: { + { + data: { + fromToken: Tokens.USDT, + fromAmount: { + $gte: '1000000', + }, + toAmount: { + $gte: '500000000000000', + }, path: { $last: { - to: Tokens.USDT, + path: { + $last: { + to: Tokens.WETH, + }, + }, }, }, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, - }, - { - data: { - fromAmount: { - $gte: '1000000', - }, - toAmount: { - $gte: '500000000000000', - }, - assets: { - $and: [{ $first: Tokens.ETH }, { $last: Tokens.USDT }], + { + data: { + assets: { + $and: [ + { + $first: Tokens.USDT, + }, + { + $last: Tokens.WETH, + }, + ], + }, + fromAmount: { + $gte: '1000000', + }, + toAmount: { + $gte: '500000000000000', + }, + partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', - }, + ], }, ], }, }) }) }) + describe('should pass filter when all parameters are valid', () => { passingTestCases.forEach((testCase) => { const { transaction, params, description } = testCase @@ -236,6 +217,7 @@ describe('Given the camelot plugin', () => { }) }) }) + describe('should not pass filter when parameters are invalid', () => { failingTestCases.forEach((testCase) => { const { transaction, params, description } = testCase @@ -244,20 +226,8 @@ describe('Given the camelot plugin', () => { expect(apply(transaction, filter)).to.be.false }) }) - test('should throw error when contract address is incorrect', async () => { - try { - const { transaction, params } = passingTestCases[0] - params.contractAddress = '0xE592427A0AEce92De3Edee1F18E0157C05861564' - const filter = await swap({ ...params }) - apply(transaction, filter) - throw new Error('Expected bridge function to throw, but it did not.') - } catch (err) { - if (err instanceof Error) { - expect(err.message).toBe('Invalid Contract Address') - } - } - }) }) + describe('all supported tokens addresses are properly checksummed', () => { test('should have all addresses properly checksummed', () => { const notChecksummed = DEFAULT_TOKEN_LIST.filter( From 5d813f251ee22e492bcd0525e7f391466e09812a Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 12:08:49 -0800 Subject: [PATCH 09/21] refactor(camelot): add path array to v3 exact output filter --- packages/camelot/src/Camelot.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/camelot/src/Camelot.ts b/packages/camelot/src/Camelot.ts index 5bd32e3b0..ed836440d 100644 --- a/packages/camelot/src/Camelot.ts +++ b/packages/camelot/src/Camelot.ts @@ -61,8 +61,15 @@ export const swap = async ( { $abiAbstract: CAMELOT_V3_EXACT_OUTPUT_ABI, params: { - tokenIn: tokenInOrWeth, - tokenOut: tokenOutOrWeth, + $or: [ + { + tokenIn: tokenInOrWeth, + tokenOut: tokenOutOrWeth, + }, + { + path: buildV3PathQuery(tokenInOrWeth, tokenOutOrWeth), + }, + ], amountInMaximum: amountIn, amountOut, }, From ee8b511a061c9b30299298f5c41d5a3c06712daa Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 12:09:13 -0800 Subject: [PATCH 10/21] test(camelot): add v3 tests --- packages/camelot/src/test-setup.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/camelot/src/test-setup.ts b/packages/camelot/src/test-setup.ts index aab13d9b6..c18ff0250 100644 --- a/packages/camelot/src/test-setup.ts +++ b/packages/camelot/src/test-setup.ts @@ -4,6 +4,8 @@ import { Tokens, createTestCase } from './utils' import { V2_SWAP_ETH, V2_SWAP_TOKENS, + V3_SWAP_ETH, + V3_TOKEN_TO_ETH, PARASWAP_SIMPLESWAP, PARASWAP_MULTISWAP, PARASWAP_MEGASWAP, @@ -16,6 +18,8 @@ import { export const passingTestCases = [ createTestCase(V2_SWAP_ETH, 'when swapping ETH for tokens'), createTestCase(V2_SWAP_TOKENS, 'when swapping tokens for tokens'), + createTestCase(V3_SWAP_ETH, 'when swapping ETH for tokens V3'), + createTestCase(V3_TOKEN_TO_ETH, 'when swapping Tokens for ETH V3'), createTestCase(PARASWAP_SIMPLESWAP, 'for simple swap'), createTestCase(PARASWAP_MULTISWAP, 'for multi swap'), createTestCase(PARASWAP_MEGASWAP, 'for mega swap'), From b338c340f97eb2942d9ca17b000eeb113a199deb Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 12:32:43 -0800 Subject: [PATCH 11/21] test(camelot): add test for V2 --- packages/camelot/src/test-setup.ts | 2 ++ packages/camelot/src/test-transactions.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/packages/camelot/src/test-setup.ts b/packages/camelot/src/test-setup.ts index c18ff0250..68b358d9b 100644 --- a/packages/camelot/src/test-setup.ts +++ b/packages/camelot/src/test-setup.ts @@ -4,6 +4,7 @@ import { Tokens, createTestCase } from './utils' import { V2_SWAP_ETH, V2_SWAP_TOKENS, + V2_TOKENS_TO_ETH, V3_SWAP_ETH, V3_TOKEN_TO_ETH, PARASWAP_SIMPLESWAP, @@ -18,6 +19,7 @@ import { export const passingTestCases = [ createTestCase(V2_SWAP_ETH, 'when swapping ETH for tokens'), createTestCase(V2_SWAP_TOKENS, 'when swapping tokens for tokens'), + createTestCase(V2_TOKENS_TO_ETH, 'when swapping Tokens for ETH V2'), createTestCase(V3_SWAP_ETH, 'when swapping ETH for tokens V3'), createTestCase(V3_TOKEN_TO_ETH, 'when swapping Tokens for ETH V3'), createTestCase(PARASWAP_SIMPLESWAP, 'for simple swap'), diff --git a/packages/camelot/src/test-transactions.ts b/packages/camelot/src/test-transactions.ts index 73e405d0e..388a99336 100644 --- a/packages/camelot/src/test-transactions.ts +++ b/packages/camelot/src/test-transactions.ts @@ -27,6 +27,25 @@ export const V2_SWAP_ETH: TestParams = { }, } +export const V2_TOKENS_TO_ETH: TestParams = { + transaction: { + chainId: 42161, + from: '0x692c2b93ee954b933837178d83946e695389673d', + hash: '0x47166452a6c86d9aae00f4c3bfafcfce779c87a0338086db1c3505d3f5a47afb', + input: + '0x52aa4c22000000000000000000000000000000000000000000108b2a2c2802909400000000000000000000000000000000000000000000000000000002a912a2b7c53ef000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000692c2b93ee954b933837178d83946e695389673d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000658f28000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000939727d85d99d0ac339bf1b76dfe30ca27c1906700000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1', + to: '0xc873fecbd354f5a56e00e710b90ef4201db2448d', + value: '0', + }, + params: { + chainId: ARBITRUM_CHAIN_ID, + tokenIn: '0x939727d85d99d0ac339bf1b76dfe30ca27c19067', // SIZE + tokenOut: Tokens.ETH, + amountIn: GreaterThanOrEqual(parseUnits('20000000', 18)), + amountOut: GreaterThanOrEqual(parseEther('0.19')), + }, +} + export const V2_SWAP_TOKENS: TestParams = { transaction: { chainId: 42161, From 9449fe5861e6aa983af6521369cceb9ece44eefb Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 12:34:13 -0800 Subject: [PATCH 12/21] fix(camelot): checksum addresses in buildV2PathQuery function --- packages/camelot/src/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/camelot/src/utils.ts b/packages/camelot/src/utils.ts index 1629a7a6d..e2430cefe 100644 --- a/packages/camelot/src/utils.ts +++ b/packages/camelot/src/utils.ts @@ -1,5 +1,5 @@ import type { ActionParams, FilterOperator } from '@rabbitholegg/questdk' -import type { Address, Hash } from 'viem' +import { type Address, type Hash, getAddress } from 'viem' export enum Tokens { ARB = '0x912CE59144191C1204E64559FE8253a0e49E6548', @@ -66,11 +66,11 @@ export const buildV2PathQuery = (tokenIn?: string, tokenOut?: string) => { const conditions: FilterOperator[] = [] if (tokenIn) { - conditions.push({ $first: tokenIn }) + conditions.push({ $first: getAddress(tokenIn) }) } if (tokenOut) { - conditions.push({ $last: tokenOut }) + conditions.push({ $last: getAddress(tokenOut) }) } return { From ff9f4e7d429396de08ec80a2000c0fa23fef9baa Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 12:34:47 -0800 Subject: [PATCH 13/21] fix(camelot): use weth address when token out is ether on v2 --- packages/camelot/src/Camelot.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/camelot/src/Camelot.ts b/packages/camelot/src/Camelot.ts index ed836440d..a5e830e97 100644 --- a/packages/camelot/src/Camelot.ts +++ b/packages/camelot/src/Camelot.ts @@ -33,8 +33,6 @@ export const swap = async ( const tokenInOrWeth = ethUsedIn ? Tokens.WETH : tokenIn const tokenOutOrWeth = ethUsedOut ? Tokens.WETH : tokenOut - console.log(buildV3PathQuery(tokenInOrWeth, tokenOutOrWeth)) - return compressJson({ chainId: chainId, from: recipient, @@ -51,7 +49,7 @@ export const swap = async ( { // camelotV2 swap $abi: CAMELOT_V2_ABI, - path: buildV2PathQuery(tokenInOrWeth, tokenOut), + path: buildV2PathQuery(tokenInOrWeth, tokenOutOrWeth), amountOutMin: amountOut, amountIn: ethUsedIn ? undefined : amountIn, }, From c41b837659861c5d28a341aae8d24efb8ca55254 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 13:43:13 -0800 Subject: [PATCH 14/21] test(camelot): add tests for output function v3 --- packages/camelot/src/test-setup.ts | 4 +++ packages/camelot/src/test-transactions.ts | 40 +++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/packages/camelot/src/test-setup.ts b/packages/camelot/src/test-setup.ts index 68b358d9b..c296212e5 100644 --- a/packages/camelot/src/test-setup.ts +++ b/packages/camelot/src/test-setup.ts @@ -7,6 +7,8 @@ import { V2_TOKENS_TO_ETH, V3_SWAP_ETH, V3_TOKEN_TO_ETH, + V3_EXACT_OUTPUT_SINGLE, + V3_EXACT_OUTPUT, PARASWAP_SIMPLESWAP, PARASWAP_MULTISWAP, PARASWAP_MEGASWAP, @@ -22,6 +24,8 @@ export const passingTestCases = [ createTestCase(V2_TOKENS_TO_ETH, 'when swapping Tokens for ETH V2'), createTestCase(V3_SWAP_ETH, 'when swapping ETH for tokens V3'), createTestCase(V3_TOKEN_TO_ETH, 'when swapping Tokens for ETH V3'), + createTestCase(V3_EXACT_OUTPUT_SINGLE, 'when swapping Tokens for ETH V3 (exactOutputSingle)'), + createTestCase(V3_EXACT_OUTPUT, 'when swapping Tokens V3 (exactOutput)'), createTestCase(PARASWAP_SIMPLESWAP, 'for simple swap'), createTestCase(PARASWAP_MULTISWAP, 'for multi swap'), createTestCase(PARASWAP_MEGASWAP, 'for mega swap'), diff --git a/packages/camelot/src/test-transactions.ts b/packages/camelot/src/test-transactions.ts index 388a99336..02514462f 100644 --- a/packages/camelot/src/test-transactions.ts +++ b/packages/camelot/src/test-transactions.ts @@ -106,6 +106,46 @@ export const V3_TOKEN_TO_ETH: TestParams = { }, } +export const V3_EXACT_OUTPUT_SINGLE: TestParams = { + transaction: { + chainId: 42161, + from: '0x7f8ce47c5eee99ff20adccfd951a94441f85b18b', + hash: '0x179c7bc15808de0861c8ab4bf4bfbe3906bf29d1ca06a1749cc75605ab7f6415', + input: + '0xdb3e2198000000000000000000000000912ce59144191c1204e64559fe8253a0e49e654800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000000000006580000000000000000000000007f8ce47c5eee99ff20adccfd951a94441f85b18b00000000000000000000000000000000000000000000000000000000658f2ff800000000000000000000000000000000000000000000000018222b4950678000000000000000000000000000000000000000000000000095943820bd24e000000000000000000000000000000000000000000000000000000000000000000000', + to: '0x1f721e2e82f6676fce4ea07a5958cf098d339e18', + value: '0', + }, + params: { + chainId: ARBITRUM_CHAIN_ID, + tokenIn: Tokens.ARB, + tokenOut: Tokens.ETH, + amountIn: GreaterThanOrEqual(parseUnits('2758', 18)), + amountOut: GreaterThanOrEqual(parseEther('1.379')), + recipient: '0x7f8ce47c5eee99ff20adccfd951a94441f85b18b', + }, +} + +export const V3_EXACT_OUTPUT: TestParams = { + transaction: { + chainId: 42161, + from: '0x22e798f9440f563b92aae24e94c75dfa499e3d3e', + hash: '0x5bd8acf55b1f4aab648bb91c9b67f048b88ed0757ed9c0bbf0eef82570aff446', + input: + '0xf28c0498000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000022e798f9440f563b92aae24e94c75dfa499e3d3e00000000000000000000000000000000000000000000000000000000658a06b200000000000000000000000000000000000000000000000f013b9e80f45f8000000000000000000000000000000000000000000000000000000000010268f7b3000000000000000000000000000000000000000000000000000000000000003cf97f4df75117a78c1a5a0dbb814af92458539fb482af49447d8a07e3bd95bd0d56f35241523fbab1ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000', + to: '0x1f721e2e82f6676fce4ea07a5958cf098d339e18', + value: '0', + }, + params: { + chainId: ARBITRUM_CHAIN_ID, + tokenIn: Tokens.USDCE, + tokenOut: '0xf97f4df75117a78c1a5a0dbb814af92458539fb4', + amountIn: GreaterThanOrEqual(parseUnits('4317', 6)), + amountOut: GreaterThanOrEqual(parseUnits('275', 18)), + recipient: '0x22e798f9440f563b92aae24e94c75dfa499e3d3e', + }, +} + export const PARASWAP_MULTISWAP: TestParams = { transaction: { chainId: 42161, From e856ff9af6bf1e5081613d5842049cc4975bba8c Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 13:43:31 -0800 Subject: [PATCH 15/21] refactor(camelot): adjust filter test --- packages/camelot/src/Camelot.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camelot/src/Camelot.test.ts b/packages/camelot/src/Camelot.test.ts index 7f3a69e0a..a845447b9 100644 --- a/packages/camelot/src/Camelot.test.ts +++ b/packages/camelot/src/Camelot.test.ts @@ -74,11 +74,11 @@ describe('Given the camelot plugin', () => { $and: [ { $regex: - '^0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + '^0x82af49447d8a07e3bd95bd0d56f35241523fbab1', }, { $regex: - '82af49447d8a07e3bd95bd0d56f35241523fbab1$', + 'fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9$', }, ], }, From 7487b206fc7653828686217860e68bc7a36a560d Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 13:43:58 -0800 Subject: [PATCH 16/21] fix(camelot): reverse tokens in v3 path query --- packages/camelot/src/Camelot.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/camelot/src/Camelot.ts b/packages/camelot/src/Camelot.ts index a5e830e97..0e32090c2 100644 --- a/packages/camelot/src/Camelot.ts +++ b/packages/camelot/src/Camelot.ts @@ -65,7 +65,8 @@ export const swap = async ( tokenOut: tokenOutOrWeth, }, { - path: buildV3PathQuery(tokenInOrWeth, tokenOutOrWeth), + // exact output has the reverse structure (tokenOut first) + path: buildV3PathQuery(tokenOutOrWeth, tokenInOrWeth), }, ], amountInMaximum: amountIn, From 4e4c63aa509b40079aad5db15092d1009a0746ba Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 13:50:31 -0800 Subject: [PATCH 17/21] test(camelot): add additional tests --- packages/camelot/src/test-setup.ts | 74 +++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/packages/camelot/src/test-setup.ts b/packages/camelot/src/test-setup.ts index c296212e5..a116a2b7a 100644 --- a/packages/camelot/src/test-setup.ts +++ b/packages/camelot/src/test-setup.ts @@ -24,7 +24,10 @@ export const passingTestCases = [ createTestCase(V2_TOKENS_TO_ETH, 'when swapping Tokens for ETH V2'), createTestCase(V3_SWAP_ETH, 'when swapping ETH for tokens V3'), createTestCase(V3_TOKEN_TO_ETH, 'when swapping Tokens for ETH V3'), - createTestCase(V3_EXACT_OUTPUT_SINGLE, 'when swapping Tokens for ETH V3 (exactOutputSingle)'), + createTestCase( + V3_EXACT_OUTPUT_SINGLE, + 'when swapping Tokens for ETH V3 (exactOutputSingle)', + ), createTestCase(V3_EXACT_OUTPUT, 'when swapping Tokens V3 (exactOutput)'), createTestCase(PARASWAP_SIMPLESWAP, 'for simple swap'), createTestCase(PARASWAP_MULTISWAP, 'for multi swap'), @@ -32,24 +35,81 @@ export const passingTestCases = [ createTestCase(PARASWAP_UNISWAP, 'for directUniV3Swap'), createTestCase(PARASWAP_BALANCER, 'for directBalancerV2'), createTestCase(PARASWAP_CURVE, 'for directCurveV2Swap'), + createTestCase(V2_SWAP_ETH, 'any/any V2', { + tokenIn: undefined, + tokenOut: undefined, + amountIn: undefined, + amountOut: undefined, + }), + createTestCase(V3_TOKEN_TO_ETH, 'any/any V2', { + tokenIn: undefined, + tokenOut: undefined, + amountIn: undefined, + amountOut: undefined, + }), + createTestCase(PARASWAP_SIMPLESWAP, 'any/any V2', { + tokenIn: undefined, + tokenOut: undefined, + amountIn: undefined, + amountOut: undefined, + }), ] export const failingTestCases = [ createTestCase(V2_SWAP_TOKENS, 'when chainId is incorrect', { chainId: 10 }), - createTestCase(V2_SWAP_TOKENS, 'when tokenIn is incorrect', { + createTestCase(V2_SWAP_TOKENS, 'when tokenIn is incorrect (V2)', { tokenIn: Tokens.WETH, }), - createTestCase(V2_SWAP_TOKENS, 'when tokenOut is incorrect', { + createTestCase(V2_SWAP_TOKENS, 'when tokenOut is incorrect (V2)', { tokenOut: Tokens.WETH, }), - createTestCase(V2_SWAP_ETH, 'when amountIn is insufficient', { + createTestCase(V2_SWAP_ETH, 'when amountIn is insufficient (V2)', { amountIn: GreaterThanOrEqual(parseEther('0.1')), }), - createTestCase(V2_SWAP_TOKENS, 'when amountOut is insufficient', { + createTestCase(V2_SWAP_TOKENS, 'when amountOut is insufficient (V2)', { amountOut: GreaterThanOrEqual(parseUnits('20', 6)), }), - createTestCase(V2_SWAP_TOKENS, 'when recipient in incorrect', { + createTestCase(V2_SWAP_TOKENS, 'when recipient in incorrect (V2)', { recipient: '0x12e80D4b52023eDd8cB2294C6948D4c5d5D5D266', }), - createTestCase(SWAP_WRONG_PARTNER, 'if swap did not originate from camelot'), + createTestCase(PARASWAP_SIMPLESWAP, 'when tokenIn is incorrect (paraswap)', { + tokenIn: Tokens.WETH, + }), + createTestCase(PARASWAP_MULTISWAP, 'when tokenOut is incorrect (paraswap)', { + tokenOut: Tokens.WETH, + }), + createTestCase( + PARASWAP_MEGASWAP, + 'when amountIn is insufficient (paraswap)', + { + amountIn: GreaterThanOrEqual(parseEther('1000')), + }, + ), + createTestCase( + PARASWAP_MULTISWAP, + 'when amountOut is insufficient (paraswap)', + { + amountOut: GreaterThanOrEqual(parseEther('1000')), + }, + ), + createTestCase(V3_EXACT_OUTPUT, 'when tokenIn is incorrect (V3)', { + tokenIn: Tokens.WETH, + }), + createTestCase(V3_SWAP_ETH, 'when tokenOut is incorrect (V3)', { + tokenOut: Tokens.WETH, + }), + createTestCase(V3_TOKEN_TO_ETH, 'when amountIn is insufficient (V3)', { + amountIn: GreaterThanOrEqual(parseEther('1000')), + }), + createTestCase( + V3_EXACT_OUTPUT_SINGLE, + 'when amountOut is insufficient (V3)', + { + amountOut: GreaterThanOrEqual(parseEther('1000')), + }, + ), + createTestCase( + SWAP_WRONG_PARTNER, + 'if swap did not originate from camelot (paraswap)', + ), ] From 8ea997f89a18e9f6808c8aef8f5821ff199c0967 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 13:51:21 -0800 Subject: [PATCH 18/21] docs(camelot): update readme --- packages/camelot/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/camelot/README.md b/packages/camelot/README.md index 6d98bace0..18d329b95 100644 --- a/packages/camelot/README.md +++ b/packages/camelot/README.md @@ -2,4 +2,6 @@ Camelot uses a few different methods to swap, which we will capture with this pl V2 Mode - This is the native swap of the camelot dapp +V3 Mode - The latest native swap contracts + Aggregator Mode - This routes the swap through either paraswap, or open ocean. \ No newline at end of file From d1b76cb8aa32dd9a67b653e3d1c78ecda861878c Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 13:51:50 -0800 Subject: [PATCH 19/21] chore(pnpm): format --- packages/camelot/src/Camelot.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camelot/src/Camelot.ts b/packages/camelot/src/Camelot.ts index 0e32090c2..4a1f21265 100644 --- a/packages/camelot/src/Camelot.ts +++ b/packages/camelot/src/Camelot.ts @@ -65,7 +65,7 @@ export const swap = async ( tokenOut: tokenOutOrWeth, }, { - // exact output has the reverse structure (tokenOut first) + // exact output has the reverse structure (tokenOut first) path: buildV3PathQuery(tokenOutOrWeth, tokenInOrWeth), }, ], From 152b99cca2e9d2c5c434c5041bf2c9c55015cc86 Mon Sep 17 00:00:00 2001 From: Mmackz Date: Fri, 29 Dec 2023 13:52:38 -0800 Subject: [PATCH 20/21] chore(pnpm): generate changes --- .changeset/chilled-snakes-greet.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chilled-snakes-greet.md diff --git a/.changeset/chilled-snakes-greet.md b/.changeset/chilled-snakes-greet.md new file mode 100644 index 000000000..291289d9a --- /dev/null +++ b/.changeset/chilled-snakes-greet.md @@ -0,0 +1,5 @@ +--- +"@rabbitholegg/questdk-plugin-camelot": minor +--- + +add support for swaps on camelot V3 From cdd30133e465e93013c13a3f07a4f0477c21879c Mon Sep 17 00:00:00 2001 From: Mmackz Date: Tue, 2 Jan 2024 20:56:58 -0800 Subject: [PATCH 21/21] refactor(camelot): use constant for paraswap partner address --- packages/camelot/src/Camelot.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/camelot/src/Camelot.ts b/packages/camelot/src/Camelot.ts index 4a1f21265..14e045605 100644 --- a/packages/camelot/src/Camelot.ts +++ b/packages/camelot/src/Camelot.ts @@ -5,7 +5,6 @@ import { } from '@rabbitholegg/questdk' import { type Address } from 'viem' import { CHAIN_ID_ARRAY, ARBITRUM_CHAIN_ID } from './chain-ids' -import { DEFAULT_TOKEN_LIST } from './contract-addresses' import { buildV2PathQuery, buildV3PathQuery, Tokens } from './utils' import { CAMELOT_V2_ABI, @@ -14,12 +13,15 @@ import { PARASWAP_ABI, } from './abi' import { + DEFAULT_TOKEN_LIST, CAMELOT_V2_ROUTER, CAMELOT_V3_ROUTER, PARASWAP_ROUTER, INTERNAL_ETH_ADDRESS, } from './contract-addresses' +const PARASWAP_PARTNER = '0x353D2d14Bb674892910685520Ac040f560CcBC06' + export const swap = async ( swap: SwapActionParams, ): Promise => { @@ -102,7 +104,7 @@ export const swap = async ( fromAmount: amountIn, toAmount: amountOut, toToken: tokenOutOrEth, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + partner: PARASWAP_PARTNER, }, }, { @@ -116,7 +118,7 @@ export const swap = async ( to: tokenOutOrEth, }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + partner: PARASWAP_PARTNER, }, }, { @@ -134,7 +136,7 @@ export const swap = async ( }, }, }, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + partner: PARASWAP_PARTNER, }, }, { @@ -143,7 +145,7 @@ export const swap = async ( assets: buildV2PathQuery(tokenIn, tokenOut), fromAmount: amountIn, toAmount: amountOut, - partner: '0x353D2d14Bb674892910685520Ac040f560CcBC06', + partner: PARASWAP_PARTNER, }, }, ],