diff --git a/src/config/protocols.ts b/src/config/protocols.ts index bd58c2d..ec31a27 100644 --- a/src/config/protocols.ts +++ b/src/config/protocols.ts @@ -127,4 +127,13 @@ export const protocols: IProtocolDefinitionMap = { type: ProtocolType.DEX, actionTags: [ActionTags.SWAP], }, + camelot: { + identifier: "camelot", + name: "Camelot", + twitter: "https://x.com/BreederDodo", + logo: "https://storage-mercle-prod.s3.amazonaws.com/public/protocol-logos/camelot.png", + website: "https://app.camelot.exchange/", + type: ProtocolType.DEX_AGGREGGATOR, + actionTags: [ActionTags.SWAP], + }, }; diff --git a/src/lib/Camelot/abis/CamelotYokRouter.json b/src/lib/Camelot/abis/CamelotYokRouter.json new file mode 100644 index 0000000..ca3741b --- /dev/null +++ b/src/lib/Camelot/abis/CamelotYokRouter.json @@ -0,0 +1,779 @@ +[ + { + "inputs": [ + { "internalType": "address[]", "name": "_adapters", "type": "address[]" }, + { + "internalType": "address[]", + "name": "_trustedTokens", + "type": "address[]" + }, + { "internalType": "address", "name": "_feeClaimer", "type": "address" }, + { + "internalType": "address", + "name": "_wrapped_native", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Recovered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "_newAdapters", + "type": "address[]" + } + ], + "name": "UpdatedAdapters", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_oldFeeClaimer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "_newFeeClaimer", + "type": "address" + } + ], + "name": "UpdatedFeeClaimer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_oldMinFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newMinFee", + "type": "uint256" + } + ], + "name": "UpdatedMinFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address[]", + "name": "_newTrustedTokens", + "type": "address[]" + } + ], + "name": "UpdatedTrustedTokens", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_tokenIn", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_tokenOut", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amountIn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_amountOut", + "type": "uint256" + } + ], + "name": "YakSwap", + "type": "event" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "ADAPTERS", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FEE_CLAIMER", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FEE_DENOMINATOR", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAINTAINER_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MIN_FEE", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NAME", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NATIVE", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "TRUSTED_TOKENS", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WNATIVE", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "adaptersCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addedMaintainer", + "type": "address" + } + ], + "name": "addMaintainer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amountIn", "type": "uint256" }, + { "internalType": "address", "name": "_tokenIn", "type": "address" }, + { "internalType": "address", "name": "_tokenOut", "type": "address" }, + { + "internalType": "address[]", + "name": "_trustedTokens", + "type": "address[]" + }, + { "internalType": "uint256", "name": "_maxSteps", "type": "uint256" } + ], + "name": "findBestPath", + "outputs": [ + { + "components": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "adapters", + "type": "address[]" + }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "internalType": "struct FormattedOffer", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amountIn", "type": "uint256" }, + { "internalType": "address", "name": "_tokenIn", "type": "address" }, + { "internalType": "address", "name": "_tokenOut", "type": "address" }, + { + "internalType": "address[]", + "name": "_trustedTokens", + "type": "address[]" + }, + { "internalType": "uint256", "name": "_maxSteps", "type": "uint256" }, + { "internalType": "uint256", "name": "_gasPrice", "type": "uint256" } + ], + "name": "findBestPathWithGas", + "outputs": [ + { + "components": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "adapters", + "type": "address[]" + }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "internalType": "struct FormattedOffer", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amountIn", "type": "uint256" }, + { "internalType": "address", "name": "_tokenIn", "type": "address" }, + { "internalType": "address", "name": "_tokenOut", "type": "address" }, + { "internalType": "uint8", "name": "_index", "type": "uint8" } + ], + "name": "queryAdapter", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "address", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amountIn", "type": "uint256" }, + { "internalType": "address", "name": "_tokenIn", "type": "address" }, + { "internalType": "address", "name": "_tokenOut", "type": "address" }, + { "internalType": "uint8[]", "name": "_options", "type": "uint8[]" } + ], + "name": "queryNoSplit", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "adapter", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "address", "name": "tokenIn", "type": "address" }, + { "internalType": "address", "name": "tokenOut", "type": "address" }, + { "internalType": "uint256", "name": "amountOut", "type": "uint256" } + ], + "internalType": "struct Query", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amountIn", "type": "uint256" }, + { "internalType": "address", "name": "_tokenIn", "type": "address" }, + { "internalType": "address", "name": "_tokenOut", "type": "address" } + ], + "name": "queryNoSplit", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "adapter", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "address", "name": "tokenIn", "type": "address" }, + { "internalType": "address", "name": "tokenOut", "type": "address" }, + { "internalType": "uint256", "name": "amountOut", "type": "uint256" } + ], + "internalType": "struct Query", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_tokenAddress", "type": "address" }, + { "internalType": "uint256", "name": "_tokenAmount", "type": "uint256" } + ], + "name": "recoverERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "recoverNative", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "removedMaintainer", + "type": "address" + } + ], + "name": "removeMaintainer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "_adapters", "type": "address[]" } + ], + "name": "setAdapters", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_wnative", "type": "address" } + ], + "name": "setAllowanceForWrapping", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_claimer", "type": "address" } + ], + "name": "setFeeClaimer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_fee", "type": "uint256" } + ], + "name": "setMinFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_trustedTokens", + "type": "address[]" + } + ], + "name": "setTrustedTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "uint256", "name": "amountOut", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { + "internalType": "address[]", + "name": "adapters", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + } + ], + "internalType": "struct Trade", + "name": "_trade", + "type": "tuple" + }, + { "internalType": "uint256", "name": "_fee", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" } + ], + "name": "swapNoSplit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "uint256", "name": "amountOut", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { + "internalType": "address[]", + "name": "adapters", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + } + ], + "internalType": "struct Trade", + "name": "_trade", + "type": "tuple" + }, + { "internalType": "uint256", "name": "_fee", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" } + ], + "name": "swapNoSplitFromETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "uint256", "name": "amountOut", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { + "internalType": "address[]", + "name": "adapters", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + } + ], + "internalType": "struct Trade", + "name": "_trade", + "type": "tuple" + }, + { "internalType": "uint256", "name": "_fee", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" } + ], + "name": "swapNoSplitToETH", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "uint256", "name": "amountOut", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { + "internalType": "address[]", + "name": "adapters", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + } + ], + "internalType": "struct Trade", + "name": "_trade", + "type": "tuple" + }, + { "internalType": "uint256", "name": "_fee", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" }, + { "internalType": "uint256", "name": "_deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "_v", "type": "uint8" }, + { "internalType": "bytes32", "name": "_r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "_s", "type": "bytes32" } + ], + "name": "swapNoSplitToETHWithPermit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "uint256", "name": "amountOut", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { + "internalType": "address[]", + "name": "adapters", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + } + ], + "internalType": "struct Trade", + "name": "_trade", + "type": "tuple" + }, + { "internalType": "uint256", "name": "_fee", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" }, + { "internalType": "uint256", "name": "_deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "_v", "type": "uint8" }, + { "internalType": "bytes32", "name": "_r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "_s", "type": "bytes32" } + ], + "name": "swapNoSplitWithPermit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "trustedTokensCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/src/lib/Camelot/abis/Router.json b/src/lib/Camelot/abis/Router.json new file mode 100644 index 0000000..3d6b584 --- /dev/null +++ b/src/lib/Camelot/abis/Router.json @@ -0,0 +1,285 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_factory", "type": "address" }, + { "internalType": "address", "name": "_WETH", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "WETH", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "tokenA", "type": "address" }, + { "internalType": "address", "name": "tokenB", "type": "address" }, + { + "internalType": "uint256", + "name": "amountADesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBDesired", + "type": "uint256" + }, + { "internalType": "uint256", "name": "amountAMin", "type": "uint256" }, + { "internalType": "uint256", "name": "amountBMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "addLiquidity", + "outputs": [ + { "internalType": "uint256", "name": "amountA", "type": "uint256" }, + { "internalType": "uint256", "name": "amountB", "type": "uint256" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { + "internalType": "uint256", + "name": "amountTokenDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { "internalType": "uint256", "name": "amountETHMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "addLiquidityETH", + "outputs": [ + { "internalType": "uint256", "name": "amountToken", "type": "uint256" }, + { "internalType": "uint256", "name": "amountETH", "type": "uint256" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" } + ], + "name": "getAmountsOut", + "outputs": [ + { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token1", "type": "address" }, + { "internalType": "address", "name": "token2", "type": "address" } + ], + "name": "getPair", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountA", "type": "uint256" }, + { "internalType": "uint256", "name": "reserveA", "type": "uint256" }, + { "internalType": "uint256", "name": "reserveB", "type": "uint256" } + ], + "name": "quote", + "outputs": [ + { "internalType": "uint256", "name": "amountB", "type": "uint256" } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "tokenA", "type": "address" }, + { "internalType": "address", "name": "tokenB", "type": "address" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" }, + { "internalType": "uint256", "name": "amountAMin", "type": "uint256" }, + { "internalType": "uint256", "name": "amountBMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "removeLiquidity", + "outputs": [ + { "internalType": "uint256", "name": "amountA", "type": "uint256" }, + { "internalType": "uint256", "name": "amountB", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { "internalType": "uint256", "name": "amountETHMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "removeLiquidityETH", + "outputs": [ + { "internalType": "uint256", "name": "amountToken", "type": "uint256" }, + { "internalType": "uint256", "name": "amountETH", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { "internalType": "uint256", "name": "amountETHMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "removeLiquidityETHSupportingFeeOnTransferTokens", + "outputs": [ + { "internalType": "uint256", "name": "amountETH", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { "internalType": "uint256", "name": "amountETHMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "bool", "name": "approveMax", "type": "bool" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "removeLiquidityETHWithPermit", + "outputs": [ + { "internalType": "uint256", "name": "amountToken", "type": "uint256" }, + { "internalType": "uint256", "name": "amountETH", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { "internalType": "uint256", "name": "amountETHMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "bool", "name": "approveMax", "type": "bool" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", + "outputs": [ + { "internalType": "uint256", "name": "amountETH", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "tokenA", "type": "address" }, + { "internalType": "address", "name": "tokenB", "type": "address" }, + { "internalType": "uint256", "name": "liquidity", "type": "uint256" }, + { "internalType": "uint256", "name": "amountAMin", "type": "uint256" }, + { "internalType": "uint256", "name": "amountBMin", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "bool", "name": "approveMax", "type": "bool" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "removeLiquidityWithPermit", + "outputs": [ + { "internalType": "uint256", "name": "amountA", "type": "uint256" }, + { "internalType": "uint256", "name": "amountB", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountOutMin", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "address", "name": "referrer", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "uint256", "name": "amountOutMin", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "address", "name": "referrer", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountIn", "type": "uint256" }, + { "internalType": "uint256", "name": "amountOutMin", "type": "uint256" }, + { "internalType": "address[]", "name": "path", "type": "address[]" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "address", "name": "referrer", "type": "address" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/src/lib/Camelot/abis/RouterV3.json b/src/lib/Camelot/abis/RouterV3.json new file mode 100644 index 0000000..f3274c3 --- /dev/null +++ b/src/lib/Camelot/abis/RouterV3.json @@ -0,0 +1,313 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_factory", "type": "address" }, + { "internalType": "address", "name": "_WNativeToken", "type": "address" }, + { "internalType": "address", "name": "_poolDeployer", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "WNativeToken", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "int256", "name": "amount0Delta", "type": "int256" }, + { "internalType": "int256", "name": "amount1Delta", "type": "int256" }, + { "internalType": "bytes", "name": "_data", "type": "bytes" } + ], + "name": "algebraSwapCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "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" + }, + { + "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" + }, + { + "inputs": [], + "name": "factory", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes[]", "name": "data", "type": "bytes[]" } + ], + "name": "multicall", + "outputs": [ + { "internalType": "bytes[]", "name": "results", "type": "bytes[]" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "poolDeployer", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "refundNativeToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "uint256", "name": "expiry", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermitAllowed", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "uint256", "name": "expiry", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermitAllowedIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermitIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, + { "internalType": "address", "name": "feeRecipient", "type": "address" } + ], + "name": "sweepTokenWithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" } + ], + "name": "unwrapWNativeToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, + { "internalType": "address", "name": "feeRecipient", "type": "address" } + ], + "name": "unwrapWNativeTokenWithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/src/lib/Camelot/abis/SwapRouter.json b/src/lib/Camelot/abis/SwapRouter.json new file mode 100644 index 0000000..f3e41de --- /dev/null +++ b/src/lib/Camelot/abis/SwapRouter.json @@ -0,0 +1,315 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_factory", "type": "address" }, + { "internalType": "address", "name": "_WNativeToken", "type": "address" }, + { "internalType": "address", "name": "_poolDeployer", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "WNativeToken", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "int256", "name": "amount0Delta", "type": "int256" }, + { "internalType": "int256", "name": "amount1Delta", "type": "int256" }, + { "internalType": "bytes", "name": "_data", "type": "bytes" } + ], + "name": "algebraSwapCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "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": "deployer", "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": "deployer", "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": "payable", + "type": "function" + }, + { + "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": "address", "name": "deployer", "type": "address" }, + { "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" + }, + { + "inputs": [], + "name": "factory", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes[]", "name": "data", "type": "bytes[]" } + ], + "name": "multicall", + "outputs": [ + { "internalType": "bytes[]", "name": "results", "type": "bytes[]" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "poolDeployer", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "refundNativeToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "uint256", "name": "expiry", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermitAllowed", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "uint256", "name": "expiry", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermitAllowedIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "selfPermitIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, + { "internalType": "address", "name": "feeRecipient", "type": "address" } + ], + "name": "sweepTokenWithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" } + ], + "name": "unwrapWNativeToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amountMinimum", "type": "uint256" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "feeBips", "type": "uint256" }, + { "internalType": "address", "name": "feeRecipient", "type": "address" } + ], + "name": "unwrapWNativeTokenWithFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/src/lib/Camelot/contracts.ts b/src/lib/Camelot/contracts.ts new file mode 100644 index 0000000..f40052a --- /dev/null +++ b/src/lib/Camelot/contracts.ts @@ -0,0 +1,87 @@ +import { ethers } from "ethers"; +import { CHAIN_ID, LISTEN_FOR_TRANSACTIONS } from "../../enums"; +import { IProtocolContractDefinitions } from "../../types"; +import SwapRouterAbi from "./abis/SwapRouter.json"; +import CamelotYokRouterAbi from "./abis/CamelotYokRouter.json"; +import RouterAbi from "./abis/Router.json"; +import RouterV3Abi from "./abis/RouterV3.json"; + +enum CONTRACT_ENUM { + SWAP_ROUTER = "SWAP_ROUTER", + ROUTER = "ROUTER", + ROUTER_V3 = "ROUTER_V3", + CAMELOT_YOK_ROUTER = "CAMELOT_YOK_ROUTER" +} +export enum EVENT_ENUM { + V2_SWAP = "0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822", + V3_SWAP = "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67", + YAK_SWAP = "0x9fc8352e52998db4087d5e6e1c1aafa38788e749e5d7a24f5cb230f737954402" +} +const contracts: IProtocolContractDefinitions = { + [CONTRACT_ENUM.SWAP_ROUTER]: { + interface: new ethers.Interface(SwapRouterAbi), + deployments: { + [CHAIN_ID.ARBITRUM]: { + address: "0xa555826C9a26E13238F657dB06E0A02431839Ef5", + listenForTransactions: [LISTEN_FOR_TRANSACTIONS.INCOMING], + } + }, + events: { + [EVENT_ENUM.V2_SWAP]: { + abi: new ethers.Interface([ + "event Swap(address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to)", + ]), + }, + }, + }, + [CONTRACT_ENUM.ROUTER]: { + interface: new ethers.Interface(RouterAbi), + deployments: { + [CHAIN_ID.ARBITRUM]: { + address: "0xc873fEcbd354f5A56E00E710B90EF4201db2448d", + listenForTransactions: [LISTEN_FOR_TRANSACTIONS.INCOMING], + } + }, + events: { + [EVENT_ENUM.V3_SWAP]: { + abi: new ethers.Interface([ + "event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)", + ]), + }, + }, + }, + [CONTRACT_ENUM.ROUTER_V3]: { + interface: new ethers.Interface(RouterV3Abi), + deployments: { + [CHAIN_ID.ARBITRUM]: { + address: "0x1F721E2E82F6676FCE4eA07A5958cF098D339e18", + listenForTransactions: [LISTEN_FOR_TRANSACTIONS.INCOMING], + } + }, + events: { + [EVENT_ENUM.V3_SWAP]: { + abi: new ethers.Interface([ + "event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)", + ]), + }, + }, + }, + [CONTRACT_ENUM.CAMELOT_YOK_ROUTER]: { + interface: new ethers.Interface(CamelotYokRouterAbi), + deployments: { + [CHAIN_ID.ARBITRUM]: { + address: "0x99D4e80DB0C023EFF8D25d8155E0dCFb5aDDeC5E", + listenForTransactions: [LISTEN_FOR_TRANSACTIONS.INCOMING], + } + }, + events: { + [EVENT_ENUM.YAK_SWAP]: { + abi: new ethers.Interface([ + "event YakSwap (address indexed _tokenIn, address indexed _tokenOut, uint256 _amountIn, uint256 _amountOut)", + ]), + }, + }, + } +}; + +export { CONTRACT_ENUM, contracts }; diff --git a/src/lib/Camelot/index.ts b/src/lib/Camelot/index.ts new file mode 100644 index 0000000..acbd489 --- /dev/null +++ b/src/lib/Camelot/index.ts @@ -0,0 +1,71 @@ +import { protocols } from "../../config"; +import { ProtocolHelper } from "../../helpers"; +import { + IProtocolContractDefinitions, + IProtocolParserExport, + ITransaction, + ITransactionAction, +} from "../../types"; +import { CONTRACT_ENUM, contracts } from "./contracts"; +import { CamelotParser } from "./parsers"; + +export default class Camelot implements IProtocolParserExport { + public readonly protocolIdentifier: string; + + constructor() { + this.protocolIdentifier = protocols.camelot.identifier; + } + + public async parseTransaction( + transaction: ITransaction + ): Promise { + const actions: ITransactionAction[] = []; + + if ( + ProtocolHelper.txnToIsListenerContract( + transaction, + CONTRACT_ENUM.SWAP_ROUTER, + contracts + ) + ) { + return await CamelotParser.parseSwapRouterTransaction(transaction, CONTRACT_ENUM.SWAP_ROUTER); + } else if ( + ProtocolHelper.txnToIsListenerContract( + transaction, + CONTRACT_ENUM.ROUTER, + contracts + ) + ) { + return await CamelotParser.parseCamelotRouterTransaction( + transaction, + CONTRACT_ENUM.ROUTER + ); + } else if ( + ProtocolHelper.txnToIsListenerContract( + transaction, + CONTRACT_ENUM.ROUTER_V3, + contracts + ) + ) { + return await CamelotParser.parseSwapRouterTransaction( + transaction, + CONTRACT_ENUM.ROUTER_V3 + ); + } else if ( + ProtocolHelper.txnToIsListenerContract( + transaction, + CONTRACT_ENUM.CAMELOT_YOK_ROUTER, + contracts + ) + ) { + return await CamelotParser.parseYakRouterTransaction( + transaction + ); + } + return actions; + } + + public getProtocolContracts(): IProtocolContractDefinitions { + return contracts; + } +} diff --git a/src/lib/Camelot/parsers.ts b/src/lib/Camelot/parsers.ts new file mode 100644 index 0000000..e046982 --- /dev/null +++ b/src/lib/Camelot/parsers.ts @@ -0,0 +1,628 @@ +import { ethers } from "ethers"; +import { ACTION_ENUM } from "../../enums"; +import { ProtocolHelper } from "../../helpers"; +import { + ISingleSwapAction, + ITransaction, + ITransactionAction, +} from "../../types"; +import { CONTRACT_ENUM, contracts, EVENT_ENUM } from "./contracts"; + +interface IV3SwapParams { + tokenIn: string; + tokenOut: string; + amountIn?: string; + amountInMaximum?: string; + amountOut?: string; + amountOutMinimum?: string; + recipient?: string; + path?: string; +} + +enum CONTRACT_FUNCTION_NAMES { + SWAP_ETH_FOR_EXACT_TOKENS = "swapETHForExactTokens", + SWAP_EXACT_ETH_FOR_TOKENS = "swapExactETHForTokens", + SWAP_EXACT_ETH_FOR_TOKENS_SUPPORTING_FEE = "swapExactETHForTokensSupportingFeeOnTransferTokens", + SWAP_EXACT_TOKENS_FOR_ETH = "swapExactTokensForETH", + SWAP_EXACT_TOKENS_FOR_ETH_SUPPORTING_FEE = "swapExactTokensForETHSupportingFeeOnTransferTokens", + SWAP_EXACT_TOKENS_FOR_TOKENS = "swapExactTokensForTokens", + SWAP_EXACT_TOKENS_FOR_TOKENS_SUPPORTING_FEE = "swapExactTokensForTokensSupportingFeeOnTransferTokens", + SWAP_TOKENS_FOR_EXACT_ETH = "swapTokensForExactETH", + SWAP_TOKENS_FOR_EXACT_TOKENS = "swapTokensForExactTokens", +} + +export class CamelotParser { + private static readonly EXACT_INPUT_SINGLE = "exactInputSingle"; + private static readonly EXACT_OUTPUT_SINGLE = "exactOutputSingle"; + private static readonly EXACT_INPUT = "exactInput"; + private static readonly EXACT_OUTPUT = "exactOutput"; + + public static async parseCamelotRouterTransaction( + transaction: ITransaction, + contractName + ): Promise { + const actions: ITransactionAction[] = []; + + const parsedTxn = ProtocolHelper.parseTransaction( + transaction, + contractName, + contracts + ); + + if (!parsedTxn) { + return actions; + } + + switch (parsedTxn.name) { + case CONTRACT_FUNCTION_NAMES.SWAP_ETH_FOR_EXACT_TOKENS: + actions.push(this.handleSwapETHForExactTokens(transaction, parsedTxn)); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_EXACT_ETH_FOR_TOKENS: + actions.push(this.handleSwapExactETHForTokens(transaction, parsedTxn)); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_EXACT_ETH_FOR_TOKENS_SUPPORTING_FEE: + actions.push( + this.handleSwapExactETHForTokensWithFee(transaction, parsedTxn) + ); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_EXACT_TOKENS_FOR_ETH: + actions.push(this.handleSwapExactTokensForETH(transaction, parsedTxn)); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_EXACT_TOKENS_FOR_ETH_SUPPORTING_FEE: + actions.push( + this.handleSwapExactTokensForETHWithFee(transaction, parsedTxn) + ); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_EXACT_TOKENS_FOR_TOKENS: + actions.push( + this.handleSwapExactTokensForTokens(transaction, parsedTxn) + ); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_EXACT_TOKENS_FOR_TOKENS_SUPPORTING_FEE: + actions.push( + this.handleSwapExactTokensForTokensWithFee(transaction, parsedTxn) + ); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_TOKENS_FOR_EXACT_ETH: + actions.push(this.handleSwapTokensForExactETH(transaction, parsedTxn)); + break; + + case CONTRACT_FUNCTION_NAMES.SWAP_TOKENS_FOR_EXACT_TOKENS: + actions.push( + this.handleSwapTokensForExactTokens(transaction, parsedTxn) + ); + break; + + default: + break; + } + return actions; + } + + public static async parseYakRouterTransaction( + transaction: ITransaction + ): Promise { + const actions: ITransactionAction[] = []; + const parsedTxn = ProtocolHelper.parseTransaction( + transaction, + CONTRACT_ENUM.CAMELOT_YOK_ROUTER, + contracts + ); + + const swapLogs = transaction.logs.filter( + (log) => log.topics[0] === EVENT_ENUM.YAK_SWAP + ); + + if(!swapLogs) return actions; + for (const swapLog of swapLogs) { + const parsedLog = ProtocolHelper.parseLog( + swapLog, + contracts.CAMELOT_YOK_ROUTER.events[EVENT_ENUM.YAK_SWAP] + ); + actions.push( + { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken: parsedLog.args._tokenIn, + toToken: parsedLog.args._tokenOut, + fromAmount: parsedLog.args._amountIn.toString(), + toAmount: parsedLog.args._amountOut.toString(), + sender: transaction.from, + recipient: parsedTxn?.args?._to, + } + ) + } + return actions; + } + + private static getTokenTransfersFromCallData( + parsedTxn: ethers.TransactionDescription + ) { + const fromToken = parsedTxn.args.path[0]; + const toToken = parsedTxn.args.path[parsedTxn.args.path.length - 1]; + return { fromToken, toToken }; + } + + private static getSwapLogEvents(transaction: ITransaction) { + const swapLogs = transaction.logs.filter( + (log) => log.topics[0] === EVENT_ENUM.V2_SWAP + ); + + if (swapLogs.length > 1) { + swapLogs.sort((a, b) => a.logIndex - b.logIndex); + } + return swapLogs; + } + + private static handleSwapETHForExactTokens( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: transaction.value.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapExactETHForTokens( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: transaction.value.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapExactETHForTokensWithFee( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: transaction.value.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapExactTokensForETH( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const swapLogs = this.getSwapLogEvents(transaction); + + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: parsedTxn.args.amountIn.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapExactTokensForETHWithFee( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: parsedTxn.args.amountIn.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapExactTokensForTokens( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: parsedTxn.args.amountIn.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapExactTokensForTokensWithFee( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: parsedTxn.args.amountIn.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapTokensForExactETH( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + const fromTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === fromToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: fromTxn.value.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + private static handleSwapTokensForExactTokens( + transaction: ITransaction, + parsedTxn: ethers.TransactionDescription + ): ISingleSwapAction { + const { fromToken, toToken } = + this.getTokenTransfersFromCallData(parsedTxn); + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const toTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + const fromTxn = erc20TransferLogs.find((log) => { + return log.contractAddress.toLowerCase() === fromToken.toLowerCase(); + }); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: fromTxn.value.toString(), + toAmount: toTxn.value.toString(), + sender: transaction.from, + recipient: parsedTxn.args.to, + }; + } + + public static async parseSwapRouterTransaction( + transaction: ITransaction, + contractName + ): Promise { + const parsedTxn = contracts[contractName].interface.parseTransaction({ + data: transaction.data, + }); + + if (!parsedTxn) return []; + + if (parsedTxn.name !== "multicall") { + const action = await this.createV3SwapAction(parsedTxn, transaction); + return action ? [action] : []; + } + + const calls = + parsedTxn.args.length === 2 ? parsedTxn.args[1] : parsedTxn.args[0]; + const result: ITransactionAction[] = []; + + for (const callData of calls) { + const subParsedTxn = contracts[ + CONTRACT_ENUM.SWAP_ROUTER + ].interface.parseTransaction({ data: callData }); + if (!subParsedTxn) continue; + const action = await this.createV3SwapAction(subParsedTxn, transaction); + if (action) { + result.push(action); + } + } + return this.removeDuplicateSwaps(result); + } + + private static removeDuplicateSwaps( + swaps: ITransactionAction[] + ): ITransactionAction[] { + const uniqueSwaps: ITransactionAction[] = []; + const seen = new Set(); + + for (const action of swaps) { + const singleSwapAction = action as ISingleSwapAction; + const key = `${singleSwapAction.type}-${singleSwapAction.fromToken.toLowerCase()}-${singleSwapAction.toToken.toLowerCase()}-${singleSwapAction.fromAmount}-${singleSwapAction.toAmount}-${singleSwapAction.sender.toLowerCase()}-${singleSwapAction.recipient.toLowerCase()}`; + + if (!seen.has(key)) { + seen.add(key); + uniqueSwaps.push(action); + } + } + + return uniqueSwaps; + } + + public static decodeV3Path(path: string): string[] { + if (!path.startsWith("0x")) return []; + + const cleanPath = path.slice(2); + const tokens: string[] = []; + + let i = 0; + while (i < cleanPath.length) { + const token = "0x" + cleanPath.slice(i, i + 40); + tokens.push(token.toLowerCase()); + + // Skip the token address (40 chars) and fee (6 chars) + i += 46; + } + + return tokens; + } + + private static async createV3SwapAction( + parsedTxn: ethers.TransactionDescription, + transaction: ITransaction + ): Promise { + const functionName = parsedTxn.name.toLowerCase(); + + const params: IV3SwapParams = { + tokenIn: parsedTxn.args.tokenIn || parsedTxn.args.params?.tokenIn, + tokenOut: parsedTxn.args.tokenOut || parsedTxn.args.params?.tokenOut, + amountIn: + parsedTxn.args.amountIn?.toString() || + parsedTxn.args.params?.amountIn?.toString(), + amountInMaximum: + parsedTxn.args.amountInMaximum?.toString() || + parsedTxn.args.params?.amountInMaximum?.toString(), + amountOut: + parsedTxn.args.amountOut?.toString() || + parsedTxn.args.params?.amountOut?.toString(), + amountOutMinimum: + parsedTxn.args.amountOutMinimum?.toString() || + parsedTxn.args.params?.amountOutMinimum?.toString(), + recipient: parsedTxn.args.recipient || parsedTxn.args.params?.recipient, + path: parsedTxn.args.path || parsedTxn.args.params?.path, + }; + const isExactInput = functionName.includes("exactinput"); + + if ( + functionName === this.EXACT_INPUT_SINGLE.toLowerCase() || + functionName === this.EXACT_OUTPUT_SINGLE.toLowerCase() + ) { + return this.createSingleV3SwapAction(params, isExactInput, transaction); + } + + if ( + functionName === this.EXACT_INPUT.toLowerCase() || + functionName === this.EXACT_OUTPUT.toLowerCase() + ) { + return this.createMultiHopV3SwapAction(params, isExactInput, transaction); + } + + return null; + } + + private static createSingleV3SwapAction( + params: IV3SwapParams, + isExactInput: boolean, + transaction: ITransaction + ): ISingleSwapAction { + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const fromTxnLogs = erc20TransferLogs.filter((log) => { + return log.contractAddress.toLowerCase() === params.tokenIn.toLowerCase(); + }); + const consolidatedFromTxns = this.consolidateFromTransactions(fromTxnLogs); + + const toTxnLogs = erc20TransferLogs.filter((log) => { + return ( + log.contractAddress.toLowerCase() === params.tokenOut.toLowerCase() + ); + }); + + const consolidatedToTxns = this.consolidateToTransactions(toTxnLogs); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken: params.tokenIn, + toToken: params.tokenOut, + fromAmount: consolidatedFromTxns[0].value.toString(), + toAmount: consolidatedToTxns[0].value.toString(), + sender: transaction.from, + recipient: params.recipient || transaction.from, + }; + } + + private static consolidateFromTransactions(transactions) { + const consolidatedMap = new Map(); + + transactions.forEach(({ fromAddress, value, contractAddress }) => { + const key = `${fromAddress}-${contractAddress}`; + + if (consolidatedMap.has(key)) { + consolidatedMap.get(key).value += value; + } else { + consolidatedMap.set(key, { fromAddress, value, contractAddress }); + } + }); + + return Array.from(consolidatedMap.values()); + } + + private static consolidateToTransactions(transactions) { + const consolidatedMap = new Map(); + + transactions.forEach(({ toAddress, value, contractAddress }) => { + const key = `${toAddress}-${contractAddress}`; + + if (consolidatedMap.has(key)) { + consolidatedMap.get(key).value += value; + } else { + consolidatedMap.set(key, { toAddress, value, contractAddress }); + } + }); + + return Array.from(consolidatedMap.values()); + } + + private static createMultiHopV3SwapAction( + params: IV3SwapParams, + isExactInput: boolean, + transaction: ITransaction + ): ISingleSwapAction | null { + if (!params.path) return null; + + const decodedPath = this.decodeV3Path(params.path); + if (decodedPath.length < 2) return null; + + const fromToken = isExactInput + ? decodedPath[0] + : decodedPath[decodedPath.length - 1]; + + const toToken = isExactInput + ? decodedPath[decodedPath.length - 1] + : decodedPath[0]; + + const erc20TransferLogs = ProtocolHelper.parseERC20TransferLogs( + transaction.logs + ); + + const fromTxn = erc20TransferLogs.filter((log) => { + return log.contractAddress.toLowerCase() === fromToken.toLowerCase(); + }); + + const consolidatedFromTxns = this.consolidateFromTransactions(fromTxn); + + const toTxn = erc20TransferLogs.filter((log) => { + return log.contractAddress.toLowerCase() === toToken.toLowerCase(); + }); + + const consolidatedToTxns = this.consolidateToTransactions(toTxn); + + return { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken, + toToken, + fromAmount: consolidatedFromTxns[0].value.toString(), + toAmount: consolidatedToTxns[0].value.toString(), + sender: transaction.from, + recipient: params.recipient || transaction.from, + }; + } +} diff --git a/src/lib/index.ts b/src/lib/index.ts index 93318f9..e45710a 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -13,6 +13,7 @@ import Sushiswap from "./Sushiswap" import Bungee from "./Bungee" import Curve from "./Curve" import Dodo from "./Dodo" +import Camelot from "./Camelot"; export const parsers = { [protocols.rhinofi.identifier]: new RhinoFi(), @@ -29,4 +30,5 @@ export const parsers = { [protocols.bungee.identifier]: new Bungee(), [protocols.curve.identifier]: new Curve(), [protocols.dodo.identifier]: new Dodo(), + [protocols.camelot.identifier]: new Camelot(), }; diff --git a/tests/lib/Camelot/data.ts b/tests/lib/Camelot/data.ts new file mode 100644 index 0000000..d9bdf06 --- /dev/null +++ b/tests/lib/Camelot/data.ts @@ -0,0 +1,91 @@ +import { ACTION_ENUM, CHAIN_ID } from "../../../src"; +import { IProtocolTestingData } from "../../../src/types"; + +export enum CAMELOT_VERSIONS { + V1 = "v1", +} + +export const thrusterData: IProtocolTestingData = { + [CAMELOT_VERSIONS.V1]: [ + { + txnHash: + "0xdf4a85d37f860c315f5e479fe1604af32a121a0e9792e0323f59b910e0f0d491", + chainId: CHAIN_ID.ARBITRUM, + emittedActions: [ + { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + toToken: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + fromAmount: "10000", + toAmount: "2722082672809", + sender: "0xd8dc994FE2b075c697e5051c89b713Bf15fa9294", + recipient: "0x0000000000000000000000000000000000000000", + }, + ], + }, + { + txnHash: + "0x1911c6f59a25b1025427345f144a96e5d23bad94c53483370ed1973c3e49a6b8", + chainId: CHAIN_ID.ARBITRUM, + emittedActions: [ + { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken: "0xCa4e51F6AD4AFd9d1068E5899De9dd7d73F3463D", + toToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + fromAmount: "12760540000000000655360", + toAmount: "46805434", + sender: "0x464fC339aDD314932920d3e060745bd7Ea3e92AD", + recipient: "0x464fC339aDD314932920d3e060745bd7Ea3e92AD", + }, + ], + }, + { + txnHash: + "0x1e9b3e3f6272c7f61e44818bb9334644df66a1dc2520bba6ea907358197ee35c", + chainId: CHAIN_ID.ARBITRUM, + emittedActions: [ + { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", + toToken: "0x7B5EB3940021Ec0e8e463D5dBB4B7B09a89DDF96", + fromAmount: "100000000", + toAmount: "19183849593628160202214", + sender: "0x622661aB4B6aB93c659e751F47eBB0c6e6ad9F48", + recipient: "0x622661aB4B6aB93c659e751F47eBB0c6e6ad9F48", + }, + ], + }, + { + txnHash: + "0x1af5ff2393b8a39f4a0a46dbc8970ce286317933bd73bf94c48ff3d4e4aa4efd", + chainId: CHAIN_ID.ARBITRUM, + emittedActions: [ + { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken: "0xACC51FFDeF63fB0c014c882267C3A17261A5eD50", + toToken: "0x68D6d2545f14751baF36c417c2CC7cdf8dA8a15b", + fromAmount: "282410767414298174531", + toAmount: "286499958724396728025", + sender: "0x46679587281C67ae9AD977c8d84A6b8a96a7CEC9", + recipient: "0x46679587281C67ae9AD977c8d84A6b8a96a7CEC9", + }, + ], + }, + { + txnHash: + "0x33f6f81118a1a1bd0207d53ec8ba53bbb3d0887334e5c29d0f2e6b2c188c1083", + chainId: CHAIN_ID.ARBITRUM, + emittedActions: [ + { + type: ACTION_ENUM.SINGLE_SWAP, + fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + toToken: "0xBfbCFe8873fE28Dfa25f1099282b088D52bbAD9C", + fromAmount: "358321144", + toAmount: "821932023742629259355", + sender: "0xcA8428042d7da7b6d6bee526A23e9899ADFfAb4c", + recipient: "0xcA8428042d7da7b6d6bee526A23e9899ADFfAb4c", + }, + ], + }, + ], +}; diff --git a/tests/lib/Camelot/index.test.ts b/tests/lib/Camelot/index.test.ts new file mode 100644 index 0000000..c890158 --- /dev/null +++ b/tests/lib/Camelot/index.test.ts @@ -0,0 +1,35 @@ +import chalk from "chalk"; +import { protocols } from "../../../src"; +import { ProtocolParserUtils } from "../../index"; +import { CAMELOT_VERSIONS, thrusterData } from "./data"; + +describe("CamelotParser", () => { + let utils: ProtocolParserUtils; + + beforeAll(async () => { + utils = new ProtocolParserUtils(protocols.camelot.identifier); + await utils.initialize(); + }); + + it("is correctly defined", () => { + utils.isValidProtocol(); + }); + + it("should parse v1 transactions correctly", async () => { + const v1Transactions = thrusterData[CAMELOT_VERSIONS.V1]; + + for (const transaction of v1Transactions) { + const actions = await utils.fetchAndParseTestTxn(transaction); + utils.assertTestTransactionForData(transaction, actions); + + console.log( + chalk.green( + "Successfully parsed transaction with actions:", + actions.map((action) => action.type).join(",") + ), + "and hash:", + transaction.txnHash + ); + } + }); +});