Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(apps/whale-api): Add consortium transaction history endpoint #1868

Open
wants to merge 65 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
d446ed5
Add burntokens rpc
helloscoopa Nov 11, 2022
5d20bef
Add minttokens tests
helloscoopa Nov 11, 2022
5526572
Merge branch 'main' into dilshan/consortium-rpcs
helloscoopa Nov 11, 2022
28aebb9
Add setgov tests
helloscoopa Nov 13, 2022
ced9ac1
Add listprices tests for pagination
helloscoopa Nov 13, 2022
a2d3c7a
Fix types in jsDocs
helloscoopa Nov 13, 2022
bdfa982
Merge branch 'main' into dilshan/consortium-rpcs
helloscoopa Nov 14, 2022
abdeb7f
Use regtest-minttoken-simulate-mainnet flag
helloscoopa Nov 15, 2022
c936b97
Add tests for unlimited global mint limits
helloscoopa Nov 16, 2022
379d77b
Resolve test failiure on PoolPairController.test.ts
helloscoopa Nov 16, 2022
1e7755a
Add new consortium attribute tests, bump docker image tag
shohamc1 Nov 17, 2022
bf10e84
Allow minting to zero
shohamc1 Nov 17, 2022
ca092bf
Remove waiting for grandcentral height
helloscoopa Nov 17, 2022
5b5b28f
Add GET consortium/transactions endpoint
helloscoopa Nov 22, 2022
7934b46
Fix change requests
helloscoopa Nov 23, 2022
f2d0234
Add test to validate pagination
helloscoopa Nov 24, 2022
64f8b99
Merge branch 'main' into epic/consortium-apis
helloscoopa Nov 28, 2022
4f1f5ea
Merge branch 'epic/consortium-apis' into dilshan/consortium-tx-histor…
Nov 30, 2022
f34be2e
Merge branch 'main' into epic/consortium-apis
helloscoopa Dec 12, 2022
4ed95e3
Fix merge conflicts
helloscoopa Dec 12, 2022
c0e8a6e
Update to support pagination
helloscoopa Dec 12, 2022
653c61d
Remove .only
helloscoopa Dec 12, 2022
b6968b8
Merge branch 'dilshan/listaccounthistory-pagination' into dilshan/con…
helloscoopa Dec 12, 2022
8f7f401
Merge branch 'main' into dilshan/listaccounthistory-pagination
helloscoopa Dec 12, 2022
9b23ca1
Merge branch 'dilshan/listaccounthistory-pagination' into dilshan/con…
helloscoopa Dec 12, 2022
ba49405
Merge branch 'main' into dilshan/listaccounthistory-pagination
helloscoopa Dec 13, 2022
9328d18
Update tests
helloscoopa Dec 13, 2022
fe89127
Add multi-address support
helloscoopa Dec 22, 2022
0c37bbc
Merge branch 'main' into dilshan/listaccounthistory-pagination
helloscoopa Dec 22, 2022
0ae9af9
Merge branch 'dilshan/listaccounthistory-pagination' into dilshan/con…
helloscoopa Dec 22, 2022
1d72f28
Use multi-address support
helloscoopa Dec 22, 2022
52064f3
Revert missing tests while fixing MC
helloscoopa Dec 22, 2022
6b12ea0
Refactor: start -> pageIndex
helloscoopa Dec 23, 2022
9feacb4
Update to support multi-txtype multi-address filter
helloscoopa Dec 23, 2022
65c88e3
Merge branch 'main' into dilshan/listaccounthistory-pagination
helloscoopa Jan 3, 2023
8f9c661
Merge branch 'main' into dilshan/add-more-filter-options-to-accounthi…
helloscoopa Jan 3, 2023
6792708
Bump image to 1929da31d
helloscoopa Jan 3, 2023
1d88862
Bump image to fe4ccb39d
helloscoopa Jan 3, 2023
c3acc51
Update docs
helloscoopa Jan 3, 2023
e6b2319
Update docs
helloscoopa Jan 3, 2023
8eb155b
Remove extra line
helloscoopa Jan 3, 2023
7433ea4
Update docs
helloscoopa Jan 3, 2023
081995d
Update packages/jellyfish-api-core/__tests__/category/account/listAcc…
helloscoopa Jan 3, 2023
c37c4b8
Add tests to validate pagination of txs in the same block
helloscoopa Jan 4, 2023
ddbbe98
Merge branch 'dilshan/listaccounthistory-pagination' into dilshan/con…
helloscoopa Jan 4, 2023
9c0dddc
Refactor getTransactionHistory func
helloscoopa Jan 5, 2023
ee8465c
Optimize getTransactionHistory func
helloscoopa Jan 5, 2023
fe75304
Remove waitForExpect
helloscoopa Jan 5, 2023
3db0c8f
Merge branch 'main' into dilshan/add-more-filter-options-to-accounthi…
helloscoopa Jan 5, 2023
f4d6b54
Update txId format regex
helloscoopa Jan 5, 2023
8789df4
Break getTransactionHistory into few functions
helloscoopa Jan 5, 2023
11c1e50
Make params optional
helloscoopa Jan 6, 2023
0a7aeb4
Merge branch 'main' into dilshan/add-more-filter-options-to-accounthi…
helloscoopa Jan 6, 2023
892283a
Merge branch 'main' into dilshan/add-more-filter-options-to-accounthi…
helloscoopa Jan 17, 2023
c16fb55
Merge branch 'main' into dilshan/add-more-filter-options-to-accounthi…
helloscoopa Jan 19, 2023
b45912d
Merge branch 'main' into dilshan/add-more-filter-options-to-accounthi…
helloscoopa Jan 19, 2023
277ccef
Merge branch 'dilshan/add-more-filter-options-to-accounthistorycount'…
helloscoopa Jan 19, 2023
2388960
Use updated accounthistorycount rpc
helloscoopa Jan 19, 2023
a47a0cf
Merge branch 'main' into dilshan/consortium-tx-history-api
helloscoopa Jan 19, 2023
cc9ab24
Update mintTokens params
helloscoopa Jan 19, 2023
19ee8f0
Merge branch 'main' into dilshan/consortium-tx-history-api
helloscoopa Jan 20, 2023
9277ab9
Merge branch 'main' into dilshan/consortium-tx-history-api
helloscoopa Jan 20, 2023
88bd9fe
Fix merge conflicts
helloscoopa Jan 22, 2023
9e171c7
Use PaginationQuery
helloscoopa Jan 25, 2023
b340fbf
Merge branch 'main' into dilshan/consortium-tx-history-api
helloscoopa Jan 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/legacy-api/src/controllers/PoolPairController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export class PoolPairController {
quote_name: quote.symbol,
quote_symbol: quote.symbol,
last_price: poolPair.priceRatio.ab,
base_volume: baseVolume.toNumber(),
quote_volume: quoteVolume.toNumber(),
base_volume: baseVolume.isNaN() ? 0 : baseVolume.toNumber(),
quote_volume: quoteVolume.isNaN() ? 0 : quoteVolume.toNumber(),
isFrozen: (poolPair.status) ? 0 : 1
}
}
Expand Down Expand Up @@ -186,8 +186,8 @@ export class PoolPairControllerV2 {
quote_name: quote.symbol,
quote_symbol: quote.symbol,
last_price: poolPair.priceRatio.ba, // inverted from v1
base_volume: baseVolume.toNumber(),
quote_volume: quoteVolume.toNumber(),
base_volume: baseVolume.isNaN() ? 0 : baseVolume.toNumber(),
quote_volume: quoteVolume.isNaN() ? 0 : quoteVolume.toNumber(),
isFrozen: (poolPair.status) ? 0 : 1
}
}
Expand Down
4 changes: 3 additions & 1 deletion apps/rich-list-api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ services:
POSTGRES_DB: defichainrichlist

