Skip to content

Commit

Permalink
feat(jellyfish-api-core): Add index based pagination support to `list…
Browse files Browse the repository at this point in the history
…accounthistory` RPC (#1923)

<!--  Thanks for sending a pull request! -->

#### What this PR does / why we need it:

**Requirement:** Upcoming consortium transaction history page on
[defiscan.live](https://defiscan.live/) requires a way to paginate the
list of transactions among consortium members.

**Issue:** Currently we only have `blockHeight` and `depth` based
pagination/cursor which we're unable to use to address the requirement
as it only cursor through blocks but not transactions., More info
[here](#1868 (comment)).

**Solution:** 
- Add index based pagination support.
- Add multi transaction types filter support.
- Add multi address filter support.

Huge thanks to @shohamc1 for supporting me on defich/ain side, refer
[defich/ain PR](DeFiCh/ain#1643)

Signed-off-by: Dilshan Madushanka <[email protected]>
Co-authored-by: Isaac Yong <[email protected]>
Co-authored-by: Kven Ho <[email protected]>
  • Loading branch information
3 people authored Jan 19, 2023
1 parent fe0f77b commit 3d0e633
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 15 deletions.
8 changes: 7 additions & 1 deletion docs/node/CATEGORIES/08-account.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ Returns information about account history
```ts title="client.account.listAccountHistory()"
interface account {
listAccountHistory (
owner: OwnerType | string = OwnerType.MINE,
owner: OwnerType | string | string[] = OwnerType.MINE,
options: AccountHistoryOptions = {
limit: 100
}
Expand All @@ -197,6 +197,7 @@ enum OwnerType {

enum DfTxType {
MINT_TOKEN = 'M',
BURN_TOKEN = 'F',
POOL_SWAP = 's',
ADD_POOL_LIQUIDITY = 'l',
REMOVE_POOL_LIQUIDITY = 'r',
Expand Down Expand Up @@ -238,7 +239,10 @@ interface AccountHistoryOptions {
no_rewards?: boolean
token?: string
txtype?: DfTxType
txtypes?: DfTxType[]
limit?: number
start?: number
including_start?: boolean
txn?: number
format?: Format
}
Expand Down Expand Up @@ -288,6 +292,7 @@ enum OwnerType {

enum DfTxType {
MINT_TOKEN = 'M',
BURN_TOKEN = 'F',
POOL_SWAP = 's',
ADD_POOL_LIQUIDITY = 'l',
REMOVE_POOL_LIQUIDITY = 'r',
Expand Down Expand Up @@ -376,6 +381,7 @@ interface account {

enum DfTxType {
MINT_TOKEN = 'M',
BURN_TOKEN = 'F',
POOL_SWAP = 's',
ADD_POOL_LIQUIDITY = 'l',
REMOVE_POOL_LIQUIDITY = 'r',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import waitForExpect from 'wait-for-expect'
import { DfTxType, BalanceTransferPayload, AccountResult, AccountOwner, Format } from '../../../src/category/account'
import { AccountOwner, AccountResult, BalanceTransferPayload, DfTxType, Format } from '../../../src/category/account'

function createTokenForContainer (container: MasterNodeRegTestContainer) {
return async (address: string, symbol: string, amount: number) => {
Expand All @@ -25,13 +25,27 @@ describe('Account', () => {
const container = new MasterNodeRegTestContainer()
const client = new ContainerAdapterClient(container)
const createToken = createTokenForContainer(container)
const txtypes = [DfTxType.BURN_TOKEN, DfTxType.MINT_TOKEN]

let addr1: string
let addr2: string

beforeAll(async () => {
await container.start()
await container.waitForReady()
await container.waitForWalletCoinbaseMaturity()
await container.waitForWalletBalanceGTE(100)
await createToken(await container.getNewAddress(), 'DBTC', 200)

addr1 = await container.getNewAddress()
addr2 = await container.getNewAddress()

await createToken(addr1, 'dBTC', 201)
await createToken(addr2, 'dETH', 100)

for (let i = 1; i <= 10; i++) {
await client.token.burnTokens(`${0.01 * i}@dBTC`, addr1)
await client.token.burnTokens(`${0.01 * i}@dETH`, addr2)
await container.generate(1)
}
})

afterAll(async () => {
Expand Down Expand Up @@ -158,7 +172,7 @@ describe('Account', () => {

it('should listAccountHistory with options token', async () => {
const options = {
token: 'DBTC'
token: 'dBTC'
}
await waitForExpect(async () => {
const accountHistories = await client.account.listAccountHistory('mine', options)
Expand All @@ -172,7 +186,7 @@ describe('Account', () => {
for (let j = 0; j < accountHistory.amounts.length; j += 1) {
const amount = accountHistory.amounts[j]
const symbol = amount.split('@')[1]
expect(symbol).toStrictEqual('DBTC')
expect(symbol).toStrictEqual('dBTC')
}
}
})
Expand Down Expand Up @@ -223,8 +237,8 @@ describe('Account', () => {
const accountHistories = await client.account.listAccountHistory('mine', options)
expect(accountHistories.length).toBeGreaterThan(0)
accountHistories.forEach(accountHistory => {
expect(accountHistory.blockHeight).toBeLessThanOrEqual(103)
if (accountHistory.blockHeight === 103) {
expect(accountHistory.blockHeight).toBeLessThanOrEqual(125)
if (accountHistory.blockHeight === 125) {
expect(accountHistory.txn).toBeLessThanOrEqual(options.txn)
}
})
Expand Down Expand Up @@ -269,7 +283,7 @@ describe('Account', () => {
const { hex, addresses } = owner

const options = {
maxBlockHeight: 103,
maxBlockHeight: 105,
txn: 1
}
const accountHistories = await client.account.listAccountHistory(hex, options)
Expand All @@ -290,7 +304,7 @@ describe('Account', () => {
const { hex, addresses } = owner

const options = {
maxBlockHeight: 103,
maxBlockHeight: 105,
txn: 0
}
const accountHistories = await client.account.listAccountHistory(hex, options)
Expand All @@ -307,7 +321,7 @@ describe('Account', () => {
it('should listAccountHistory with options format', async () => {
{ // amount format should be id
const options = {
token: 'DBTC',
token: 'dBTC',
format: Format.ID
}
const accountHistories = await client.account.listAccountHistory('mine', options)
Expand All @@ -325,7 +339,7 @@ describe('Account', () => {

{ // amount format should be symbol
const options = {
token: 'DBTC',
token: 'dBTC',
format: Format.SYMBOL
}
const accountHistories = await client.account.listAccountHistory('mine', options)
Expand All @@ -336,7 +350,7 @@ describe('Account', () => {
for (let j = 0; j < accountHistory.amounts.length; j += 1) {
const amount = accountHistory.amounts[j]
const symbol = amount.split('@')[1]
expect(symbol).toStrictEqual('DBTC')
expect(symbol).toStrictEqual('dBTC')
}
}
}
Expand All @@ -358,6 +372,34 @@ describe('Account', () => {
}
}
})

it('should listAccountHistory with multiple addresses', async () => {
const accountHistoryAll = await client.account.listAccountHistory([addr1, addr2])
expect(accountHistoryAll.length).toStrictEqual(34)
})

it('should listAccountHistory with multiple addresses and txtypes', async () => {
const accountHistoryAll = await client.account.listAccountHistory([addr1, addr2], { txtypes })
expect(accountHistoryAll.length).toStrictEqual(22)
})

it('should listAccountHistory with multiple addresses, txtypes along with index based pagination', async () => {
const accountHistoryAll = await client.account.listAccountHistory([addr1, addr2], { txtypes })
expect(accountHistoryAll.length).toStrictEqual(22)

const accountHistory1 = await client.account.listAccountHistory([addr1, addr2], { start: 2, txtypes })
expect(accountHistory1[0].txid).toStrictEqual(accountHistoryAll[3].txid)
expect(accountHistory1.length).toStrictEqual(19)

const accountHistory2 = await client.account.listAccountHistory([addr1, addr2], { start: 2, including_start: true, txtypes })
expect(accountHistory2[0].txid).toStrictEqual(accountHistoryAll[2].txid)
expect(accountHistory2.length).toStrictEqual(20)

const accountHistory3 = await client.account.listAccountHistory([addr1, addr2], { start: 2, including_start: true, limit: 3, txtypes })
expect(accountHistory3[0].txid).toStrictEqual(accountHistoryAll[2].txid)
expect(accountHistory3[2].txid).toStrictEqual(accountHistoryAll[4].txid)
expect(accountHistory3.length).toStrictEqual(3)
})
})

describe('listAccountHistory', () => {
Expand Down
11 changes: 9 additions & 2 deletions packages/jellyfish-api-core/src/category/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export enum OwnerType {

export enum DfTxType {
MINT_TOKEN = 'M',
BURN_TOKEN = 'F',
POOL_SWAP = 's',
ADD_POOL_LIQUIDITY = 'l',
REMOVE_POOL_LIQUIDITY = 'r',
Expand Down Expand Up @@ -293,20 +294,23 @@ export class Account {
/**
* Returns information about account history
*
* @param {OwnerType | string} [owner=OwnerType.MINE] single account ID (CScript or address) or reserved words 'mine' to list history for all owned accounts or 'all' to list whole DB
* @param {OwnerType | string | string[]} [owner=OwnerType.MINE] Single/multiple account ID(s) (CScript or address) or reserved words 'mine' to list history for all owned accounts or 'all' to list whole DB
* @param {AccountHistoryOptions} [options]
* @param {number} [options.maxBlockHeight] Optional height to iterate from (down to genesis block), (default = chaintip).
* @param {number} [options.depth] Maximum depth, from the genesis block is the default
* @param {boolean} [options.no_rewards] Filter out rewards
* @param {string} [options.token] Filter by token
* @param {DfTxType} [options.txtype] Filter by transaction type. See DfTxType.
* @param {DfTxType[]} [options.txtypes] Filter multiple transaction types, supported letter from {CustomTxType}.
* @param {number} [options.limit=100] Maximum number of records to return, 100 by default
* @param {number} [options.start] Number of entries to skip
* @param {boolean} [options.including_start=false] If true, then iterate including starting position. False by default
* @param {number} [options.txn] Order in block, unlimited by default
* @param {Format} [options.format] Set the return amount format, Format.SYMBOL by default
* @return {Promise<AccountHistory[]>}
*/
async listAccountHistory (
owner: OwnerType | string = OwnerType.MINE,
owner: OwnerType | string | string[] = OwnerType.MINE,
options: AccountHistoryOptions = {
limit: 100
}
Expand Down Expand Up @@ -539,7 +543,10 @@ export interface AccountHistoryOptions {
no_rewards?: boolean
token?: string
txtype?: DfTxType
txtypes?: DfTxType[]
limit?: number
start?: number
including_start?: boolean
txn?: number
format?: Format
}
Expand Down

0 comments on commit 3d0e633

Please sign in to comment.