From 02a722a64430f9b84d87b57c5ddf4f64c628bd7d Mon Sep 17 00:00:00 2001 From: websterlcl Date: Tue, 22 Jun 2021 17:00:28 +0800 Subject: [PATCH 1/3] added testpoolswap --- .../category/poolpair/testpoolswap.test.ts | 183 ++++++++++++++++++ .../src/category/poolpair.ts | 25 +++ packages/testing/src/token.ts | 2 +- website/docs/jellyfish/api/poolpair.md | 21 ++ 4 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts diff --git a/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts b/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts new file mode 100644 index 0000000000..101a342835 --- /dev/null +++ b/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts @@ -0,0 +1,183 @@ +import { MasterNodeRegTestContainer } from '@defichain/testcontainers' +import { ContainerAdapterClient } from '../../container_adapter_client' +import BigNumber from 'bignumber.js' +import { addPoolLiquidity, createPoolPair, createToken, getNewAddress, mintTokens } from '@defichain/testing' +import { RpcApiError } from '../../../src' +import { PoolPairInfo, PoolPairsResult } from '@defichain/jellyfish-api-core/category/poolpair' + +describe('Poolpair', () => { + const container = new MasterNodeRegTestContainer() + const client = new ContainerAdapterClient(container) + + beforeAll(async () => { + await container.start() + await container.waitForReady() + await container.waitForWalletCoinbaseMaturity() + }) + + afterAll(async () => { + await container.stop() + }) + + it('should testpoolswap', async () => { + const tokenAddress = await getNewAddress(container) + const dfiAddress = await getNewAddress(container) + const poolLiquidityAddress = await getNewAddress(container) + + await createToken(container, 'CAT', { collateralAddress: tokenAddress }) + await mintTokens(container, 'CAT', { address: dfiAddress }) + await createPoolPair(container, 'CAT', 'DFI') + await addPoolLiquidity(container, { + tokenA: 'CAT', + amountA: 1000, + tokenB: 'DFI', + amountB: 500, + shareAddress: poolLiquidityAddress + }) + + const poolpairResult: PoolPairsResult = await container.call('getpoolpair', ['CAT-DFI']) + expect(Object.keys(poolpairResult).length).toStrictEqual(1) + + // Note(canonbrother): simulate poolswap calculation to find reserveB + // case: swap 6 CAT to DFI + // 1000 : 500 = sqrt(1000 * 500) = 707.10678118 + // 1666 : ? = sqrt(1666 * ?) = 707.10678118 + // ? = 707.10678118^2 / 1666 = 300.120048014 + // 1666 : 300.120048014 = 707.10678118 + // 500 - 300.120048014 = 199.879951986 <-- testpoolswap returns + const poolpair: PoolPairInfo = Object.values(poolpairResult)[0] + const reserveAAfter: BigNumber = new BigNumber(poolpair.reserveA).plus(666) + const reserveBAfter: BigNumber = new BigNumber(poolpair.totalLiquidity).pow(2).div(reserveAAfter) + + const result = await client.poolpair.testPoolSwap({ + from: tokenAddress, + tokenFrom: 'CAT', + amountFrom: 666, + to: await getNewAddress(container), + tokenTo: 'DFI' + }) // 199.99999729@0 + + const testPoolSwapResultAmount = new BigNumber(result.split('@')[0]) // 199.99999729 + const swapped = new BigNumber(poolpair.reserveB).minus(reserveBAfter) // 199.87995198635029880408 + + // Note(canonbrother): the result has slightly diff, use toFixed() to ignore the decimals + expect(swapped.toFixed(0)).toStrictEqual(testPoolSwapResultAmount.toFixed(0)) + }) + + it('should testpoolswap with maxPrice', async () => { + expect.assertions(0) + + const tokenAddress = await getNewAddress(container) + const dfiAddress = await getNewAddress(container) + const poolLiquidityAddress = await getNewAddress(container) + + await createToken(container, 'ELF', { collateralAddress: tokenAddress }) + await mintTokens(container, 'ELF', { address: dfiAddress }) + await createPoolPair(container, 'ELF', 'DFI') + await addPoolLiquidity(container, { + tokenA: 'ELF', + amountA: 200, + tokenB: 'DFI', + amountB: 500, + shareAddress: poolLiquidityAddress + }) + + try { + await client.poolpair.testPoolSwap({ + from: tokenAddress, + tokenFrom: 'ELF', + amountFrom: 666, + to: await getNewAddress(container), + tokenTo: 'DFI', + // reserveA/reserveB: 0.4 + // reserveB/reserveA: 2.5 + // if set maxPrice lower than 2.5 will hit error + maxPrice: 2.5 + }) + } catch (err) { + // should not reach here + expect(err).toBeUndefined() + } + }) + + it('should be failed as maxPrice is set lower than reserveB/reserveA', async () => { + const tokenAddress = await getNewAddress(container) + const dfiAddress = await getNewAddress(container) + const poolLiquidityAddress = await getNewAddress(container) + + await createToken(container, 'ELF', { collateralAddress: tokenAddress }) + await mintTokens(container, 'ELF', { address: dfiAddress }) + await createPoolPair(container, 'ELF', 'DFI') + await addPoolLiquidity(container, { + tokenA: 'ELF', + amountA: 1000, + tokenB: 'DFI', + amountB: 500, + shareAddress: poolLiquidityAddress + }) + + const promise = client.poolpair.testPoolSwap({ + from: tokenAddress, + tokenFrom: 'ELF', + amountFrom: 666, + to: await getNewAddress(container), + tokenTo: 'DFI', + // reserveA/reserveB: 2 + // reserveB/reserveA: 0.5 + // set maxPrice lower than 0.5 will hit error + maxPrice: 0.4 + }) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Price is higher than indicated') + }) + + it('testpoolswap should not affect the ori poolpair data', async () => { + const tokenAddress = await getNewAddress(container) + const dfiAddress = await getNewAddress(container) + const poolLiquidityAddress = await getNewAddress(container) + + await createToken(container, 'DOG', { collateralAddress: tokenAddress }) + await mintTokens(container, 'DOG', { address: dfiAddress }) + await createPoolPair(container, 'DOG', 'DFI') + await addPoolLiquidity(container, { + tokenA: 'DOG', + amountA: 1000, + tokenB: 'DFI', + amountB: 500, + shareAddress: poolLiquidityAddress + }) + + const poolpairResultBefore: PoolPairsResult = await container.call('getpoolpair', ['DOG-DFI']) + expect(Object.keys(poolpairResultBefore).length).toStrictEqual(1) + + await client.poolpair.testPoolSwap({ + from: tokenAddress, + tokenFrom: 'CAT', + amountFrom: 666, + to: await getNewAddress(container), + tokenTo: 'DFI' + }) + + const poolpairResultAfter: PoolPairsResult = await container.call('getpoolpair', ['DOG-DFI']) + expect(Object.keys(poolpairResultAfter).length).toStrictEqual(1) + + expect(poolpairResultBefore).toStrictEqual(poolpairResultAfter) + }) + + it('should be failed as lack of liquidity', async () => { + const tokenBatAddress = await getNewAddress(container) + await createToken(container, 'BAT') + await mintTokens(container, 'BAT') + await createPoolPair(container, 'BAT', 'DFI') + + const promise = client.poolpair.testPoolSwap({ + from: tokenBatAddress, + tokenFrom: 'BAT', + amountFrom: 13, + to: await getNewAddress(container), + tokenTo: 'DFI' + }) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Lack of liquidity') + }) +}) diff --git a/packages/jellyfish-api-core/src/category/poolpair.ts b/packages/jellyfish-api-core/src/category/poolpair.ts index 690d7ec8af..a79eaefbcb 100644 --- a/packages/jellyfish-api-core/src/category/poolpair.ts +++ b/packages/jellyfish-api-core/src/category/poolpair.ts @@ -104,6 +104,22 @@ export class PoolPair { const { isMineOnly = true } = options return await this.client.call('listpoolshares', [pagination, verbose, isMineOnly], 'bignumber') } + + /** + * Create a test pool swap transaction to check pool swap's return result + * + * @param {TestPoolSwapMetadata} metadata a provided information to create test pool swap transaction + * @param {string} metadata.from address of the owner of tokenFrom + * @param {string} metadata.tokenFrom swap from token {symbol/id} + * @param {number} metadata.amountFrom amount from tokenA + * @param {to} metadata.to address of the owner of tokenTo + * @param {tokenTo} metadata.tokenTo swap to token {symbol/id} + * @param {maxPrice} [metadata.maxPrice] acceptable max price + * @return {Promise} // return format 'amount@token' + */ + async testPoolSwap (metadata: TestPoolSwapMetadata): Promise { + return await this.client.call('testpoolswap', [metadata], 'bignumber') + } } export interface CreatePoolPairMetadata { @@ -181,3 +197,12 @@ export interface AddPoolLiquidityUTXO { export interface PoolShareOptions { isMineOnly?: boolean } + +export interface TestPoolSwapMetadata { + from: string + tokenFrom: string + amountFrom: number + to: string + tokenTo: string + maxPrice?: number +} diff --git a/packages/testing/src/token.ts b/packages/testing/src/token.ts index 73a118594c..b0490e0d74 100644 --- a/packages/testing/src/token.ts +++ b/packages/testing/src/token.ts @@ -55,7 +55,7 @@ export async function mintTokens ( options?: MintTokensOptions ): Promise { const address = options?.address ?? await getNewAddress(container) - const utxoAmount = options?.utxoAmount ?? 100 + const utxoAmount = options?.utxoAmount ?? 2000 const mintAmount = options?.mintAmount ?? 2000 await utxosToAccount(container, utxoAmount, { address }) diff --git a/website/docs/jellyfish/api/poolpair.md b/website/docs/jellyfish/api/poolpair.md index ce84c58853..a8d691c95d 100644 --- a/website/docs/jellyfish/api/poolpair.md +++ b/website/docs/jellyfish/api/poolpair.md @@ -160,3 +160,24 @@ interface PoolShareOptions { isMineOnly?: boolean } ``` + +## testPoolSwap + +Create a test pool swap transaction to check pool swap's return result + +```ts title="client.poolpair.testPoolSwap()" +interface poolpair { + testPoolSwap (metadata: TestPoolSwapMetadata): Promise { + return await this.client.call('testpoolswap', [metadata], 'bignumber') + } +} + +interface TestPoolSwapMetadata { + from: string + tokenFrom: string + amountFrom: number + to: string + tokenTo: string + maxPrice?: number +} +``` \ No newline at end of file From 302559ad9475fae6f649d2b1be362becb88271ab Mon Sep 17 00:00:00 2001 From: websterlcl Date: Tue, 22 Jun 2021 17:31:48 +0800 Subject: [PATCH 2/3] fix duplicated token --- .../__tests__/category/poolpair/testpoolswap.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts b/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts index 101a342835..1e58842582 100644 --- a/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts @@ -105,11 +105,11 @@ describe('Poolpair', () => { const dfiAddress = await getNewAddress(container) const poolLiquidityAddress = await getNewAddress(container) - await createToken(container, 'ELF', { collateralAddress: tokenAddress }) - await mintTokens(container, 'ELF', { address: dfiAddress }) - await createPoolPair(container, 'ELF', 'DFI') + await createToken(container, 'FOX', { collateralAddress: tokenAddress }) + await mintTokens(container, 'FOX', { address: dfiAddress }) + await createPoolPair(container, 'FOX', 'DFI') await addPoolLiquidity(container, { - tokenA: 'ELF', + tokenA: 'FOX', amountA: 1000, tokenB: 'DFI', amountB: 500, @@ -118,7 +118,7 @@ describe('Poolpair', () => { const promise = client.poolpair.testPoolSwap({ from: tokenAddress, - tokenFrom: 'ELF', + tokenFrom: 'FOX', amountFrom: 666, to: await getNewAddress(container), tokenTo: 'DFI', From f86d26c25e3f59772b72d828fa07980b71f824de Mon Sep 17 00:00:00 2001 From: websterlcl Date: Tue, 22 Jun 2021 17:51:54 +0800 Subject: [PATCH 3/3] typo --- .../__tests__/category/poolpair/testpoolswap.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts b/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts index 1e58842582..fe8de7da8a 100644 --- a/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/poolpair/testpoolswap.test.ts @@ -39,7 +39,7 @@ describe('Poolpair', () => { expect(Object.keys(poolpairResult).length).toStrictEqual(1) // Note(canonbrother): simulate poolswap calculation to find reserveB - // case: swap 6 CAT to DFI + // case: swap 666 CAT to DFI // 1000 : 500 = sqrt(1000 * 500) = 707.10678118 // 1666 : ? = sqrt(1666 * ?) = 707.10678118 // ? = 707.10678118^2 / 1666 = 300.120048014