diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..fa29cdff --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +** \ No newline at end of file diff --git a/README.md b/README.md index 83b16c21..603279d2 100644 --- a/README.md +++ b/README.md @@ -113,10 +113,16 @@ export interface GasSettings { getGasPrice: () => Promise; } +export type IRouterMethods = { + [key in IRouterMethodName]: string; +}; + export interface CloneUniswapContractDetailsV2 { routerAddress: string; factoryAddress: string; pairAddress: string; + routerAbi?: JsonFragment[]; + routerMethods?: Partial; } export interface CloneUniswapContractDetailsV3 { @@ -178,7 +184,7 @@ export class UniswapPairSettings { cloneUniswapContractDetails?: CloneUniswapContractDetails | undefined; customNetwork?: CustomNetwork | undefined; }) { - this.slippage = settings?.slippage || 0.005; + this.slippage = settings?.slippage || 0.0005; this.deadlineMinutes = settings?.deadlineMinutes || 20; this.disableMultihops = settings?.disableMultihops || false; this.gasSettings = settings?.gasSettings; @@ -222,10 +228,10 @@ const uniswapPair = new UniswapPair({ ethereumAddress: '0xB1E6079212888f0bE0cf55874B2EB9d7a5e02cD9', chainId: ChainId.MAINNET, settings: new UniswapPairSettings({ - // if not supplied it will use `0.005` which is 0.5% + // if not supplied it will use `0.0005` which is 0.5% // please pass it in as a full number decimal so 0.7% // would be 0.007 - slippage: 0.005, + slippage: 0.0005, // if not supplied it will use 20 a deadline minutes deadlineMinutes: 20, // if not supplied it will try to use multihops @@ -260,10 +266,10 @@ const uniswapPair = new UniswapPair({ chainId: ChainId.MAINNET, providerUrl: YOUR_PROVIDER_URL, settings: new UniswapPairSettings({ - // if not supplied it will use `0.005` which is 0.5% + // if not supplied it will use `0.0005` which is 0.5% // please pass it in as a full number decimal so 0.7% // would be 0.007 - slippage: 0.005, + slippage: 0.0005, // if not supplied it will use 20 a deadline minutes deadlineMinutes: 20, // if not supplied it will try to use multihops @@ -297,10 +303,10 @@ const uniswapPair = new UniswapPair({ ethereumAddress: '0xB1E6079212888f0bE0cf55874B2EB9d7a5e02cD9', ethereumProvider: YOUR_WEB3_ETHERS_OR_CUSTOM_ETHEREUM_PROVIDER, settings: new UniswapPairSettings({ - // if not supplied it will use `0.005` which is 0.5% + // if not supplied it will use `0.0005` which is 0.5% // please pass it in as a full number decimal so 0.7% // would be 0.007 - slippage: 0.005, + slippage: 0.0005, // if not supplied it will use 20 a deadline minutes deadlineMinutes: 20, // if not supplied it will try to use multihops @@ -418,17 +424,31 @@ export interface TradeContext { // this will be formatted in readable number // so you can render straight out the box expectedConvertQuote: string; + // A portion of each trade goes to + // liquidity providers as a protocol of incentive + // v2 always = (0.3%) + // v3 always = (0.0%) for v3, use `liquidityProviderFeesV3` + liquidityProviderFee: string; // A portion of each trade goes to // liquidity providers as a protocol of incentive // v2 always = (0.3%) + // v3 always = (0.0%) for v3, use `liquidityProviderFeePercentsV3` + liquidityProviderFeePercent: number; + // A portion of each trade goes to + // liquidity providers as a protocol of incentive + // length must be routePath.length - 1 + // v2 always = [] // v3 depends on the fee amount sent on that pool // - low = 0.05% // - medium = 0.3% // - high = 1% - liquidityProviderFee: string; + // For multihop swap + // Sequence of Fees + // ex: USDC>USDT>WETH>WBTC => [(USDC-USDT fee), (USDT-WETH fee), (WETH-WBTC fee)] + liquidityProviderFeesV3: string[]; // A portion of each trade goes to // liquidity providers as a protocol of incentive - // v2 always = (0.3%) + // v2 always = [] // v3 depends on the fee amount sent on that pool // - low = 0.05% // - medium = 0.3% @@ -437,7 +457,7 @@ export interface TradeContext { // aka 0.05% = 0.0005 // 0.3% = 0.003 // 1% = 0.01 - liquidityProviderFeePercent: number; + liquidityProviderFeePercentsV3: number[]; // A unix datestamp in when this trade expires // if it does expiry while looking at it as long // as you are hooked onto `quoteChanged$` that will @@ -465,6 +485,7 @@ export interface TradeContext { routePathArray: string[]; uniswapVersion: UniswapVersion; liquidityProviderFee: number; + liquidityProviderFeesV3: number[]; quoteDirection: TradeDirection; }[]; // if the allowance approved for moving tokens is below the amount sending to the @@ -891,8 +912,10 @@ console.log(trade); minAmountConvertQuote: '0.014400465273974444', maximumSent: null, expectedConvertQuote: '0.014730394044348867', - liquidityProviderFee: '0.030000000000000000', - liquidityProviderFeePercent: 0.003, + liquidityProviderFee: 0, + liquidityProviderFeePercent: 0, + liquidityProviderFeesV3: ['0.030000000000000000','0.0000500000000000000','0.030000000000000000'], + liquidityProviderFeePercentsV3: [0.003, 0.0005, 0.003], tradeExpires: 1612189240, routePathTokenMap: [ { @@ -959,7 +982,8 @@ console.log(trade); '0x1985365e9f78359a9B6AD760e32412f4a445E862', ], uniswapVersion: 'v2', - liquidityProviderFee: 0.003 + liquidityProviderFee: 0.003, + liquidityProviderFeesV3: [], }, { expectedConvertQuote: '0.014606303273323544', @@ -1001,7 +1025,8 @@ console.log(trade); '0x1985365e9f78359a9B6AD760e32412f4a445E862', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.003, 0.0005, 0.003], }, { expectedConvertQuote: '0.013997397994408657', @@ -1043,7 +1068,8 @@ console.log(trade); '0x1985365e9f78359a9B6AD760e32412f4a445E862', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.003], }, { expectedConvertQuote: '0.000000298264906505', @@ -1085,7 +1111,8 @@ console.log(trade); '0x1985365e9f78359a9B6AD760e32412f4a445E862', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.003, 0.0005, 0.0005], }, ], hasEnoughAllowance: true, @@ -1165,8 +1192,10 @@ console.log(trade); minAmountConvertQuote: '446878.20758208', maximumSent: null, expectedConvertQuote: '449123.82671566', - liquidityProviderFee: '0.030000000000000000', - liquidityProviderFeePercent: 0.003, + liquidityProviderFee: 0, + liquidityProviderFeePercent: 0, + liquidityProviderFeesV3: ['0.030000000000000000'], + liquidityProviderFeePercentsV3: [0.003], tradeExpires: 1612189240, routePathTokenMap: [ { @@ -1242,7 +1271,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v2', - liquidityProviderFee: 0.003 + liquidityProviderFee: 0.003, + liquidityProviderFeesV3: [] }, { expectedConvertQuote: '446400.4834047', @@ -1276,7 +1306,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.01] }, { expectedConvertQuote: '446400.4834047', @@ -1310,7 +1341,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.01] }, { expectedConvertQuote: '446356.68778218', @@ -1344,7 +1376,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005] }, { expectedConvertQuote: '446356.68778218', @@ -1378,7 +1411,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005] }, { expectedConvertQuote: '446345.24608428', @@ -1412,7 +1446,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003] }, { expectedConvertQuote: '446345.24608428', @@ -1446,7 +1481,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003] }, { expectedConvertQuote: '347402.73288796', @@ -1480,7 +1516,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003] }, { expectedConvertQuote: '346246.52439964', @@ -1522,7 +1559,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '346246.52439964', @@ -1564,7 +1602,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '346246.52439964', @@ -1606,7 +1645,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '345845.48248206', @@ -1648,7 +1688,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '345845.48248206', @@ -1690,7 +1731,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '345845.48248206', @@ -1732,7 +1774,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '153353.27776886', @@ -1766,7 +1809,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.01] }, { expectedConvertQuote: '153171.51955671', @@ -1808,7 +1852,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.0005] }, { expectedConvertQuote: '153171.51955671', @@ -1850,7 +1895,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.0005] }, { expectedConvertQuote: '153171.51955671', @@ -1892,7 +1938,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.0005] }, { expectedConvertQuote: '153099.84287111', @@ -1934,7 +1981,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '153099.84287111', @@ -1976,7 +2024,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '153099.84287111', @@ -2018,7 +2067,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '10090.42827381', @@ -2060,7 +2110,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '10090.42827381', @@ -2102,7 +2153,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '176.25846115', @@ -2144,7 +2196,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '176.25846115', @@ -2186,7 +2239,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.00167195', @@ -2228,7 +2282,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.00167195', @@ -2270,7 +2325,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.003] }, { expectedConvertQuote: '0.00167195', @@ -2312,7 +2368,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.00167195', @@ -2346,7 +2403,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005] }, { expectedConvertQuote: '0.00167195', @@ -2388,7 +2446,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.00167195', @@ -2430,7 +2489,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.003] }, { expectedConvertQuote: '0.00167195', @@ -2472,7 +2532,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.003] }, { expectedConvertQuote: '0.00167195', @@ -2514,7 +2575,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.00167195', @@ -2556,7 +2618,8 @@ console.log(trade); '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, ], } @@ -2609,8 +2672,10 @@ console.log(trade); minAmountConvertQuote: '0.00022040807282109', maximumSent: null, expectedConvertQuote: '0.00022151807282109', - liquidityProviderFee: '0.03000000', - liquidityProviderFeePercent: 0.003, + liquidityProviderFee: 0, + liquidityProviderFeePercent: 0, + liquidityProviderFeesV3: ['0.03000000', '0.000500000', '0.03000000'], + liquidityProviderFeePercentsV3: [0.003, 0.0005, 0.003], tradeExpires: 1612189240, routePathTokenMap: [ { @@ -2690,7 +2755,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v2', - liquidityProviderFee: 0.003 + liquidityProviderFee: 0.003, + liquidityProviderFeesV3: [] }, { expectedConvertQuote: '0.00022151807282109', @@ -2732,7 +2798,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.003, 0.003, 0.003] }, { expectedConvertQuote: '0.000217400884509221', @@ -2758,7 +2825,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005] }, { expectedConvertQuote: '0.000216692105524981', @@ -2792,7 +2860,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003] }, { expectedConvertQuote: '0.000216165414503092', @@ -2834,7 +2903,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.0005] }, { expectedConvertQuote: '0.000216165414503092', @@ -2876,7 +2946,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.0005] }, { expectedConvertQuote: '0.000216165414503092', @@ -2918,7 +2989,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005, 0.0005] }, { expectedConvertQuote: '0.000216113740987982', @@ -2960,7 +3032,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.000216113740987982', @@ -3002,7 +3075,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.000216113740987982', @@ -3044,7 +3118,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.000207416610491746', @@ -3078,7 +3153,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.0005] }, { expectedConvertQuote: '0.000206879660311982', @@ -3120,7 +3196,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000206879660311982', @@ -3162,7 +3239,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000206879660311982', @@ -3204,7 +3282,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000206675889551395', @@ -3246,7 +3325,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000206675889551395', @@ -3288,7 +3368,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000206675889551395', @@ -3330,7 +3411,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000201332888879835', @@ -3372,7 +3454,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000201332888879835', @@ -3414,7 +3497,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.00000000454541448', @@ -3456,7 +3540,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.00000000454541448', @@ -3498,7 +3583,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000000004421040886', @@ -3532,7 +3618,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003] }, { expectedConvertQuote: '0.000000004406314787', @@ -3574,7 +3661,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.000000004406314787', @@ -3616,7 +3704,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.000000004406314787', @@ -3658,7 +3747,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.003] }, { expectedConvertQuote: '0.000000003689610342', @@ -3700,7 +3790,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000000003689610342', @@ -3742,7 +3833,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, { expectedConvertQuote: '0.000000003689610342', @@ -3784,7 +3876,8 @@ console.log(trade); '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', ], uniswapVersion: 'v3', - liquidityProviderFee: 0.0005 + liquidityProviderFee: 0, + liquidityProviderFeesV3: [0.0005, 0.003, 0.0005] }, ], hasEnoughAllowance: true, diff --git a/src/common/tokens/usdc.ts b/src/common/tokens/usdc.ts index 5e525d7c..bb673895 100644 --- a/src/common/tokens/usdc.ts +++ b/src/common/tokens/usdc.ts @@ -11,7 +11,7 @@ export class USDC { return { chainId: ChainId.MAINNET, contractAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - decimals: 18, + decimals: 6, symbol: 'USDC', name: 'USD Coin', }; diff --git a/src/common/tokens/usdt.ts b/src/common/tokens/usdt.ts index dce26953..56a46b3c 100644 --- a/src/common/tokens/usdt.ts +++ b/src/common/tokens/usdt.ts @@ -11,7 +11,7 @@ export class USDT { return { chainId: ChainId.MAINNET, contractAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - decimals: 18, + decimals: 6, symbol: 'USDT', name: 'Tether USD', }; diff --git a/src/common/utils/hexlify.ts b/src/common/utils/hexlify.ts index e84fae55..ff1605dc 100644 --- a/src/common/utils/hexlify.ts +++ b/src/common/utils/hexlify.ts @@ -7,5 +7,5 @@ import { hexlify as EthersHexlify } from 'ethers/lib/utils'; * @param value The value */ export function hexlify(value: BigNumber): string { - return EthersHexlify(EthersBigNumber.from(value.toFixed())); + return EthersHexlify(EthersBigNumber.from(value.toFixed(0))); } diff --git a/src/common/utils/is-same-address.spec.ts b/src/common/utils/is-same-address.spec.ts new file mode 100644 index 00000000..c5194427 --- /dev/null +++ b/src/common/utils/is-same-address.spec.ts @@ -0,0 +1,19 @@ +import { isSameAddress } from './is-same-address'; + +describe('isSameAddress', () => { + it('should return true if they are all valid ethereum addresses', () => { + expect(isSameAddress('0x45Cd08334aeedd8a06265B2Ae302E3597d8fAA28', '0x45cd08334aeedd8a06265b2ae302e3597d8faa28')).toEqual( + true + ); + }); + + it('should return false when one of them is a invalid address', () => { + expect(isSameAddress('0xC1912fEE45d61C87Cc5EA59DaE31190FFFFf232d', '0xaaaaa')).toEqual( + false + ); + }); + + it('should return false when both of them are invalid address', () => { + expect(isSameAddress('0x0', '0x1')).toEqual(false); + }); +}); diff --git a/src/common/utils/is-same-address.ts b/src/common/utils/is-same-address.ts new file mode 100644 index 00000000..718678cb --- /dev/null +++ b/src/common/utils/is-same-address.ts @@ -0,0 +1,10 @@ +import { ethers } from "ethers"; +import { removeEthFromContractAddress } from "../tokens/eth"; + +export function isSameAddress(address1: string, address2: string): boolean { + return ( + ethers.utils.isAddress(removeEthFromContractAddress(address1)) && + ethers.utils.isAddress(removeEthFromContractAddress(address2)) && + address1.toLowerCase() === address2.toLowerCase() + ); +} \ No newline at end of file diff --git a/src/factories/pair/models/clone-uniswap-contract-details.ts b/src/factories/pair/models/clone-uniswap-contract-details.ts index 1dfb198c..1bf04126 100644 --- a/src/factories/pair/models/clone-uniswap-contract-details.ts +++ b/src/factories/pair/models/clone-uniswap-contract-details.ts @@ -1,13 +1,19 @@ +import { JsonFragment } from '@ethersproject/abi'; +import { IRouterMethods } from '../../router/models/route-methods'; + export interface CloneUniswapContractDetailsV2 { routerAddress: string; factoryAddress: string; pairAddress: string; + routerAbi?: JsonFragment[]; + routerMethods?: Partial; } export interface CloneUniswapContractDetailsV3 { routerAddress: string; factoryAddress: string; quoterAddress: string; + routerAbi?: JsonFragment[]; } export interface CloneUniswapContractDetails { diff --git a/src/factories/pair/models/current-trade-context.ts b/src/factories/pair/models/current-trade-context.ts index 815a66cd..656f64cd 100644 --- a/src/factories/pair/models/current-trade-context.ts +++ b/src/factories/pair/models/current-trade-context.ts @@ -9,6 +9,7 @@ export interface CurrentTradeContext { fromToken: Token; toToken: Token; liquidityProviderFee: string; + liquidityProviderFeesV3: string[]; transaction: Transaction; routeText: string; tradeExpires: number; diff --git a/src/factories/pair/models/trade-context.ts b/src/factories/pair/models/trade-context.ts index cb48b728..a8ac5629 100644 --- a/src/factories/pair/models/trade-context.ts +++ b/src/factories/pair/models/trade-context.ts @@ -14,6 +14,8 @@ export interface TradeContext { expectedConvertQuote: string; liquidityProviderFee: string; liquidityProviderFeePercent: number; + liquidityProviderFeesV3: string[]; + liquidityProviderFeePercentsV3: number[]; tradeExpires: number; routePathTokenMap: Token[]; routeText: string; diff --git a/src/factories/pair/uniswap-pair.factory.ts b/src/factories/pair/uniswap-pair.factory.ts index ab8f32e4..7615b322 100644 --- a/src/factories/pair/uniswap-pair.factory.ts +++ b/src/factories/pair/uniswap-pair.factory.ts @@ -298,6 +298,7 @@ export class UniswapPairFactory { fromToken: trade.fromToken, toToken: trade.toToken, liquidityProviderFee: trade.liquidityProviderFee, + liquidityProviderFeesV3: trade.liquidityProviderFeesV3, transaction: trade.transaction, routeText: trade.routeText, tradeExpires: trade.tradeExpires, @@ -319,7 +320,7 @@ export class UniswapPairFactory { ); const bestRouteQuote = bestRouteQuotes.bestRouteQuote; - + const tradeContext: TradeContext = { uniswapVersion: bestRouteQuote.uniswapVersion, quoteDirection: direction, @@ -333,15 +334,20 @@ export class UniswapPairFactory { ? null : bestRouteQuote.expectedConvertQuoteOrTokenAmountInMaxWithSlippage, expectedConvertQuote: bestRouteQuote.expectedConvertQuote, - liquidityProviderFee: + liquidityProviderFee: direction === TradeDirection.input + ? baseConvertRequest.times(bestRouteQuote.uniswapVersion === UniswapVersion.v3 ? 0 : bestRouteQuote.liquidityProviderFee).toFixed(this.fromToken.decimals) + : new BigNumber(bestRouteQuote.expectedConvertQuote) + .times(bestRouteQuote.uniswapVersion === UniswapVersion.v3 ? 0 : bestRouteQuote.liquidityProviderFee) + .toFixed(this.fromToken.decimals), + liquidityProviderFeePercent: bestRouteQuote.liquidityProviderFee, + liquidityProviderFeesV3: bestRouteQuote.liquidityProviderFeesV3.map((f) => direction === TradeDirection.input - ? baseConvertRequest - .times(bestRouteQuote.liquidityProviderFee) - .toFixed(this.fromToken.decimals) + ? baseConvertRequest.times(f).toFixed(this.fromToken.decimals) : new BigNumber(bestRouteQuote.expectedConvertQuote) - .times(bestRouteQuote.liquidityProviderFee) - .toFixed(this.fromToken.decimals), - liquidityProviderFeePercent: bestRouteQuote.liquidityProviderFee, + .times(f) + .toFixed(this.fromToken.decimals) + ), + liquidityProviderFeePercentsV3: bestRouteQuote.liquidityProviderFeesV3, tradeExpires: bestRouteQuote.tradeExpires, routePathTokenMap: bestRouteQuote.routePathArrayTokenMap, routeText: bestRouteQuote.routeText, @@ -404,15 +410,20 @@ export class UniswapPairFactory { ? null : bestRouteQuote.expectedConvertQuoteOrTokenAmountInMaxWithSlippage, expectedConvertQuote: bestRouteQuote.expectedConvertQuote, - liquidityProviderFee: + liquidityProviderFee: direction === TradeDirection.input + ? baseConvertRequest.times(bestRouteQuote.uniswapVersion === UniswapVersion.v3 ? 0 : bestRouteQuote.liquidityProviderFee).toFixed(this.fromToken.decimals) + : new BigNumber(bestRouteQuote.expectedConvertQuote) + .times(bestRouteQuote.uniswapVersion === UniswapVersion.v3 ? 0 : bestRouteQuote.liquidityProviderFee) + .toFixed(this.fromToken.decimals), + liquidityProviderFeePercent: bestRouteQuote.liquidityProviderFee, + liquidityProviderFeesV3: bestRouteQuote.liquidityProviderFeesV3.map((f) => direction === TradeDirection.input - ? baseConvertRequest - .times(bestRouteQuote.liquidityProviderFee) - .toFixed(this.fromToken.decimals) + ? baseConvertRequest.times(f).toFixed(this.fromToken.decimals) : new BigNumber(bestRouteQuote.expectedConvertQuote) - .times(bestRouteQuote.liquidityProviderFee) - .toFixed(this.fromToken.decimals), - liquidityProviderFeePercent: bestRouteQuote.liquidityProviderFee, + .times(f) + .toFixed(this.fromToken.decimals) + ), + liquidityProviderFeePercentsV3: bestRouteQuote.liquidityProviderFeesV3, tradeExpires: bestRouteQuote.tradeExpires, routePathTokenMap: bestRouteQuote.routePathArrayTokenMap, routeText: bestRouteQuote.routeText, @@ -470,15 +481,20 @@ export class UniswapPairFactory { ? null : bestRouteQuote.expectedConvertQuoteOrTokenAmountInMaxWithSlippage, expectedConvertQuote: bestRouteQuote.expectedConvertQuote, - liquidityProviderFee: + liquidityProviderFee: direction === TradeDirection.input + ? baseConvertRequest.times(bestRouteQuote.uniswapVersion === UniswapVersion.v3 ? 0 : bestRouteQuote.liquidityProviderFee).toFixed(this.fromToken.decimals) + : new BigNumber(bestRouteQuote.expectedConvertQuote) + .times(bestRouteQuote.uniswapVersion === UniswapVersion.v3 ? 0 : bestRouteQuote.liquidityProviderFee) + .toFixed(this.fromToken.decimals), + liquidityProviderFeePercent: bestRouteQuote.liquidityProviderFee, + liquidityProviderFeesV3: bestRouteQuote.liquidityProviderFeesV3.map((f) => direction === TradeDirection.input - ? baseConvertRequest - .times(bestRouteQuote.liquidityProviderFee) - .toFixed(this.fromToken.decimals) + ? baseConvertRequest.times(f).toFixed(this.fromToken.decimals) : new BigNumber(bestRouteQuote.expectedConvertQuote) - .times(bestRouteQuote.liquidityProviderFee) - .toFixed(this.fromToken.decimals), - liquidityProviderFeePercent: bestRouteQuote.liquidityProviderFee, + .times(f) + .toFixed(this.fromToken.decimals) + ), + liquidityProviderFeePercentsV3: bestRouteQuote.liquidityProviderFeesV3, tradeExpires: bestRouteQuote.tradeExpires, routePathTokenMap: bestRouteQuote.routePathArrayTokenMap, routeText: bestRouteQuote.routeText, @@ -580,4 +596,4 @@ export class UniswapPairFactory { } } } -} +} \ No newline at end of file diff --git a/src/factories/router/models/route-context.ts b/src/factories/router/models/route-context.ts index e5d73fa7..9295c6a2 100644 --- a/src/factories/router/models/route-context.ts +++ b/src/factories/router/models/route-context.ts @@ -3,4 +3,5 @@ import { Token } from '../../token/models/token'; export interface RouteContext { route: Token[]; liquidityProviderFee: number; + liquidityProviderFeesV3: number[]; } diff --git a/src/factories/router/models/route-methods.ts b/src/factories/router/models/route-methods.ts new file mode 100644 index 00000000..f19d96d1 --- /dev/null +++ b/src/factories/router/models/route-methods.ts @@ -0,0 +1,62 @@ + +export type IRouterMethodName = + | 'WETH' + | 'addLiquidity' + | 'addLiquidityETH' + | 'factory' + | 'getAmountIn' + | 'getAmountOut' + | 'getAmountsIn' + | 'getAmountsOut' + | 'quote' + | 'removeLiquidity' + | 'removeLiquidityETH' + | 'removeLiquidityETHSupportingFeeOnTransferTokens' + | 'removeLiquidityETHWithPermit' + | 'removeLiquidityETHWithPermitSupportingFeeOnTransferTokens' + | 'removeLiquidityWithPermit' + | 'swapETHForExactTokens' + | 'swapExactETHForTokens' + | 'swapExactETHForTokensSupportingFeeOnTransferTokens' + | 'swapExactTokensForETH' + | 'swapExactTokensForETHSupportingFeeOnTransferTokens' + | 'swapExactTokensForTokens' + | 'swapExactTokensForTokensSupportingFeeOnTransferTokens' + | 'swapTokensForExactETH' + | 'swapTokensForExactTokens'; + +export type IRouterMethods = { + [key in IRouterMethodName]: string; +}; + +export const DEFAULT_ROUTER_METHOD: IRouterMethods = { + WETH: 'WETH', + addLiquidity: 'addLiquidity', + addLiquidityETH: 'addLiquidityETH', + factory: 'factory', + getAmountIn: 'getAmountIn', + getAmountOut: 'getAmountOut', + getAmountsIn: 'getAmountsIn', + getAmountsOut: 'getAmountsOut', + quote: 'quote', + removeLiquidity: 'removeLiquidity', + removeLiquidityETH: 'removeLiquidityETH', + removeLiquidityETHSupportingFeeOnTransferTokens: + 'removeLiquidityETHSupportingFeeOnTransferTokens', + removeLiquidityETHWithPermit: 'removeLiquidityETHWithPermit', + removeLiquidityETHWithPermitSupportingFeeOnTransferTokens: + 'removeLiquidityETHWithPermitSupportingFeeOnTransferTokens', + removeLiquidityWithPermit: 'removeLiquidityWithPermit', + swapETHForExactTokens: 'swapETHForExactTokens', + swapExactETHForTokens: 'swapExactETHForTokens', + swapExactETHForTokensSupportingFeeOnTransferTokens: + 'swapExactETHForTokensSupportingFeeOnTransferTokens', + swapExactTokensForETH: 'swapExactTokensForETH', + swapExactTokensForETHSupportingFeeOnTransferTokens: + 'swapExactTokensForETHSupportingFeeOnTransferTokens', + swapExactTokensForTokens: 'swapExactTokensForTokens', + swapExactTokensForTokensSupportingFeeOnTransferTokens: + 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + swapTokensForExactETH: 'swapTokensForExactETH', + swapTokensForExactTokens: 'swapTokensForExactTokens', +}; \ No newline at end of file diff --git a/src/factories/router/models/route-quote-trade-context.ts b/src/factories/router/models/route-quote-trade-context.ts index f5c921b8..8ef45a31 100644 --- a/src/factories/router/models/route-quote-trade-context.ts +++ b/src/factories/router/models/route-quote-trade-context.ts @@ -4,4 +4,5 @@ export interface RouteQuoteTradeContext { uniswapVersion: UniswapVersion; routePathArray: string[]; liquidityProviderFee: number; + liquidityProviderFeesV3: number[]; } diff --git a/src/factories/router/models/route-quote.ts b/src/factories/router/models/route-quote.ts index a0ca1403..59ff2422 100644 --- a/src/factories/router/models/route-quote.ts +++ b/src/factories/router/models/route-quote.ts @@ -13,6 +13,7 @@ export interface RouteQuote { routePathArray: string[]; uniswapVersion: UniswapVersion; liquidityProviderFee: number; + liquidityProviderFeesV3: number[]; quoteDirection: TradeDirection; gasPriceEstimatedBy?: string | undefined; } diff --git a/src/factories/router/models/token-routes.ts b/src/factories/router/models/token-routes.ts index e84787c1..3c2a397e 100644 --- a/src/factories/router/models/token-routes.ts +++ b/src/factories/router/models/token-routes.ts @@ -1,9 +1,9 @@ -import { Token } from '../../token/models/token'; +import { Pool, Token } from "../../token/models/token"; export interface TokenRoutes { token: Token; pairs: { - fromTokenPairs?: Token[] | undefined; - toTokenPairs?: Token[] | undefined; + fromTokenPairs?: Pool[] | undefined; + toTokenPairs?: Pool[] | undefined; }; } diff --git a/src/factories/router/uniswap-router.factory.ts b/src/factories/router/uniswap-router.factory.ts index c4ffbb71..ef487f9c 100644 --- a/src/factories/router/uniswap-router.factory.ts +++ b/src/factories/router/uniswap-router.factory.ts @@ -5,7 +5,9 @@ import { ContractCallResults, } from 'ethereum-multicall'; import { + ExactInputRequest, ExactInputSingleRequest, + ExactOutputRequest, ExactOutputSingleRequest, } from '../../ABI/types/uniswap-router-v3'; import { CoinGecko } from '../../coin-gecko'; @@ -27,6 +29,7 @@ import { WETHContract } from '../../common/tokens/weth'; import { deepClone } from '../../common/utils/deep-clone'; import { formatEther } from '../../common/utils/format-ether'; import { hexlify } from '../../common/utils/hexlify'; +import { isSameAddress } from '../../common/utils/is-same-address'; import { onlyUnique } from '../../common/utils/only-unique'; import { parseEther } from '../../common/utils/parse-ether'; import { toEthersBigNumber } from '../../common/utils/to-ethers-big-number'; @@ -43,7 +46,7 @@ import { TradeDirection } from '../pair/models/trade-direction'; import { Transaction } from '../pair/models/transaction'; import { UniswapPairSettings } from '../pair/models/uniswap-pair-settings'; import { AllowanceAndBalanceOf } from '../token/models/allowance-balance-of'; -import { Token } from '../token/models/token'; +import { Pool, Token } from '../token/models/token'; import { TokensFactory } from '../token/tokens.factory'; import { RouterDirection } from './enums/router-direction'; import { AllPossibleRoutes } from './models/all-possible-routes'; @@ -70,14 +73,17 @@ export class UniswapRouterFactory { this._ethersProvider, uniswapContracts.v2.getRouterAddress( this._settings.cloneUniswapContractDetails - ) + ), + uniswapContracts.v2.getRouterAbi(this._settings.cloneUniswapContractDetails), + uniswapContracts.v2.getRouterMethods(this._settings.cloneUniswapContractDetails) ); private _uniswapRouterContractFactoryV3 = new UniswapRouterContractFactoryV3( this._ethersProvider, uniswapContracts.v3.getRouterAddress( this._settings.cloneUniswapContractDetails - ) + ), + uniswapContracts.v3.getRouterAbi(this._settings.cloneUniswapContractDetails) ); private _tokensFactory = new TokensFactory( @@ -121,7 +127,7 @@ export class UniswapRouterFactory { findPairs = [[[this._fromToken, this._toToken]]]; } - // console.log(JSON.stringify(findPairs, null, 4)); + // console.log('find Pairts', JSON.stringify(findPairs, null, 4)); const contractCallContext: ContractCallContext[] = []; @@ -143,6 +149,8 @@ export class UniswapRouterFactory { ) { const fromToken = findPairs[pairs][tokenPairs][0]; const toToken = findPairs[pairs][tokenPairs][1]; + if (isSameAddress(fromToken.contractAddress, toToken.contractAddress)) + continue; contractCallContext[0].calls.push({ reference: `${fromToken.contractAddress}-${toToken.contractAddress}-${fromToken.symbol}/${toToken.symbol}`, @@ -158,41 +166,43 @@ export class UniswapRouterFactory { // for now v3 quotes will just be direct aka UNI > AAVE etc! if (this._settings.uniswapVersions.includes(UniswapVersion.v3)) { + let v3Calls = []; + for (let pairs = 0; pairs < findPairs.length; pairs++) { + for ( + let tokenPairs = 0; + tokenPairs < findPairs[pairs].length; + tokenPairs++ + ) { + const fromToken = findPairs[pairs][tokenPairs][0]; + const toToken = findPairs[pairs][tokenPairs][1]; + + if (isSameAddress(fromToken.contractAddress, toToken.contractAddress)) + continue; + + for (let fee = 0; fee < 3; fee++) { + const feeAmount = [FeeAmount.LOW, FeeAmount.MEDIUM, FeeAmount.HIGH][ + fee + ]; + v3Calls.push({ + reference: `${fromToken.contractAddress}-${toToken.contractAddress}-${fromToken.symbol}/${toToken.symbol}-${feeAmount}`, + methodName: 'getPool', + methodParameters: [ + removeEthFromContractAddress(fromToken.contractAddress), + removeEthFromContractAddress(toToken.contractAddress), + feeAmount, + ], + }); + } + } + } + contractCallContext.push({ reference: UniswapVersion.v3, contractAddress: uniswapContracts.v3.getFactoryAddress( this._settings.cloneUniswapContractDetails ), abi: UniswapContractContextV3.factoryAbi, - calls: [ - { - reference: `${this._fromToken.contractAddress}-${this._toToken.contractAddress}-${this._fromToken.symbol}/${this._toToken.symbol}`, - methodName: 'getPool', - methodParameters: [ - removeEthFromContractAddress(this._fromToken.contractAddress), - removeEthFromContractAddress(this._toToken.contractAddress), - FeeAmount.LOW, - ], - }, - { - reference: `${this._fromToken.contractAddress}-${this._toToken.contractAddress}-${this._fromToken.symbol}/${this._toToken.symbol}`, - methodName: 'getPool', - methodParameters: [ - removeEthFromContractAddress(this._fromToken.contractAddress), - removeEthFromContractAddress(this._toToken.contractAddress), - FeeAmount.MEDIUM, - ], - }, - { - reference: `${this._fromToken.contractAddress}-${this._toToken.contractAddress}-${this._fromToken.symbol}/${this._toToken.symbol}`, - methodName: 'getPool', - methodParameters: [ - removeEthFromContractAddress(this._fromToken.contractAddress), - removeEthFromContractAddress(this._toToken.contractAddress), - FeeAmount.HIGH, - ], - }, - ], + calls: v3Calls, }); } @@ -208,8 +218,6 @@ export class UniswapRouterFactory { c.returnValues[0] !== '0x0000000000000000000000000000000000000000' ); - // console.log(JSON.stringify(results.callsReturnContext, null, 4)); - const fromTokenRoutes: TokenRoutes = { token: this._fromToken, pairs: { @@ -258,6 +266,7 @@ export class UniswapRouterFactory { }); } + // console.log('allMainRoutes'); // console.log(JSON.stringify(allMainRoutes, null, 4)); allPossibleRoutes.v2 = this.workOutAllPossibleRoutes( @@ -270,30 +279,70 @@ export class UniswapRouterFactory { if (this._settings.uniswapVersions.includes(UniswapVersion.v3)) { const results = contractCallResults.results[UniswapVersion.v3]; - for (let i = 0; i < results.callsReturnContext.length; i++) { - if ( - results.callsReturnContext[i].returnValues[0] !== - '0x0000000000000000000000000000000000000000' - ) { - let liquidityProviderFee!: FeeAmount; - switch (i) { - case 0: - liquidityProviderFee = FeeAmount.LOW; - break; - case 1: - liquidityProviderFee = FeeAmount.MEDIUM; - break; - case 2: - liquidityProviderFee = FeeAmount.HIGH; - break; - } + const availablePairs = results.callsReturnContext.filter( + (c) => + c.returnValues[0] !== '0x0000000000000000000000000000000000000000' + ); + // console.log('available pairs', availablePairs); - allPossibleRoutes.v3.push({ - route: [this._fromToken, this._toToken], - liquidityProviderFee: feeToPercent(liquidityProviderFee), - }); - } + const fromTokenRoutes: TokenRoutes = { + token: this._fromToken, + pairs: { + fromTokenPairs: this.getTokenAvailablePairsV3( + this._fromToken, + availablePairs, + RouterDirection.from + ), + }, + }; + + const toTokenRoutes: TokenRoutes = { + token: this._toToken, + pairs: { + toTokenPairs: this.getTokenAvailablePairsV3( + this._toToken, + availablePairs, + RouterDirection.to + ), + }, + }; + + // console.log('fromTokenRoutes'); + // console.log(JSON.stringify(fromTokenRoutes, null, 4)); + // console.log('break'); + // console.log('toTokenRoutes'); + // console.log(JSON.stringify(toTokenRoutes, null, 4)); + // console.log('break'); + + const allMainRoutes: TokenRoutes[] = []; + + for (let i = 0; i < this.allMainTokens.length; i++) { + const fromTokenPairs = this.getTokenAvailablePairsV3( + this.allMainTokens[i], + availablePairs, + RouterDirection.from + ); + + const toTokenPairs = this.getTokenAvailablePairsV3( + this.allMainTokens[i], + availablePairs, + RouterDirection.to + ); + + allMainRoutes.push({ + token: this.allMainTokens[i], + pairs: { fromTokenPairs, toTokenPairs }, + }); } + + // console.log('allMainRoutes'); + // console.log(JSON.stringify(allMainRoutes, null, 4)); + + allPossibleRoutes.v3 = this.workOutAllPossibleRoutesV3( + fromTokenRoutes, + toTokenRoutes, + allMainRoutes + ); } // console.log(JSON.stringify(allPossibleRoutes, null, 4)); @@ -321,7 +370,9 @@ export class UniswapRouterFactory { contractAddress: uniswapContracts.v2.getRouterAddress( this._settings.cloneUniswapContractDetails ), - abi: UniswapContractContextV2.routerAbi, + abi: uniswapContracts.v2.getRouterAbi( + this._settings.cloneUniswapContractDetails + ), calls: [], context: routes.v2, }); @@ -358,25 +409,49 @@ export class UniswapRouterFactory { return removeEthFromContractAddress(c.contractAddress); }); - contractCallContext[ - this._settings.uniswapVersions.includes(UniswapVersion.v2) ? 1 : 0 - ].calls.push({ - reference: `route${i}`, - methodName: - direction === TradeDirection.input - ? 'quoteExactInputSingle' - : 'quoteExactOutputSingle', - methodParameters: [ - routeCombo[0], - routeCombo[1], - percentToFeeAmount(routes.v3[i].liquidityProviderFee), - tradeAmount, - 0, - ], - }); + if (routeCombo.length == 2) { + contractCallContext[ + this._settings.uniswapVersions.includes(UniswapVersion.v2) ? 1 : 0 + ].calls.push({ + reference: `route${i}`, + methodName: + direction === TradeDirection.input + ? 'quoteExactInputSingle' + : 'quoteExactOutputSingle', + methodParameters: [ + routeCombo[0], + routeCombo[1], + percentToFeeAmount(routes.v3[i].liquidityProviderFeesV3[0]), + tradeAmount, + 0, + ], + }); + } else if (routeCombo.length > 2) { + contractCallContext[ + this._settings.uniswapVersions.includes(UniswapVersion.v2) ? 1 : 0 + ].calls.push({ + reference: `route${i}`, + methodName: + direction === TradeDirection.input + ? 'quoteExactInput' + : 'quoteExactOutput', + methodParameters: [ + this.getEncodedPoolsPath( + routeCombo, + routes.v3[i].liquidityProviderFeesV3, + direction + ), + tradeAmount, + ], + }); + } else { + continue; + } } } + // console.log('contractCallContext', contractCallContext); + const contractCallResults = await this._multicall.call(contractCallContext); return this.buildRouteQuotesFromResults( @@ -439,6 +514,7 @@ export class UniswapRouterFactory { routePathArray: route.routePathArray, uniswapVersion: route.uniswapVersion, liquidityProviderFee: route.liquidityProviderFee, + liquidityProviderFeesV3: route.liquidityProviderFeesV3, quoteDirection: route.quoteDirection, gasPriceEstimatedBy: route.gasPriceEstimatedBy, }; @@ -505,7 +581,7 @@ export class UniswapRouterFactory { return this.generateTradeDataForV3Input( parseEther(ethAmountIn), convertedMinTokens, - routeQuoteTradeContext.liquidityProviderFee, + routeQuoteTradeContext, deadline ); default: @@ -547,7 +623,7 @@ export class UniswapRouterFactory { return this.generateTradeDataForV3Output( amountOut, parseEther(ethAmountInMax), - routeQuoteTradeContext.liquidityProviderFee, + routeQuoteTradeContext, deadline ); default: @@ -591,7 +667,7 @@ export class UniswapRouterFactory { return this.generateTradeDataForV3Input( amountIn, parseEther(ethAmountOutMin), - routeQuoteTradeContext.liquidityProviderFee, + routeQuoteTradeContext, deadline ); default: @@ -635,7 +711,7 @@ export class UniswapRouterFactory { return this.generateTradeDataForV3Output( parseEther(ethAmountOut), amountInMax, - routeQuoteTradeContext.liquidityProviderFee, + routeQuoteTradeContext, deadline ); default: @@ -680,7 +756,7 @@ export class UniswapRouterFactory { return this.generateTradeDataForV3Input( amountIn, amountMin, - routeQuoteTradeContext.liquidityProviderFee, + routeQuoteTradeContext, deadline ); default: @@ -726,7 +802,7 @@ export class UniswapRouterFactory { return this.generateTradeDataForV3Output( amountOut, amountInMax, - routeQuoteTradeContext.liquidityProviderFee, + routeQuoteTradeContext, deadline ); default: @@ -740,38 +816,63 @@ export class UniswapRouterFactory { /** * Generate trade data for v3 * @param tokenAmount The token amount - * @param tokenAmountOut The min token amount out - * @param liquidityProviderFee The liquidity provider fee + * @param tokenAmountMin The min token amount out + * @param routeQuoteTradeContext The route quote trade context * @param deadline The deadline it expiries unix time */ private generateTradeDataForV3Input( tokenAmount: BigNumber, tokenAmountMin: BigNumber, - liquidityProviderFee: number, + routeQuoteTradeContext: RouteQuoteTradeContext, deadline: string ): string { const isNativeReceivingNativeEth = isNativeEth( this._toToken.contractAddress ); - const params: ExactInputSingleRequest = { - tokenIn: removeEthFromContractAddress(this._fromToken.contractAddress), - tokenOut: removeEthFromContractAddress(this._toToken.contractAddress), - fee: percentToFeeAmount(liquidityProviderFee), - recipient: - isNativeReceivingNativeEth === true - ? '0x0000000000000000000000000000000000000000' - : this._ethereumAddress, - deadline, - amountIn: hexlify(tokenAmount), - amountOutMinimum: hexlify(tokenAmountMin), - sqrtPriceLimitX96: 0, - }; + const isSingle = routeQuoteTradeContext.routePathArray.length == 2; const multicallData: string[] = []; + if (isSingle) { + const params: ExactInputSingleRequest = { + tokenIn: removeEthFromContractAddress(this._fromToken.contractAddress), + tokenOut: removeEthFromContractAddress(this._toToken.contractAddress), + fee: percentToFeeAmount(routeQuoteTradeContext.liquidityProviderFeesV3[0]), + recipient: + isNativeReceivingNativeEth === true + ? '0x0000000000000000000000000000000000000000' + : this._ethereumAddress, + deadline, + amountIn: hexlify(tokenAmount), + amountOutMinimum: hexlify(tokenAmountMin), + sqrtPriceLimitX96: 0, + }; + + multicallData.push( + this._uniswapRouterContractFactoryV3.exactInputSingle(params) + ); + } else { + const params: ExactInputRequest = { + path: this.getEncodedPoolsPath( + routeQuoteTradeContext.routePathArray.map((r) => + removeEthFromContractAddress(r) + ), + routeQuoteTradeContext.liquidityProviderFeesV3, + TradeDirection.input + ), + recipient: + isNativeReceivingNativeEth === true + ? '0x0000000000000000000000000000000000000000' + : this._ethereumAddress, + deadline, + amountIn: hexlify(tokenAmount), + amountOutMinimum: hexlify(tokenAmountMin), + }; + + multicallData.push( + this._uniswapRouterContractFactoryV3.exactInput(params) + ); + } - multicallData.push( - this._uniswapRouterContractFactoryV3.exactInputSingle(params) - ); if (isNativeEth(this._toToken.contractAddress)) { multicallData.push( this._uniswapRouterContractFactoryV3.unwrapWETH9( @@ -786,40 +887,62 @@ export class UniswapRouterFactory { /** * Generate trade data for v3 - * @param tokenAmountInMax The amount in max - * @param ethAmountOut The amount to receive - * @param liquidityProviderFee The liquidity provider fee + * @param amountOut The amount in max + * @param amountInMaximum The amount to receive + * @param routeQuoteTradeContext The route quote trade context * @param deadline The deadline it expiries unix time */ private generateTradeDataForV3Output( amountOut: BigNumber, amountInMaximum: BigNumber, - liquidityProviderFee: number, + routeQuoteTradeContext: RouteQuoteTradeContext, deadline: string ): string { const isNativeReceivingNativeEth = isNativeEth( this._toToken.contractAddress ); - const params: ExactOutputSingleRequest = { - tokenIn: removeEthFromContractAddress(this._fromToken.contractAddress), - tokenOut: removeEthFromContractAddress(this._toToken.contractAddress), - fee: percentToFeeAmount(liquidityProviderFee), - recipient: - isNativeReceivingNativeEth === true - ? '0x0000000000000000000000000000000000000000' - : this._ethereumAddress, - deadline, - amountOut: hexlify(amountOut), - amountInMaximum: hexlify(amountInMaximum), - sqrtPriceLimitX96: 0, - }; - + const isSingle = routeQuoteTradeContext.routePathArray.length == 2; const multicallData: string[] = []; + if (isSingle) { + const params: ExactOutputSingleRequest = { + tokenIn: removeEthFromContractAddress(this._fromToken.contractAddress), + tokenOut: removeEthFromContractAddress(this._toToken.contractAddress), + fee: percentToFeeAmount(routeQuoteTradeContext.liquidityProviderFee[0]), + recipient: + isNativeReceivingNativeEth === true + ? '0x0000000000000000000000000000000000000000' + : this._ethereumAddress, + deadline, + amountOut: hexlify(amountOut), + amountInMaximum: hexlify(amountInMaximum), + sqrtPriceLimitX96: 0, + }; + + multicallData.push( + this._uniswapRouterContractFactoryV3.exactOutputSingle(params) + ); + } else { + const params: ExactOutputRequest = { + path: this.getEncodedPoolsPath( + routeQuoteTradeContext.routePathArray, + routeQuoteTradeContext.liquidityProviderFeesV3, + TradeDirection.output + ), + recipient: + isNativeReceivingNativeEth === true + ? '0x0000000000000000000000000000000000000000' + : this._ethereumAddress, + deadline, + amountOut: hexlify(amountOut), + amountInMaximum: hexlify(amountInMaximum), + }; + + multicallData.push( + this._uniswapRouterContractFactoryV3.exactOutput(params) + ); + } - multicallData.push( - this._uniswapRouterContractFactoryV3.exactOutputSingle(params) - ); if (isNativeEth(this._toToken.contractAddress)) { multicallData.push( this._uniswapRouterContractFactoryV3.unwrapWETH9( @@ -1250,7 +1373,8 @@ export class UniswapRouterFactory { (t) => fromTokenRoutes.pairs.fromTokenPairs!.find( (f) => - f.contractAddress.toLowerCase() === t.contractAddress.toLowerCase() + f.token.contractAddress.toLowerCase() === + t.token.contractAddress.toLowerCase() ) ); @@ -1258,13 +1382,14 @@ export class UniswapRouterFactory { if ( fromTokenRoutes.pairs.fromTokenPairs!.find( (t) => - t.contractAddress.toLowerCase() === + t.token.contractAddress.toLowerCase() === toTokenRoutes.token.contractAddress.toLowerCase() ) ) { routes.push({ route: [fromTokenRoutes.token, toTokenRoutes.token], liquidityProviderFee: this.LIQUIDITY_PROVIDER_FEE_V2, + liquidityProviderFeesV3: [], }); } @@ -1273,21 +1398,23 @@ export class UniswapRouterFactory { if ( jointCompatibleRoutes.find( (c) => - c.contractAddress.toLowerCase() === + c.token.contractAddress.toLowerCase() === tokenRoute.token.contractAddress.toLowerCase() ) ) { routes.push({ route: [fromTokenRoutes.token, tokenRoute.token, toTokenRoutes.token], liquidityProviderFee: this.LIQUIDITY_PROVIDER_FEE_V2, + liquidityProviderFeesV3: [], }); for (let f = 0; f < fromTokenRoutes.pairs.fromTokenPairs!.length; f++) { - const fromSupportedToken = fromTokenRoutes.pairs.fromTokenPairs![f]; + const fromSupportedToken = + fromTokenRoutes.pairs.fromTokenPairs![f].token; if ( tokenRoute.pairs.toTokenPairs!.find( (pair) => - pair.contractAddress.toLowerCase() === + pair.token.contractAddress.toLowerCase() === fromSupportedToken.contractAddress.toLowerCase() ) ) { @@ -1304,17 +1431,18 @@ export class UniswapRouterFactory { routes.push({ route: workedOutFromRoute, liquidityProviderFee: this.LIQUIDITY_PROVIDER_FEE_V2, + liquidityProviderFeesV3: [], }); } } } for (let f = 0; f < toTokenRoutes.pairs.toTokenPairs!.length; f++) { - const toSupportedToken = toTokenRoutes.pairs.toTokenPairs![f]; + const toSupportedToken = toTokenRoutes.pairs.toTokenPairs![f].token; if ( tokenRoute.pairs.fromTokenPairs!.find( (pair) => - pair.contractAddress.toLowerCase() === + pair.token.contractAddress.toLowerCase() === toSupportedToken.contractAddress.toLowerCase() ) ) { @@ -1332,6 +1460,7 @@ export class UniswapRouterFactory { routes.push({ route: workedOutToRoute, liquidityProviderFee: this.LIQUIDITY_PROVIDER_FEE_V2, + liquidityProviderFeesV3: [], }); } } @@ -1364,43 +1493,263 @@ export class UniswapRouterFactory { private getFromRouterDirectionAvailablePairs( token: Token, allAvailablePairs: CallReturnContext[] - ): Token[] { - const fromRouterDirection = allAvailablePairs.filter( - (c) => c.reference.split('-')[0] === token.contractAddress + ): Pool[] { + const fromRouterDirection = allAvailablePairs.filter((c) => + isSameAddress(token.contractAddress, c.reference.split('-')[0]) ); - const tokens: Token[] = []; + const pools: Pool[] = []; for (let index = 0; index < fromRouterDirection.length; index++) { const context = fromRouterDirection[index]; - tokens.push( - this.allTokens.find( - (t) => t.contractAddress === context.reference.split('-')[1] - )! - ); + pools.push({ + token: this.allTokens.find((t) => + isSameAddress(t.contractAddress, context.reference.split('-')[1]) + )!, + }); } - return tokens; + return pools; } private getToRouterDirectionAvailablePairs( token: Token, allAvailablePairs: CallReturnContext[] - ): Token[] { - const toRouterDirection = allAvailablePairs.filter( - (c) => c.reference.split('-')[1] === token.contractAddress + ): Pool[] { + const fromRouterDirection = allAvailablePairs.filter((c) => + isSameAddress(token.contractAddress, c.reference.split('-')[1]) ); - const tokens: Token[] = []; - - for (let index = 0; index < toRouterDirection.length; index++) { - const context = toRouterDirection[index]; - tokens.push( - this.allTokens.find( - (t) => t.contractAddress === context.reference.split('-')[0] - )! - ); + const pools: Pool[] = []; + + for (let index = 0; index < fromRouterDirection.length; index++) { + const context = fromRouterDirection[index]; + pools.push({ + token: this.allTokens.find((t) => + isSameAddress(t.contractAddress, context.reference.split('-')[0]) + )!, + }); + } + + return pools; + } + + /** + * Works out every possible route it can take - v2 only + * @param fromTokenRoutes The from token routes + * @param toTokenRoutes The to token routes + * @param allMainRoutes All the main routes + */ + private workOutAllPossibleRoutesV3( + fromTokenRoutes: TokenRoutes, + toTokenRoutes: TokenRoutes, + allMainRoutes: TokenRoutes[] + ): RouteContext[] { + const jointCompatibleRoutes = toTokenRoutes.pairs.toTokenPairs!.filter( + (t) => + fromTokenRoutes.pairs.fromTokenPairs!.find((f) => + isSameAddress(f.token.contractAddress, t.token.contractAddress) + ) + ); + + const routes: RouteContext[] = []; + const directRoute = fromTokenRoutes.pairs.fromTokenPairs!.find((t) => + isSameAddress( + t.token.contractAddress, + toTokenRoutes.token.contractAddress + ) + ); + if (directRoute) { + routes.push({ + route: [fromTokenRoutes.token, toTokenRoutes.token], + liquidityProviderFee: 0, + liquidityProviderFeesV3: [feeToPercent(directRoute.fee!)], + }); + } + + for (let i = 0; i < allMainRoutes.length; i++) { + const tokenRoute = allMainRoutes[i]; + if ( + jointCompatibleRoutes.find((c) => + isSameAddress( + c.token.contractAddress, + tokenRoute.token.contractAddress + ) + ) + ) { + const feeFrom2Main = fromTokenRoutes.pairs.fromTokenPairs!.find((t) => + isSameAddress( + t.token.contractAddress, + tokenRoute.token.contractAddress + ) + )!.fee; + + const feeMain2To = toTokenRoutes.pairs.toTokenPairs!.find((t) => + isSameAddress( + t.token.contractAddress, + tokenRoute.token.contractAddress + ) + )!.fee; + + routes.push({ + route: [fromTokenRoutes.token, tokenRoute.token, toTokenRoutes.token], + liquidityProviderFee: 0, + liquidityProviderFeesV3: [ + feeToPercent(feeFrom2Main!), + feeToPercent(feeMain2To!), + ], + }); + + for (let f = 0; f < fromTokenRoutes.pairs.fromTokenPairs!.length; f++) { + const fromSupportedToken = + fromTokenRoutes.pairs.fromTokenPairs![f].token; + if ( + tokenRoute.pairs.toTokenPairs!.find((pair) => + isSameAddress( + pair.token.contractAddress, + fromSupportedToken.contractAddress + ) + ) + ) { + const workedOutFromRoute = [ + fromTokenRoutes.token, + fromSupportedToken, + tokenRoute.token, + toTokenRoutes.token, + ]; + if ( + workedOutFromRoute.filter(onlyUnique).length === + workedOutFromRoute.length + ) { + const feeFrom2Support = + fromTokenRoutes.pairs.fromTokenPairs![f].fee; + const feeSupport2Main = tokenRoute.pairs.toTokenPairs!.find( + (pair) => + isSameAddress( + pair.token.contractAddress, + fromSupportedToken.contractAddress + ) + )!.fee; + + routes.push({ + route: workedOutFromRoute, + liquidityProviderFee: 0, + liquidityProviderFeesV3: [ + feeToPercent(feeFrom2Support!), + feeToPercent(feeSupport2Main!), + feeToPercent(feeMain2To!), + ], + }); + } + } + } + + for (let f = 0; f < toTokenRoutes.pairs.toTokenPairs!.length; f++) { + const toSupportedToken = toTokenRoutes.pairs.toTokenPairs![f].token!; + if ( + tokenRoute.pairs.fromTokenPairs!.find( + (pair) => + pair.token.contractAddress.toLowerCase() === + toSupportedToken.contractAddress.toLowerCase() + ) + ) { + const workedOutToRoute = [ + fromTokenRoutes.token, + tokenRoute.token, + toSupportedToken, + toTokenRoutes.token, + ]; + + if ( + workedOutToRoute.filter(onlyUnique).length === + workedOutToRoute.length + ) { + const feeMain2Support = tokenRoute.pairs.fromTokenPairs!.find( + (pair) => + pair.token.contractAddress.toLowerCase() === + toSupportedToken.contractAddress.toLowerCase() + )!.fee; + const feeSupport2To = toTokenRoutes.pairs.toTokenPairs![f].fee; + + routes.push({ + route: workedOutToRoute, + liquidityProviderFee: 0, + liquidityProviderFeesV3: [ + feeToPercent(feeFrom2Main!), + feeToPercent(feeMain2Support!), + feeToPercent(feeSupport2To!), + ], + }); + } + } + } + } + } + + return routes; + } + + private getTokenAvailablePairsV3( + token: Token, + allAvailablePairs: CallReturnContext[], + direction: RouterDirection + ) { + switch (direction) { + case RouterDirection.from: + return this.getFromRouterDirectionAvailablePairsV3( + token, + allAvailablePairs + ); + case RouterDirection.to: + return this.getToRouterDirectionAvailablePairsV3( + token, + allAvailablePairs + ); + } + } + + private getFromRouterDirectionAvailablePairsV3( + token: Token, + allAvailablePairs: CallReturnContext[] + ): Pool[] { + const pools: Pool[] = []; + + for (let index = 0; index < allAvailablePairs.length; index++) { + const context = allAvailablePairs[index]; + if ( + isSameAddress(context.reference.split('-')[0], token.contractAddress) + ) { + pools.push({ + token: this.allTokens.find((t) => + isSameAddress(t.contractAddress, context.reference.split('-')[1]) + )!, + fee: parseInt(context.reference.split('-')[3]) as FeeAmount, + }); + } + } + + return pools; + } + + private getToRouterDirectionAvailablePairsV3( + token: Token, + allAvailablePairs: CallReturnContext[] + ): Pool[] { + const pools: Pool[] = []; + + for (let index = 0; index < allAvailablePairs.length; index++) { + const context = allAvailablePairs[index]; + if ( + isSameAddress(context.reference.split('-')[1], token.contractAddress) + ) { + pools.push({ + token: this.allTokens.find((t) => + isSameAddress(t.contractAddress, context.reference.split('-')[0]) + )!, + fee: parseInt(context.reference.split('-')[3]) as FeeAmount, + }); + } } - return tokens; + return pools; } /** @@ -1557,11 +1906,16 @@ export class UniswapRouterFactory { ); const tradeExpires = this.generateTradeDeadlineUnixTime(); + const routePathArray = + uniswapVersion === UniswapVersion.v2 + ? callReturnContext.methodParameters[1] + : routeContext.route.map((r) => r.contractAddress); const routeQuoteTradeContext: RouteQuoteTradeContext = { uniswapVersion, liquidityProviderFee: routeContext.liquidityProviderFee, - routePathArray: callReturnContext.methodParameters[1], + liquidityProviderFeesV3: routeContext.liquidityProviderFeesV3, + routePathArray: routePathArray, }; const data = direction === TradeDirection.input @@ -1582,42 +1936,25 @@ export class UniswapRouterFactory { switch (uniswapVersion) { case UniswapVersion.v2: + case UniswapVersion.v3: return { expectedConvertQuote, expectedConvertQuoteOrTokenAmountInMaxWithSlippage, transaction, tradeExpires, - routePathArrayTokenMap: callReturnContext.methodParameters[1].map( - (c: string) => { - return this.allTokens.find((t) => t.contractAddress === c); - } - ), - routeText: callReturnContext.methodParameters[1] + routePathArrayTokenMap: routePathArray.map((c: string) => { + return this.allTokens.find((t) => t.contractAddress === c); + }), + routeText: routePathArray .map((c: string) => { return this.allTokens.find((t) => t.contractAddress === c)! .symbol; }) .join(' > '), - // route array is always in the 1 index of the method parameters - routePathArray: callReturnContext.methodParameters[1], - uniswapVersion, - liquidityProviderFee: routeContext.liquidityProviderFee, - quoteDirection: direction, - }; - case UniswapVersion.v3: - return { - expectedConvertQuote, - expectedConvertQuoteOrTokenAmountInMaxWithSlippage, - transaction, - tradeExpires, - routePathArrayTokenMap: [this._fromToken, this._toToken], - routeText: `${this._fromToken.symbol} > ${this._toToken.symbol}`, - routePathArray: [ - this._fromToken.contractAddress, - this._toToken.contractAddress, - ], + routePathArray: routePathArray, uniswapVersion, - liquidityProviderFee: routeContext.liquidityProviderFee, + liquidityProviderFee: uniswapVersion === UniswapVersion.v2 ? routeContext.liquidityProviderFee : 0, + liquidityProviderFeesV3: routeContext.liquidityProviderFeesV3, quoteDirection: direction, }; default: @@ -1662,10 +1999,16 @@ export class UniswapRouterFactory { ); const tradeExpires = this.generateTradeDeadlineUnixTime(); + const routePathArray = + uniswapVersion === UniswapVersion.v2 + ? callReturnContext.methodParameters[1] + : routeContext.route.map((r) => r.contractAddress); + const routeQuoteTradeContext: RouteQuoteTradeContext = { uniswapVersion, liquidityProviderFee: routeContext.liquidityProviderFee, - routePathArray: callReturnContext.methodParameters[1], + liquidityProviderFeesV3: routeContext.liquidityProviderFeesV3, + routePathArray: routePathArray, }; const data = direction === TradeDirection.input @@ -1692,12 +2035,13 @@ export class UniswapRouterFactory { switch (uniswapVersion) { case UniswapVersion.v2: + case UniswapVersion.v3: return { expectedConvertQuote, expectedConvertQuoteOrTokenAmountInMaxWithSlippage, transaction, tradeExpires, - routePathArrayTokenMap: callReturnContext.methodParameters[1].map( + routePathArrayTokenMap: routePathArray.map( (c: string, index: number) => { const token = deepClone( this.allTokens.find((t) => t.contractAddress === c)! @@ -1712,7 +2056,7 @@ export class UniswapRouterFactory { return token; } ), - routeText: callReturnContext.methodParameters[1] + routeText: routePathArray .map((c: string, index: number) => { if (index === 0) { return this.getNativeTokenSymbol(); @@ -1722,36 +2066,10 @@ export class UniswapRouterFactory { }) .join(' > '), // route array is always in the 1 index of the method parameters - routePathArray: callReturnContext.methodParameters[1], + routePathArray: routePathArray, uniswapVersion, - liquidityProviderFee: routeContext.liquidityProviderFee, - quoteDirection: direction, - }; - case UniswapVersion.v3: - return { - expectedConvertQuote, - expectedConvertQuoteOrTokenAmountInMaxWithSlippage, - transaction, - tradeExpires, - routePathArrayTokenMap: [ - turnTokenIntoEthForResponse( - this._fromToken, - this._settings?.customNetwork?.nativeCurrency - ), - this._toToken, - ], - routeText: `${ - turnTokenIntoEthForResponse( - this._fromToken, - this._settings?.customNetwork?.nativeCurrency - ).symbol - } > ${this._toToken.symbol}`, - routePathArray: [ - this._fromToken.contractAddress, - this._toToken.contractAddress, - ], - uniswapVersion, - liquidityProviderFee: routeContext.liquidityProviderFee, + liquidityProviderFee: uniswapVersion === UniswapVersion.v2 ? routeContext.liquidityProviderFee : 0, + liquidityProviderFeesV3: routeContext.liquidityProviderFeesV3, quoteDirection: direction, }; default: @@ -1796,11 +2114,18 @@ export class UniswapRouterFactory { ); const tradeExpires = this.generateTradeDeadlineUnixTime(); + const routePathArray = + uniswapVersion === UniswapVersion.v2 + ? callReturnContext.methodParameters[1] + : routeContext.route.map((r) => r.contractAddress); + const routeQuoteTradeContext: RouteQuoteTradeContext = { uniswapVersion, liquidityProviderFee: routeContext.liquidityProviderFee, - routePathArray: callReturnContext.methodParameters[1], + liquidityProviderFeesV3: routeContext.liquidityProviderFeesV3, + routePathArray: routePathArray, }; + const data = direction === TradeDirection.input ? this.generateTradeDataErc20ToEthInput( @@ -1820,17 +2145,18 @@ export class UniswapRouterFactory { switch (uniswapVersion) { case UniswapVersion.v2: + case UniswapVersion.v3: return { expectedConvertQuote, expectedConvertQuoteOrTokenAmountInMaxWithSlippage, transaction, tradeExpires, - routePathArrayTokenMap: callReturnContext.methodParameters[1].map( + routePathArrayTokenMap: routePathArray.map( (c: string, index: number) => { const token = deepClone( this.allTokens.find((t) => t.contractAddress === c)! ); - if (index === callReturnContext.methodParameters[1].length - 1) { + if (index === routePathArray.length - 1) { return turnTokenIntoEthForResponse( token, this._settings?.customNetwork?.nativeCurrency @@ -1840,9 +2166,9 @@ export class UniswapRouterFactory { return token; } ), - routeText: callReturnContext.methodParameters[1] + routeText: routePathArray .map((c: string, index: number) => { - if (index === callReturnContext.methodParameters[1].length - 1) { + if (index === routePathArray.length - 1) { return this.getNativeTokenSymbol(); } return this.allTokens.find((t) => t.contractAddress === c)! @@ -1850,36 +2176,10 @@ export class UniswapRouterFactory { }) .join(' > '), // route array is always in the 1 index of the method parameters - routePathArray: callReturnContext.methodParameters[1], + routePathArray: routePathArray, uniswapVersion, - liquidityProviderFee: routeContext.liquidityProviderFee, - quoteDirection: direction, - }; - case UniswapVersion.v3: - return { - expectedConvertQuote, - expectedConvertQuoteOrTokenAmountInMaxWithSlippage, - transaction, - tradeExpires, - routePathArrayTokenMap: [ - this._fromToken, - turnTokenIntoEthForResponse( - this._toToken, - this._settings?.customNetwork?.nativeCurrency - ), - ], - routeText: `${this._fromToken.symbol} > ${ - turnTokenIntoEthForResponse( - this._toToken, - this._settings?.customNetwork?.nativeCurrency - ).symbol - }`, - routePathArray: [ - this._fromToken.contractAddress, - this._toToken.contractAddress, - ], - uniswapVersion, - liquidityProviderFee: routeContext.liquidityProviderFee, + liquidityProviderFee: uniswapVersion === UniswapVersion.v2 ? routeContext.liquidityProviderFee : 0, + liquidityProviderFeesV3: routeContext.liquidityProviderFeesV3, quoteDirection: direction, }; default: @@ -1990,6 +2290,42 @@ export class UniswapRouterFactory { } } + /** + * Converts uni v3 route to encoded bytes string to pass it to contract. + * Structure of encoded string: '0x${tokenAddress_0}${toHex(fee_0)}${tokenAddress_1}${toHex(fee_1)}...${tokenAddress_n}. + * toHex(fee_i) must be of length 6, so leading zeroes are added. + * @param tokens Tokens, included in route. + * @return string Encoded string. + */ + private getEncodedPoolsPath( + tokens: string[], + fees: number[], + direction: TradeDirection + ): string { + if (tokens.length < 2 || tokens.length - fees.length != 1) { + throw new UniswapError( + 'Invalid V3 Route', + ErrorCodes.tradePathIsNotSupported + ); + } + const convertedTokens = + direction == TradeDirection.input ? tokens : deepClone(tokens).reverse(); + const convertedFees = + direction == TradeDirection.input ? fees : deepClone(fees).reverse(); + + const initialTokenAddress = convertedTokens[0]; + let contractPath = initialTokenAddress.slice(2).toLowerCase(); + + for (let i = 0; i < convertedFees.length; i++) { + contractPath += percentToFeeAmount(convertedFees[i]) + .toString(16) + .padStart(6, '0'); + contractPath += convertedTokens[i + 1].slice(2).toLowerCase(); + } + + return `0x${contractPath}`; + } + /** * Get the trade path */ @@ -2297,4 +2633,4 @@ export class UniswapRouterFactory { return ETH_SYMBOL; } -} +} \ No newline at end of file diff --git a/src/factories/router/v2/uniswap-router-contract.factory.v2.ts b/src/factories/router/v2/uniswap-router-contract.factory.v2.ts index e55726c1..1160a22f 100644 --- a/src/factories/router/v2/uniswap-router-contract.factory.v2.ts +++ b/src/factories/router/v2/uniswap-router-contract.factory.v2.ts @@ -1,18 +1,25 @@ -import { BigNumberish, BytesLike } from 'ethers'; -import { ContractContext as RouterContractContext } from '../../../ABI/types/uniswap-router-v2'; +import { BigNumber, BigNumberish, BytesLike, Contract } from 'ethers'; +import { JsonFragment } from '@ethersproject/abi'; import { EthersProvider } from '../../../ethers-provider'; import { UniswapContractContextV2 } from '../../../uniswap-contract-context/uniswap-contract-context-v2'; +import { DEFAULT_ROUTER_METHOD, IRouterMethods } from '../models/route-methods'; export class UniswapRouterContractFactoryV2 { - private _uniswapRouterContract = - this._ethersProvider.getContract( - JSON.stringify(UniswapContractContextV2.routerAbi), - this._routerAddress - ); + private _uniswapRouterContract = this._ethersProvider.getContract( + JSON.stringify(this._routerAbi), + this._routerAddress + ); + + private _uniswapRouterNames: IRouterMethods = { + ...DEFAULT_ROUTER_METHOD, + ...this._routerMethods, + }; constructor( private _ethersProvider: EthersProvider, - private _routerAddress: string = UniswapContractContextV2.routerAddress + private _routerAddress: string = UniswapContractContextV2.routerAddress, + private _routerAbi: JsonFragment[] = UniswapContractContextV2.routerAbi, + private _routerMethods: Partial = DEFAULT_ROUTER_METHOD ) {} public addLiquidity( @@ -26,7 +33,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'addLiquidity', + this._uniswapRouterNames['addLiquidity'], [ tokenA, tokenB, @@ -49,7 +56,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'addLiquidityETH', + this._uniswapRouterNames['addLiquidityETH'], [token, amountTokenDesired, amountTokenMin, amountETHMin, to, deadline] ); } @@ -66,7 +73,7 @@ export class UniswapRouterContractFactoryV2 { amountIn, path ); - return amounts.map((c) => c.toHexString()); + return amounts.map((c: BigNumber) => c.toHexString()); } public async quote( @@ -89,7 +96,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'removeLiquidity', + this._uniswapRouterNames['removeLiquidity'], [tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline] ); } @@ -103,7 +110,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'removeLiquidity', + this._uniswapRouterNames['removeLiquidity'], [token, liquidity, amountTokenMin, amountETHMin, to, deadline] ); } @@ -117,7 +124,9 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'removeLiquidityETHSupportingFeeOnTransferTokens', + this._uniswapRouterNames[ + 'removeLiquidityETHSupportingFeeOnTransferTokens' + ], [token, liquidity, amountTokenMin, amountETHMin, to, deadline] ); } @@ -135,7 +144,7 @@ export class UniswapRouterContractFactoryV2 { s: BytesLike ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'removeLiquidityETHWithPermit', + this._uniswapRouterNames['removeLiquidityETHWithPermit'], [ token, liquidity, @@ -164,7 +173,9 @@ export class UniswapRouterContractFactoryV2 { s: BytesLike ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'removeLiquidityETHWithPermitSupportingFeeOnTransferTokens', + this._uniswapRouterNames[ + 'removeLiquidityETHWithPermitSupportingFeeOnTransferTokens' + ], [ token, liquidity, @@ -194,7 +205,7 @@ export class UniswapRouterContractFactoryV2 { s: BytesLike ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'removeLiquidityWithPermit', + this._uniswapRouterNames['removeLiquidityWithPermit'], [ tokenA, tokenB, @@ -218,7 +229,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapExactETHForTokens', + this._uniswapRouterNames['swapExactETHForTokens'], [amountOutMin, path, to, deadline] ); } @@ -230,7 +241,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapETHForExactTokens', + this._uniswapRouterNames['swapETHForExactTokens'], [amountOut, path, to, deadline] ); } @@ -243,7 +254,9 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapExactETHForTokensSupportingFeeOnTransferTokens', + this._uniswapRouterNames[ + 'swapExactETHForTokensSupportingFeeOnTransferTokens' + ], [amountIn, amountOutMin, path, to, deadline] ); } @@ -256,7 +269,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapExactTokensForETH', + this._uniswapRouterNames['swapExactTokensForETH'], [amountIn, amountOutMin, path, to, deadline] ); } @@ -269,7 +282,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapTokensForExactETH', + this._uniswapRouterNames['swapTokensForExactETH'], [amountOut, amountInMax, path, to, deadline] ); } @@ -282,7 +295,9 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapExactTokensForETHSupportingFeeOnTransferTokens', + this._uniswapRouterNames[ + 'swapExactTokensForETHSupportingFeeOnTransferTokens' + ], [amountIn, amountOutMin, path, to, deadline] ); } @@ -295,7 +310,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapExactTokensForTokens', + this._uniswapRouterNames['swapExactTokensForTokens'], [amountIn, amountOutMin, path, to, deadline] ); } @@ -308,7 +323,7 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapTokensForExactTokens', + this._uniswapRouterNames['swapTokensForExactTokens'], [amountOut, amountInMax, path, to, deadline] ); } @@ -321,7 +336,9 @@ export class UniswapRouterContractFactoryV2 { deadline: BigNumberish ): string { return this._uniswapRouterContract.interface.encodeFunctionData( - 'swapExactTokensForTokensSupportingFeeOnTransferTokens', + this._uniswapRouterNames[ + 'swapExactTokensForTokensSupportingFeeOnTransferTokens' + ], [amountIn, amountOutMin, path, to, deadline] ); } diff --git a/src/factories/router/v3/uniswap-router-contract.factory.v3.ts b/src/factories/router/v3/uniswap-router-contract.factory.v3.ts index b7c477ba..0736db6d 100644 --- a/src/factories/router/v3/uniswap-router-contract.factory.v3.ts +++ b/src/factories/router/v3/uniswap-router-contract.factory.v3.ts @@ -1,7 +1,10 @@ import { BigNumberish, BytesLike } from 'ethers'; +import { JsonFragment } from '@ethersproject/abi'; import { ContractContext as RouterContractContext, + ExactInputRequest, ExactInputSingleRequest, + ExactOutputRequest, ExactOutputSingleRequest, } from '../../../ABI/types/uniswap-router-v3'; import { EthersProvider } from '../../../ethers-provider'; @@ -10,13 +13,14 @@ import { UniswapContractContextV3 } from '../../../uniswap-contract-context/unis export class UniswapRouterContractFactoryV3 { private _uniswapRouterContract = this._ethersProvider.getContract( - JSON.stringify(UniswapContractContextV3.routerAbi), + JSON.stringify(this._routerAbi), this._routerAddress ); constructor( private _ethersProvider: EthersProvider, - private _routerAddress: string = UniswapContractContextV3.routerAddress + private _routerAddress: string = UniswapContractContextV3.routerAddress, + private _routerAbi: JsonFragment[] = UniswapContractContextV3.routerAbi ) {} /** @@ -41,6 +45,28 @@ export class UniswapRouterContractFactoryV3 { ); } + /** + * Exact input + * @param params The parameters + */ + public exactInput(params: ExactInputRequest): string { + return this._uniswapRouterContract.interface.encodeFunctionData( + 'exactInput', + [params] + ); + } + + /** + * The exact output + * @param params The parameters + */ + public exactOutput(params: ExactOutputRequest): string { + return this._uniswapRouterContract.interface.encodeFunctionData( + 'exactOutput', + [params] + ); + } + /** * Unwrap eth * @param amountMinimum The amount min diff --git a/src/factories/token/models/token.ts b/src/factories/token/models/token.ts index 2f4d4bd9..f39f1c46 100644 --- a/src/factories/token/models/token.ts +++ b/src/factories/token/models/token.ts @@ -1,4 +1,5 @@ import { ChainId } from '../../../enums/chain-id'; +import { FeeAmount } from '../../router/v3/enums/fee-amount-v3'; export interface Token { chainId: ChainId; @@ -7,3 +8,8 @@ export interface Token { symbol: string; name: string; } + +export interface Pool { + token: Token; + fee?: FeeAmount | undefined; +} diff --git a/src/factories/token/token.factory.public.spec.ts b/src/factories/token/token.factory.public.spec.ts index f20ca26b..3b83f2e3 100644 --- a/src/factories/token/token.factory.public.spec.ts +++ b/src/factories/token/token.factory.public.spec.ts @@ -168,7 +168,7 @@ describe('TokenFactoryPublic', () => { describe('erc20', () => { it('totalSupply', async () => { const result = await tokenFactory.totalSupply(); - expect(result).toEqual('0x0f4229ebe353e7b6'); + expect(result).toEqual('0x0f3be988963624b6'); }); }); diff --git a/src/factories/token/token.factory.spec.ts b/src/factories/token/token.factory.spec.ts index bb06706a..188720ab 100644 --- a/src/factories/token/token.factory.spec.ts +++ b/src/factories/token/token.factory.spec.ts @@ -164,7 +164,7 @@ describe('TokenFactory', () => { describe('erc20', () => { it('totalSupply', async () => { const result = await tokenFactory.totalSupply(); - expect(result).toEqual('0x0f4229ebe353e7b6'); + expect(result).toEqual('0x0f3be988963624b6'); }); }); diff --git a/src/uniswap-contract-context/get-uniswap-contracts.ts b/src/uniswap-contract-context/get-uniswap-contracts.ts index e6e42a95..1e7b1293 100644 --- a/src/uniswap-contract-context/get-uniswap-contracts.ts +++ b/src/uniswap-contract-context/get-uniswap-contracts.ts @@ -42,6 +42,34 @@ export const uniswapContracts = { return UniswapContractContextV2.pairAddress; }, + + getRouterAbi: ( + cloneUniswapContractDetails: CloneUniswapContractDetails | undefined + ) => { + if ( + cloneUniswapContractDetails && + cloneUniswapContractDetails.v2Override && + cloneUniswapContractDetails.v2Override.routerAbi + ) { + return cloneUniswapContractDetails.v2Override.routerAbi; + } + + return UniswapContractContextV2.routerAbi; + }, + + getRouterMethods: ( + cloneUniswapContractDetails: CloneUniswapContractDetails | undefined + ) => { + if ( + cloneUniswapContractDetails && + cloneUniswapContractDetails.v2Override && + cloneUniswapContractDetails.v2Override.routerMethods + ) { + return cloneUniswapContractDetails.v2Override.routerMethods; + } + + return UniswapContractContextV2.routerMethods; + }, }, v3: { getRouterAddress: ( @@ -82,5 +110,19 @@ export const uniswapContracts = { return UniswapContractContextV3.quoterAddress; }, + + getRouterAbi: ( + cloneUniswapContractDetails: CloneUniswapContractDetails | undefined + ) => { + if ( + cloneUniswapContractDetails && + cloneUniswapContractDetails.v3Override && + cloneUniswapContractDetails.v3Override.routerAbi + ) { + return cloneUniswapContractDetails.v3Override.routerAbi; + } + + return UniswapContractContextV3.routerAbi; + }, }, }; diff --git a/src/uniswap-contract-context/uniswap-contract-context-v2.ts b/src/uniswap-contract-context/uniswap-contract-context-v2.ts index 43c2d9d8..0f286b6a 100644 --- a/src/uniswap-contract-context/uniswap-contract-context-v2.ts +++ b/src/uniswap-contract-context/uniswap-contract-context-v2.ts @@ -1,4 +1,8 @@ import { JsonFragment } from '@ethersproject/abi'; +import { + DEFAULT_ROUTER_METHOD, + IRouterMethods, +} from "../factories/router/models/route-methods"; export class UniswapContractContextV2 { /** @@ -30,4 +34,9 @@ export class UniswapContractContextV2 { * Uniswap v2 pair */ public static pairAbi: JsonFragment[] = require('../ABI/uniswap-pair-v2.json'); + + /** + * Router Methods + */ + public static routerMethods: IRouterMethods = DEFAULT_ROUTER_METHOD; }