UNPKG

@rholabs/rho-sdk

Version:

Rho Protocol SDK

458 lines (384 loc) 16.1 kB
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'