From b54633318c373e978effe4e306a5852cfb745587 Mon Sep 17 00:00:00 2001 From: surangap Date: Fri, 2 Jul 2021 23:35:53 +0800 Subject: [PATCH] Interface `icx_listorders` RPC (#402) * Added ICXOrderBook.listOrders() function, testing and documentation * Refactored code. * Refactored code. * Refactored code. --- .../category/icxorderbook/closeOffer.test.ts | 53 ++ .../category/icxorderbook/icx_setup.ts | 13 + .../category/icxorderbook/listOrders.test.ts | 547 ++++++++++++++++++ .../src/category/icxorderbook.ts | 65 +++ website/docs/jellyfish/api/icxorderbook.md | 52 ++ 5 files changed, 730 insertions(+) create mode 100644 packages/jellyfish-api-core/__tests__/category/icxorderbook/listOrders.test.ts diff --git a/packages/jellyfish-api-core/__tests__/category/icxorderbook/closeOffer.test.ts b/packages/jellyfish-api-core/__tests__/category/icxorderbook/closeOffer.test.ts index 9928a3de6a..5b9af0b953 100644 --- a/packages/jellyfish-api-core/__tests__/category/icxorderbook/closeOffer.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/icxorderbook/closeOffer.test.ts @@ -81,6 +81,59 @@ describe('ICXOrderBook.closeOffer', () => { expect(accountBTCAfterOfferClose).toStrictEqual(accountBTCBeforeOffer) }) + // NOTE(surangap): This test is failing. + // it('should close an offer for sell BTC order from chain:BTC to chain:DFI', async () => { + // // create order - maker + // const order: ICXOrder = { + // chainFrom: 'BTC', + // tokenTo: idDFI, + // ownerAddress: accountDFI, + // amountFrom: new BigNumber(2), + // orderPrice: new BigNumber(100) + // } + // const createOrderResult: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + // const createOrderTxId = createOrderResult.txid + // await container.generate(1) + + // // list ICX orders and check status + // const ordersAfterCreateOrder: Record = await client.call('icx_listorders', [], 'bignumber') + // expect((ordersAfterCreateOrder as Record)[createOrderTxId].status).toStrictEqual(ICXOrderStatus.OPEN) + + // const accountBTCBeforeOffer: Record = await client.call('getaccount', [accountBTC, {}, true], 'bignumber') + // // make offer to partial amount 10 DFI - taker + // const offer: ICXOffer = { + // orderTx: createOrderTxId, + // amount: new BigNumber(10), // + // ownerAddress: accountBTC, + // receivePubkey: '0348790cb93b203a8ea5ce07279cb209d807b535b2ca8b0988a6f7a6578e41f7a5' + // } + // const makeOfferResult = await client.icxorderbook.makeOffer(offer, []) + // const makeOfferTxId = makeOfferResult.txid + // await container.generate(1) + + // const accountBTCAfterOffer: Record = await client.call('getaccount', [accountBTC, {}, true], 'bignumber') + // // check fee of 0.01 DFI has been reduced from the accountBTCBefore[idDFI] + // // Fee = takerFeePerBTC(inBTC) * amount(inBTC) * DEX DFI per BTC rate + // expect(accountBTCAfterOffer[idDFI]).toStrictEqual(accountBTCBeforeOffer[idDFI].minus(0.01)) + + // // List the ICX offers for orderTx = createOrderTxId and check + // const ordersAfterMakeOffer: Record = await client.call('icx_listorders', [{ orderTx: createOrderTxId }], 'bignumber') + // expect(Object.keys(ordersAfterMakeOffer).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + // expect((ordersAfterMakeOffer as Record)[makeOfferTxId].status).toStrictEqual(ICXOrderStatus.OPEN) + + // // close offer makeOfferTxId - taker + // await client.icxorderbook.closeOffer(makeOfferTxId) + // await container.generate(1) + + // // List the ICX offers for orderTx = createOrderTxId and check no more offers + // const ordersAfterCloseOffer: Record = await await container.call('icx_listorders', [{ orderTx: createOrderTxId }]) + // expect(Object.keys(ordersAfterCloseOffer).length).toBe(1) // extra entry for the warning text returned by the RPC atm. + + // // check accountBTC balance, should be the same as accountBTCBeforeOffer + // const accountBTCAfterOfferClose: Record = await client.call('getaccount', [accountBTC, {}, true], 'bignumber') + // expect(accountBTCAfterOfferClose).toStrictEqual(accountBTCBeforeOffer) + // }) + it('should close an offer with input utxos', async () => { // create order - maker const order: ICXOrder = { diff --git a/packages/jellyfish-api-core/__tests__/category/icxorderbook/icx_setup.ts b/packages/jellyfish-api-core/__tests__/category/icxorderbook/icx_setup.ts index 6edbc47394..01f57f6312 100644 --- a/packages/jellyfish-api-core/__tests__/category/icxorderbook/icx_setup.ts +++ b/packages/jellyfish-api-core/__tests__/category/icxorderbook/icx_setup.ts @@ -107,4 +107,17 @@ export class ICXSetup { expect(result.ICX_TAKERFEE_PER_BTC as number).toStrictEqual(fee) ICX_TAKERFEE_PER_BTC = result.ICX_TAKERFEE_PER_BTC as number } + + async closeAllOpenOffers (): Promise { + const orders = await this.container.call('icx_listorders', []) + for (const orderTx of Object.keys(orders).splice(1)) { + const offers = await this.container.call('icx_listorders', [{ orderTx: orderTx }]) + for (const offerTx of Object.keys(offers).splice(1)) { + if (offers[offerTx].status === 'OPEN') { + await this.container.call('icx_closeoffer', [offerTx]) + } + } + } + await this.container.generate(1) + } } diff --git a/packages/jellyfish-api-core/__tests__/category/icxorderbook/listOrders.test.ts b/packages/jellyfish-api-core/__tests__/category/icxorderbook/listOrders.test.ts new file mode 100644 index 0000000000..c87ad27fa0 --- /dev/null +++ b/packages/jellyfish-api-core/__tests__/category/icxorderbook/listOrders.test.ts @@ -0,0 +1,547 @@ +import { ContainerAdapterClient } from '../../container_adapter_client' +import { MasterNodeRegTestContainer } from '@defichain/testcontainers' +import { + ICXGenericResult, ICXOfferInfo, ICXOrderInfo, ICXOrder, ICXOffer, ICXOrderStatus, ICXOrderType +} from '../../../src/category/icxorderbook' +import BigNumber from 'bignumber.js' +import { accountBTC, accountDFI, DEX_DFI_PER_BTC_RATE, ICXSetup, ICX_TAKERFEE_PER_BTC, idDFI, symbolBTC, symbolDFI } from './icx_setup' + +describe('ICXOrderBook.listOrders', () => { + const container = new MasterNodeRegTestContainer() + const client = new ContainerAdapterClient(container) + const icxSetup = new ICXSetup(container) + + beforeAll(async () => { + await container.start() + await container.waitForReady() + await container.waitForWalletCoinbaseMaturity() + await icxSetup.createAccounts() + await icxSetup.createBTCToken() + await icxSetup.initializeTokensIds() + await icxSetup.mintBTCtoken(100) + await icxSetup.fundAccount(accountDFI, symbolDFI, 500) + await icxSetup.fundAccount(accountBTC, symbolDFI, 10) // for fee + await icxSetup.createBTCDFIPool() + await icxSetup.addLiquidityToBTCDFIPool(1, 100) + await icxSetup.setTakerFee(0.001) + }) + + afterAll(async () => { + await container.stop() + }) + + afterEach(async () => { + await icxSetup.closeAllOpenOffers() + }) + + it('should list all the orders', async () => { + // create first order - maker + const order: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(15), + orderPrice: new BigNumber(0.01) + } + const creatOrderResult: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = creatOrderResult.txid + await container.generate(1) + + // get order createOrderTxId and check + const retrivedOrder: Record = await client.icxorderbook.getOrder(createOrderTxId) + expect((retrivedOrder as Record)[createOrderTxId]).toStrictEqual( + { + // status: ICXOrderStatus.OPEN, //NOTE(surangap): uncomment after ain/#571 + type: ICXOrderType.INTERNAL, + tokenFrom: symbolDFI, + chainTo: order.chainTo, + receivePubkey: order.receivePubkey, + ownerAddress: order.ownerAddress, + amountFrom: order.amountFrom, + amountToFill: order.amountFrom, + orderPrice: order.orderPrice, + amountToFillInToAsset: order.amountFrom.multipliedBy(order.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + + // create second order - maker + const order2: ICXOrder = { + chainFrom: 'BTC', + tokenTo: idDFI, + ownerAddress: accountDFI, + amountFrom: new BigNumber(2), + orderPrice: new BigNumber(100) + } + const createOrder2Result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = createOrder2Result.txid + await container.generate(1) + + // list orders and check + const ordersAfterCreateOrder2: Record = await client.icxorderbook.listOrders() + // check details for createOrder2TxId + expect((ordersAfterCreateOrder2 as Record)[createOrder2TxId]).toStrictEqual( + { + status: ICXOrderStatus.OPEN, + type: ICXOrderType.EXTERNAL, + tokenTo: order2.tokenTo === '0' ? symbolDFI : symbolBTC, + chainFrom: order2.chainFrom, + ownerAddress: order2.ownerAddress, + amountFrom: order2.amountFrom, + amountToFill: order2.amountFrom, + orderPrice: order2.orderPrice, + amountToFillInToAsset: order2.amountFrom.multipliedBy(order2.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + // check details for createOrderTxId + expect((ordersAfterCreateOrder2 as Record)[createOrderTxId]).toStrictEqual( + { + status: ICXOrderStatus.OPEN, + type: ICXOrderType.INTERNAL, + tokenFrom: symbolDFI, + chainTo: order.chainTo, + receivePubkey: order.receivePubkey, + ownerAddress: order.ownerAddress, + amountFrom: order.amountFrom, + amountToFill: order.amountFrom, + orderPrice: order.orderPrice, + amountToFillInToAsset: order.amountFrom.multipliedBy(order.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + }) + + it('should list correct offers for an order', async () => { + // create two orders - maker + const order: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(15), + orderPrice: new BigNumber(0.01) + } + const createOrderResult: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = createOrderResult.txid + await container.generate(1) + + // create second order - maker + const order2: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(20), + orderPrice: new BigNumber(0.01) + } + const createOrder2Result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = createOrder2Result.txid + await container.generate(1) + + // create offer to order createOrderTxId + const offer: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.1), // 10 DFI = 0.1 BTC + ownerAddress: accountBTC + } + const makeOfferResult = await client.icxorderbook.makeOffer(offer, []) + const makeOfferTxId = makeOfferResult.txid + await container.generate(1) + + // create offer to order createOrder2TxId + const offer2: ICXOffer = { + orderTx: createOrder2TxId, + amount: new BigNumber(0.2), // 20 DFI = 0.2 BTC + ownerAddress: accountBTC + } + const makeOffer2Result = await client.icxorderbook.makeOffer(offer2, []) + const makeOffer2TxId = makeOffer2Result.txid + await container.generate(1) + + // list offers for createOrder2TxId and check + const offersForOrder2: Record = await client.icxorderbook.listOrders({ orderTx: createOrder2TxId }) + expect((offersForOrder2 as Record)[makeOffer2TxId]).toStrictEqual( + { + orderTx: createOrder2TxId, + status: ICXOrderStatus.OPEN, + amount: offer2.amount, + amountInFromAsset: offer2.amount.dividedBy(order2.orderPrice), + ownerAddress: offer2.ownerAddress, + takerFee: offer2.amount.multipliedBy(ICX_TAKERFEE_PER_BTC).multipliedBy(DEX_DFI_PER_BTC_RATE), + expireHeight: expect.any(BigNumber) + } + ) + + // list offers for createOrderTxId and check + const offersForOrder1: Record = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + expect((offersForOrder1 as Record)[makeOfferTxId]).toStrictEqual( + { + orderTx: createOrderTxId, + status: ICXOrderStatus.OPEN, + amount: offer.amount, + amountInFromAsset: offer.amount.dividedBy(order.orderPrice), + ownerAddress: offer.ownerAddress, + takerFee: offer.amount.multipliedBy(ICX_TAKERFEE_PER_BTC).multipliedBy(DEX_DFI_PER_BTC_RATE), + expireHeight: expect.any(BigNumber) + } + ) + }) + + it('should test ICXListOrderOptions.limit parameter', async () => { + // create an order - maker + const order: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(15), + orderPrice: new BigNumber(0.01) + } + const createOrderResult: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = createOrderResult.txid + await container.generate(1) + + // create second order - maker + const order2: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(15), + orderPrice: new BigNumber(0.01) + } + const createOrder2Result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = createOrder2Result.txid + await container.generate(1) + + // list ICX orders anc check + const ordersAfterCreateOrder2: Record = await client.icxorderbook.listOrders() + expect((ordersAfterCreateOrder2 as Record)[createOrderTxId]).toStrictEqual( + { + status: ICXOrderStatus.OPEN, + type: ICXOrderType.INTERNAL, + tokenFrom: symbolDFI, + chainTo: order.chainTo, + receivePubkey: order.receivePubkey, + ownerAddress: order.ownerAddress, + amountFrom: order.amountFrom, + amountToFill: order.amountFrom, + orderPrice: order.orderPrice, + amountToFillInToAsset: order.amountFrom.multipliedBy(order.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + expect((ordersAfterCreateOrder2 as Record)[createOrder2TxId]).toStrictEqual( + { + status: ICXOrderStatus.OPEN, + type: ICXOrderType.INTERNAL, + tokenFrom: order2.tokenFrom === '0' ? symbolDFI : symbolBTC, + chainTo: order2.chainTo, + receivePubkey: order2.receivePubkey, + ownerAddress: order2.ownerAddress, + amountFrom: order2.amountFrom, + amountToFill: order2.amountFrom, + orderPrice: order2.orderPrice, + amountToFillInToAsset: order2.amountFrom.multipliedBy(order2.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + + // now list orders with ICXListOrderOptions.limit=1 + const ordersWithLimit1: Record = await client.icxorderbook.listOrders({ limit: 1 }) + expect(Object.keys(ordersWithLimit1).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + + const accountBTCBeforeOffer: Record = await client.call('getaccount', [accountBTC, {}, true], 'bignumber') + // make offer to partial amount 10 DFI - taker + const offer: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.10), // 0.10 BTC = 10 DFI + ownerAddress: accountBTC + } + const makeOfferResult = await client.icxorderbook.makeOffer(offer, []) + const makeOfferTxId = makeOfferResult.txid + await container.generate(1) + + // make second offer to partial amount 2 DFI - taker + const offer2: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.02), // 0.02 BTC = 2 DFI + ownerAddress: accountBTC + } + const makeOffer2Result = await client.icxorderbook.makeOffer(offer2, []) + const makeOffer2TxId = makeOffer2Result.txid + await container.generate(1) + + const accountBTCAfterOffer: Record = await client.call('getaccount', [accountBTC, {}, true], 'bignumber') + + // check fee of 0.012 DFI has been reduced from the accountBTCBeforeOffer[idDFI] + // Fee = takerFeePerBTC(inBTC) * amount(inBTC) * DEX DFI per BTC rate + expect(accountBTCAfterOffer[idDFI]).toStrictEqual(accountBTCBeforeOffer[idDFI].minus(0.012)) + + // List the ICX offers for orderTx = createOrderTxId and check + const offesForOrder1: Record = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + expect(Object.keys(offesForOrder1).length).toBe(3) // extra entry for the warning text returned by the RPC atm. + expect((offesForOrder1 as Record)[makeOfferTxId]).toStrictEqual( + { + orderTx: createOrderTxId, + status: ICXOrderStatus.OPEN, + amount: offer.amount, + amountInFromAsset: offer.amount.dividedBy(order.orderPrice), + ownerAddress: offer.ownerAddress, + takerFee: offer.amount.multipliedBy(ICX_TAKERFEE_PER_BTC).multipliedBy(DEX_DFI_PER_BTC_RATE), + expireHeight: expect.any(BigNumber) + } + ) + expect((offesForOrder1 as Record)[makeOffer2TxId]).toStrictEqual( + { + orderTx: createOrderTxId, + status: ICXOrderStatus.OPEN, + amount: offer2.amount, + amountInFromAsset: offer2.amount.dividedBy(order.orderPrice), + ownerAddress: offer2.ownerAddress, + takerFee: offer2.amount.multipliedBy(ICX_TAKERFEE_PER_BTC).multipliedBy(DEX_DFI_PER_BTC_RATE), + expireHeight: expect.any(BigNumber) + } + ) + + // now list offers for orderTx = createOrderTxId with ICXListOrderOptions.limit=1 + const offesForOrder1WithLimit1: Record = await client.icxorderbook.listOrders({ orderTx: createOrderTxId, limit: 1 }) + expect(Object.keys(offesForOrder1WithLimit1).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + }) + + it('should test ICXListOrderOptions.closed parameter', async () => { + // create an order - maker + const order: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(20), + orderPrice: new BigNumber(0.01) + } + const createOrderResult: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = createOrderResult.txid + await container.generate(1) + + // create second order - maker + const order2: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(15), + orderPrice: new BigNumber(0.01) + } + const createOrder2Result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = createOrder2Result.txid + await container.generate(1) + + // list ICX orders anc check + const ordersAfterCreateOrder2: Record = await client.icxorderbook.listOrders() + expect((ordersAfterCreateOrder2 as Record)[createOrderTxId]).toStrictEqual( + { + status: ICXOrderStatus.OPEN, + type: ICXOrderType.INTERNAL, + tokenFrom: symbolDFI, + chainTo: order.chainTo, + receivePubkey: order.receivePubkey, + ownerAddress: order.ownerAddress, + amountFrom: order.amountFrom, + amountToFill: order.amountFrom, + orderPrice: order.orderPrice, + amountToFillInToAsset: order.amountFrom.multipliedBy(order.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + expect((ordersAfterCreateOrder2 as Record)[createOrder2TxId]).toStrictEqual( + { + status: ICXOrderStatus.OPEN, + type: ICXOrderType.INTERNAL, + tokenFrom: order2.tokenFrom === '0' ? symbolDFI : symbolBTC, + chainTo: order2.chainTo, + receivePubkey: order2.receivePubkey, + ownerAddress: order2.ownerAddress, + amountFrom: order2.amountFrom, + amountToFill: order2.amountFrom, + orderPrice: order2.orderPrice, + amountToFillInToAsset: order2.amountFrom.multipliedBy(order2.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + + // now close createOrder2TxId + const closeOrder2Result = await container.call('icx_closeorder', [createOrder2TxId]) + const closeOrder2TxId = closeOrder2Result.txid + await container.generate(1) + + // list ICX orders and check + const ordersAfterCloseOrder = await client.icxorderbook.listOrders() + expect((ordersAfterCloseOrder as Record)[createOrderTxId]).toStrictEqual( + { + status: ICXOrderStatus.OPEN, + type: ICXOrderType.INTERNAL, + tokenFrom: symbolDFI, + chainTo: order.chainTo, + receivePubkey: order.receivePubkey, + ownerAddress: order.ownerAddress, + amountFrom: order.amountFrom, + amountToFill: order.amountFrom, + orderPrice: order.orderPrice, + amountToFillInToAsset: order.amountFrom.multipliedBy(order.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber) + } + ) + + // list ICX orders with ICXListOrderOptions.closed=true and check + const ordersAfterCloseOrderWithClosedTrue = await client.icxorderbook.listOrders({ closed: true }) + expect((ordersAfterCloseOrderWithClosedTrue as Record)[createOrder2TxId]).toStrictEqual( + { + status: ICXOrderStatus.CLOSED, + type: ICXOrderType.INTERNAL, + tokenFrom: order2.tokenFrom === '0' ? symbolDFI : symbolBTC, + chainTo: order2.chainTo, + receivePubkey: order2.receivePubkey, + ownerAddress: order2.ownerAddress, + amountFrom: order2.amountFrom, + amountToFill: order2.amountFrom, + orderPrice: order2.orderPrice, + amountToFillInToAsset: order2.amountFrom.multipliedBy(order2.orderPrice), + height: expect.any(BigNumber), + expireHeight: expect.any(BigNumber), + closeHeight: expect.any(BigNumber), + closeTx: closeOrder2TxId + } + ) + + const accountBTCBeforeOffer: Record = await client.call('getaccount', [accountBTC, {}, true], 'bignumber') + // make offer to partial amount 10 DFI - taker + const offer: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.10), // 0.10 BTC = 10 DFI + ownerAddress: accountBTC + } + const makeOfferResult = await client.icxorderbook.makeOffer(offer, []) + const makeOfferTxId = makeOfferResult.txid + await container.generate(1) + + // make second offer to partial amount 10 DFI - taker + const offer2: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.10), // 0.10 BTC = 10 DFI + ownerAddress: accountBTC + } + const makeOffer2Result = await client.icxorderbook.makeOffer(offer2, []) + const makeOffer2TxId = makeOffer2Result.txid + await container.generate(1) + + const accountBTCAfterOffer: Record = await client.call('getaccount', [accountBTC, {}, true], 'bignumber') + // check fee of 0.02 DFI has been reduced from the accountBTCBeforeOffer[idDFI] + // Fee = takerFeePerBTC(inBTC) * amount(inBTC) * DEX DFI per BTC rate + expect(accountBTCAfterOffer[idDFI]).toStrictEqual(accountBTCBeforeOffer[idDFI].minus(0.02)) + + // List the ICX offers for orderTx = createOrderTxId and check + const offersForOrder1: Record = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + expect(Object.keys(offersForOrder1).length).toBe(3) // extra entry for the warning text returned by the RPC atm. + expect((offersForOrder1 as Record)[makeOfferTxId]).toStrictEqual( + { + orderTx: createOrderTxId, + status: ICXOrderStatus.OPEN, + amount: offer.amount, + amountInFromAsset: offer.amount.dividedBy(order.orderPrice), + ownerAddress: offer.ownerAddress, + takerFee: offer.amount.multipliedBy(ICX_TAKERFEE_PER_BTC).multipliedBy(DEX_DFI_PER_BTC_RATE), + expireHeight: expect.any(BigNumber) + } + ) + expect((offersForOrder1 as Record)[makeOffer2TxId]).toStrictEqual( + { + orderTx: createOrderTxId, + status: ICXOrderStatus.OPEN, + amount: offer2.amount, + amountInFromAsset: offer2.amount.dividedBy(order.orderPrice), + ownerAddress: offer2.ownerAddress, + takerFee: offer2.amount.multipliedBy(ICX_TAKERFEE_PER_BTC).multipliedBy(DEX_DFI_PER_BTC_RATE), + expireHeight: expect.any(BigNumber) + } + ) + + // now close makeOffer2TxId + await client.icxorderbook.closeOffer(makeOffer2TxId) + await container.generate(1) + + // List the ICX offers for orderTx = createOrderTxId and check + const offersForOrder1AfterCloseOffer: Record = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + expect(Object.keys(offersForOrder1AfterCloseOffer).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + expect((offersForOrder1AfterCloseOffer as Record)[makeOfferTxId]).toStrictEqual( + { + orderTx: createOrderTxId, + status: ICXOrderStatus.OPEN, + amount: offer.amount, + amountInFromAsset: offer.amount.dividedBy(order.orderPrice), + ownerAddress: offer.ownerAddress, + takerFee: offer.amount.multipliedBy(ICX_TAKERFEE_PER_BTC).multipliedBy(DEX_DFI_PER_BTC_RATE), + expireHeight: expect.any(BigNumber) + } + ) + + // now list the ICX offers for orderTx = createOrderTxId with ICXListOrderOptions.closed=true and check + const offersForOrder1AfterCloseOfferWithClosedTrue: Record = await client.icxorderbook.listOrders({ orderTx: createOrderTxId, closed: true }) + expect(Object.keys(offersForOrder1AfterCloseOfferWithClosedTrue).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + const offerInfoRetrived = offersForOrder1AfterCloseOfferWithClosedTrue as Record + expect(offerInfoRetrived[makeOffer2TxId].orderTx).toStrictEqual(offer2.orderTx) + expect(offerInfoRetrived[makeOffer2TxId].status).toStrictEqual(ICXOrderStatus.CLOSED) + expect(offerInfoRetrived[makeOffer2TxId].ownerAddress).toStrictEqual(offer2.ownerAddress) + }) + + it('should return an empty set when ICXListOrderOptions.orderTx is invalid', async () => { + // create an order - maker + const order: ICXOrder = { + tokenFrom: idDFI, + chainTo: 'BTC', + ownerAddress: accountDFI, + receivePubkey: '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', + amountFrom: new BigNumber(15), + orderPrice: new BigNumber(0.01) + } + const createOrderResult: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = createOrderResult.txid + await container.generate(1) + + // create offer to order createOrderTxId + const offer: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.1), // 10 DFI = 0.1 BTC + ownerAddress: accountBTC + } + await client.icxorderbook.makeOffer(offer, []) + await container.generate(1) + + // list offers for Tx Id "123" and check + const ordersForInvalidOrderTX123: Record = await client.icxorderbook.listOrders({ orderTx: '123' }) + expect(ordersForInvalidOrderTX123).toStrictEqual( + { + WARNING: 'ICX and Atomic Swap are experimental features. You might end up losing your funds. USE IT AT YOUR OWN RISK.' + } + ) + + // NOTE(surangap): This list all the orders in the system which is incorrect + // Uncomment once C++ side is fixed. + // list offers for Tx Id "INVALID_ORDER_TX_ID" and check + // const ordersForInvalidOrderTX: Record = await client.icxorderbook.listOrders({ orderTx: 'INVALID_ORDER_TX_ID' }) + // expect(ordersForInvalidOrderTX).toStrictEqual( + // { + // WARNING: 'ICX and Atomic Swap are experimental features. You might end up losing your funds. USE IT AT YOUR OWN RISK.' + // } + // ) + }) +}) diff --git a/packages/jellyfish-api-core/src/category/icxorderbook.ts b/packages/jellyfish-api-core/src/category/icxorderbook.ts index 197929cb5d..de1831193d 100644 --- a/packages/jellyfish-api-core/src/category/icxorderbook.ts +++ b/packages/jellyfish-api-core/src/category/icxorderbook.ts @@ -96,6 +96,55 @@ export class ICXOrderBook { 'bignumber' ) } + + /** + * Returns information about offers based on order and ICXListOrderOptions passed + * + * @param {} options + * @param {string} options.orderTx Order txid to list all offers for this order + * @param {ICXListOrderOptions} options + * @param {string} [options.token] Token asset + * @param {string} [options.chain] Chain asset + * @param {string} [options.orderTx] Order txid to list all offers for this order + * @param {number} [options.limit = 50] Maximum number of orders to return (default: 50) + * @param {boolean} [options.closed] Display closed orders (default: false) + * @return {Promise>} Object indluding details of offers. + */ + async listOrders (options: { orderTx: string } & ICXListOrderOptions): Promise> + + /** + * Returns information about orders or fillorders based on ICXListOrderOptions passed + * + * @param {ICXListOrderOptions} options + * @param {string} [options.token] Token asset + * @param {string} [options.chain] Chain asset + * @param {string} [options.orderTx] Order txid to list all offers for this order + * @param {number} [options.limit = 50] Maximum number of orders to return (default: 50) + * @param {boolean} [options.closed] Display closed orders (default: false) + * @return {Promise>} Object indluding details of orders and offers. + */ + async listOrders (options?: ICXListOrderOptions): Promise> + + /** + * Returns information about orders or fillorders based on ICXListOrderOptions passed + * + * @param {ICXListOrderOptions} options + * @param {string} [options.token] Token asset + * @param {string} [options.chain] Chain asset + * @param {string} [options.orderTx] Order txid to list all offers for this order + * @param {number} [options.limit = 50] Maximum number of orders to return (default: 50) + * @param {boolean} [options.closed] Display closed orders (default: false) + * @return {Promise>} Object indluding details of the transaction. + */ + async listOrders (options: ICXListOrderOptions = {}): Promise> { + return await this.client.call( + 'icx_listorders', + [ + options + ], + 'bignumber' + ) + } } /** ICX order */ export interface ICXOrder { @@ -192,6 +241,8 @@ export interface ICXOrderInfo { expireHeight: BigNumber /** Close height */ closeHeight?: BigNumber + /** Close order transaction Id */ + closeTx?: string /** Expired or not */ expired?: boolean } @@ -215,3 +266,17 @@ export interface ICXOfferInfo { /** Expire height */ expireHeight: BigNumber } + +/** ICX listOrder options */ +export interface ICXListOrderOptions { + /** Token asset */ + token?: string + /** Chain asset */ + chain?: string + /** Order txid to list all offers for this order */ + orderTx?: string + /** Maximum number of orders to return (default: 50) */ + limit?: number + /** Display closed orders (default: false) */ + closed?: boolean +} diff --git a/website/docs/jellyfish/api/icxorderbook.md b/website/docs/jellyfish/api/icxorderbook.md index 271709f79c..b473459e8f 100644 --- a/website/docs/jellyfish/api/icxorderbook.md +++ b/website/docs/jellyfish/api/icxorderbook.md @@ -118,6 +118,58 @@ interface ICXOrderInfo { height: BigNumber expireHeight: BigNumber closeHeight?: BigNumber + closeTx?: string + expired?: boolean +} + +interface ICXOfferInfo { + orderTx: string + status: ICXOrderStatus + amount: BigNumber + amountInFromAsset: BigNumber + ownerAddress: string + receivePubkey?: string + takerFee: BigNumber + expireHeight: BigNumber +} +``` + +## listOrders + +Returns information about orders or fillorders based on ICXListOrderOptions passed + +```ts title="client.icxorderbook.listOrders()" +interface icxorderbook { + listOrders (options: { orderTx: string } & ICXListOrderOptions): Promise> + listOrders (options?: ICXListOrderOptions): Promise> + listOrders (options: ICXListOrderOptions = {}): Promise> +} + +interface ICXListOrderOptions { + token?: string + chain?: string + orderTx?: string + limit?: number + closed?: boolean +} + +interface ICXOrderInfo { + status: ICXOrderStatus + type: ICXOrderType + tokenFrom: string + chainTo?: string + receivePubkey?: string + chainFrom?: string + tokenTo?: string + ownerAddress: string + amountFrom: BigNumber + amountToFill: BigNumber + orderPrice: BigNumber + amountToFillInToAsset: BigNumber + height: BigNumber + expireHeight: BigNumber + closeHeight?: BigNumber + closeTx?: string expired?: boolean }