Skip to content
This repository has been archived by the owner on Jun 3, 2022. It is now read-only.

feat(module-indexer, module-api, whale-api-client): Future swap indexer and list API #940

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
245 changes: 245 additions & 0 deletions packages/whale-api-client/__tests__/api/address.future.swap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { Testing } from '@defichain/jellyfish-testing'
import { StubWhaleApiClient } from '../stub.client'
import { StubService } from '../stub.service'
import { WhaleApiClient } from '../../src'
import BigNumber from 'bignumber.js'
import { FutureSwap } from '@defichain/jellyfish-api-core/dist/category/account'

let container: MasterNodeRegTestContainer
let service: StubService
let client: WhaleApiClient
let testing: Testing
let colAddr: string
let fromAddr: string
let fromAddr1: string
const attributeKey = 'ATTRIBUTES'

async function setup (): Promise<void> {
colAddr = await testing.generateAddress()
await testing.token.dfi({ address: colAddr, amount: 300000 })
await testing.token.create({ symbol: 'BTC', collateralAddress: colAddr })
await testing.generate(1)
await testing.token.mint({ symbol: 'BTC', amount: 20000 })
await testing.generate(1)

// loan scheme
await testing.container.call('createloanscheme', [100, 1, 'default'])
await testing.generate(1)

// price oracle
const priceFeeds = [
{ token: 'DFI', currency: 'USD' },
{ token: 'BTC', currency: 'USD' },
{ token: 'TSLA', currency: 'USD' },
{ token: 'DUSD', currency: 'USD' }
]

const addr = await testing.generateAddress()
const oracleId = await testing.rpc.oracle.appointOracle(addr, priceFeeds, { weightage: 1 })
await testing.generate(1)

const timestamp = Math.floor(new Date().getTime() / 1000)
await testing.rpc.oracle.setOracleData(
oracleId,
timestamp,
{
prices: [
{ tokenAmount: '1@DFI', currency: 'USD' },
{ tokenAmount: '10000@BTC', currency: 'USD' },
{ tokenAmount: '2@TSLA', currency: 'USD' },
{ tokenAmount: '1@DUSD', currency: 'USD' }
]
}
)
await testing.generate(1)

// collateral tokens
await testing.rpc.loan.setCollateralToken({
token: 'DFI',
factor: new BigNumber(1),
fixedIntervalPriceId: 'DFI/USD'
})

await testing.rpc.loan.setCollateralToken({
token: 'BTC',
factor: new BigNumber(0.5),
fixedIntervalPriceId: 'BTC/USD'
})

// loan token
await testing.rpc.loan.setLoanToken({
symbol: 'TSLA',
fixedIntervalPriceId: 'TSLA/USD'
})
await testing.generate(1)

await testing.rpc.loan.setLoanToken({
symbol: 'DUSD',
fixedIntervalPriceId: 'DUSD/USD'
})
await testing.generate(1)

// create a vault and take loans
const vaultAddr = await testing.generateAddress()
const vaultId = await testing.rpc.vault.createVault({
ownerAddress: vaultAddr,
loanSchemeId: 'default'
})
await testing.generate(1)

await testing.rpc.vault.depositToVault({
vaultId: vaultId, from: colAddr, amount: '100000@DFI'
})
await testing.generate(1)

// wait till the price valid.
await testing.container.waitForPriceValid('TSLA/USD')
await testing.container.waitForPriceValid('DUSD/USD')

// take multiple loans
await testing.rpc.loan.takeLoan({
vaultId: vaultId,
to: colAddr,
amounts: ['300@TSLA', '500@DUSD']
})
await testing.generate(1)

// Futures setup
// set the dfip2203/active to false
await testing.rpc.masternode.setGov({ [attributeKey]: { 'v0/params/dfip2203/active': 'false' } })
await testing.generate(1)

// set dfip2203 params
const futInterval = 25
const futRewardPercentage = 0.05
await testing.rpc.masternode.setGov({ [attributeKey]: { 'v0/params/dfip2203/reward_pct': `${futRewardPercentage}`, 'v0/params/dfip2203/block_period': `${futInterval}` } })
await testing.generate(1)

// set the dfip2203/active to true
await testing.rpc.masternode.setGov({ [attributeKey]: { 'v0/params/dfip2203/active': 'true' } })
await testing.generate(1)

// Retrieve and verify gov vars
const attributes = await testing.rpc.masternode.getGov(attributeKey)
expect(attributes.ATTRIBUTES['v0/params/dfip2203/active']).toStrictEqual('true')
expect(attributes.ATTRIBUTES['v0/params/dfip2203/reward_pct']).toStrictEqual(`${futRewardPercentage}`)
expect(attributes.ATTRIBUTES['v0/params/dfip2203/block_period']).toStrictEqual(`${futInterval}`)
}

