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

Commit

Permalink
Add loanToken get and list endpoints (#435)
Browse files Browse the repository at this point in the history
* Add loanToken endpoint

* Fix test

* Apply changes as per code review of another PR (loan scheme)

* Fix wrong docker image

* Quick push

* Quick push

* Quick push switch branch

* Stable version

* Fix type

* Fix type

* Remove unused id

* Don't use any

* Improve code

* Change string or number to Bignumber

* Fix failed test

* Fix failed test

* Revert "Change string or number to Bignumber"

This reverts commit 97e931e.

* Don't use BigNumber. Use string

* Round number => number.
Decimals => string

* added `token: TokenData` to
LoanToken and CollateralToken

Co-authored-by: Fuxing Loh <[email protected]>
  • Loading branch information
jingyi2811 and fuxingloh authored Oct 27, 2021
1 parent 6ebbeb8 commit c5e051c
Show file tree
Hide file tree
Showing 9 changed files with 657 additions and 61 deletions.
66 changes: 56 additions & 10 deletions packages/whale-api-client/__tests__/api/loan.collateral.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,21 +108,44 @@ afterAll(async () => {

describe('list', () => {
it('should listCollateralTokens', async () => {
const result = await client.loan.listCollateral()
const result = await client.loan.listCollateralToken()
expect(result.length).toStrictEqual(4)

// Not deterministic ordering due to use of id
expect(result[0]).toStrictEqual({
token: expect.any(String),
tokenId: expect.any(String),
priceFeedId: expect.any(String),
factor: expect.any(String),
activateAfterBlock: expect.any(Number)
activateAfterBlock: expect.any(Number),
token: {
collateralAddress: expect.any(String),
creation: {
height: expect.any(Number),
tx: expect.any(String)
},
decimal: 8,
destruction: {
height: -1,
tx: expect.any(String)
},
displaySymbol: expect.any(String),
finalized: false,
id: expect.any(String),
isDAT: true,
isLPS: false,
limit: '0',
mintable: true,
minted: '0',
name: expect.any(String),
symbol: expect.any(String),
symbolKey: expect.any(String),
tradeable: true
}
})
})

it('should listLoanSchemes with pagination', async () => {
const first = await client.loan.listCollateral(2)
it('should listCollateral with pagination', async () => {
const first = await client.loan.listCollateralToken(2)

expect(first.length).toStrictEqual(2)
expect(first.hasNext).toStrictEqual(true)
Expand All @@ -144,20 +167,43 @@ describe('list', () => {

describe('get', () => {
it('should get collateral token by symbol', async () => {
const data = await client.loan.getCollateral('AAPL')
const data = await client.loan.getCollateralToken('AAPL')
expect(data).toStrictEqual({
token: 'AAPL',
tokenId: collateralTokenId1,
factor: '0.1',
priceFeedId: 'AAPL/USD',
activateAfterBlock: 108
activateAfterBlock: 108,
token: {
collateralAddress: expect.any(String),
creation: {
height: expect.any(Number),
tx: expect.any(String)
},
decimal: 8,
destruction: {
height: -1,
tx: expect.any(String)
},
displaySymbol: 'dAAPL',
finalized: false,
id: expect.any(String),
isDAT: true,
isLPS: false,
limit: '0',
mintable: true,
minted: '0',
name: 'AAPL',
symbol: 'AAPL',
symbolKey: expect.any(String),
tradeable: true
}
})
})

it('should fail due to getting non-existent or malformed collateral token id', async () => {
expect.assertions(4)
try {
await client.loan.getCollateral('999')
await client.loan.getCollateralToken('999')
} catch (err) {
expect(err).toBeInstanceOf(WhaleApiException)
expect(err.error).toStrictEqual({
Expand All @@ -170,7 +216,7 @@ describe('get', () => {
}

try {
await client.loan.getCollateral('$*@')
await client.loan.getCollateralToken('$*@')
} catch (err) {
expect(err).toBeInstanceOf(WhaleApiException)
expect(err.error).toStrictEqual({
Expand Down
220 changes: 220 additions & 0 deletions packages/whale-api-client/__tests__/api/loan.token.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { StubWhaleApiClient } from '../stub.client'
import { StubService } from '../stub.service'
import { WhaleApiClient, WhaleApiException } from '../../src'
import BigNumber from 'bignumber.js'
import { Testing } from '@defichain/jellyfish-testing'
import { LoanMasterNodeRegTestContainer } from '@defichain/testcontainers'

let container: LoanMasterNodeRegTestContainer
let service: StubService
let client: WhaleApiClient

beforeAll(async () => {
container = new LoanMasterNodeRegTestContainer()
service = new StubService(container)
client = new StubWhaleApiClient(service)

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

const testing = Testing.create(container)

const oracleId = await testing.container.call('appointoracle', [await testing.generateAddress(), [
{ token: 'AAPL', currency: 'USD' },
{ token: 'TSLA', currency: 'USD' },
{ token: 'MSFT', currency: 'USD' },
{ token: 'FB', currency: 'USD' }
], 1])
await testing.generate(1)

await testing.rpc.oracle.setOracleData(oracleId, Math.floor(new Date().getTime() / 1000), {
prices: [{
tokenAmount: '1.5@AAPL',
currency: 'USD'
}]
})
await testing.rpc.oracle.setOracleData(oracleId, Math.floor(new Date().getTime() / 1000), {
prices: [{
tokenAmount: '2.5@TSLA',
currency: 'USD'
}]
})
await testing.rpc.oracle.setOracleData(oracleId, Math.floor(new Date().getTime() / 1000), {
prices: [{
tokenAmount: '3.5@MSFT',
currency: 'USD'
}]
})
await testing.rpc.oracle.setOracleData(oracleId, Math.floor(new Date().getTime() / 1000), {
prices: [{
tokenAmount: '4.5@FB',
currency: 'USD'
}]
})
await testing.generate(1)

await testing.container.call('setloantoken', [{
symbol: 'AAPL',
fixedIntervalPriceId: 'AAPL/USD',
mintable: false,
interest: new BigNumber(0.01)
}])
await testing.generate(1)

await testing.container.call('setloantoken', [{
symbol: 'TSLA',
fixedIntervalPriceId: 'TSLA/USD',
mintable: false,
interest: new BigNumber(0.02)
}])
await testing.generate(1)

await testing.container.call('setloantoken', [{
symbol: 'MSFT',
fixedIntervalPriceId: 'MSFT/USD',
mintable: false,
interest: new BigNumber(0.03)
}])
await testing.generate(1)

await testing.container.call('setloantoken', [{
symbol: 'FB',
fixedIntervalPriceId: 'FB/USD',
mintable: false,
interest: new BigNumber(0.04)
}])
await testing.generate(1)
})

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

describe('list', () => {
it('should listLoanTokens', async () => {
const result = await client.loan.listLoanToken()
expect(result.length).toStrictEqual(4)
expect(result[0]).toStrictEqual({
tokenId: expect.any(String),
interest: expect.any(String),
fixedIntervalPriceId: expect.any(String),
token: {
collateralAddress: expect.any(String),
creation: {
height: expect.any(Number),
tx: expect.any(String)
},
decimal: 8,
destruction: {
height: -1,
tx: expect.any(String)
},
displaySymbol: expect.any(String),
finalized: false,
id: expect.any(String),
isDAT: true,
isLPS: false,
limit: '0',
mintable: false,
minted: '0',
name: '',
symbol: expect.any(String),
symbolKey: expect.any(String),
tradeable: true
}
})

expect(result[1].tokenId.length).toStrictEqual(64)
expect(result[2].tokenId.length).toStrictEqual(64)
expect(result[3].tokenId.length).toStrictEqual(64)
})

it('should listLoanTokens with pagination', async () => {
const first = await client.loan.listLoanToken(2)

expect(first.length).toStrictEqual(2)
expect(first.hasNext).toStrictEqual(true)
expect(first.nextToken?.length).toStrictEqual(64)

const next = await client.paginate(first)

expect(next.length).toStrictEqual(2)
expect(next.hasNext).toStrictEqual(true)
expect(next.nextToken?.length).toStrictEqual(64)

const last = await client.paginate(next)

expect(last.length).toStrictEqual(0)
expect(last.hasNext).toStrictEqual(false)
expect(last.nextToken).toBeUndefined()
})
})

describe('get', () => {
it('should get loan token by symbol', async () => {
const data = await client.loan.getLoanToken('AAPL')
expect(data).toStrictEqual({
tokenId: expect.any(String),
fixedIntervalPriceId: 'AAPL/USD',
interest: '0.01',
token: {
collateralAddress: expect.any(String),
creation: {
height: expect.any(Number),
tx: expect.any(String)
},
decimal: 8,
destruction: {
height: -1,
tx: expect.any(String)
},
displaySymbol: 'dAAPL',
finalized: false,
id: '1',
isDAT: true,
isLPS: false,
limit: '0',
mintable: false,
minted: '0',
name: '',
symbol: 'AAPL',
symbolKey: 'AAPL',
tradeable: true
}
})
})

it('should fail due to getting non-existent or malformed loan token id', async () => {
expect.assertions(4)
try {
await client.loan.getLoanToken('999')
} catch (err) {
expect(err).toBeInstanceOf(WhaleApiException)
expect(err.error).toStrictEqual({
code: 404,
type: 'NotFound',
at: expect.any(Number),
message: 'Unable to find loan token',
url: '/v0.0/regtest/loans/tokens/999'
})
}

try {
await client.loan.getLoanToken('$*@')
} catch (err) {
expect(err).toBeInstanceOf(WhaleApiException)
expect(err.error).toStrictEqual({
code: 404,
type: 'NotFound',
at: expect.any(Number),
message: 'Unable to find loan token',
url: '/v0.0/regtest/loans/tokens/$*@'
})
}
})
})
37 changes: 33 additions & 4 deletions packages/whale-api-client/src/api/loan.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WhaleApiClient } from '../whale.api.client'
import { ApiPagedResponse } from '../whale.api.response'
import { TokenData } from './tokens'

export class Loan {
constructor (private readonly client: WhaleApiClient) {
Expand Down Expand Up @@ -33,19 +34,40 @@ export class Loan {
* @param {string} next set of collateral tokens
* @return {Promise<ApiPagedResponse<CollateralToken>>}
*/
async listCollateral (size: number = 30, next?: string): Promise<ApiPagedResponse<CollateralToken>> {
async listCollateralToken (size: number = 30, next?: string): Promise<ApiPagedResponse<CollateralToken>> {
return await this.client.requestList('GET', 'loans/collaterals', size, next)
}

/**
* Get information about a collateral token with given collateralToken id.
* Get information about a collateral token with given collateral token id.
*
* @param {string} id collateralToken id to get
* @return {Promise<CollateralToken>}
*/
async getCollateral (id: string): Promise<CollateralToken> {
async getCollateralToken (id: string): Promise<CollateralToken> {
return await this.client.requestData('GET', `loans/collaterals/${id}`)
}

/**
* Paginate query loan tokens.
*
* @param {number} size of loan token to query
* @param {string} next set of loan tokens
* @return {Promise<ApiPagedResponse<LoanToken>>}
*/
async listLoanToken (size: number = 30, next?: string): Promise<ApiPagedResponse<LoanToken>> {
return await this.client.requestList('GET', 'loans/tokens', size, next)
}

/**
* Get information about a loan token with given loan token id.
*
* @param {string} id loanToken id to get
* @return {Promise<LoanToken>}
*/
async getLoanToken (id: string): Promise<LoanToken> {
return await this.client.requestData('GET', `loans/tokens/${id}`)
}
}

export interface LoanScheme {
Expand All @@ -55,9 +77,16 @@ export interface LoanScheme {
}

export interface CollateralToken {
token: string
tokenId: string
token: TokenData
factor: string
priceFeedId: string
activateAfterBlock: number
}

export interface LoanToken {
tokenId: string
token: TokenData
interest: string
fixedIntervalPriceId: string
}
Loading

0 comments on commit c5e051c

Please sign in to comment.