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..370855b46e --- /dev/null +++ b/packages/jellyfish-api-core/__tests__/category/icxorderbook/listOrders.test.ts @@ -0,0 +1,328 @@ +import { ContainerAdapterClient } from '../../container_adapter_client' +import { MasterNodeRegTestContainer } from '@defichain/testcontainers' +import { + ICXGenericResult, ICXOfferInfo, ICXOrderInfo, ICXOrder, ICXOffer, ICXOrderStatus +} from '../../../src/category/icxorderbook' +import BigNumber from 'bignumber.js' +import { accountBTC, accountDFI, checkBTCBuyOfferDetails, checkBTCSellOrderDetails, checkDFIBuyOfferDetails, checkDFISellOrderDetails, idDFI, setup } from './common.test' + +describe('Should test ICXOrderBook.listOrders', () => { + const container = new MasterNodeRegTestContainer() + const client = new ContainerAdapterClient(container) + + beforeAll(async () => { + await container.start() + await container.waitForReady() + await container.waitForWalletCoinbaseMaturity() + await setup(container) + }) + + afterAll(async () => { + await container.stop() + }) + + afterEach(async () => { + // cleanup code here + }) + + 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) + } + let result: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = result.txid + await container.generate(1) + + // get order createOrderTxId and check + let retrivedOrder: Record = await client.icxorderbook.getOrder(createOrderTxId) + await checkDFISellOrderDetails(container, order, createOrderTxId, retrivedOrder as Record) + + // create second order - maker + const order2: ICXOrder = { + chainFrom: 'BTC', + tokenTo: idDFI, + ownerAddress: accountDFI, + amountFrom: new BigNumber(2), + orderPrice: new BigNumber(100) + } + result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = result.txid + await container.generate(1) + + // list orders and check + retrivedOrder = await client.icxorderbook.listOrders() + // check details for createOrder2TxId + await checkBTCSellOrderDetails(container, order2, createOrder2TxId, retrivedOrder as Record) + // check details for createOrderTxId + await checkDFISellOrderDetails(container, order, createOrderTxId, retrivedOrder as Record) + }) + + 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) + } + let result: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = result.txid + await container.generate(1) + + // create second order - maker + const order2: ICXOrder = { + chainFrom: 'BTC', + tokenTo: idDFI, + ownerAddress: accountDFI, + amountFrom: new BigNumber(2), + orderPrice: new BigNumber(100) + } + result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = result.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 + } + result = await client.icxorderbook.makeOffer(offer, []) + const makeOfferTxId = result.txid + await container.generate(1) + + // create offer to order createOrderTxId + const offer2: ICXOffer = { + orderTx: createOrder2TxId, + amount: new BigNumber(1), // + ownerAddress: accountBTC, + receivePubkey: '0348790cb93b203a8ea5ce07279cb209d807b535b2ca8b0988a6f7a6578e41f7a5' + } + result = await client.icxorderbook.makeOffer(offer2, []) + const makeOffer2TxId = result.txid + await container.generate(1) + + // list offers for createOrderTxId and check + let orders: Record = await client.icxorderbook.listOrders({ orderTx: createOrder2TxId }) + await checkBTCBuyOfferDetails(container, offer2, makeOffer2TxId, orders as Record) + + // list offers for createOrder2TxId and check + orders = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + await checkDFIBuyOfferDetails(container, offer, makeOfferTxId, orders as Record) + }) + + 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) + } + let result: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = result.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) + } + result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = result.txid + await container.generate(1) + + // list ICX orders anc check + let orders: Record = await client.icxorderbook.listOrders() + await checkDFISellOrderDetails(container, order, createOrderTxId, orders as Record) + await checkDFISellOrderDetails(container, order2, createOrder2TxId, orders as Record) + + // now list orders with ICXListOrderOptions.limit=1 + orders = await client.icxorderbook.listOrders({ limit: 1 }) + expect(Object.keys(orders).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + + // make offer to partial amout 10 DFI - taker + const offer: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.10), // 0.10 BTC = 10 DFI + ownerAddress: accountBTC + } + const accountBTCBeforeOffer = await container.call('getaccount', [accountBTC, {}, true]) + result = await client.icxorderbook.makeOffer(offer, []) + const makeOfferTxId = result.txid + await container.generate(1) + + // make second offer to partial amout 2 DFI - taker + const offer2: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.02), // 0.02 BTC = 2 DFI + ownerAddress: accountBTC + } + result = await client.icxorderbook.makeOffer(offer2, []) + const makeOffer2TxId = result.txid + await container.generate(1) + + const accountBTCAfterOffer = await container.call('getaccount', [accountBTC, {}, true]) + + // check fee of 0.012 DFI has been reduced from the accountBTCBeforeOffer[idDFI] + // Fee = takerFeePerBTC(inBTC) * amount(inBTC) * DEX DFI per BTC rate + expect(Number(accountBTCAfterOffer[idDFI])).toStrictEqual(Number(accountBTCBeforeOffer[idDFI]) - Number(0.012)) + + // List the ICX offers for orderTx = createOrderTxId and check + orders = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + expect(Object.keys(orders).length).toBe(3) // extra entry for the warning text returned by the RPC atm. + await checkDFIBuyOfferDetails(container, offer, makeOfferTxId, orders as Record) + await checkDFIBuyOfferDetails(container, offer2, makeOffer2TxId, orders as Record) + + // now list orders with ICXListOrderOptions.limit=1 + orders = await client.icxorderbook.listOrders({ orderTx: createOrderTxId, limit: 1 }) + expect(Object.keys(orders).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) + } + let result: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = result.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) + } + result = await client.icxorderbook.createOrder(order2, []) + const createOrder2TxId = result.txid + await container.generate(1) + + // list ICX orders anc check + let orders: Record = await client.icxorderbook.listOrders() + await checkDFISellOrderDetails(container, order, createOrderTxId, orders as Record) + await checkDFISellOrderDetails(container, order2, createOrder2TxId, orders as Record) + + // now close createOrder2TxId + await container.call('icx_closeorder', [createOrder2TxId]) + await container.generate(1) + + // list ICX orders and check + orders = await client.icxorderbook.listOrders() + await checkDFISellOrderDetails(container, order, createOrderTxId, orders as Record) + + // list ICX orders with ICXListOrderOptions.closed=true and check + orders = await client.icxorderbook.listOrders({ closed: true }) + const orderInfoRetrived = orders as Record + expect(orderInfoRetrived[createOrder2TxId].status).toStrictEqual(ICXOrderStatus.CLOSED) + expect(orderInfoRetrived[createOrder2TxId].ownerAddress).toStrictEqual(order2.ownerAddress) + + // make offer to partial amout 10 DFI - taker + const offer: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.10), // 0.10 BTC = 10 DFI + ownerAddress: accountBTC + } + // const accountBTCBeforeOffer = await container.call('getaccount', [accountBTC, {}, true]) + result = await client.icxorderbook.makeOffer(offer, []) + const makeOfferTxId = result.txid + await container.generate(1) + + // make second offer to partial amout 10 DFI - taker + const offer2: ICXOffer = { + orderTx: createOrderTxId, + amount: new BigNumber(0.10), // 0.10 BTC = 10 DFI + ownerAddress: accountBTC + } + result = await client.icxorderbook.makeOffer(offer2, []) + const makeOffer2TxId = result.txid + await container.generate(1) + + // const accountBTCAfterOffer = await container.call('getaccount', [accountBTC, {}, true]) + + // NOTE(surangap): why below check is failing? + // check fee of 0.02 DFI has been reduced from the accountBTCBeforeOffer[idDFI] + // Fee = takerFeePerBTC(inBTC) * amount(inBTC) * DEX DFI per BTC rate + // expect(Number(accountBTCAfterOffer[idDFI])).toStrictEqual(Number(accountBTCBeforeOffer[idDFI]) - Number(0.02)) + + // List the ICX offers for orderTx = createOrderTxId and check + orders = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + expect(Object.keys(orders).length).toBe(3) // extra entry for the warning text returned by the RPC atm. + await checkDFIBuyOfferDetails(container, offer, makeOfferTxId, orders as Record) + await checkDFIBuyOfferDetails(container, offer2, makeOffer2TxId, orders as Record) + + // now close makeOffer2TxId + await client.icxorderbook.closeOffer(makeOffer2TxId) + await container.generate(1) + + // List the ICX offers for orderTx = createOrderTxId and check + orders = await client.icxorderbook.listOrders({ orderTx: createOrderTxId }) + expect(Object.keys(orders).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + await checkDFIBuyOfferDetails(container, offer, makeOfferTxId, orders as Record) + + // now list the ICX offers for orderTx = createOrderTxId with ICXListOrderOptions.closed=true and check + orders = await client.icxorderbook.listOrders({ orderTx: createOrderTxId, closed: true }) + expect(Object.keys(orders).length).toBe(2) // extra entry for the warning text returned by the RPC atm. + const offerInfoRetrived = orders as Record + expect(offerInfoRetrived[makeOffer2TxId].orderTx).toStrictEqual(offer2.orderTx) + expect(offerInfoRetrived[makeOffer2TxId].status).toStrictEqual(ICXOrderStatus.CLOSED) + expect(offerInfoRetrived[makeOffer2TxId].ownerAddress).toStrictEqual(offer2.ownerAddress) + }) + + // NOTE(surangap): try to write a parameterized test for all invlaid inputs of ICXListOrderOptions. Ideally client.icxorderbook.listOrders() + // should return empty set for all such cases. but at the moment C++ side implemenation does not go with that. + + 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) + } + let result: ICXGenericResult = await client.icxorderbook.createOrder(order, []) + const createOrderTxId = result.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 + } + result = await client.icxorderbook.makeOffer(offer, []) + await container.generate(1) + + // list offers for Tx Id "123" and check + const orders: Record = await client.icxorderbook.listOrders({ orderTx: '123' }) + expect(orders).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 fb805e2e4e..3929e9a9d5 100644 --- a/packages/jellyfish-api-core/src/category/icxorderbook.ts +++ b/packages/jellyfish-api-core/src/category/icxorderbook.ts @@ -96,6 +96,27 @@ export class ICXOrderBook { 'bignumber' ) } + + /** + * 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] 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 { @@ -217,3 +238,17 @@ export interface ICXOfferInfo { /** Expire height */ expireHeight: number } + +/** 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 6d5660b56c..b7dbd755ae 100644 --- a/website/docs/jellyfish/api/icxorderbook.md +++ b/website/docs/jellyfish/api/icxorderbook.md @@ -132,3 +132,51 @@ interface ICXOfferInfo { expireHeight: number } ``` + +## listOrders + +Returns information about orders or fillorders based on ICXListOrderOptions passed + +```ts title="client.icxorderbook.listOrders()" +interface icxorderbook { + 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: number + expireHeight: number + closeHeight?: number + expired?: boolean +} + +interface ICXOfferInfo { + orderTx: string + status: ICXOrderStatus + amount: BigNumber + amountInFromAsset: BigNumber + ownerAddress: string + receivePubkey?: string + takerFee: BigNumber + expireHeight: number +} +```