async function swap (
addr: string, amt: number, fromToken: string, toToken?: string
): Promise<void> {
await testing.rpc.account.accountToAccount(colAddr, { [addr]: `${amt}@${fromToken}` })
await testing.generate(1)

const fswap: FutureSwap = {
address: addr,
amount: `${amt}@${fromToken}`
}
if (toToken !== undefined) {
fswap.destination = toToken
}
await testing.rpc.account.futureSwap(fswap)
await testing.generate(1)
}

async function withdraw (
addr: string, amt: number, fromToken: string, toToken?: string
): Promise<void> {
const fswap: FutureSwap = {
address: addr,
amount: `${amt}@${fromToken}`
}
if (toToken !== undefined) {
fswap.destination = toToken
}
await testing.rpc.account.withdrawFutureSwap(fswap)
await testing.generate(1)
}

beforeAll(async () => {
container = new MasterNodeRegTestContainer()
service = new StubService(container)
client = new StubWhaleApiClient(service)
testing = Testing.create(container)

await testing.container.start()
await testing.container.waitForWalletCoinbaseMaturity()
await service.start()

await setup()

const current = await testing.container.getBlockCount()
const next = await testing.container.call('getfutureswapblock')
await testing.generate(next - current)

{
fromAddr = await testing.generateAddress()
await swap(fromAddr, 1.92, 'TSLA')
await swap(fromAddr, 2.3576, 'TSLA')
await swap(fromAddr, 3.487004, 'DUSD', 'TSLA')
await swap(fromAddr, 5.67751, 'DUSD', 'TSLA')
await swap(fromAddr, 0.8, 'TSLA')

await withdraw(fromAddr, 0.0005, 'DUSD', 'TSLA')
await withdraw(fromAddr, 4.444, 'TSLA')
}

{
fromAddr1 = await testing.generateAddress()
await swap(fromAddr1, 5.78, 'TSLA')
await swap(fromAddr1, 24.000256, 'DUSD', 'TSLA')

await withdraw(fromAddr1, 24.000256, 'DUSD', 'TSLA')
await withdraw(fromAddr1, 5.78, 'TSLA')
}

const height = await testing.container.getBlockCount()
await testing.generate(1)
await service.waitForIndexedHeight(height)
})

afterAll(async () => {
try {
await service.stop()
} finally {
await testing.container.stop()
}
})

it('should listFutureSwap', async () => {
const list = await client.address.listFutureSwap(fromAddr, 1, 30)

{
const first = await client.address.listFutureSwap(fromAddr, 1, 3)
expect(first.length).toStrictEqual(3)
expect(first.hasNext).toStrictEqual(true)
expect(first[0].id).toStrictEqual(list[0].id)
expect(first[1].id).toStrictEqual(list[1].id)
expect(first[2].id).toStrictEqual(list[2].id)

const next = await client.address.listFutureSwap(fromAddr, 1, 3, first.nextToken)
expect(next.length).toStrictEqual(3)
expect(next.hasNext).toStrictEqual(true)
expect(next[0].id).toStrictEqual(list[3].id)
expect(next[1].id).toStrictEqual(list[4].id)
expect(next[2].id).toStrictEqual(list[5].id)

const last = await client.address.listFutureSwap(fromAddr, 1, 3, next.nextToken)
expect(last.length).toStrictEqual(1)
expect(last.hasNext).toStrictEqual(false)
expect(last[0].id).toStrictEqual(list[6].id)
}

{
const res = await client.address.listFutureSwap(fromAddr1, 1, 30)
expect(res.length).toStrictEqual(4)
expect(res.hasNext).toStrictEqual(false)
}
})

it('should get empty as out of range', async () => {
const empty = await client.address.listFutureSwap(fromAddr, 999, 30)
expect(empty.length).toStrictEqual(0)
})
31 changes: 31 additions & 0 deletions packages/whale-api-client/src/api/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@ export class Address {
async listTransactionUnspent (address: string, size: number = 30, next?: string): Promise<ApiPagedResponse<AddressUnspent>> {
return await this.client.requestList('GET', `address/${address}/transactions/unspent`, size, next)
}

/**
* List future swap
*
* @param {string} address to list future swap
* @param {number} height to set range of future swap
* @param {string} size to query
canonbrother marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} next token for next slice of FutureSwap
* @return {Promise<ApiPagedResponse<FutureSwap>>}
*/
async listFutureSwap (address: string, height: number, size: number = 30, next?: string): Promise<ApiPagedResponse<FutureSwap>> {
return await this.client.requestList('GET', `address/${address}/future-swaps/${height}`, size, next)
canonbrother marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
Expand Down Expand Up @@ -218,3 +231,21 @@ export interface AddressHistory {
time: number
}
}

export interface FutureSwap {
id: string
key: string
sort: string
source: {
token: number
amount: string
}
destination: number
withdraw: boolean
block: {
hash: string
height: number
medianTime: number
time: number
}
}
Loading