@rholabs/rho-sdk
Version:
Rho Protocol SDK
458 lines (384 loc) • 16.1 kB
text/typescript
import { RouterABI, ViewABI, QuoterABI, ERC20ABI, ACMABI } from './abi'
import { parseConfig } from './config'
import {
FutureInfo, GrantUserRoleMethodMap,
LiquidityDistribution,
LiquidityQuote,
MakerLiquidityDistribution,
MarginState,
MarketInfo,
MarketOraclePackages,
MarketPortfolio,
OraclePackage, RevokeUserRoleMethodMap,
TradeQuote, UserRole
} from './typings'
import {
DepositParams,
ExecuteTradeParams,
LiquidatePositionParams,
LiquidityPositionQuoteParams,
MarketUserOracleParams,
PaginationParams,
MarketFutureOraclePaginationParams,
RhoSDKConfig,
RhoSDKParams,
TradeQuoteParams,
TransferPositionsOwnershipParams,
WithdrawParams,
UserOraclePaginationParams,
OraclePaginationParams,
UserPaginationParams,
MarketFutureOracleParams,
LiquidityOperationParams
} from './sdk-typings'
import { OracleAPI } from './api/oracle'
import {
BrowserProvider,
Contract,
ethers,
JsonRpcProvider,
JsonRpcSigner,
TransactionReceipt,
Wallet,
TransactionRequest
} from 'ethers'
import { getUserRoleBytes } from './utils'
import { DataServiceAPI } from './api/dataservice'
const defaultLimit = 100
export default class RhoSDK {
public config: RhoSDKConfig
private router: Contract
private view: Contract
private quoter: Contract
private acm?: Contract
private signer?: Wallet | JsonRpcSigner
public signerAddress: string = ''
public readonly provider: JsonRpcProvider | BrowserProvider
public oracleAPI: OracleAPI
public dataServiceAPI: DataServiceAPI
constructor(_config?: RhoSDKParams) {
const config = parseConfig(_config)
this.config = config
if (config.provider) {
this.provider = config.provider
} else {
this.provider = new JsonRpcProvider(config.rpcUrl)
}
if (config.privateKey) {
this.signer = new ethers.Wallet(config.privateKey, this.provider)
} else if (config.signer) {
this.signer = config.signer
}
if (this.signer) {
this.router = new ethers.Contract(config.routerAddress, RouterABI, this.signer)
this.setSignerAddress(this.signer.address)
} else {
this.router = new ethers.Contract(config.routerAddress, RouterABI, this.provider)
}
this.view = new ethers.Contract(config.viewAddress, ViewABI, this.provider)
this.quoter = new ethers.Contract(config.quoterAddress, QuoterABI, this.provider)
this.oracleAPI = new OracleAPI({
oracleServiceUrl: config.oracleServiceUrl,
})
this.dataServiceAPI = new DataServiceAPI({ network: config.network })
}
public setSignerAddress(address: string) {
this.signerAddress = address
}
public setPrivateKey(privateKey: string) {
const signer = new ethers.Wallet(privateKey, this.provider)
this.setSigner(signer)
}
public setSigner(signer: Wallet | JsonRpcSigner) {
this.signer = signer
this.setSignerAddress(signer.address)
this.router = new ethers.Contract(this.config.routerAddress, RouterABI, this.signer)
this.acm = undefined // acm contract will be initialized on acm method call
}
public async getBalance(address: string): Promise<bigint> {
return this.provider.getBalance(address)
}
public getNonce(): Promise<number> {
return this.signer.getNonce()
}
public async getMarketsOraclePackages(): Promise<MarketOraclePackages[]> {
const packages = await this.oracleAPI.getOraclePackages()
return packages.map(oraclePackage => {
return {
marketId: oraclePackage.marketId,
packages: [oraclePackage]
}
})
}
public async getOraclePackage(marketId: string): Promise<OraclePackage | undefined> {
return await this.oracleAPI.getMarketOraclePackage(marketId)
}
public async getBalanceOf(contractAddress: string, userAddress: string): Promise<bigint> {
const erc20Contract = new ethers.Contract(contractAddress, ERC20ABI, this.provider)
return await erc20Contract.balanceOf(userAddress)
}
public async getAllowance(contractAddress: string, userAddress: string, spenderAddress: string): Promise<bigint> {
const erc20Contract = new ethers.Contract(contractAddress, ERC20ABI, this.provider)
return await erc20Contract.allowance(userAddress, spenderAddress)
}
public async setAllowance(
contractAddress: string,
spenderAddress: string,
amount: bigint
): Promise<TransactionReceipt> {
const erc20Contract = new ethers.Contract(contractAddress, ERC20ABI, this.signer)
return await erc20Contract.approve(spenderAddress, amount)
}
public async getActiveMarketIds(params: PaginationParams = {}): Promise<string[]> {
const { offset = 0, limit = defaultLimit } = params
return await this.view.allActiveMarketsIds(offset, limit)
}
public async getPortfolioMarketIds(params: UserPaginationParams): Promise<string[]> {
const { offset = 0, limit = defaultLimit } = params
return await this.view.portfolioMarketIds(params.userAddress, offset, limit)
}
public async getActiveMarkets(params: OraclePaginationParams = {}): Promise<MarketInfo[]> {
const { offset = 0, limit = defaultLimit } = params
return await this.view.activeMarketsInfo(offset, limit, [])
}
public async getPortfolio(params: Omit<UserOraclePaginationParams, 'oraclePackages'> & {
oraclePackages?: MarketOraclePackages[]
}): Promise<MarketPortfolio[]> {
const { userAddress, offset = 0, limit = defaultLimit } = params
const oraclePackages = params.oraclePackages || await this.getMarketsOraclePackages()
return await this.view.portfolio(userAddress, offset, limit, oraclePackages)
}
public async getMarketPortfolio(params: MarketUserOracleParams): Promise<MarketPortfolio> {
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(params.marketId)]
return await this.view.marketPortfolio(params.marketId, params.userAddress, oraclePackages)
}
public async getMarginDetails(params: MarketUserOracleParams): Promise<MarginState> {
const { marketId, userAddress } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return this.view.marginDetails(marketId, userAddress, oraclePackages)
}
public async getWithdrawableMargin(params: MarketUserOracleParams): Promise<bigint> {
const { marketId, userAddress } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.view.withdrawableMargin(marketId, userAddress, oraclePackages)
}
public async getPoolLiquidityDistribution(
params: MarketFutureOraclePaginationParams
): Promise<LiquidityDistribution> {
const { marketId, futureId, offset = 0, limit = defaultLimit } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
const [provisionDistribution, currentFutureRate, intervalLiquidity] = await this.view.poolLiquidityDistribution(
futureId,
oraclePackages,
offset,
limit
)
return {
provisionDistribution,
currentFutureRate,
intervalLiquidity
}
}
public async getMakerLiquidityDistribution(
params: {futureId: string} & UserPaginationParams
): Promise<MakerLiquidityDistribution> {
const { userAddress, futureId, offset = 0, limit = defaultLimit } = params
const [currentFutureRate, intervalLiquidity] = await this.view.makerLiquidityDistribution(
futureId,
userAddress,
offset,
limit
)
return {
currentFutureRate,
intervalLiquidity
}
}
public async futuresInfoCloseToMaturityWithoutIndex(params: {
marketId: string,
maturityBufferSeconds: number,
}): Promise<FutureInfo[]> {
const { marketId, maturityBufferSeconds } = params
return await this.view.futuresInfoCloseToMaturityWithoutIndex(marketId, maturityBufferSeconds)
}
public async isLiquidatable(params: MarketUserOracleParams): Promise<boolean> {
const { marketId, userAddress } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.quoter.isLiquidatable(marketId, userAddress, oraclePackages)
}
public async isProvisionCancellable(params: MarketUserOracleParams): Promise<boolean> {
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(params.marketId)]
return await this.quoter.isProvisionCancellable(params.marketId, params.userAddress, oraclePackages)
}
public async cancelProvisions(params: MarketUserOracleParams): Promise<TransactionReceipt> {
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(params.marketId)]
return await this.router.cancelProvisions(params.marketId, params.userAddress, oraclePackages)
}
public async getTradeQuote(params: TradeQuoteParams): Promise<TradeQuote> {
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(params.marketId)]
return await this.quoter.quoteTrade(params.futureId, params.notional, params.userAddress, oraclePackages)
}
public async getLiquidityProvisionQuote(params: LiquidityPositionQuoteParams): Promise<LiquidityQuote> {
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(params.marketId)]
return await this.quoter.quoteLiquidityProvision(
params.futureId,
params.notional,
params.userAddress,
params.operation,
params.lowerBound,
params.upperBound,
oraclePackages
)
}
// Router methods
public async liquidatePositions(params: LiquidatePositionParams): Promise<TransactionReceipt> {
const { marketId, futureIds, positionsPercentage, userAddress, settleMaturedPositions = false } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.router.liquidatePositions(marketId, futureIds, positionsPercentage, userAddress, settleMaturedPositions, oraclePackages)
}
public async persistIndexAtMaturity(
params: {
futureId: string,
oraclePackage: OraclePackage
}
): Promise<TransactionReceipt> {
return await this.router.persistIndexAtMaturity(params.futureId, params.oraclePackage)
}
public async executeTrade(params: ExecuteTradeParams, txRequestParams?: TransactionRequest): Promise<TransactionReceipt> {
const { marketId, deadline = Date.now() + 5 * 60 * 1000, settleMaturedPositions = true } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
const args: any[] = [
params.futureId,
params.riskDirection,
params.notional,
params.futureRateLimit,
params.depositAmount,
deadline,
settleMaturedPositions,
oraclePackages
]
if(txRequestParams) {
args.push(txRequestParams)
}
return await this.router.executeTrade(...args)
}
public async executeTradeEstimateGas(params: ExecuteTradeParams, txRequestParams?: TransactionRequest): Promise<bigint> {
const { marketId, deadline = Date.now() + 5 * 60 * 1000, settleMaturedPositions = true } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
const args: any[] = [
params.futureId,
params.riskDirection,
params.notional,
params.futureRateLimit,
params.depositAmount,
deadline,
settleMaturedPositions,
oraclePackages
]
if(txRequestParams) {
args.push(txRequestParams)
}
return await this.router.executeTrade.estimateGas(...args)
}
public async deposit(params: DepositParams): Promise<TransactionReceipt> {
const { marketId, userAddress, amount, settleMaturedPositions = true } = params
const oraclePackages = params.oraclePackages || []
return await this.router.deposit(marketId, userAddress, amount, settleMaturedPositions, oraclePackages)
}
public async withdraw(params: WithdrawParams): Promise<TransactionReceipt> {
const { marketId, amount, unwrapNativeToken = false, settleMaturedPositions = true } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.router.withdraw(marketId, unwrapNativeToken, amount, settleMaturedPositions, oraclePackages)
}
public async provideLiquidity(params: LiquidityOperationParams): Promise<TransactionReceipt> {
const { marketId, deadline = Date.now() + 5 * 60 * 1000, settleMaturedPositions = true } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.router.provideLiquidity(
params.futureId,
params.notional,
params.collateral,
params.lowerBound,
params.upperBound,
deadline,
settleMaturedPositions,
oraclePackages
)
}
public async removeLiquidity(params: LiquidityOperationParams): Promise<TransactionReceipt> {
const { marketId, deadline = Date.now() + 5 * 60 * 1000, settleMaturedPositions = true } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.router.removeLiquidity(
params.futureId,
params.notional,
params.collateral,
params.lowerBound,
params.upperBound,
deadline,
settleMaturedPositions,
oraclePackages
)
}
public async quotePositionsOwnershipTransfer(
params: MarketUserOracleParams & { liquidator: string }
): Promise<{ transferAmount: bigint; depositAmount: bigint }> {
const { marketId, userAddress, liquidator } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.quoter.quotePositionsOwnershipTransfer(
marketId,
userAddress,
liquidator,
oraclePackages
)
}
public async transferPositionsOwnership(params: TransferPositionsOwnershipParams): Promise<TransactionReceipt> {
const { marketId, userAddress, depositAmount, settleMaturedPositions = true } = params
const oraclePackages = params.oraclePackages || [await this.getOraclePackage(marketId)]
return await this.router.transferPositionsOwnership(
marketId,
userAddress,
depositAmount,
settleMaturedPositions,
oraclePackages
)
}
private async getAcmContract() {
if(!this.acm) {
const acmAddress = await this.router.getAcm()
this.acm = new ethers.Contract(acmAddress, ACMABI, this.signer || this.provider)
}
return this.acm
}
public async getUsers(params: { role: UserRole } & PaginationParams): Promise<string[]> {
const { role, offset = 0, limit = 100 } = params
const acm = await this.getAcmContract()
return await acm.getRoleAddresses(getUserRoleBytes(role), offset, limit)
}
public async getUsersCount(params: { role: UserRole }): Promise<bigint> {
const { role } = params
const acm = await this.getAcmContract()
return await acm.getRoleAddressesCount(getUserRoleBytes(role))
}
public async hasRole(params: { address: string, role: UserRole }): Promise<boolean> {
const { address, role } = params
const acm = await this.getAcmContract()
return await acm.hasRole(getUserRoleBytes(role), address)
}
public async grantRole(params: { address: string, role: UserRole }): Promise<TransactionReceipt> {
const { address, role } = params
const acm = await this.getAcmContract()
const grantContractMethod = GrantUserRoleMethodMap[role]
return await acm[grantContractMethod](address)
}
public async revokeRole(params: { address: string, role: UserRole }): Promise<TransactionReceipt> {
const { address, role } = params
const acm = await this.getAcmContract()
const revokeContractMethod = RevokeUserRoleMethodMap[role]
return await acm[revokeContractMethod](address)
}
}
export * from './typings'
export * from './sdk-typings'
export * from './utils'
export * from './api/oracle'
export * from './api/subgraph'
export * from './api/dataservice'