From 5de709f8755015774209d7ead33b78b4b3bcc7db Mon Sep 17 00:00:00 2001 From: tonzgao Date: Wed, 7 Jun 2023 16:16:28 -0300 Subject: [PATCH] feat(raft): add position presenter (#2728) Co-authored-by: William Poulin --- ...raft.position.contract-position-fetcher.ts | 14 ++ .../contracts/abis/raft-liquiditation.json | 31 ++++ .../contracts/ethers/RaftLiquiditation.ts | 152 ++++++++++++++++++ .../factories/RaftLiquiditation__factory.ts | 85 ++++++++++ .../raft/contracts/ethers/factories/index.ts | 1 + src/apps/raft/contracts/ethers/index.ts | 2 + src/apps/raft/contracts/index.ts | 6 +- .../raft/ethereum/raft.position-presenter.ts | 45 ++++++ 8 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 src/apps/raft/contracts/abis/raft-liquiditation.json create mode 100644 src/apps/raft/contracts/ethers/RaftLiquiditation.ts create mode 100644 src/apps/raft/contracts/ethers/factories/RaftLiquiditation__factory.ts create mode 100644 src/apps/raft/ethereum/raft.position-presenter.ts diff --git a/src/apps/raft/common/raft.position.contract-position-fetcher.ts b/src/apps/raft/common/raft.position.contract-position-fetcher.ts index cd39df70e..5409feb46 100644 --- a/src/apps/raft/common/raft.position.contract-position-fetcher.ts +++ b/src/apps/raft/common/raft.position.contract-position-fetcher.ts @@ -46,6 +46,20 @@ export abstract class EthereumRaftContractPositionFetcher extends ContractPositi ]; } + async getDataProps({ + contractPosition, + multicall, + }) { + const positionManager = this.raftContractFactory.raftPositionManager({ address: this.positionManagerAddress, network: this.network }) + const liquidiationContractAddress = await multicall.wrap(positionManager).splitLiquidationCollateral(this.collateral) + const liquidationContract = this.raftContractFactory.raftLiquiditation({ address: liquidiationContractAddress, network: this.network }) + + const collateralToken = contractPosition.tokens[0]; + const minCRatio = Number(await multicall.wrap(liquidationContract).MCR()) / 10 ** collateralToken.decimals + + return { minCRatio }; + } + async getLabel(): Promise { const baseTokens = await this.appToolkit.getBaseTokenPrices(this.network); const tokenFound = baseTokens.find(p => p.address === this.collateral); diff --git a/src/apps/raft/contracts/abis/raft-liquiditation.json b/src/apps/raft/contracts/abis/raft-liquiditation.json new file mode 100644 index 000000000..72e2040c6 --- /dev/null +++ b/src/apps/raft/contracts/abis/raft-liquiditation.json @@ -0,0 +1,31 @@ +[ + { + "inputs": [], + "name": "LOW_TOTAL_DEBT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MCR", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "totalCollateral", "type": "uint256" }, + { "internalType": "uint256", "name": "totalDebt", "type": "uint256" }, + { "internalType": "uint256", "name": "price", "type": "uint256" }, + { "internalType": "bool", "name": "isRedistribution", "type": "bool" } + ], + "name": "split", + "outputs": [ + { "internalType": "uint256", "name": "collateralToSendToProtocol", "type": "uint256" }, + { "internalType": "uint256", "name": "collateralToSentToLiquidator", "type": "uint256" } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/src/apps/raft/contracts/ethers/RaftLiquiditation.ts b/src/apps/raft/contracts/ethers/RaftLiquiditation.ts new file mode 100644 index 000000000..567397a65 --- /dev/null +++ b/src/apps/raft/contracts/ethers/RaftLiquiditation.ts @@ -0,0 +1,152 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + PopulatedTransaction, + Signer, + utils, +} from 'ethers'; +import type { FunctionFragment, Result } from '@ethersproject/abi'; +import type { Listener, Provider } from '@ethersproject/providers'; +import type { TypedEventFilter, TypedEvent, TypedListener, OnEvent, PromiseOrValue } from './common'; + +export interface RaftLiquiditationInterface extends utils.Interface { + functions: { + 'LOW_TOTAL_DEBT()': FunctionFragment; + 'MCR()': FunctionFragment; + 'split(uint256,uint256,uint256,bool)': FunctionFragment; + }; + + getFunction(nameOrSignatureOrTopic: 'LOW_TOTAL_DEBT' | 'MCR' | 'split'): FunctionFragment; + + encodeFunctionData(functionFragment: 'LOW_TOTAL_DEBT', values?: undefined): string; + encodeFunctionData(functionFragment: 'MCR', values?: undefined): string; + encodeFunctionData( + functionFragment: 'split', + values: [ + PromiseOrValue, + PromiseOrValue, + PromiseOrValue, + PromiseOrValue, + ], + ): string; + + decodeFunctionResult(functionFragment: 'LOW_TOTAL_DEBT', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'MCR', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'split', data: BytesLike): Result; + + events: {}; +} + +export interface RaftLiquiditation extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: RaftLiquiditationInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined, + ): Promise>; + + listeners(eventFilter?: TypedEventFilter): Array>; + listeners(eventName?: string): Array; + removeAllListeners(eventFilter: TypedEventFilter): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + LOW_TOTAL_DEBT(overrides?: CallOverrides): Promise<[BigNumber]>; + + MCR(overrides?: CallOverrides): Promise<[BigNumber]>; + + split( + totalCollateral: PromiseOrValue, + totalDebt: PromiseOrValue, + price: PromiseOrValue, + isRedistribution: PromiseOrValue, + overrides?: CallOverrides, + ): Promise< + [BigNumber, BigNumber] & { + collateralToSendToProtocol: BigNumber; + collateralToSentToLiquidator: BigNumber; + } + >; + }; + + LOW_TOTAL_DEBT(overrides?: CallOverrides): Promise; + + MCR(overrides?: CallOverrides): Promise; + + split( + totalCollateral: PromiseOrValue, + totalDebt: PromiseOrValue, + price: PromiseOrValue, + isRedistribution: PromiseOrValue, + overrides?: CallOverrides, + ): Promise< + [BigNumber, BigNumber] & { + collateralToSendToProtocol: BigNumber; + collateralToSentToLiquidator: BigNumber; + } + >; + + callStatic: { + LOW_TOTAL_DEBT(overrides?: CallOverrides): Promise; + + MCR(overrides?: CallOverrides): Promise; + + split( + totalCollateral: PromiseOrValue, + totalDebt: PromiseOrValue, + price: PromiseOrValue, + isRedistribution: PromiseOrValue, + overrides?: CallOverrides, + ): Promise< + [BigNumber, BigNumber] & { + collateralToSendToProtocol: BigNumber; + collateralToSentToLiquidator: BigNumber; + } + >; + }; + + filters: {}; + + estimateGas: { + LOW_TOTAL_DEBT(overrides?: CallOverrides): Promise; + + MCR(overrides?: CallOverrides): Promise; + + split( + totalCollateral: PromiseOrValue, + totalDebt: PromiseOrValue, + price: PromiseOrValue, + isRedistribution: PromiseOrValue, + overrides?: CallOverrides, + ): Promise; + }; + + populateTransaction: { + LOW_TOTAL_DEBT(overrides?: CallOverrides): Promise; + + MCR(overrides?: CallOverrides): Promise; + + split( + totalCollateral: PromiseOrValue, + totalDebt: PromiseOrValue, + price: PromiseOrValue, + isRedistribution: PromiseOrValue, + overrides?: CallOverrides, + ): Promise; + }; +} diff --git a/src/apps/raft/contracts/ethers/factories/RaftLiquiditation__factory.ts b/src/apps/raft/contracts/ethers/factories/RaftLiquiditation__factory.ts new file mode 100644 index 000000000..5214d7f16 --- /dev/null +++ b/src/apps/raft/contracts/ethers/factories/RaftLiquiditation__factory.ts @@ -0,0 +1,85 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +import { Contract, Signer, utils } from 'ethers'; +import type { Provider } from '@ethersproject/providers'; +import type { RaftLiquiditation, RaftLiquiditationInterface } from '../RaftLiquiditation'; + +const _abi = [ + { + inputs: [], + name: 'LOW_TOTAL_DEBT', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'MCR', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'totalCollateral', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'totalDebt', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'price', + type: 'uint256', + }, + { + internalType: 'bool', + name: 'isRedistribution', + type: 'bool', + }, + ], + name: 'split', + outputs: [ + { + internalType: 'uint256', + name: 'collateralToSendToProtocol', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'collateralToSentToLiquidator', + type: 'uint256', + }, + ], + stateMutability: 'pure', + type: 'function', + }, +]; + +export class RaftLiquiditation__factory { + static readonly abi = _abi; + static createInterface(): RaftLiquiditationInterface { + return new utils.Interface(_abi) as RaftLiquiditationInterface; + } + static connect(address: string, signerOrProvider: Signer | Provider): RaftLiquiditation { + return new Contract(address, _abi, signerOrProvider) as RaftLiquiditation; + } +} diff --git a/src/apps/raft/contracts/ethers/factories/index.ts b/src/apps/raft/contracts/ethers/factories/index.ts index 7d2973d4f..00abd02fb 100644 --- a/src/apps/raft/contracts/ethers/factories/index.ts +++ b/src/apps/raft/contracts/ethers/factories/index.ts @@ -1,5 +1,6 @@ /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ +export { RaftLiquiditation__factory } from './RaftLiquiditation__factory'; export { RaftPositionManager__factory } from './RaftPositionManager__factory'; export { RaftToken__factory } from './RaftToken__factory'; diff --git a/src/apps/raft/contracts/ethers/index.ts b/src/apps/raft/contracts/ethers/index.ts index 454c7951b..7022afb24 100644 --- a/src/apps/raft/contracts/ethers/index.ts +++ b/src/apps/raft/contracts/ethers/index.ts @@ -1,8 +1,10 @@ /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ +export type { RaftLiquiditation } from './RaftLiquiditation'; export type { RaftPositionManager } from './RaftPositionManager'; export type { RaftToken } from './RaftToken'; export * as factories from './factories'; +export { RaftLiquiditation__factory } from './factories/RaftLiquiditation__factory'; export { RaftPositionManager__factory } from './factories/RaftPositionManager__factory'; export { RaftToken__factory } from './factories/RaftToken__factory'; diff --git a/src/apps/raft/contracts/index.ts b/src/apps/raft/contracts/index.ts index 2d874588e..934e06dac 100644 --- a/src/apps/raft/contracts/index.ts +++ b/src/apps/raft/contracts/index.ts @@ -4,7 +4,7 @@ import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; import { ContractFactory } from '~contract/contracts'; import { Network } from '~types/network.interface'; -import { RaftPositionManager__factory, RaftToken__factory } from './ethers'; +import { RaftLiquiditation__factory, RaftPositionManager__factory, RaftToken__factory } from './ethers'; // eslint-disable-next-line type ContractOpts = { address: string; network: Network }; @@ -15,6 +15,9 @@ export class RaftContractFactory extends ContractFactory { super((network: Network) => appToolkit.getNetworkProvider(network)); } + raftLiquiditation({ address, network }: ContractOpts) { + return RaftLiquiditation__factory.connect(address, this.appToolkit.getNetworkProvider(network)); + } raftPositionManager({ address, network }: ContractOpts) { return RaftPositionManager__factory.connect(address, this.appToolkit.getNetworkProvider(network)); } @@ -23,5 +26,6 @@ export class RaftContractFactory extends ContractFactory { } } +export type { RaftLiquiditation } from './ethers'; export type { RaftPositionManager } from './ethers'; export type { RaftToken } from './ethers'; diff --git a/src/apps/raft/ethereum/raft.position-presenter.ts b/src/apps/raft/ethereum/raft.position-presenter.ts new file mode 100644 index 000000000..1e2dccda7 --- /dev/null +++ b/src/apps/raft/ethereum/raft.position-presenter.ts @@ -0,0 +1,45 @@ +import { Inject } from '@nestjs/common'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { + buildDollarDisplayItem, + buildPercentageDisplayItem, +} from '~app-toolkit/helpers/presentation/display-item.present'; +import { ContractPositionBalance } from '~position/position-balance.interface'; +import { MetadataItemWithLabel } from '~balance/balance-fetcher.interface'; +import { PositionPresenterTemplate, ReadonlyBalances } from '~position/template/position-presenter.template'; + +import { RaftContractFactory } from '../contracts'; + +export const positionManagerAddress = '0x5f59b322eb3e16a0c78846195af1f588b77403fc' + +export type RaftPositionPresenterDataProps = { + minCRatio: number; +}; + +export class EthereumRaftPositionPresenter extends PositionPresenterTemplate { + constructor( + @Inject(RaftContractFactory) protected readonly contractFactory: RaftContractFactory, + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + ) { + super(); + } + + override metadataItemsForBalanceGroup( + groupLabel: string, + balances: ReadonlyBalances, + dataProps?: RaftPositionPresenterDataProps, + ): MetadataItemWithLabel[] { + + const collateral = (balances[0] as ContractPositionBalance)?.tokens[0] + const collateralUSD = collateral?.balanceUSD ?? 0; + const debt = (balances[0] as ContractPositionBalance)?.tokens[1]?.balanceUSD ?? 0; + const cRatio = Math.abs(debt) > 0 ? Math.abs(collateralUSD / debt) : 0; + const liquidationPrice = dataProps?.minCRatio ? (dataProps.minCRatio * debt) / collateral.balance : 0 + + return [ + { label: 'C-Ratio', ...buildPercentageDisplayItem(cRatio) }, + { label: 'Liquidation Price', ...buildDollarDisplayItem(liquidationPrice) }, + ]; + } +}