@rholabs/rho-sdk
Version:
Rho Protocol SDK
498 lines (446 loc) • 12.7 kB
text/typescript
import * as dotenv from 'dotenv'
import { ethers } from 'ethers'
import { describe, expect, test } from '@jest/globals'
import RhoSDK, { LiquidityOperation, RiskDirection, UserRole } from '../src/v1'
import { MarketInfo, marginTotal } from '../src'
dotenv.config()
const privateKey = process.env.TEST_PRIVATE_KEY || ''
let testAddress = ''
const waitTimeout = 60000
let sdk: RhoSDK
let markets: MarketInfo[] = []
const sleep = (timeout: number) => new Promise((resolve) => setTimeout(resolve, timeout))
beforeAll(async () => {
sdk = new RhoSDK({ network: 'testnet', privateKey })
testAddress = sdk.signerAddress
markets = await sdk.getActiveMarkets()
console.log('Init test SDK, signer address', sdk.signerAddress, ', markets count:', markets.length)
})
describe('Common methods', () => {
test(
'getBalance',
async () => {
const balance = await sdk.getBalance(testAddress)
expect(balance).toBeGreaterThanOrEqual(0n)
},
waitTimeout
)
test(
'getNonce',
async () => {
const nonce = await sdk.getNonce()
expect(nonce).toBeGreaterThanOrEqual(0)
},
waitTimeout
)
})
describe('Check RPC', () => {
test(
'Mainnet RPC',
async () => {
const mainnetSDK = new RhoSDK({ network: 'mainnet' })
const markets = await mainnetSDK.getActiveMarkets()
expect(markets.length).toBeGreaterThan(0)
},
waitTimeout
)
test(
'Testnet RPC',
async () => {
const markets = await sdk.getActiveMarkets()
expect(markets.length).toBeGreaterThan(0)
},
waitTimeout
)
})
describe('ERC20 methods', () => {
test(
'getBalanceOf',
async () => {
const balance = await sdk.getBalanceOf(markets[0].descriptor.underlying, testAddress)
expect(balance).toBeGreaterThanOrEqual(0n)
},
waitTimeout
)
test(
'getAllowance',
async () => {
const allowance = await sdk.getAllowance(markets[0].descriptor.underlying, testAddress, testAddress)
expect(allowance).toBeGreaterThanOrEqual(0n)
},
waitTimeout
)
test(
'setAllowance',
async () => {
const erc20Address = markets[0].descriptor.underlying
const balance = await sdk.getBalanceOf(erc20Address, testAddress)
const result = await sdk.setAllowance(erc20Address, testAddress, balance)
expect(result.hash).toMatch('0x')
},
waitTimeout
)
})
describe('Oracle service data', () => {
test(
'getOraclePackage',
async () => {
const marketId = markets[0].descriptor.id
const oraclePackage = await sdk.getOraclePackage(marketId)
expect(oraclePackage).toBeDefined()
if (oraclePackage) {
expect(oraclePackage.marketId).toBeDefined()
expect(oraclePackage.marketId).toBe(marketId)
expect(typeof oraclePackage.timestamp).toBe('number')
expect(+oraclePackage.indexValue).toBeGreaterThan(0)
}
},
waitTimeout
)
})
describe('View methods', () => {
test(
'getActiveMarketIds',
async () => {
const marketIds = await sdk.getActiveMarketIds()
expect(marketIds.length).toBeGreaterThan(0)
},
waitTimeout
)
test(
'getPortfolioMarketIds',
async () => {
const marketIds = await sdk.getPortfolioMarketIds({ userAddress: testAddress, offset: 0, limit: 10 })
expect(marketIds.length).toBeGreaterThanOrEqual(0)
},
waitTimeout
)
test(
'getMarkets',
async () => {
expect(markets.length).toBeGreaterThan(0)
},
waitTimeout
)
test(
'getPortfolio',
async () => {
const portfolio = await sdk.getPortfolio({
userAddress: testAddress,
})
expect(portfolio.length).toBeGreaterThanOrEqual(0)
},
waitTimeout
)
test(
'getMarketPortfolio',
async () => {
const portfolio = await sdk.getPortfolio({
userAddress: testAddress
})
const marketId = portfolio[0].descriptor.id
const marketPortfolio = await sdk.getMarketPortfolio({
marketId,
userAddress: testAddress
})
expect(marketPortfolio.descriptor.id).toBe(marketId)
},
waitTimeout
)
test(
'getMarginDetails',
async () => {
const margin = await sdk.getMarginDetails({
marketId: markets[0].descriptor.id,
userAddress: testAddress
})
expect(margin.initialMarginThreshold).toBeGreaterThanOrEqual(0n)
},
waitTimeout
)
test(
'getWithdrawableMargin',
async () => {
const margin = await sdk.getWithdrawableMargin({
marketId: markets[0].descriptor.id,
userAddress: testAddress
})
expect(margin).toBeGreaterThanOrEqual(0n)
},
waitTimeout
)
test(
'getLiquidityDistribution',
async () => {
const liquidityDistribution = await sdk.getPoolLiquidityDistribution({
marketId: markets[0].descriptor.id,
futureId: markets[0].futures[0].id
})
expect(liquidityDistribution.intervalLiquidity.length).toBeGreaterThan(0)
expect(liquidityDistribution.provisionDistribution.total).toBeGreaterThan(0n)
expect(liquidityDistribution.currentFutureRate).toBeGreaterThan(-100n)
},
waitTimeout
)
test(
'getMakerLiquidityDistribution',
async () => {
const liquidityDistribution = await sdk.getMakerLiquidityDistribution({
userAddress: testAddress,
futureId: markets[0].futures[0].id
})
expect(liquidityDistribution.intervalLiquidity.length).toBeGreaterThanOrEqual(0)
expect(liquidityDistribution.currentFutureRate).toBeGreaterThan(-100n)
},
waitTimeout
)
test(
'isLiquidatable',
async () => {
const [market] = markets
const isLiquidatable = await sdk.isLiquidatable({
marketId: market.descriptor.id,
userAddress: testAddress
})
expect(isLiquidatable).toBe(false)
},
waitTimeout
)
test(
'isProvisionCancellable',
async () => {
const [market] = markets
const isProvisionCancellable = await sdk.isProvisionCancellable({
marketId: market.descriptor.id,
userAddress: testAddress
})
expect(isProvisionCancellable).toBe(false)
},
waitTimeout
)
test(
'futuresInfoCloseToMaturityWithoutIndex',
async () => {
const [market] = markets
const futures = await sdk.futuresInfoCloseToMaturityWithoutIndex({
marketId: market.descriptor.id,
maturityBufferSeconds: 12 * 24 * 60_60_1000
})
expect(futures.length).toBeGreaterThan(0)
},
waitTimeout
)
})
describe('Quote methods', () => {
test(
'getTradeQuote',
async () => {
const [market] = markets
const [future] = market.futures
const quote = await sdk.getTradeQuote({
marketId: market.descriptor.id,
futureId: future.id,
userAddress: testAddress,
notional: 100n * 10n ** market.descriptor.underlyingDecimals
})
expect(quote.insufficientLiquidityForPayer).toBe(false)
expect(quote.insufficientLiquidityForReceiver).toBe(false)
expect(quote.receiverQuote.newMargin.collateral).toBeGreaterThanOrEqual(0n)
},
waitTimeout
)
test(
'getLiquidityProvisionQuote',
async () => {
const [market] = markets
const [future] = market.futures
const quote = await sdk.getLiquidityProvisionQuote({
marketId: market.descriptor.id,
futureId: future.id,
userAddress: testAddress,
operation: LiquidityOperation.PROVIDE,
notional: 1n * 10n ** market.descriptor.underlyingDecimals,
lowerBound: (10 ** 16).toString(),
upperBound: (5 * 10 ** 16).toString()
})
expect(quote.totalFutureProvisionPayerDv01).toBeGreaterThanOrEqual(0n)
},
waitTimeout
)
})
describe('Router', () => {
test(
'executeTradeEstimateGas',
async () => {
const [market] = markets
const [future] = market.futures
const marketId = market.descriptor.id
const futureId = future.id
const gas = await sdk.executeTradeEstimateGas({
marketId,
futureId,
riskDirection: RiskDirection.RECEIVER,
notional: 1n * 10n ** market.descriptor.underlyingDecimals,
futureRateLimit: 10000n,
depositAmount: 0n,
})
expect(gas).toBeGreaterThan(0n)
},
waitTimeout
)
test(
'executeTrade',
async () => {
const [market] = markets
const [future] = market.futures
const marketId = market.descriptor.id
const futureId = future.id
const notional = 1n * 10n ** market.descriptor.underlyingDecimals
const tradeQuote = await sdk.getTradeQuote({
marketId: market.descriptor.id,
futureId: future.id,
userAddress: testAddress,
notional
})
const selectedQuote = tradeQuote.receiverQuote
const futureRateLimit = selectedQuote.tradeInfo.tradeRate + BigInt(0.1 * 10 ** 16) * BigInt(-1)
let depositAmount = selectedQuote.newMarginThreshold - marginTotal(selectedQuote.newMargin)
if (depositAmount < 0n) {
depositAmount = 0n
}
const trade = await sdk.executeTrade({
marketId,
futureId,
riskDirection: RiskDirection.RECEIVER,
notional,
futureRateLimit,
depositAmount
})
expect(trade.from).toBe(sdk.signerAddress)
expect(trade.hash).toMatch('0x')
},
waitTimeout
)
test(
'executeTrade with custom nonce',
async () => {
const [market] = markets
const [future] = market.futures
const marketId = market.descriptor.id
const futureId = future.id
const notional = 1n * 10n ** market.descriptor.underlyingDecimals
const tradeQuote = await sdk.getTradeQuote({
marketId: market.descriptor.id,
futureId: future.id,
userAddress: testAddress,
notional
})
const selectedQuote = tradeQuote.receiverQuote
const futureRateLimit = selectedQuote.tradeInfo.tradeRate + BigInt(0.1 * 10 ** 16) * BigInt(-1)
let depositAmount = selectedQuote.newMarginThreshold - marginTotal(selectedQuote.newMargin)
if (depositAmount < 0n) {
depositAmount = 0n
}
const nonce = await sdk.getNonce()
expect(nonce).toBeGreaterThan(0)
const trade = await sdk.executeTrade({
marketId,
futureId,
riskDirection: RiskDirection.RECEIVER,
notional,
futureRateLimit,
depositAmount
}, {
nonce,
})
expect(trade.hash).toMatch('0x')
await sleep(5000)
const nonceAfter = await sdk.getNonce()
expect(nonceAfter).toBeGreaterThan(nonce)
},
waitTimeout
)
test(
'deposit',
async () => {
const marketId = markets[0].descriptor.id
const tx = await sdk.deposit({
marketId,
userAddress: sdk.signerAddress,
amount: 100n
})
expect(tx.hash).toMatch('0x')
},
waitTimeout
)
test(
'withdraw',
async () => {
const marketId = markets[0].descriptor.id
const marginState = await sdk.getMarginDetails({
marketId,
userAddress: testAddress
})
const delta = marginState.margin.collateral - marginState.lpMarginThreshold
if (delta > 0) {
const tx = await sdk.withdraw({
marketId,
amount: 100n
})
expect(tx.hash).toMatch('0x')
}
},
waitTimeout
)
})
describe('Access Control Manager', () => {
const tempWallet = ethers.Wallet.createRandom()
test(
'getUsersCount',
async () => {
const count = await sdk.getUsersCount({ role: UserRole.PROTOCOL_USER })
expect(count).toBeGreaterThan(0)
}
)
test(
'getUsers',
async () => {
const limit = 10
const users = await sdk.getUsers({
role: UserRole.PROTOCOL_USER,
offset: 0,
limit
})
expect(users.length).toBe(limit)
}
)
test(
'hasRole',
async () => {
const hasRole = await sdk.hasRole({
role: UserRole.PROTOCOL_USER,
address: tempWallet.address
})
expect(hasRole).toBe(false)
}
)
test(
'grant and revoke role',
async () => {
const role = UserRole.PROTOCOL_USER
const address = tempWallet.address
const grantResult = await sdk.grantRole({
role,
address
})
expect(grantResult.hash).toMatch('0x')
await sleep(5000)
const revokeResult = await sdk.revokeRole({
role,
address
})
expect(revokeResult.hash).toMatch('0x')
},
waitTimeout
)
})