defi-blockchain:
image: defi/defichain:HEAD-49fba65ce
image: defi/defichain:master-3a65f4571
ports:
- "19554:19554"
command: >
Expand Down Expand Up @@ -47,4 +47,6 @@ services:
-fortcanningspringheight=13
-fortcanninggreatworldheight=14
-fortcanningepilogueheight=15
-grandcentralheight=16
-regtest-skip-loan-collateral-validation
-regtest-minttoken-simulate-mainnet=0
4 changes: 3 additions & 1 deletion apps/whale-api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '3.7'

services:
defi-blockchain:
image: defi/defichain:HEAD-49fba65ce
image: defi/defichain:master-3a65f4571
ports:
- "19554:19554"
command: >
Expand Down Expand Up @@ -36,7 +36,9 @@ services:
-fortcanningspringheight=13
-fortcanninggreatworldheight=14
-fortcanningepilogueheight=15
-grandcentralheight=16
-regtest-skip-loan-collateral-validation
-regtest-minttoken-simulate-mainnet=0

defi-whale:
build:
Expand Down
8 changes: 6 additions & 2 deletions apps/whale-api/src/module.api/_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { PoolPairPricesService } from './poolpair.prices.service'
import { LegacyController } from './legacy.controller'
import { LegacySubgraphService } from './legacy.subgraph.service'
import { PoolPairFeesService } from './poolpair.fees.service'
import { ConsortiumController } from './consortium.controller'
import { ConsortiumService } from './consortium.service'

