From fcb4eb59080e2d1d79038ae1264e75d75373a559 Mon Sep 17 00:00:00 2001 From: OjusWiZard Date: Thu, 14 Dec 2023 13:00:56 +0530 Subject: [PATCH] feat (quipuswap): add tests for quipuswap class and controller Signed-off-by: OjusWiZard --- package.json | 2 +- src/connectors/quipuswap/quipuswap.ts | 1 - .../quipuswap/quipuswap.routes.test.ts | 435 ++++++++++++++++++ .../connectors/quipuswap/quipuswap.test.ts | 93 ++++ yarn.lock | 8 +- 5 files changed, 533 insertions(+), 6 deletions(-) create mode 100644 test-bronze/connectors/quipuswap/quipuswap.routes.test.ts create mode 100644 test-bronze/connectors/quipuswap/quipuswap.test.ts diff --git a/package.json b/package.json index 45026b36e9..a181a4818a 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@pancakeswap/v3-sdk": "^3.7.0", "@pangolindex/sdk": "^1.1.0", "@perp/sdk-curie": "^1.16.0", - "@quipuswap/tokens-whitelist": "^1.1.22", + "@quipuswap/tokens-whitelist": "^1.1.24", "@sushiswap/sdk": "^5.0.0-canary.116", "@taquito/rpc": "^17.0.0", "@taquito/signer": "^17.0.0", diff --git a/src/connectors/quipuswap/quipuswap.ts b/src/connectors/quipuswap/quipuswap.ts index 5cb815103d..e3d3bd28e1 100644 --- a/src/connectors/quipuswap/quipuswap.ts +++ b/src/connectors/quipuswap/quipuswap.ts @@ -87,7 +87,6 @@ export class QuipuSwap extends QuipuBase { * @param baseToken Token output from the transaction * @param quoteToken Token input for the transaction * @param amount Amount of `baseToken` desired from the transaction - * @param allowedSlippage (Optional) should be of the form '1/10'. */ public estimateBuyTrade( baseToken: string, diff --git a/test-bronze/connectors/quipuswap/quipuswap.routes.test.ts b/test-bronze/connectors/quipuswap/quipuswap.routes.test.ts new file mode 100644 index 0000000000..ebd76d39a9 --- /dev/null +++ b/test-bronze/connectors/quipuswap/quipuswap.routes.test.ts @@ -0,0 +1,435 @@ +import { BigNumber } from 'bignumber.js'; +import express from 'express'; +import { Express } from 'express-serve-static-core'; +import request from 'supertest'; +import { AmmRoutes } from '../../../src/amm/amm.routes'; +import { patch, unpatch } from '../../../test/services/patch'; +import { Tezos } from '../../../src/chains/tezos/tezos'; +import { QuipuSwap } from '../../../src/connectors/quipuswap/quipuswap'; +let app: Express; +let tezos: Tezos; +let quipuswap: QuipuSwap; + + +beforeAll(async () => { + app = express(); + app.use(express.json()); + + tezos = Tezos.getInstance('mainnet'); + await tezos.init(); + quipuswap = QuipuSwap.getInstance('mainnet'); + + + app.use('/amm', AmmRoutes.router); +}); + +afterEach(() => { + unpatch(); +}); + +afterAll(async () => { + await tezos.close(); +}); + +const address: string = 'tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV'; + +const patchGetWallet = () => { + patch(tezos, 'getWallet', () => { + return { + signer: { + publicKeyHash: () => 'tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV' + }, + estimate: { + batch: () => [ + { + totalCost: 100, + gasLimit: 100, + }, + { + totalCost: 200, + gasLimit: 200, + } + ] + } + }; + }); +}; + +const patchGasPrice = () => { + patch(tezos, 'gasPrice', () => 123456); +}; + +const patchEstimateBuyTrade = () => { + patch(quipuswap, 'estimateBuyTrade', () => { + return { + trade: [], + inputAmount: new BigNumber(1000000), + outputAmount: new BigNumber(1000000), + price: new BigNumber(1), + }; + }); +}; + +const patchEstimateSellTrade = () => { + patch(quipuswap, 'estimateSellTrade', () => { + return { + trade: [], + inputAmount: new BigNumber(1000000), + outputAmount: new BigNumber(1000000), + price: new BigNumber(1), + }; + }); +}; + +const patchExecuteTrade = () => { + patch(quipuswap, 'executeTrade', () => { + return { hash: '000000000000000', operations: [{ counter: 21 }] }; + }); +}; + +describe('POST /amm/price', () => { + it('should return 200 for BUY', async () => { + patchGetWallet(); + patchGasPrice(); + patchEstimateBuyTrade(); + patchExecuteTrade(); + + await request(app) + .post(`/amm/price`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + side: 'BUY', + }) + .set('Accept', 'application/json') + .expect(200) + .then((res: any) => { + expect(res.body.amount).toEqual('1.000000'); + expect(res.body.rawAmount).toEqual('1000000'); + }); + }); + + it('should return 200 for SELL', async () => { + patchGetWallet(); + patchGasPrice(); + patchEstimateSellTrade(); + patchExecuteTrade(); + + await request(app) + .post(`/amm/price`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect(200) + .then((res: any) => { + expect(res.body.amount).toEqual('1.000000'); + expect(res.body.rawAmount).toEqual('1000000'); + }); + }); + + it('should return 500 for unrecognized quote symbol', async () => { + patchGetWallet(); + patchEstimateSellTrade(); + + await request(app) + .post(`/amm/price`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: '$', + base: 'XTZ', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect(500); + }); + + it('should return 500 for unrecognized base symbol', async () => { + patchGetWallet(); + patchEstimateSellTrade(); + + await request(app) + .post(`/amm/price`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: '$', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect(500); + }); + + it('should return 500 when the estimateSellTrade operation fails', async () => { + patchGetWallet(); + patchEstimateSellTrade(); + + patch(quipuswap, 'estimateSellTrade', () => { + return 'error'; + }); + + await request(app) + .post(`/amm/price`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect(500); + }); +}); + +describe('POST /amm/trade', () => { + const patchForBuy = () => { + patchGetWallet(); + patchGasPrice(); + patchEstimateBuyTrade(); + patchExecuteTrade(); + }; + + it('should return 200 for BUY', async () => { + patchForBuy(); + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'BUY', + }) + .set('Accept', 'application/json') + .expect(200) + .then((res: any) => { + expect(res.body.nonce).toEqual(21); + }); + }); + + const patchForSell = () => { + patchGetWallet(); + patchGasPrice(); + patchEstimateSellTrade(); + patchExecuteTrade(); + }; + + it('should return 200 for SELL', async () => { + patchForSell(); + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect(200) + .then((res: any) => { + expect(res.body.nonce).toEqual(21); + }); + }); + + it('should return 200 for SELL with limitPrice', async () => { + patchForSell(); + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'SELL', + limitPrice: '1', + }) + .set('Accept', 'application/json') + .expect(200); + }); + + it('should return 200 for BUY with limitPrice', async () => { + patchForBuy(); + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'BUY', + limitPrice: '999999999999999999999', + }) + .set('Accept', 'application/json') + .expect(200); + }); + + it('should return 500 for BUY with price greater than limitPrice', async () => { + patchForBuy(); + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'BUY', + limitPrice: '0.9', + }) + .set('Accept', 'application/json') + .expect(500); + }); + + it('should return 500 for SELL with price lower than limitPrice', async () => { + patchForSell(); + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'SELL', + limitPrice: '99999999999', + }) + .set('Accept', 'application/json') + .expect(500); + }); + + it('should return 404 when parameters are incorrect', async () => { + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: 1, + address: 'da8', + side: 'comprar', + }) + .set('Accept', 'application/json') + .expect(404); + }); + + it('should return 500 when the routerSwap operation fails', async () => { + patchGetWallet(); + patch(quipuswap, 'routerSwap', () => { + return 'error'; + }); + + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'SELL', + maxFeePerGas: '5000000000', + maxPriorityFeePerGas: '5000000000', + }) + .set('Accept', 'application/json') + .expect(500); + }); + + it('should return 500 when the priceSwapOut operation fails', async () => { + patchGetWallet(); + patch(quipuswap, 'priceSwapOut', () => { + return 'error'; + }); + + await request(app) + .post(`/amm/trade`) + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + quote: 'USDT', + base: 'XTZ', + amount: '1', + address, + side: 'BUY', + maxFeePerGas: '5000000000', + maxPriorityFeePerGas: '5000000000', + }) + .set('Accept', 'application/json') + .expect(500); + }); +}); + +describe('POST /amm/estimateGas', () => { + it('should return 200 for valid connector', async () => { + patchGasPrice(); + + await request(app) + .post('/amm/estimateGas') + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'quipuswap', + }) + .set('Accept', 'application/json') + .expect(200) + .then((res: any) => { + expect(res.body.network).toEqual('mainnet'); + expect(res.body.gasPrice).toEqual(0.123456); + expect(res.body.gasCost).toEqual('0.001852'); + }); + }); + + it('should return 500 for invalid connector', async () => { + patchGasPrice(); + + await request(app) + .post('/amm/estimateGas') + .send({ + chain: 'tezos', + network: 'mainnet', + connector: 'pangolin', + }) + .set('Accept', 'application/json') + .expect(500); + }); +}); diff --git a/test-bronze/connectors/quipuswap/quipuswap.test.ts b/test-bronze/connectors/quipuswap/quipuswap.test.ts new file mode 100644 index 0000000000..d174b14a54 --- /dev/null +++ b/test-bronze/connectors/quipuswap/quipuswap.test.ts @@ -0,0 +1,93 @@ +import BigNumber from 'bignumber.js'; +import { Tezosish } from '../../../src/services/common-interfaces'; +import { patch } from '../../../test/services/patch'; +import { Tezos } from '../../../src/chains/tezos/tezos'; +import { QuipuSwap } from '../../../src/connectors/quipuswap/quipuswap'; + + +describe('QuipuSwap', () => { + let quipuswap: QuipuSwap; + let tezos: Tezosish; + + const patchProvider = () => { + patch(tezos.provider.signer, 'publicKeyHash', () => 'tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV'); + patch(tezos.provider.contract, 'batch', () => { + return { + send: () => { + return { + status: 'applied', + hash: 'hash', + results: [] + } + } + } + }); + }; + + beforeAll(async () => { + tezos = Tezos.getInstance('mainnet'); + quipuswap = QuipuSwap.getInstance('mainnet'); + + await tezos.init(); + await quipuswap.init(); + }); + + describe('gasLimitEstimate', () => { + it('should return the gas limit estimate', () => { + const gasLimitEstimate = quipuswap.gasLimitEstimate; + expect(gasLimitEstimate).toEqual(15000); + }); + }); + + describe('getAllowedSlippage', () => { + it('should return the allowed slippage from the configuration', () => { + const allowedSlippage = quipuswap.getAllowedSlippage(); + expect(allowedSlippage).toEqual(new BigNumber('0.5')); + }); + + it('should return the allowed slippage from the parameter', () => { + const allowedSlippage = quipuswap.getAllowedSlippage('1/20'); + expect(allowedSlippage).toEqual(new BigNumber('5')); + }); + + it('should return the allowed slippage from the configuration if the parameter is invalid', () => { + const allowedSlippage = quipuswap.getAllowedSlippage('invalid'); + expect(allowedSlippage).toEqual(new BigNumber('0.5')); + }); + }); + + describe('estimateSellTrade', () => { + it('should return the expected trade for a valid trade', async () => { + const baseToken = 'QUIPU'; + const quoteToken = 'XTZ'; + const amount = new BigNumber(1); + const expectedTrade = quipuswap.estimateSellTrade(baseToken, quoteToken, amount); + expect(expectedTrade.outputAmount).toBeDefined(); + expect(expectedTrade.trade).toBeDefined(); + }); + }); + + describe('estimateBuyTrade', () => { + it('should return the expected trade for a valid trade', async () => { + const baseToken = 'DOGA'; + const quoteToken = 'XTZ'; + const amount = new BigNumber(1); + const expectedTrade = quipuswap.estimateBuyTrade(baseToken, quoteToken, amount); + expect(expectedTrade.inputAmount).toBeDefined(); + expect(expectedTrade.trade).toBeDefined(); + }); + }); + + describe('executeTrade', () => { + it('should execute the trade and return the hash and operations', async () => { + patchProvider(); + const baseToken = 'CTEZ'; + const quoteToken = 'XTZ'; + const amount = new BigNumber(1); + const expectedTrade = quipuswap.estimateBuyTrade(baseToken, quoteToken, amount); + const executedTrade = await quipuswap.executeTrade(tezos.provider, expectedTrade.trade); + expect(executedTrade.hash).toBeDefined(); + expect(executedTrade.operations).toBeDefined(); + }); + }); +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 46a9a3e06b..e2cb915da1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3063,10 +3063,10 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@quipuswap/tokens-whitelist@^1.1.22": - version "1.1.22" - resolved "https://registry.yarnpkg.com/@quipuswap/tokens-whitelist/-/tokens-whitelist-1.1.22.tgz#95754e90ea29633380a64ae2bb6a1b225e8b68d8" - integrity sha512-NWVrNHn1/y0665zvZb/l8kyxwdNfYN67zYrLHXd01BM1bFWxkwheh3CC6YlSzL/9kanIf4doP5qWpeAa+waEKw== +"@quipuswap/tokens-whitelist@^1.1.24": + version "1.1.24" + resolved "https://registry.yarnpkg.com/@quipuswap/tokens-whitelist/-/tokens-whitelist-1.1.24.tgz#0a9270727439dc304e739e48f7f2da7e71d68b81" + integrity sha512-EtKEcsAJm3v5RYcFndtAWNWU/vT78XDvM2xoGJjQZVk/9zIQiJwMlXdfFNyP2VtWUJqGTYIuSQHutL9TJuxqqw== "@redux-saga/core@^1.0.0": version "1.2.3"