diff --git a/packages/veraswap-app/src/atoms/atoms.ts b/packages/veraswap-app/src/atoms/atoms.ts index 4040b503..5a69817a 100644 --- a/packages/veraswap-app/src/atoms/atoms.ts +++ b/packages/veraswap-app/src/atoms/atoms.ts @@ -239,8 +239,9 @@ export const resetTransactionStateAtom = atom(null, (_, set) => { export const slippageAtom = atom<"auto" | number>("auto"); +/** Slippage tolerance in percentage. If slippageAtom is "auto", defaults to 2.5% */ export const slippageToleranceAtom = atom((get) => { const slippage = get(slippageAtom); - if (slippage === "auto") return 0.5; + if (slippage === "auto") return 2.5; // default to 2.5% return slippage; }); diff --git a/packages/veraswap-sdk/src/chains/arbitrum.ts b/packages/veraswap-sdk/src/chains/arbitrum.ts index f88a79dc..c5849477 100644 --- a/packages/veraswap-sdk/src/chains/arbitrum.ts +++ b/packages/veraswap-sdk/src/chains/arbitrum.ts @@ -6,8 +6,8 @@ export const arbitrum = { ...arbitrumChain, rpcUrls: { default: { - http: ["https://lb.drpc.org/ogrpc?network=arbitrum&dkey=AhYfrLlxSE3QsswFtgfKNqu1Ait49nQR75sVnqSgS7QB"], - webSocket: ["wss://lb.drpc.org/ogws?network=arbitrum&dkey=AhYfrLlxSE3QsswFtgfKNqu1Ait49nQR75sVnqSgS7QB"], + http: ["https://arbitrum-mainnet.core.chainstack.com/db36c07f913d8dbb8cdf6a8831c1bd23"], + webSocket: ["wss://arbitrum-mainnet.core.chainstack.com/db36c07f913d8dbb8cdf6a8831c1bd23"], }, }, custom: { diff --git a/packages/veraswap-sdk/src/chains/base.ts b/packages/veraswap-sdk/src/chains/base.ts index 13abb1e8..97f5082e 100644 --- a/packages/veraswap-sdk/src/chains/base.ts +++ b/packages/veraswap-sdk/src/chains/base.ts @@ -6,8 +6,11 @@ export const base = { ...baseChain, rpcUrls: { default: { - http: ["https://lb.drpc.org/ogrpc?network=base&dkey=AhYfrLlxSE3QsswFtgfKNqu1Ait49nQR75sVnqSgS7QB"], - webSocket: ["wss://lb.drpc.org/ogws?network=base&dkey=AhYfrLlxSE3QsswFtgfKNqu1Ait49nQR75sVnqSgS7QB"], + // TODO:investigate issue with Chainstack causing a revert error when fetching quote + // http: ["https://base-mainnet.core.chainstack.com/a3cbc100238b9972f7d4f44c446a2c16"], + // webSocket: ["wss://base-mainnet.core.chainstack.com/a3cbc100238b9972f7d4f44c446a2c16"], + http: ["https://base-mainnet.g.alchemy.com/v2/VFO0zUhBMB8WDuge_ro4KzKRkvwyrYas"], + webSocket: ["wss://base-mainnet.g.alchemy.com/v2/VFO0zUhBMB8WDuge_ro4KzKRkvwyrYas"], }, }, custom: { diff --git a/packages/veraswap-sdk/src/constants/uniswap.ts b/packages/veraswap-sdk/src/constants/uniswap.ts index b8e0c7ba..9c0e0bdf 100644 --- a/packages/veraswap-sdk/src/constants/uniswap.ts +++ b/packages/veraswap-sdk/src/constants/uniswap.ts @@ -161,7 +161,6 @@ export function getUniswapContracts(params?: { owner?: Address }) { /** Uniswap contracts for local deployment via CREATE2 & salt = bytes32(0) */ export const LOCAL_UNISWAP_CONTRACTS = getUniswapContracts(); -//TODO: Add metaquoter address (compute using poolManager address) export interface UniswapContracts { weth9: Address; v2Factory?: Address; @@ -176,7 +175,6 @@ export interface UniswapContracts { universalRouter: Address; } -// TODO: FIXME fix all weth9 addresses /** Uniswap contracts by chain */ export const UNISWAP_CONTRACTS: Record = { [opChainL1.id]: LOCAL_UNISWAP_CONTRACTS, diff --git a/packages/veraswap-sdk/src/uniswap/quote/getUniswapRoute.test.ts b/packages/veraswap-sdk/src/uniswap/quote/getUniswapRoute.test.ts index 3dccff58..1e2053e6 100644 --- a/packages/veraswap-sdk/src/uniswap/quote/getUniswapRoute.test.ts +++ b/packages/veraswap-sdk/src/uniswap/quote/getUniswapRoute.test.ts @@ -10,7 +10,7 @@ import { LOCAL_UNISWAP_CONTRACTS } from "../../constants/uniswap.js"; import { getUniswapV4Address } from "../../currency/currency.js"; import { anvilClientL1, wagmiConfig } from "../../test/constants.js"; import { addCommandsToRoutePlanner } from "../addCommandsToRoutePlanner.js"; -import { CommandType, RoutePlanner } from "../routerCommands.js"; +import { getCommandName, RoutePlanner } from "../routerCommands.js"; import { getRouterCommandsForQuote, getUniswapRouteExactIn } from "./getUniswapRoute.js"; @@ -32,7 +32,6 @@ describe("uniswap/quote/getUniswapRoute.test.ts", function () { // A/ETH, B/ETH Pools Exist // A/B Pool Does Not Exist const tokenA = getUniswapV4Address(LOCAL_CURRENCIES[0]); // 2 A tokens after this (Hyperlane) - const tokenB = getUniswapV4Address(LOCAL_CURRENCIES[3]); // 2 B tokens after this (Hyperlane) const tokenL34 = getUniswapV4Address(LOCAL_CURRENCIES[6]); const tokenL3 = getUniswapV4Address(LOCAL_CURRENCIES[7]); const tokenL4 = getUniswapV4Address(LOCAL_CURRENCIES[8]); @@ -52,514 +51,6 @@ describe("uniswap/quote/getUniswapRoute.test.ts", function () { // Helper function to get balance of a token const getBalance = (token: Address) => getBalanceForAddress(token, anvilClientL1.account.address); - describe("V4 Single", () => { - test("A -> L4", async () => { - const currencyIn = tokenA; - const currencyOut = tokenL4; - const currencyHops: Address[] = []; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn, - contracts, - }); - expect(route).toBeDefined(); - const { quote, amountOut, value } = route!; - - const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - await opChainL1Client.waitForTransactionReceipt({ hash }); - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - }); - - test("PERMIT2_TRANSFER_FROM command", async () => { - const currencyIn = tokenA; - - const veraswapFeeRecipient = "0x000000000000000000000000000000000000beef" as Address; - const feeRecipientBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapFeeRecipient); - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - - const feeAmount = 1000n; - - const routePlanner = new RoutePlanner(); - routePlanner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [currencyIn, veraswapFeeRecipient, feeAmount]); - - //Execute - const value = 0n; - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - await opChainL1Client.waitForTransactionReceipt({ hash }); - - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const feeRecipientBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapFeeRecipient); - - // Input balance decreased by exact amount - expect(currencyInBalanceBeforeSwap - currencyInBalanceAfterSwap).toBe(feeAmount); - // Input balance increased by exact amount - expect(feeRecipientBalanceAfterSwap - feeRecipientBalanceBeforeSwap).toBe(feeAmount); - }); - - test("A -> L4 with Veraswap fee", async () => { - const currencyIn = tokenA; - const currencyOut = tokenL4; - const currencyHops: Address[] = []; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - const feeBips = 100n; // 1% referral fee - const bipsDenominator = 10000n; // 100% = 10000 bips - const feeAmount = (amountIn * feeBips) / bipsDenominator; - const amountInWithoutFee = amountIn - feeAmount; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn: amountInWithoutFee, - contracts, - }); - expect(route).toBeDefined(); - const { quote, amountOut, value } = route!; - - const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; - const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; - const commands = getRouterCommandsForQuote({ - currencyIn, - currencyOut, - amountIn, - contracts, - veraswapFeeRecipient, - ...quote, - }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - await opChainL1Client.waitForTransactionReceipt({ hash }); - - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount - }); - - test("A -> L4 with Veraswap and referral fee", async () => { - const currencyIn = tokenA; - const currencyOut = tokenL4; - const currencyHops: Address[] = []; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - const feeBips = 100n; // 1% referral fee - const bipsDenominator = 10000n; // 100% = 10000 bips - const feeAmount = (amountIn * feeBips) / bipsDenominator; - const amountInWithoutFee = amountIn - feeAmount * 2n; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn: amountInWithoutFee, - contracts, - }); - expect(route).toBeDefined(); - const { quote, amountOut, value } = route!; - - const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; - const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; - - const referralAddress = "0x00000000000000000000000000000000000beef2" as Address; - const referralFeeRecipient = { address: referralAddress, bips: feeBips }; - - const commands = getRouterCommandsForQuote({ - currencyIn, - currencyOut, - amountIn, - contracts, - veraswapFeeRecipient, - referralFeeRecipient, - ...quote, - }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - const referralBalanceBeforeSwap = await getBalanceForAddress(currencyIn, referralAddress); - - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - await opChainL1Client.waitForTransactionReceipt({ hash }); - - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - const referralBalanceAfterSwap = await getBalanceForAddress(currencyIn, referralAddress); - - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount - expect(referralBalanceAfterSwap).toBe(referralBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount - }); - - test("ETH -> L4 with Veraswap fee", async () => { - const currencyIn = zeroAddress; // ETH - const currencyOut = tokenL4; - const currencyHops: Address[] = []; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - const feeBips = 100n; // 1% referral fee - const bipsDenominator = 10000n; // 100% = 10000 bips - const feeAmount = (amountIn * feeBips) / bipsDenominator; - const amountInWithoutFee = amountIn - feeAmount; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn: amountInWithoutFee, - contracts, - }); - expect(route).toBeDefined(); - const { quote, amountOut } = route!; - - const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; - const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; - const commands = getRouterCommandsForQuote({ - currencyIn, - currencyOut, - amountIn, - contracts, - veraswapFeeRecipient, - ...quote, - }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value: amountIn, // need to pass full amount in for ETH, value is amount after fees - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); - - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - - const gasUsed = receipt.gasUsed; - const effectiveGasPrice = receipt.effectiveGasPrice; - const gasCost = gasUsed * effectiveGasPrice; - - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn - gasCost); // Input balance decreased by exact amount and gas cost - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount - }); - - test("ETH -> L4 with Veraswap and referral fee", async () => { - const currencyIn = zeroAddress; // ETH - const currencyOut = tokenL4; - const currencyHops: Address[] = []; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - const feeBips = 100n; // 1% referral fee - const bipsDenominator = 10000n; // 100% = 10000 bips - const feeAmount = (amountIn * feeBips) / bipsDenominator; - const amountInWithoutFee = amountIn - feeAmount * 2n; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn: amountInWithoutFee, - contracts, - }); - expect(route).toBeDefined(); - const { quote, amountOut } = route!; - - const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; - const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; - - const referralAddress = "0x00000000000000000000000000000000000beef2" as Address; - const referralFeeRecipient = { address: referralAddress, bips: feeBips }; - - const commands = getRouterCommandsForQuote({ - currencyIn, - currencyOut, - amountIn, - contracts, - veraswapFeeRecipient, - referralFeeRecipient, - ...quote, - }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - const referralBalanceBeforeSwap = await getBalanceForAddress(currencyIn, referralAddress); - - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value: amountIn, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); - - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); - const referralBalanceAfterSwap = await getBalanceForAddress(currencyIn, referralAddress); - - const gasUsed = receipt.gasUsed; - const effectiveGasPrice = receipt.effectiveGasPrice; - const gasCost = gasUsed * effectiveGasPrice; - - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn - gasCost); // Input balance decreased by exact amount and gas cost - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount - expect(referralBalanceAfterSwap).toBe(referralBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount - }); - }); - - describe("V3 Single", () => { - test("A -> L3", async () => { - const currencyIn = tokenA; - const currencyOut = tokenL3; - const currencyHops: Address[] = []; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn, - contracts: { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }, - }); - expect(route).toBeDefined(); - const { quote, amountOut, value } = route!; - - const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - await opChainL1Client.waitForTransactionReceipt({ hash }); - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - }); - }); - - describe("V4 Multihop", () => { - test("A -> L4 -> B", async () => { - const currencyIn = tokenA; - const currencyOut = tokenB; - const currencyHops = [tokenL4]; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn, - contracts, - }); - expect(route).toBeDefined(); - const { quote, amountOut, value } = route!; - - const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - await opChainL1Client.waitForTransactionReceipt({ hash }); - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - }); - }); - - describe("V3 Multihop", () => { - test("A -> L3 -> B", async () => { - const currencyIn = tokenA; - const currencyOut = tokenB; - const currencyHops = [tokenL3]; - - const contracts = { - weth9: LOCAL_UNISWAP_CONTRACTS.weth9, - metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, - }; - - // Route - const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { - chainId: opChainL1.id, - currencyIn, - currencyOut, - currencyHops, - amountIn, - contracts, - }); - expect(route).toBeDefined(); - const { quote, amountOut, value } = route!; - - const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); - - const routePlanner = new RoutePlanner(); - addCommandsToRoutePlanner(routePlanner, commands); - - //Execute - const currencyInBalanceBeforeSwap = await getBalance(currencyIn); - const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); - const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); - const hash = await anvilClientL1.writeContract({ - abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], - address: LOCAL_UNISWAP_CONTRACTS.universalRouter, - value, - functionName: "execute", - args: [routePlanner.commands, routePlanner.inputs, deadline], - }); - await opChainL1Client.waitForTransactionReceipt({ hash }); - const currencyInBalanceAfterSwap = await getBalance(currencyIn); - const currencyOutBalanceAfterSwap = await getBalance(currencyOut); - expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount - expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount - }); - }); - describe("Mixed V4 -> V3", () => { test("L4 -> L34 -> L3", async () => { const currencyIn = tokenL4; @@ -627,8 +118,11 @@ describe("uniswap/quote/getUniswapRoute.test.ts", function () { }); expect(route).toBeDefined(); const { quote, amountOut, value } = route!; + console.log({ tokenA, tokenZ, weth: LOCAL_UNISWAP_CONTRACTS.weth9 }); + console.log({ path: quote.bestQuoteMultihop.path }); const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); + console.log({ commandsKeys: commands.map((c) => [getCommandName(c[0]), c[1].toString()]) }); const routePlanner = new RoutePlanner(); addCommandsToRoutePlanner(routePlanner, commands); @@ -644,9 +138,17 @@ describe("uniswap/quote/getUniswapRoute.test.ts", function () { functionName: "execute", args: [routePlanner.commands, routePlanner.inputs, deadline], }); + console.log({ hash }); await opChainL1Client.waitForTransactionReceipt({ hash }); const currencyInBalanceAfterSwap = await getBalance(currencyIn); const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + console.log({ + currencyOutBalanceBeforeSwap, + currencyOutBalanceAfterSwap, + currencyOut, + amountOut, + diff: currencyOutBalanceAfterSwap - currencyOutBalanceBeforeSwap, + }); expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount }); diff --git a/packages/veraswap-sdk/src/uniswap/quote/getUniswapRouteV3.test.ts b/packages/veraswap-sdk/src/uniswap/quote/getUniswapRouteV3.test.ts new file mode 100644 index 00000000..07a5b378 --- /dev/null +++ b/packages/veraswap-sdk/src/uniswap/quote/getUniswapRouteV3.test.ts @@ -0,0 +1,291 @@ +import { QueryClient } from "@tanstack/react-query"; +import { Address, parseAbi, parseUnits, zeroAddress } from "viem"; +import { describe, expect, test } from "vitest"; + +import { IERC20 } from "../../artifacts/IERC20.js"; +import { IUniversalRouter } from "../../artifacts/IUniversalRouter.js"; +import { opChainL1, opChainL1Client } from "../../chains/supersim.js"; +import { LOCAL_CURRENCIES } from "../../constants/tokens.js"; +import { LOCAL_UNISWAP_CONTRACTS } from "../../constants/uniswap.js"; +import { getUniswapV4Address } from "../../currency/currency.js"; +import { anvilClientL1, wagmiConfig } from "../../test/constants.js"; +import { addCommandsToRoutePlanner } from "../addCommandsToRoutePlanner.js"; +import { RoutePlanner } from "../routerCommands.js"; + +import { getRouterCommandsForQuote, getUniswapRouteExactIn } from "./getUniswapRoute.js"; + +//TODO: Add deeper tests with ETH wrap/unwrap +describe("uniswap/quote/getUniswapRoute.test.ts", function () { + const queryClient = new QueryClient(); + // Uniswap Error Abi + const UniswapErrorAbi = parseAbi([ + "error DeltaNotNegative(address)", + "error DeltaNotPositive(address)", + "error CurrencyNotSettled()", + "error PoolNotInitialized()", + "error V3TooLittleReceived()", + "error SwapAmountCannotBeZero()", + ]); + + const amountIn = parseUnits("0.01", 18); + + // A/ETH, B/ETH Pools Exist + // A/B Pool Does Not Exist + const tokenA = getUniswapV4Address(LOCAL_CURRENCIES[0]); // 2 A tokens after this (Hyperlane) + const tokenB = getUniswapV4Address(LOCAL_CURRENCIES[3]); // 2 B tokens after this (Hyperlane) + const tokenL3 = getUniswapV4Address(LOCAL_CURRENCIES[7]); + const tokenL4 = getUniswapV4Address(LOCAL_CURRENCIES[8]); + + const getBalanceForAddress = function (token: Address, address: Address) { + if (token === zeroAddress) return opChainL1Client.getBalance({ address }); + + return opChainL1Client.readContract({ + address: token, + abi: IERC20.abi, + functionName: "balanceOf", + args: [address], + }); + }; + + // Helper function to get balance of a token + const getBalance = (token: Address) => getBalanceForAddress(token, anvilClientL1.account.address); + + test("A -> L3", async () => { + const currencyIn = tokenA; + const currencyOut = tokenL3; + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn, + contracts: { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + await opChainL1Client.waitForTransactionReceipt({ hash }); + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + }); + + test("L4 -> ETH", async () => { + const currencyIn = tokenL4; + const currencyOut = zeroAddress; // ETH + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); + // console.log( + // receipt.logs.map((l) => ({ + // address: l.address, + // ...decodeEventLog({ + // abi: events, + // data: l.data, + // topics: l.topics, + // }), + // })), + // ); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + + const gasUsed = receipt.gasUsed; + const effectiveGasPrice = receipt.effectiveGasPrice; + const gasCost = gasUsed * effectiveGasPrice; + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut - gasCost); // Output balance increased by variable amount minus gas cost + // expect(currencyOutBalanceAfterSwap).toBe(amountOut); // Output balance increased by variable amount minus gas cost + }); + + test("L4 -> ETH with Veraswap and referral fee", async () => { + const currencyIn = tokenL4; + const currencyOut = zeroAddress; // ETH + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const feeBips = 25n; // 0.25% referral fee + const bipsDenominator = 10000n; // 100% = 10000 bips + const feeAmount = (amountIn * feeBips) / bipsDenominator; + const amountInWithoutFee = amountIn - feeAmount * 2n; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn: amountInWithoutFee, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; + const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; + + const referralAddress = "0x00000000000000000000000000000000000beef2" as Address; + const referralFeeRecipient = { address: referralAddress, bips: feeBips }; + + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + veraswapFeeRecipient, + referralFeeRecipient, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceBeforeSwap = await getBalanceForAddress(currencyIn, referralAddress); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceAfterSwap = await getBalanceForAddress(currencyIn, referralAddress); + + const gasUsed = receipt.gasUsed; + const effectiveGasPrice = receipt.effectiveGasPrice; + const gasCost = gasUsed * effectiveGasPrice; + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut - gasCost); // Output balance increased by variable amount minus gas cost + expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + expect(referralBalanceAfterSwap).toBe(referralBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + }); + + test("A -> L3 -> B", async () => { + const currencyIn = tokenA; + const currencyOut = tokenB; + const currencyHops = [tokenL3]; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + await opChainL1Client.waitForTransactionReceipt({ hash }); + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + }); +}); diff --git a/packages/veraswap-sdk/src/uniswap/quote/getUniswapRouteV4.test.ts b/packages/veraswap-sdk/src/uniswap/quote/getUniswapRouteV4.test.ts new file mode 100644 index 00000000..a8d6191c --- /dev/null +++ b/packages/veraswap-sdk/src/uniswap/quote/getUniswapRouteV4.test.ts @@ -0,0 +1,710 @@ +import { QueryClient } from "@tanstack/react-query"; +import { Address, parseAbi, parseEventLogs, parseUnits, zeroAddress } from "viem"; +import { describe, expect, test } from "vitest"; + +import { IERC20 } from "../../artifacts/IERC20.js"; +import { Swap } from "../../artifacts/IPoolManager.js"; +import { IUniversalRouter } from "../../artifacts/IUniversalRouter.js"; +import { opChainL1, opChainL1Client } from "../../chains/supersim.js"; +import { LOCAL_CURRENCIES } from "../../constants/tokens.js"; +import { LOCAL_UNISWAP_CONTRACTS } from "../../constants/uniswap.js"; +import { getUniswapV4Address } from "../../currency/currency.js"; +import { anvilClientL1, wagmiConfig } from "../../test/constants.js"; +import { addCommandsToRoutePlanner } from "../addCommandsToRoutePlanner.js"; +import { CommandType, RoutePlanner } from "../routerCommands.js"; + +import { getRouterCommandsForQuote, getUniswapRouteExactIn } from "./getUniswapRoute.js"; + +//TODO: Add deeper tests with ETH wrap/unwrap +describe("uniswap/quote/getUniswapRouteV4.test.ts", function () { + const queryClient = new QueryClient(); + // Uniswap Error Abi + const UniswapErrorAbi = parseAbi([ + "error DeltaNotNegative(address)", + "error DeltaNotPositive(address)", + "error CurrencyNotSettled()", + "error PoolNotInitialized()", + "error V3TooLittleReceived()", + "error SwapAmountCannotBeZero()", + ]); + + const amountIn = parseUnits("0.01", 18); + + // A/ETH, B/ETH Pools Exist + // A/B Pool Does Not Exist + const tokenA = getUniswapV4Address(LOCAL_CURRENCIES[0]); // 2 A tokens after this (Hyperlane) + const tokenB = getUniswapV4Address(LOCAL_CURRENCIES[3]); // 2 B tokens after this (Hyperlane) + const tokenL3 = getUniswapV4Address(LOCAL_CURRENCIES[7]); + const tokenL4 = getUniswapV4Address(LOCAL_CURRENCIES[8]); + + const getBalanceForAddress = function (token: Address, address: Address) { + if (token === zeroAddress) return opChainL1Client.getBalance({ address }); + + return opChainL1Client.readContract({ + address: token, + abi: IERC20.abi, + functionName: "balanceOf", + args: [address], + }); + }; + + // Helper function to get balance of a token + const getBalance = (token: Address) => getBalanceForAddress(token, anvilClientL1.account.address); + + test("A -> L4", async () => { + const currencyIn = tokenA; + const currencyOut = tokenL4; + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + await opChainL1Client.waitForTransactionReceipt({ hash }); + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + }); + + test("PERMIT2_TRANSFER_FROM command", async () => { + const currencyIn = tokenA; + + const veraswapFeeRecipient = "0x000000000000000000000000000000000000beef" as Address; + const feeRecipientBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapFeeRecipient); + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + + const feeAmount = 1000n; + + const routePlanner = new RoutePlanner(); + routePlanner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [currencyIn, veraswapFeeRecipient, feeAmount]); + + //Execute + const value = 0n; + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const feeRecipientBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapFeeRecipient); + + // Input balance decreased by exact amount + expect(currencyInBalanceBeforeSwap - currencyInBalanceAfterSwap).toBe(feeAmount); + // Input balance increased by exact amount + expect(feeRecipientBalanceAfterSwap - feeRecipientBalanceBeforeSwap).toBe(feeAmount); + }); + + test("A -> L4 with Veraswap fee", async () => { + const currencyIn = tokenA; + const currencyOut = tokenL4; + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const feeBips = 100n; // 1% referral fee + const bipsDenominator = 10000n; // 100% = 10000 bips + const feeAmount = (amountIn * feeBips) / bipsDenominator; + const amountInWithoutFee = amountIn - feeAmount; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn: amountInWithoutFee, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; + const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + veraswapFeeRecipient, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + }); + + test("A -> L4 with Veraswap and referral fee", async () => { + const currencyIn = tokenA; + const currencyOut = tokenL4; + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const feeBips = 100n; // 1% referral fee + const bipsDenominator = 10000n; // 100% = 10000 bips + const feeAmount = (amountIn * feeBips) / bipsDenominator; + const amountInWithoutFee = amountIn - feeAmount * 2n; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn: amountInWithoutFee, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; + const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; + + const referralAddress = "0x00000000000000000000000000000000000beef2" as Address; + const referralFeeRecipient = { address: referralAddress, bips: feeBips }; + + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + veraswapFeeRecipient, + referralFeeRecipient, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceBeforeSwap = await getBalanceForAddress(currencyIn, referralAddress); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceAfterSwap = await getBalanceForAddress(currencyIn, referralAddress); + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + expect(referralBalanceAfterSwap).toBe(referralBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + }); + + test("ETH -> L3 with Veraswap fee", async () => { + const currencyIn = zeroAddress; // ETH + const currencyOut = tokenL3; + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const feeBips = 100n; // 1% referral fee + const bipsDenominator = 10000n; // 100% = 10000 bips + const feeAmount = (amountIn * feeBips) / bipsDenominator; + const amountInWithoutFee = amountIn - feeAmount; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn: amountInWithoutFee, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut } = route!; + + const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; + const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + veraswapFeeRecipient, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value: amountIn, // need to pass full amount in for ETH, value is amount after fees + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + + const gasUsed = receipt.gasUsed; + const effectiveGasPrice = receipt.effectiveGasPrice; + const gasCost = gasUsed * effectiveGasPrice; + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn - gasCost); // Input balance decreased by exact amount and gas cost + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + }); + + test("ETH -> L3 with Veraswap and referral fee", async () => { + const currencyIn = zeroAddress; // ETH + const currencyOut = tokenL3; + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const feeBips = 100n; // 1% referral fee + const bipsDenominator = 10000n; // 100% = 10000 bips + const feeAmount = (amountIn * feeBips) / bipsDenominator; + const amountInWithoutFee = amountIn - feeAmount * 2n; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn: amountInWithoutFee, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut } = route!; + + const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; + const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; + + const referralAddress = "0x00000000000000000000000000000000000beef2" as Address; + const referralFeeRecipient = { address: referralAddress, bips: feeBips }; + + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + veraswapFeeRecipient, + referralFeeRecipient, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceBeforeSwap = await getBalanceForAddress(currencyIn, referralAddress); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value: amountIn, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceAfterSwap = await getBalanceForAddress(currencyIn, referralAddress); + + const gasUsed = receipt.gasUsed; + const effectiveGasPrice = receipt.effectiveGasPrice; + const gasCost = gasUsed * effectiveGasPrice; + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn - gasCost); // Input balance decreased by exact amount and gas cost + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + expect(referralBalanceAfterSwap).toBe(referralBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + }); + + test.only("L3 -> WETH", async () => { + const currencyIn = tokenL3; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const currencyOut = contracts.weth9; + const currencyHops: Address[] = []; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const commandsFromFn = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + // NOTE: With this, the output amount is not right + addCommandsToRoutePlanner(routePlanner, commandsFromFn); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + // const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const currencyOutBalanceBeforeSwap = await getBalance(contracts.weth9); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + + await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + // const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const currencyOutBalanceAfterSwap = await getBalance(contracts.weth9); + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBeGreaterThan(currencyOutBalanceBeforeSwap); + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount minus gas cost + }); + + test("L3 -> ETH", async () => { + const currencyIn = tokenL3; + const currencyOut = zeroAddress; // ETH + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const currencyHops: Address[] = []; + + // Same as calling getUniswapRouteExactIn + // const metaQuote = await opChainL1Client.readContract({ + // address: contracts.metaQuoter, + // abi: [metaQuoteExactInputSingle], + // functionName: "metaQuoteExactInputSingle", + // args: [ + // { + // exactCurrency: currencyIn, + // variableCurrency: currencyOut, + // exactAmount: amountIn, + // poolKeyOptions: Object.values(DEFAULT_POOL_PARAMS), + // }, + // ], + // }); + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut } = route!; + + expect(quote.bestQuoteSingle.poolKey.hooks).toBe(zeroAddress); + + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + ...quote, + }); + // const v4TradePlan = new V4Planner(); + // v4TradePlan.addAction(Actions.SETTLE, [currencyIn, amountIn, true]); + // v4TradePlan.addAction(Actions.SWAP_EXACT_IN_SINGLE, [ + // { + // poolKey: quote.bestQuoteSingle.poolKey, + // zeroForOne: quote.bestQuoteSingle.zeroForOne, + // amountIn, + // amountOutMinimum: amountOut, + // hookData: quote.bestQuoteSingle.hookData, + // }, + // ]); + // v4TradePlan.addAction(Actions.TAKE, [currencyOut, ACTION_CONSTANTS.MSG_SENDER, amountOut]); + // const commandInput = v4TradePlan.finalize(); + // const commandsFromPlan = [[CommandType.V4_SWAP, [commandInput]]] as unknown as CreateCommandParamsGeneric[]; + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + // addCommandsToRoutePlanner(routePlanner, commandsFromPlan); + + // Commpare V4_SWAP command inputs + // expect(commands[0][1][0]).toBe(commandsFromPlan[0][1][0]); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const poolManagerCurrencyOutBalanceBeforeSwap = await getBalanceForAddress( + currencyOut, + LOCAL_UNISWAP_CONTRACTS.v4PoolManager, + ); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value: 0n, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); + + // Can be used for debugging with if `cast run ${hash} --rpc-url http://127.0.0.1:8547 -vvv` + // Make sure to be running `pnpm test:watch src/uniswap/quote/getUniswapRouteV4.test.ts` + // console.log({ hash: receipt.transactionHash }); + + const swapEvent = parseEventLogs({ abi: [Swap], eventName: "Swap", logs: receipt.logs, strict: true })[0]; + const swapEventAmountOut = swapEvent.args.amount0; + expect(swapEventAmountOut).toBe(amountOut); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const poolManagerCurrencyOutBalanceAfterSwap = await getBalanceForAddress( + currencyOut, + LOCAL_UNISWAP_CONTRACTS.v4PoolManager, + ); + + // Swap done with the pool manager. Ensure its ETH balance decreases exactly by amountOut + expect(poolManagerCurrencyOutBalanceAfterSwap).toBe(poolManagerCurrencyOutBalanceBeforeSwap - amountOut); + + const gasUsed = receipt.gasUsed; + const effectiveGasPrice = receipt.effectiveGasPrice; + const gasCost = gasUsed * effectiveGasPrice; + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut - gasCost); // Output balance increased by variable amount minus gas cost + }); + + test("L3 -> ETH with Veraswap and referral fee", async () => { + const currencyIn = tokenL3; + const currencyOut = zeroAddress; // ETH + const currencyHops: Address[] = []; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + const feeBips = 25n; // 0.25% referral fee + const bipsDenominator = 10000n; // 100% = 10000 bips + const feeAmount = (amountIn * feeBips) / bipsDenominator; + const amountInWithoutFee = amountIn - feeAmount * 2n; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn: amountInWithoutFee, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const veraswapAddress = "0x000000000000000000000000000000000000beef" as Address; + const veraswapFeeRecipient = { address: veraswapAddress, bips: feeBips }; + + const referralAddress = "0x00000000000000000000000000000000000beef2" as Address; + const referralFeeRecipient = { address: referralAddress, bips: feeBips }; + + const commands = getRouterCommandsForQuote({ + currencyIn, + currencyOut, + amountIn, + contracts, + veraswapFeeRecipient, + referralFeeRecipient, + ...quote, + }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const veraswapBalanceBeforeSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceBeforeSwap = await getBalanceForAddress(currencyIn, referralAddress); + + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + const receipt = await opChainL1Client.waitForTransactionReceipt({ hash }); + + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + const veraswapBalanceAfterSwap = await getBalanceForAddress(currencyIn, veraswapAddress); + const referralBalanceAfterSwap = await getBalanceForAddress(currencyIn, referralAddress); + + const gasUsed = receipt.gasUsed; + const effectiveGasPrice = receipt.effectiveGasPrice; + const gasCost = gasUsed * effectiveGasPrice; + + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut - gasCost); // Output balance increased by variable amount minus gas cost + expect(veraswapBalanceAfterSwap).toBe(veraswapBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + expect(referralBalanceAfterSwap).toBe(referralBalanceBeforeSwap + feeAmount); // Input balance increased by exact amount + }); + + test("A -> L4 -> B", async () => { + const currencyIn = tokenA; + const currencyOut = tokenB; + const currencyHops = [tokenL4]; + + const contracts = { + weth9: LOCAL_UNISWAP_CONTRACTS.weth9, + metaQuoter: LOCAL_UNISWAP_CONTRACTS.metaQuoter, + }; + + // Route + const route = await getUniswapRouteExactIn(queryClient, wagmiConfig, { + chainId: opChainL1.id, + currencyIn, + currencyOut, + currencyHops, + amountIn, + contracts, + }); + expect(route).toBeDefined(); + const { quote, amountOut, value } = route!; + + const commands = getRouterCommandsForQuote({ currencyIn, currencyOut, amountIn, contracts, ...quote }); + + const routePlanner = new RoutePlanner(); + addCommandsToRoutePlanner(routePlanner, commands); + + //Execute + const currencyInBalanceBeforeSwap = await getBalance(currencyIn); + const currencyOutBalanceBeforeSwap = await getBalance(currencyOut); + const deadline = BigInt(Math.floor(Date.now() / 1000) + 600); + const hash = await anvilClientL1.writeContract({ + abi: [...IUniversalRouter.abi, ...UniswapErrorAbi], + address: LOCAL_UNISWAP_CONTRACTS.universalRouter, + value, + functionName: "execute", + args: [routePlanner.commands, routePlanner.inputs, deadline], + }); + await opChainL1Client.waitForTransactionReceipt({ hash }); + const currencyInBalanceAfterSwap = await getBalance(currencyIn); + const currencyOutBalanceAfterSwap = await getBalance(currencyOut); + expect(currencyInBalanceAfterSwap).toBe(currencyInBalanceBeforeSwap - amountIn); // Input balance decreased by exact amount + expect(currencyOutBalanceAfterSwap).toBe(currencyOutBalanceBeforeSwap + amountOut); // Output balance increased by variable amount + }); +}); diff --git a/packages/veraswap-sdk/src/uniswap/routerCommands.ts b/packages/veraswap-sdk/src/uniswap/routerCommands.ts index 880fbda0..153017d4 100644 --- a/packages/veraswap-sdk/src/uniswap/routerCommands.ts +++ b/packages/veraswap-sdk/src/uniswap/routerCommands.ts @@ -326,3 +326,37 @@ export function createCommand( return { type, encodedInput: parameters[0] as any }; } } + +const commandTypeString = { + [CommandType.V3_SWAP_EXACT_IN]: "V3_SWAP_EXACT_IN", + [CommandType.V3_SWAP_EXACT_OUT]: "V3_SWAP_EXACT_OUT", + [CommandType.PERMIT2_TRANSFER_FROM]: "PERMIT2_TRANSFER_FROM", + [CommandType.PERMIT2_PERMIT_BATCH]: "PERMIT2_PERMIT_BATCH", + [CommandType.SWEEP]: "SWEEP", + [CommandType.TRANSFER]: "TRANSFER", + [CommandType.PAY_PORTION]: "PAY_PORTION", + + [CommandType.V2_SWAP_EXACT_IN]: "V2_SWAP_EXACT_IN", + [CommandType.V2_SWAP_EXACT_OUT]: "V2_SWAP_EXACT_OUT", + [CommandType.PERMIT2_PERMIT]: "PERMIT2_PERMIT", + [CommandType.WRAP_ETH]: "WRAP_ETH", + [CommandType.UNWRAP_WETH]: "UNWRAP_WETH", + [CommandType.PERMIT2_TRANSFER_FROM_BATCH]: "PERMIT2_TRANSFER_FROM_BATCH", + [CommandType.BALANCE_CHECK_ERC20]: "BALANCE_CHECK_ERC20", + + [CommandType.V4_SWAP]: "V4_SWAP", + [CommandType.V3_POSITION_MANAGER_PERMIT]: "V3_POSITION_MANAGER_PERMIT", + [CommandType.V3_POSITION_MANAGER_CALL]: "V3_POSITION_MANAGER_CALL", + [CommandType.V4_INITIALIZE_POOL]: "V4_INITIALIZE_POOL", + [CommandType.V4_POSITION_MANAGER_CALL]: "V4_POSITION_MANAGER_CALL", + + [CommandType.EXECUTE_SUB_PLAN]: "EXECUTE_SUB_PLAN", + + // Custom Commands + [CommandType.CALL_TARGET]: "CALL_TARGET", +}; + +// First part of the keys is the numbered indices, second part is the names +export function getCommandName(command: CommandType): string { + return commandTypeString[command]; +} diff --git a/packages/veraswap-sdk/test/CommandsBuilder/CommandsBuilderV4Test.s.sol b/packages/veraswap-sdk/test/CommandsBuilder/CommandsBuilderV4Test.s.sol index 5ad7afa6..8849b802 100644 --- a/packages/veraswap-sdk/test/CommandsBuilder/CommandsBuilderV4Test.s.sol +++ b/packages/veraswap-sdk/test/CommandsBuilder/CommandsBuilderV4Test.s.sol @@ -23,7 +23,6 @@ contract CommandsBuilderV4Test is UniswapBaseTest { super.setUp(); } - /***** V4 *****/ // A -> B function test_V4_A_B() public { PoolUtils.createV4Pool(tokenA, tokenB, v4PositionManager, 10 ether); @@ -145,13 +144,8 @@ contract CommandsBuilderV4Test is UniswapBaseTest { // Currency (Currency currencyIn, Currency currencyOut) = (tokenA, weth9); PathKey[] memory path = new PathKey[](1); - path[0] = PathKey({ - intermediateCurrency: eth, - fee: 3000, - tickSpacing: 60, - hooks: IHooks(address(0)), - hookData: "" - }); + path[0] = + PathKey({intermediateCurrency: eth, fee: 3000, tickSpacing: 60, hooks: IHooks(address(0)), hookData: ""}); (bytes memory commands, bytes[] memory commandInputs) = CommandsBuilderLibrary.getSwapExactInCommands( weth9, @@ -225,13 +219,8 @@ contract CommandsBuilderV4Test is UniswapBaseTest { // Currency (Currency currencyIn, Currency currencyOut) = (tokenA, tokenB); PathKey[] memory path = new PathKey[](2); - path[0] = PathKey({ - intermediateCurrency: liq4, - fee: 3000, - tickSpacing: 60, - hooks: IHooks(address(0)), - hookData: "" - }); + path[0] = + PathKey({intermediateCurrency: liq4, fee: 3000, tickSpacing: 60, hooks: IHooks(address(0)), hookData: ""}); path[1] = PathKey({ intermediateCurrency: currencyOut, fee: 3000,