From 2f16f010ab02a7334702d9f15bb794d588138dd8 Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 14:05:30 +0900 Subject: [PATCH 01/12] refactor: `DEV_PRIVATE_KEY` -> `TESTNET_PRIVATE_KEY` --- examples/cancel-order.ts | 2 +- examples/claim-order.ts | 2 +- examples/limit-order.ts | 2 +- examples/market-order.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/cancel-order.ts b/examples/cancel-order.ts index 2de51fe..f48044b 100644 --- a/examples/cancel-order.ts +++ b/examples/cancel-order.ts @@ -15,7 +15,7 @@ const main = async () => { const walletClient = createWalletClient({ chain: arbitrumSepolia, account: privateKeyToAccount( - (process.env.DEV_PRIVATE_KEY || '0x') as `0x${string}`, + (process.env.TESTNET_PRIVATE_KEY || '0x') as `0x${string}`, ), transport: http(), }) diff --git a/examples/claim-order.ts b/examples/claim-order.ts index eb67f94..8a61feb 100644 --- a/examples/claim-order.ts +++ b/examples/claim-order.ts @@ -15,7 +15,7 @@ const main = async () => { const walletClient = createWalletClient({ chain: arbitrumSepolia, account: privateKeyToAccount( - (process.env.DEV_PRIVATE_KEY || '0x') as `0x${string}`, + (process.env.TESTNET_PRIVATE_KEY || '0x') as `0x${string}`, ), transport: http(), }) diff --git a/examples/limit-order.ts b/examples/limit-order.ts index 695db97..0f14cc0 100644 --- a/examples/limit-order.ts +++ b/examples/limit-order.ts @@ -10,7 +10,7 @@ const main = async () => { const walletClient = createWalletClient({ chain: arbitrumSepolia, account: privateKeyToAccount( - (process.env.DEV_PRIVATE_KEY || '0x') as `0x${string}`, + (process.env.TESTNET_PRIVATE_KEY || '0x') as `0x${string}`, ), transport: http(), }) diff --git a/examples/market-order.ts b/examples/market-order.ts index b0bc182..85a02d7 100644 --- a/examples/market-order.ts +++ b/examples/market-order.ts @@ -10,7 +10,7 @@ const main = async () => { const walletClient = createWalletClient({ chain: arbitrumSepolia, account: privateKeyToAccount( - (process.env.DEV_PRIVATE_KEY || '0x') as `0x${string}`, + (process.env.TESTNET_PRIVATE_KEY || '0x') as `0x${string}`, ), transport: http(), }) From 8b45867814cba4b8d032b77cd682672b9569af21 Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 14:05:42 +0900 Subject: [PATCH 02/12] feat: add some examples --- examples/open-market.ts | 48 +++++++++++++++++++++++++++++++++++++++++ examples/pool/open.ts | 33 ++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 examples/open-market.ts create mode 100644 examples/pool/open.ts diff --git a/examples/open-market.ts b/examples/open-market.ts new file mode 100644 index 0000000..c1a85a1 --- /dev/null +++ b/examples/open-market.ts @@ -0,0 +1,48 @@ +import { createWalletClient, http, parseUnits } from 'viem' +import { arbitrumSepolia } from 'viem/chains' +import { privateKeyToAccount } from 'viem/accounts' +import * as dotenv from 'dotenv' +import { getMarket, openMarket } from '@clober/v2-sdk' + +dotenv.config() + +const main = async () => { + const walletClient = createWalletClient({ + chain: arbitrumSepolia, + account: privateKeyToAccount( + (process.env.TESTNET_PRIVATE_KEY || '0x') as `0x${string}`, + ), + transport: http(), + }) + + const transaction1 = await openMarket({ + chainId: arbitrumSepolia.id, + inputToken: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + outputToken: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + }) + const hash1 = await walletClient.sendTransaction({ + ...transaction1, + gasPrice: parseUnits('1', 9), + }) + console.log(`open market hash: ${hash1}`) + + const transaction2 = await openMarket({ + chainId: arbitrumSepolia.id, + inputToken: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + outputToken: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + }) + const hash2 = await walletClient.sendTransaction({ + ...transaction2, + gasPrice: parseUnits('1', 9), + }) + console.log(`open market hash: ${hash2}`) + + const market = await getMarket({ + chainId: arbitrumSepolia.id, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + }) + console.log(`market: `, market) +} + +main() diff --git a/examples/pool/open.ts b/examples/pool/open.ts new file mode 100644 index 0000000..b887967 --- /dev/null +++ b/examples/pool/open.ts @@ -0,0 +1,33 @@ +import * as dotenv from 'dotenv' +import { createWalletClient, http, parseUnits, zeroHash } from 'viem' +import { arbitrumSepolia } from 'viem/chains' +import { privateKeyToAccount } from 'viem/accounts' + +import { openPool } from '../../src' + +dotenv.config() + +const main = async () => { + const walletClient = createWalletClient({ + chain: arbitrumSepolia, + account: privateKeyToAccount( + (process.env.TESTNET_PRIVATE_KEY || '0x') as `0x${string}`, + ), + transport: http(), + }) + + const transaction = await openPool({ + chainId: arbitrumSepolia.id, + userAddress: '0x5F79EE8f8fA862E98201120d83c4eC39D9468D49', + tokenA: '0x00BFD44e79FB7f6dd5887A9426c8EF85A0CD23e0', + tokenB: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + }) + const hash = await walletClient.sendTransaction({ + ...transaction, + gasPrice: parseUnits('1', 9), + }) + console.log(`open pool hash: ${hash}`) +} + +main() From 4822eabe47aae2a94373e9df15fb05d51514f3f6 Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 14:06:23 +0900 Subject: [PATCH 03/12] feat: add test script in gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f48bd1a..5525a1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore +test.ts .graphclient/ docs From 1e038cbdabaf146ade1c2e6747e6c169cc31dcad Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 14:06:38 +0900 Subject: [PATCH 04/12] test: fix open pool test --- test/open.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/open.test.ts b/test/open.test.ts index 5875aef..3957927 100644 --- a/test/open.test.ts +++ b/test/open.test.ts @@ -103,11 +103,12 @@ test('try open pool', async () => { clients.map(({ testClient }) => { return testClient.reset({ jsonRpcUrl: FORK_URL, - blockNumber: 66686973n, + blockNumber: 83889075n, }) }), ) const { publicClient, walletClient } = clients[0] as any + const transaction1 = await openPool({ chainId: cloberTestChain.id, userAddress: '0x5F79EE8f8fA862E98201120d83c4eC39D9468D49', @@ -148,11 +149,12 @@ test('try open pool', async () => { }) expect(afterPool.isOpened).toEqual(true) - const transaction2 = await openMarket({ + const transaction2 = await openPool({ chainId: cloberTestChain.id, userAddress: '0x5F79EE8f8fA862E98201120d83c4eC39D9468D49', - inputToken: '0x0000000000000000000000000000000000000000', - outputToken: '0xEfC8df673Ac18CFa6b92A1eE8939C84506C9Faf3', + tokenA: '0xEfC8df673Ac18CFa6b92A1eE8939C84506C9Faf3', + tokenB: '0x0000000000000000000000000000000000000000', + salt: zeroHash, options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, From 4336d604b722dd3e4a0924e41850a51cc446c625 Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 15:25:18 +0900 Subject: [PATCH 05/12] fix: fix `emptyERC20PermitParams` --- src/constants/permit.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/constants/permit.ts b/src/constants/permit.ts index ede1cc7..654a393 100644 --- a/src/constants/permit.ts +++ b/src/constants/permit.ts @@ -1,9 +1,11 @@ +import { zeroHash } from 'viem' + export const emptyERC20PermitParams = { permitAmount: 0n, signature: { deadline: 0n, v: 0, - r: '0x', - s: '0x', + r: zeroHash, + s: zeroHash, }, } From 9d8051e7a62e810da97ec9b054a8d956c747ddd9 Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 15:25:45 +0900 Subject: [PATCH 06/12] test: add tc for add liquidity without swap --- test/add-liquidity.test.ts | 350 +++++++++++++++++++++++++++++++++++++ test/utils/currency.ts | 40 +++++ 2 files changed, 390 insertions(+) create mode 100644 test/add-liquidity.test.ts diff --git a/test/add-liquidity.test.ts b/test/add-liquidity.test.ts new file mode 100644 index 0000000..d9d5c07 --- /dev/null +++ b/test/add-liquidity.test.ts @@ -0,0 +1,350 @@ +import { expect, beforeEach, test } from 'vitest' +import { addLiquidity, getPool } from '@clober/v2-sdk' +import { formatUnits, zeroHash } from 'viem' + +import { cloberTestChain } from '../src/constants/test-chain' + +import { account, FORK_URL } from './utils/constants' +import { createProxyClients } from './utils/utils' +import { fetchLPBalance, fetchTokenBalance } from './utils/currency' + +const clients = createProxyClients( + Array.from({ length: 2 }, () => Math.floor(new Date().getTime())).map( + (id) => id, + ), +) + +beforeEach(async () => { + await Promise.all( + clients.map(({ testClient }) => { + return testClient.reset({ + jsonRpcUrl: FORK_URL, + blockNumber: 83907945n, + }) + }), + ) +}) + +test('Add liquidity without swap - 1', async () => { + const { publicClient, walletClient } = clients[0] as any + + const pool = await getPool({ + chainId: cloberTestChain.id, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + let [beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = + await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx1, result: result1 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '1.0', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash1 = await walletClient.sendTransaction({ + ...tx1!, + account, + gasPrice: tx1!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash1 }) + let [afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result1.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result1.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result1.lpCurrency.amount, + ) + + // add liquidity more + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx2, result: result2 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '0.7', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash2 = await walletClient.sendTransaction({ + ...tx2!, + account, + gasPrice: tx2!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash2 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result2.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result2.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result2.lpCurrency.amount, + ) +}) + +test('Add liquidity without swap - 2', async () => { + const { publicClient, walletClient } = clients[0] as any + + const pool = await getPool({ + chainId: cloberTestChain.id, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + let [beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = + await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx1, result: result1 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '0.7', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash1 = await walletClient.sendTransaction({ + ...tx1!, + account, + gasPrice: tx1!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash1 }) + let [afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result1.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result1.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result1.lpCurrency.amount, + ) + + // add liquidity more + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx2, result: result2 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '1.0', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash2 = await walletClient.sendTransaction({ + ...tx2!, + account, + gasPrice: tx2!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash2 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result2.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result2.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result2.lpCurrency.amount, + ) +}) + +// test('Add liquidity with swap', async () => { +// const { publicClient, walletClient } = clients[0] as any +// }) diff --git a/test/utils/currency.ts b/test/utils/currency.ts index 38527cc..07fe51a 100644 --- a/test/utils/currency.ts +++ b/test/utils/currency.ts @@ -1,6 +1,7 @@ import { PublicClient } from 'viem' import { CHAIN_IDS } from '../../src' +import { CONTRACT_ADDRESSES } from '../../src/constants/addresses' const _abi = [ { @@ -37,3 +38,42 @@ export const fetchTokenBalance = async ( args: [userAddress], }) } + +export const fetchLPBalance = async ( + publicClient: PublicClient, + chainId: CHAIN_IDS, + tokenId: bigint, + userAddress: `0x${string}`, +): Promise => { + return publicClient.readContract({ + address: CONTRACT_ADDRESSES[chainId]!.Rebalancer, + abi: [ + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'balanceOf', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + ] as const, + functionName: 'balanceOf', + args: [userAddress, tokenId], + }) +} From 00b023af92b69099ecd2d20cd1a3211f648b38cc Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 16:48:25 +0900 Subject: [PATCH 07/12] fix: fix `testnetPrice` --- src/apis/odos.ts | 4 ++-- src/call.ts | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/apis/odos.ts b/src/apis/odos.ts index a735fc7..c981d06 100644 --- a/src/apis/odos.ts +++ b/src/apis/odos.ts @@ -49,7 +49,7 @@ export async function fetchQuote({ amountOut: parseUnits( ( Number(formatUnits(amountIn, tokenIn.decimals)) * testnetPrice - ).toFixed(), + ).toString(), tokenOut.decimals, ), pathId: '0x', @@ -119,7 +119,7 @@ export async function fetchCallData({ const amountOut = parseUnits( ( Number(formatUnits(amountIn, tokenIn.decimals)) * testnetPrice - ).toFixed(), + ).toString(), tokenOut.decimals, ) diff --git a/src/call.ts b/src/call.ts index 43b8a26..405907d 100644 --- a/src/call.ts +++ b/src/call.ts @@ -1097,7 +1097,6 @@ export const addLiquidity = async ({ options?: { slippage?: number disableSwap?: boolean - testnetPrice?: string // token1 amount per token0 token0PermitParams?: ERC20PermitParam token1PermitParams?: ERC20PermitParam useSubgraph?: boolean @@ -1178,12 +1177,9 @@ export const addLiquidity = async ({ } if (!disableSwap) { - const token0Price = Number( - options?.testnetPrice ? options.testnetPrice : '1', - ) - const currencyBPerCurrencyA = isAddressEqual(token1, pool.currencyB.address) - ? token0Price - : 1 / token0Price + const currencyBPerCurrencyA = + Number(formatUnits(pool.liquidityB, pool.currencyB.decimals)) / + Number(formatUnits(pool.liquidityA, pool.currencyA.decimals)) const swapAmountA = parseUnits('1', pool.currencyA.decimals) const { amountOut: swapAmountB } = await fetchQuote({ chainId, From 8c57dde48a5edccb56de0e7ded819e540dc8139f Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 16:48:40 +0900 Subject: [PATCH 08/12] test: create add liquidity sith swap --- test/add-liquidity.test.ts | 472 ++++++++++++++++++++++++++++++++++++- 1 file changed, 469 insertions(+), 3 deletions(-) diff --git a/test/add-liquidity.test.ts b/test/add-liquidity.test.ts index d9d5c07..e3be474 100644 --- a/test/add-liquidity.test.ts +++ b/test/add-liquidity.test.ts @@ -345,6 +345,472 @@ test('Add liquidity without swap - 2', async () => { ) }) -// test('Add liquidity with swap', async () => { -// const { publicClient, walletClient } = clients[0] as any -// }) +test('Add liquidity one side with swap', async () => { + const { publicClient, walletClient } = clients[0] as any + + const pool = await getPool({ + chainId: cloberTestChain.id, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + let [beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = + await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx1, result: result1 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '1.0', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash1 = await walletClient.sendTransaction({ + ...tx1!, + account, + gasPrice: tx1!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash1 }) + let [afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result1.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result1.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result1.lpCurrency.amount, + ) + + // add liquidity more + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx2, result: result2 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '0', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash2 = await walletClient.sendTransaction({ + ...tx2!, + account, + gasPrice: tx2!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash2 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result2.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result2.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result2.lpCurrency.amount, + ) + + // add liquidity more + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx3, result: result3 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '0', + amount1: '1', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash3 = await walletClient.sendTransaction({ + ...tx3!, + account, + gasPrice: tx2!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash3 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result3.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result3.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result3.lpCurrency.amount, + ) + + expect(result2.lpCurrency.amount).toBe(result3.lpCurrency.amount) +}) + +test('Add liquidity two sides with swap', async () => { + const { publicClient, walletClient } = clients[0] as any + + const pool = await getPool({ + chainId: cloberTestChain.id, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + let [beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = + await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx1, result: result1 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '1.0', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash1 = await walletClient.sendTransaction({ + ...tx1!, + account, + gasPrice: tx1!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash1 }) + let [afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result1.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result1.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result1.lpCurrency.amount, + ) + + // add liquidity more + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx2, result: result2 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '0.2', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash2 = await walletClient.sendTransaction({ + ...tx2!, + account, + gasPrice: tx2!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash2 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result2.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result2.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result2.lpCurrency.amount, + ) + + // add liquidity more + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx3, result: result3 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '400', + amount1: '1', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash3 = await walletClient.sendTransaction({ + ...tx3!, + account, + gasPrice: tx3!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash3 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result3.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result3.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result3.lpCurrency.amount, + ) + + expect(result2.lpCurrency.amount).toBe(result3.lpCurrency.amount) +}) From 37cd9ea37fd62224bfcdb6eb08ba16e381c3b007 Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 16:53:01 +0900 Subject: [PATCH 09/12] feat: prevent passing native eth --- src/call.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/call.ts b/src/call.ts index 405907d..dc5f7e2 100644 --- a/src/call.ts +++ b/src/call.ts @@ -1109,6 +1109,12 @@ export const addLiquidity = async ({ lpCurrency: Currency6909Flow } }> => { + if ( + isAddressEqual(token0, zeroAddress) || + isAddressEqual(token1, zeroAddress) + ) { + throw new Error('ETH is not supported for adding liquidity') + } const publicClient = createPublicClient({ chain: CHAIN_MAP[chainId], transport: options?.rpcUrl ? http(options.rpcUrl) : http(), From 508291444eec96684deb4125296feb5af51c20f1 Mon Sep 17 00:00:00 2001 From: graykode Date: Wed, 25 Sep 2024 17:26:25 +0900 Subject: [PATCH 10/12] test: add `remove liquidity` tc --- test/remove-liquidity.test.ts | 260 ++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 test/remove-liquidity.test.ts diff --git a/test/remove-liquidity.test.ts b/test/remove-liquidity.test.ts new file mode 100644 index 0000000..4a6f73a --- /dev/null +++ b/test/remove-liquidity.test.ts @@ -0,0 +1,260 @@ +import { expect, beforeEach, test } from 'vitest' +import { addLiquidity, getPool, removeLiquidity } from '@clober/v2-sdk' +import { formatUnits, zeroHash } from 'viem' + +import { cloberTestChain } from '../src/constants/test-chain' + +import { account, FORK_URL } from './utils/constants' +import { createProxyClients } from './utils/utils' +import { fetchLPBalance, fetchTokenBalance } from './utils/currency' + +const clients = createProxyClients( + Array.from({ length: 2 }, () => Math.floor(new Date().getTime())).map( + (id) => id, + ), +) + +beforeEach(async () => { + await Promise.all( + clients.map(({ testClient }) => { + return testClient.reset({ + jsonRpcUrl: FORK_URL, + blockNumber: 83907945n, + }) + }), + ) +}) + +test('Remove liquidity', async () => { + const { publicClient, walletClient } = clients[0] as any + + const pool = await getPool({ + chainId: cloberTestChain.id, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + let [beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = + await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx1, result: result1 } = await addLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount0: '2000', + amount1: '1.0', + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash1 = await walletClient.sendTransaction({ + ...tx1!, + account, + gasPrice: tx1!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash1 }) + let [afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + expect(formatUnits(beforeUSDCBalance - afterUSDCBalance, 6)).toBe( + result1.currencyA.amount, + ) + expect(formatUnits(beforeWETHBalance - afterWETHBalance, 18)).toBe( + result1.currencyB.amount, + ) + expect(formatUnits(afterLPBalance - beforeLPBalance, 18)).toBe( + result1.lpCurrency.amount, + ) + + // remove liquidity + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx2, result: result2 } = await removeLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount: (Number(result1.lpCurrency.amount) / 2).toString(), + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash2 = await walletClient.sendTransaction({ + ...tx2!, + account, + gasPrice: tx2!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash2 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(afterUSDCBalance - beforeUSDCBalance, 6)).toBe( + result2.currencyA.amount, + ) + expect(formatUnits(afterWETHBalance - beforeWETHBalance, 18)).toBe( + result2.currencyB.amount, + ) + expect(formatUnits(beforeLPBalance - afterLPBalance, 18)).toBe( + result2.lpCurrency.amount, + ) + + // remove liquidity + ;[beforeUSDCBalance, beforeWETHBalance, beforeLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + const { transaction: tx3, result: result3 } = await removeLiquidity({ + chainId: cloberTestChain.id, + userAddress: account.address, + token0: '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + token1: '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + salt: zeroHash, + amount: (Number(result1.lpCurrency.amount) / 2).toString(), + options: { + rpcUrl: publicClient.transport.url!, + useSubgraph: false, + }, + }) + + const hash3 = await walletClient.sendTransaction({ + ...tx3!, + account, + gasPrice: tx3!.gasPrice! * 2n, + }) + await publicClient.waitForTransactionReceipt({ hash: hash3 }) + ;[afterUSDCBalance, afterWETHBalance, afterLPBalance] = await Promise.all([ + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0x00bfd44e79fb7f6dd5887a9426c8ef85a0cd23e0', + account.address, + ), + fetchTokenBalance( + publicClient, + cloberTestChain.id, + '0xF2e615A933825De4B39b497f6e6991418Fb31b78', + account.address, + ), + fetchLPBalance( + publicClient, + cloberTestChain.id, + BigInt(pool.key), + account.address, + ), + ]) + + expect(formatUnits(afterUSDCBalance - beforeUSDCBalance, 6)).toBe( + result3.currencyA.amount, + ) + expect(formatUnits(afterWETHBalance - beforeWETHBalance, 18)).toBe( + result3.currencyB.amount, + ) + expect(formatUnits(beforeLPBalance - afterLPBalance, 18)).toBe( + result3.lpCurrency.amount, + ) + + expect(result2.currencyA.amount).toBe(result3.currencyA.amount) + expect(result2.currencyB.amount).toBe(result3.currencyB.amount) +}) From 66bea6bbdf8d6e202d81156088c413ce01248311 Mon Sep 17 00:00:00 2001 From: graykode Date: Thu, 26 Sep 2024 01:31:03 +0900 Subject: [PATCH 11/12] feat: revert testnetPrice --- src/apis/odos.ts | 4 ++-- src/call.ts | 22 +++++++++++++++++----- test/add-liquidity.test.ts | 11 ++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/apis/odos.ts b/src/apis/odos.ts index c981d06..a109759 100644 --- a/src/apis/odos.ts +++ b/src/apis/odos.ts @@ -35,7 +35,7 @@ export async function fetchQuote({ tokenOut: Currency slippageLimitPercent: number userAddress: `0x${string}` - testnetPrice?: number // tokenOutAmount per tokenIn, for testnet chains only + testnetPrice?: number | undefined // tokenOutAmount per tokenIn, for testnet chains only }): Promise<{ amountOut: bigint pathId: string @@ -106,7 +106,7 @@ export async function fetchCallData({ tokenOut: Currency slippageLimitPercent: number userAddress: `0x${string}` - testnetPrice?: number // tokenOutAmount per tokenIn, for testnet chains only + testnetPrice?: number | undefined // tokenOutAmount per tokenIn, for testnet chains only }): Promise<{ amountOut: bigint data: `0x${string}` diff --git a/src/call.ts b/src/call.ts index dc5f7e2..e2dc513 100644 --- a/src/call.ts +++ b/src/call.ts @@ -30,7 +30,7 @@ import { parsePrice, } from './utils/prices' import { invertTick, toPrice } from './utils/tick' -import { getExpectedInput, getExpectedOutput } from './view' +import { getExpectedInput, getExpectedOutput, getQuoteToken } from './view' import { toBookId } from './utils/book-id' import { fetchIsApprovedForAll } from './utils/approval' import { fetchOnChainOrders } from './utils/order' @@ -1099,6 +1099,7 @@ export const addLiquidity = async ({ disableSwap?: boolean token0PermitParams?: ERC20PermitParam token1PermitParams?: ERC20PermitParam + testnetPrice?: number useSubgraph?: boolean } & DefaultWriteContractOptions }): Promise<{ @@ -1183,9 +1184,18 @@ export const addLiquidity = async ({ } if (!disableSwap) { - const currencyBPerCurrencyA = - Number(formatUnits(pool.liquidityB, pool.currencyB.decimals)) / - Number(formatUnits(pool.liquidityA, pool.currencyA.decimals)) + const currencyBPerCurrencyA = options?.testnetPrice + ? isAddressEqual( + getQuoteToken({ + chainId, + token0, + token1, + }), + pool.currencyA.address, + ) + ? 1 / Number(options.testnetPrice) + : Number(options.testnetPrice) + : undefined const swapAmountA = parseUnits('1', pool.currencyA.decimals) const { amountOut: swapAmountB } = await fetchQuote({ chainId, @@ -1230,7 +1240,9 @@ export const addLiquidity = async ({ tokenOut: pool.currencyA, slippageLimitPercent, userAddress: CONTRACT_ADDRESSES[chainId]!.Minter, - testnetPrice: 1 / currencyBPerCurrencyA, + testnetPrice: currencyBPerCurrencyA + ? 1 / currencyBPerCurrencyA + : undefined, }) swapParams.data = calldata amountA += actualDeltaA diff --git a/test/add-liquidity.test.ts b/test/add-liquidity.test.ts index e3be474..8f3ef14 100644 --- a/test/add-liquidity.test.ts +++ b/test/add-liquidity.test.ts @@ -19,7 +19,7 @@ beforeEach(async () => { clients.map(({ testClient }) => { return testClient.reset({ jsonRpcUrl: FORK_URL, - blockNumber: 83907945n, + blockNumber: 84040472n, }) }), ) @@ -72,6 +72,7 @@ test('Add liquidity without swap - 1', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + disableSwap: true, }, }) @@ -144,6 +145,7 @@ test('Add liquidity without swap - 1', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + disableSwap: true, }, }) @@ -232,6 +234,7 @@ test('Add liquidity without swap - 2', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + disableSwap: true, }, }) @@ -304,6 +307,7 @@ test('Add liquidity without swap - 2', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + disableSwap: true, }, }) @@ -392,6 +396,7 @@ test('Add liquidity one side with swap', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + disableSwap: true, }, }) @@ -464,6 +469,7 @@ test('Add liquidity one side with swap', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + testnetPrice: 2000, }, }) @@ -537,6 +543,7 @@ test('Add liquidity one side with swap', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + testnetPrice: 2000, }, }) @@ -699,6 +706,7 @@ test('Add liquidity two sides with swap', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + testnetPrice: 2000, }, }) @@ -772,6 +780,7 @@ test('Add liquidity two sides with swap', async () => { options: { rpcUrl: publicClient.transport.url!, useSubgraph: false, + testnetPrice: 2000, }, }) From 393f982d25bd5728f011f6fb2d9ca6a2031515f3 Mon Sep 17 00:00:00 2001 From: graykode Date: Thu, 26 Sep 2024 12:24:04 +0900 Subject: [PATCH 12/12] feat: v0.0.67 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b551a8..485bb6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@clober/v2-sdk", - "version": "0.0.66", + "version": "0.0.67", "description": "🛠 An SDK for building applications on top of Clober V2", "files": [ "dist"