/**
* Exposed ApiModule for public interfacing
Expand All @@ -56,7 +58,8 @@ import { PoolPairFeesService } from './poolpair.fees.service'
FeeController,
RawtxController,
LoanController,
LegacyController
LegacyController,
ConsortiumController
],
providers: [
{
Expand Down Expand Up @@ -100,7 +103,8 @@ import { PoolPairFeesService } from './poolpair.fees.service'
},
inject: [ConfigService]
},
LegacySubgraphService
LegacySubgraphService,
ConsortiumService
],
exports: [
DeFiDCache
Expand Down
308 changes: 308 additions & 0 deletions apps/whale-api/src/module.api/consortium.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
import { ConsortiumController } from './consortium.controller'
import { TestingGroup } from '@defichain/jellyfish-testing'
import { createTestingApp, stopTestingApp, waitForIndexedHeight } from '../e2e.module'
import { NestFastifyApplication } from '@nestjs/platform-fastify'

describe('getTransactionHistory', () => {
const tGroup = TestingGroup.create(2)
const alice = tGroup.get(0)
const bob = tGroup.get(1)
const symbolBTC = 'dBTC'
const symbolETH = 'dETH'
let accountAlice: string, accountBob: string
let idBTC: string
let idETH: string
let app: NestFastifyApplication
let controller: ConsortiumController
const txIdMatcher = expect.stringMatching(/[0-f]{64}/)
Fixed Show fixed Hide fixed

beforeAll(async () => {
await tGroup.start()
await alice.container.waitForWalletCoinbaseMaturity()

app = await createTestingApp(alice.container)
controller = app.get(ConsortiumController)

await setup()
})

afterAll(async () => {
await stopTestingApp(tGroup, app)
})

async function setGovAttr (ATTRIBUTES: object): Promise<void> {
const hash = await alice.rpc.masternode.setGov({ ATTRIBUTES })
expect(hash).toBeTruthy()
await alice.generate(1)
}

async function setMemberInfo (tokenId: string, memberInfo: Array<{ id: string, name: string, backingId: string, ownerAddress: string, mintLimit: string, dailyMintLimit: string }>): Promise<void> {
const infoObjs = memberInfo.map(mi => `
"${mi.id}":{
"name":"${mi.name}",
"ownerAddress":"${mi.ownerAddress}",
"backingId":"${mi.backingId}",
"dailyMintLimit":${mi.dailyMintLimit},
"mintLimit":${mi.mintLimit}
}`
)

return await setGovAttr({ [`v0/consortium/${tokenId}/members`]: `{${infoObjs.join(',')}}` })
}

async function setup (): Promise<void> {
accountAlice = await alice.generateAddress()
accountBob = await bob.generateAddress()

await alice.token.create({
symbol: symbolBTC,
name: symbolBTC,
isDAT: true,
mintable: true,
tradeable: true,
collateralAddress: accountAlice
})
await alice.generate(1)

await alice.token.create({
symbol: symbolETH,
name: symbolETH,
isDAT: true,
mintable: true,
tradeable: true,
collateralAddress: accountAlice
})
await alice.generate(1)

await alice.container.fundAddress(accountBob, 10)
await alice.generate(1)
idBTC = await alice.token.getTokenId(symbolBTC)
idETH = await alice.token.getTokenId(symbolETH)

await setGovAttr({
'v0/params/feature/consortium': 'true',
[`v0/consortium/${idBTC}/mint_limit`]: '10',
[`v0/consortium/${idBTC}/mint_limit_daily`]: '5',
[`v0/consortium/${idETH}/mint_limit`]: '20',
[`v0/consortium/${idETH}/mint_limit_daily`]: '10'
})

await setMemberInfo(idBTC, [{
id: '01',
name: 'alice',
ownerAddress: accountAlice,
backingId: 'abc',
dailyMintLimit: '5.00000000',
mintLimit: '10.00000000'
}, {
id: '02',
name: 'bob',
ownerAddress: accountBob,
backingId: 'def,hij',
dailyMintLimit: '5.00000000',
mintLimit: '10.00000000'
}])

await setMemberInfo(idETH, [{
id: '01',
name: 'alice',
ownerAddress: accountAlice,
backingId: '',
dailyMintLimit: '10.00000000',
mintLimit: '20.00000000'
}, {
id: '02',
name: 'bob',
ownerAddress: accountBob,
backingId: 'lmn,opq',
dailyMintLimit: '10.00000000',
mintLimit: '20.00000000'
}])

await alice.rpc.token.mintTokens(`1@${symbolBTC}`)
await alice.generate(5)

await alice.rpc.token.mintTokens(`2@${symbolETH}`)
await alice.generate(5)

await alice.rpc.token.burnTokens(`1@${symbolETH}`, accountAlice)
await alice.generate(5)

await bob.rpc.token.mintTokens(`4@${symbolBTC}`)
await bob.generate(5)

await bob.rpc.token.burnTokens(`2@${symbolBTC}`, accountBob)
await bob.generate(5)

const height = await alice.container.getBlockCount()
await alice.generate(1)
await waitForIndexedHeight(app, height)
}

it('should throw an error if the limit is invalid', async () => {
await expect(controller.getTransactionHistory({ limit: 51 })).rejects.toThrow('InvalidLimit')
await expect(controller.getTransactionHistory({ limit: 0 })).rejects.toThrow('InvalidLimit')
})

it('should throw an error if the search term is invalid', async () => {
await expect(controller.getTransactionHistory({ search: 'a', limit: 1 })).rejects.toThrow('InvalidSearchTerm')
await expect(controller.getTransactionHistory({ search: 'a'.repeat(65), limit: 1 })).rejects.toThrow('InvalidSearchTerm')
})

it('should throw an error if the max block height is invalid', async () => {
await expect(controller.getTransactionHistory({ maxBlockHeight: -2, limit: 1 })).rejects.toThrow('InvalidMaxBlockHeight')
})

it('should filter transactions with search term (member name)', async () => {
const info = await controller.getTransactionHistory({ search: 'alice', limit: 20 })

expect(info.transactions.length).toStrictEqual(3)
expect(info.transactions).toStrictEqual([
{ txId: txIdMatcher, type: 'Burn', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '-1.00000000' }], address: accountAlice, block: 119 },
{ txId: txIdMatcher, type: 'Mint', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '2.00000000' }], address: accountAlice, block: 114 },
{ txId: txIdMatcher, type: 'Mint', member: 'alice', tokenAmounts: [{ token: 'dBTC', amount: '1.00000000' }], address: accountAlice, block: 109 }
])
expect(info.total).toStrictEqual(3)
})

it('should filter transactions with search term (owner address)', async () => {
const info = await controller.getTransactionHistory({ search: accountAlice, limit: 20 })

expect(info.transactions.length).toStrictEqual(3)
expect(info.transactions).toStrictEqual([
{ txId: txIdMatcher, type: 'Burn', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '-1.00000000' }], address: accountAlice, block: 119 },
{ txId: txIdMatcher, type: 'Mint', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '2.00000000' }], address: accountAlice, block: 114 },
{ txId: txIdMatcher, type: 'Mint', member: 'alice', tokenAmounts: [{ token: 'dBTC', amount: '1.00000000' }], address: accountAlice, block: 109 }
])
expect(info.total).toStrictEqual(3)
})

it('should filter transactions with search term (transaction id)', async () => {
const tx = (await alice.rpc.account.listAccountHistory(accountAlice))[0]

const info = await controller.getTransactionHistory({ search: tx.txid, limit: 20 })

expect(info.transactions.length).toStrictEqual(1)
expect(info.transactions).toStrictEqual([
{ txId: tx.txid, type: 'Burn', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '-1.00000000' }], address: accountAlice, block: 119 }
])
expect(info.total).toStrictEqual(1)
})

it('should limit transactions', async () => {
const info = await controller.getTransactionHistory({ limit: 3 })

expect(info.transactions.length).toStrictEqual(3)
expect(info.transactions).toStrictEqual([
{ txId: txIdMatcher, type: 'Burn', member: 'bob', tokenAmounts: [{ token: 'dBTC', amount: '-2.00000000' }], address: accountBob, block: 129 },
{ txId: txIdMatcher, type: 'Mint', member: 'bob', tokenAmounts: [{ token: 'dBTC', amount: '4.00000000' }], address: accountBob, block: 124 },
{ txId: txIdMatcher, type: 'Burn', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '-1.00000000' }], address: accountAlice, block: 119 }
])
expect(info.total).toStrictEqual(5)
})

it('should filter and limit transactions at the same time', async () => {
const info = await controller.getTransactionHistory({ search: accountAlice, limit: 2 })

expect(info.transactions.length).toStrictEqual(2)
expect(info.transactions).toStrictEqual([
{ txId: txIdMatcher, type: 'Burn', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '-1.00000000' }], address: accountAlice, block: 119 },
{ txId: txIdMatcher, type: 'Mint', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '2.00000000' }], address: accountAlice, block: 114 }
])
expect(info.total).toStrictEqual(3)
})

it('should get transactions upto a specific block height with a limit', async () => {
const info = await controller.getTransactionHistory({ limit: 3, maxBlockHeight: 124 })

expect(info.transactions.length).toStrictEqual(3)
expect(info.transactions).toStrictEqual([
{ txId: txIdMatcher, type: 'Mint', member: 'bob', tokenAmounts: [{ token: 'dBTC', amount: '4.00000000' }], address: accountBob, block: 124 },
{ txId: txIdMatcher, type: 'Burn', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '-1.00000000' }], address: accountAlice, block: 119 },
{ txId: txIdMatcher, type: 'Mint', member: 'alice', tokenAmounts: [{ token: 'dETH', amount: '2.00000000' }], address: accountAlice, block: 114 }
])
expect(info.total).toStrictEqual(5)
})

it('should return empty list of transactions for invalid search term', async () => {
const info = await controller.getTransactionHistory({ search: 'invalid-term', limit: 20 })

expect(info.transactions.length).toStrictEqual(0)
expect(info.total).toStrictEqual(0)
})

it('should not return other transactions from consortium members apart from mints or burns', async () => {
const { txid } = await alice.container.fundAddress(accountBob, 11.5)

const info = await controller.getTransactionHistory({ search: txid, limit: 20 })

expect(info.transactions.length).toStrictEqual(0)
expect(info.total).toStrictEqual(0)
})

it('should paginate properly', async () => {
const page1 = await controller.getTransactionHistory({ limit: 2 })

expect(page1).toStrictEqual({
transactions: [
{
type: 'Burn',
member: 'bob',
tokenAmounts: [{ token: 'dBTC', amount: '-2.00000000' }],
txId: txIdMatcher,
address: accountBob,
block: 129
},
{
type: 'Mint',
member: 'bob',
tokenAmounts: [{ token: 'dBTC', amount: '4.00000000' }],
txId: txIdMatcher,
address: accountBob,
block: 124
}
],
total: 5
})

const page2 = await controller.getTransactionHistory({ limit: 2, maxBlockHeight: page1.transactions[page1.transactions.length - 1].block - 1 })
helloscoopa marked this conversation as resolved.
Show resolved Hide resolved

expect(page2).toStrictEqual({
transactions: [
{
type: 'Burn',
member: 'alice',
tokenAmounts: [{ token: 'dETH', amount: '-1.00000000' }],
txId: txIdMatcher,
address: accountAlice,
block: 119
},
{
type: 'Mint',
member: 'alice',
tokenAmounts: [{ token: 'dETH', amount: '2.00000000' }],
txId: txIdMatcher,
address: accountAlice,
block: 114
}
],
total: 5
})

const page3 = await controller.getTransactionHistory({ limit: 2, maxBlockHeight: page2.transactions[page2.transactions.length - 1].block - 1 })

expect(page3).toStrictEqual({
transactions: [
{
type: 'Mint',
member: 'alice',
tokenAmounts: [{ token: 'dBTC', amount: '1.00000000' }],
txId: txIdMatcher,
address: accountAlice,
block: 109
}
],
total: 5
})
})
})
Loading