From 701dc990cac4925583eb21b4edabc55b7aa31432 Mon Sep 17 00:00:00 2001 From: William Poulin Date: Thu, 5 Jan 2023 13:27:37 -0500 Subject: [PATCH] fix(inverse-firm): Migration to template (#2046) * fix(inverse-firm): Linting * fix(rpc-url): Updated Ethereum network RPC url enabling fetching data older than 128 blocks * fix(inverse-firm): Migration to template * chore(inverse-firm): Add links --- .../inverse-firm/common/inverse-firm.utils.ts | 7 - .../ethereum/inverse-firm.balance-fetcher.ts | 66 ------ ...rse-firm.loan.contract-position-fetcher.ts | 207 +++++++++--------- .../inverse-firm/inverse-firm.definition.ts | 9 +- src/apps/inverse-firm/inverse-firm.module.ts | 12 +- .../network-provider.registry.ts | 2 +- 6 files changed, 109 insertions(+), 194 deletions(-) delete mode 100644 src/apps/inverse-firm/common/inverse-firm.utils.ts delete mode 100644 src/apps/inverse-firm/ethereum/inverse-firm.balance-fetcher.ts diff --git a/src/apps/inverse-firm/common/inverse-firm.utils.ts b/src/apps/inverse-firm/common/inverse-firm.utils.ts deleted file mode 100644 index 64414b111..000000000 --- a/src/apps/inverse-firm/common/inverse-firm.utils.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Dbr } from '../contracts'; -import { uniq } from 'lodash'; - -export const getMarkets = async (dbrContract: Dbr) => { - const logs = await dbrContract.queryFilter(dbrContract.filters.AddMarket()); - return uniq(logs.map(l => l.args.market)); -} \ No newline at end of file diff --git a/src/apps/inverse-firm/ethereum/inverse-firm.balance-fetcher.ts b/src/apps/inverse-firm/ethereum/inverse-firm.balance-fetcher.ts deleted file mode 100644 index 58c7fc606..000000000 --- a/src/apps/inverse-firm/ethereum/inverse-firm.balance-fetcher.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Inject } from '@nestjs/common'; - -import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; -import { Register } from '~app-toolkit/decorators'; -import { presentBalanceFetcherResponse } from '~app-toolkit/helpers/presentation/balance-fetcher-response.present'; -import { BalanceFetcher } from '~balance/balance-fetcher.interface'; -import { Network } from '~types/network.interface'; - -import { INVERSE_FIRM_DEFINITION } from '../inverse-firm.definition'; -import { isBorrowed, isSupplied } from '~position/position.utils'; -import { InverseFirmContractFactory } from '../contracts'; -import { drillBalance } from '~app-toolkit'; - -const network = Network.ETHEREUM_MAINNET; - -@Register.BalanceFetcher(INVERSE_FIRM_DEFINITION.id, network) -export class EthereumInverseFirmBalanceFetcher implements BalanceFetcher { - constructor( - @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, - @Inject(InverseFirmContractFactory) private readonly inverseFirmContractFactory: InverseFirmContractFactory, - ) { } - - async getLoanBalances(address: string) { - return this.appToolkit.helpers.contractPositionBalanceHelper.getContractPositionBalances( - { - address, - appId: INVERSE_FIRM_DEFINITION.id, - groupId: INVERSE_FIRM_DEFINITION.groups.loan.id, - network: Network.ETHEREUM_MAINNET, - resolveBalances: async ({ address, contractPosition, multicall }) => { - const suppliedToken = contractPosition.tokens.find(isSupplied)!; - const borrowedToken = contractPosition.tokens.find(isBorrowed)!; - - const contract = - this.inverseFirmContractFactory.simpleMarket({ address: contractPosition.address, network }); - - const personalEscrow = await multicall.wrap(contract).escrows(address); - - const escrowContract = - this.inverseFirmContractFactory.simpleEscrow({ address: personalEscrow, network }); - - const [depositRaw, debtRaw] = await Promise.all([ - multicall.wrap(escrowContract).balance(), - multicall.wrap(contract).debts(address), - ]); - - return [ - drillBalance(suppliedToken, depositRaw.toString()), - drillBalance(borrowedToken, debtRaw.toString(), { isDebt: true }), - ]; - }, - } - ); - } - - async getBalances(address: string) { - const loans = await this.getLoanBalances(address); - - return presentBalanceFetcherResponse([ - { - label: "Lending", - assets: loans, - }, - ]); - } -} diff --git a/src/apps/inverse-firm/ethereum/inverse-firm.loan.contract-position-fetcher.ts b/src/apps/inverse-firm/ethereum/inverse-firm.loan.contract-position-fetcher.ts index f273328c8..f4cfb0203 100644 --- a/src/apps/inverse-firm/ethereum/inverse-firm.loan.contract-position-fetcher.ts +++ b/src/apps/inverse-firm/ethereum/inverse-firm.loan.contract-position-fetcher.ts @@ -1,118 +1,109 @@ import { Inject } from '@nestjs/common'; +import { BigNumberish } from 'ethers'; +import { uniq } from 'lodash'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; +import { getLabelFromToken } from '~app-toolkit/helpers/presentation/image.present'; +import { DefaultDataProps } from '~position/display.interface'; +import { MetaType } from '~position/position.interface'; +import { ContractPositionTemplatePositionFetcher } from '~position/template/contract-position.template.position-fetcher'; +import { + GetDisplayPropsParams, + GetTokenBalancesParams, + GetTokenDefinitionsParams, +} from '~position/template/contract-position.template.types'; + +import { InverseFirmContractFactory, SimpleMarket } from '../contracts'; + +export type InverseFirmLoanContractPositionDefinition = { + address: string; + suppliedTokenAddress: string; + borrowedTokenAddress: string; +}; + +@PositionTemplate() +export class EthereumInverseFirmLoanContractPositionFetcher extends ContractPositionTemplatePositionFetcher< + SimpleMarket, + DefaultDataProps, + InverseFirmLoanContractPositionDefinition +> { + groupLabel = 'Lending'; -import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { ContractPosition, MetaType } from '~position/position.interface'; -import { Network } from '~types/network.interface'; - -import { InverseFirmContractFactory } from '../contracts'; -import { INVERSE_FIRM_DEFINITION } from '../inverse-firm.definition'; -import _ from 'lodash'; -import { ContractType } from '~position/contract.interface'; -import { borrowed, supplied } from '~position/position.utils'; -import { getMarkets } from '../common/inverse-firm.utils'; -import { getImagesFromToken } from '~app-toolkit/helpers/presentation/image.present'; - -const appId = INVERSE_FIRM_DEFINITION.id; -const dola = INVERSE_FIRM_DEFINITION.dola; -const dbr = INVERSE_FIRM_DEFINITION.dbr; -const groupId = INVERSE_FIRM_DEFINITION.groups.loan.id; -const network = Network.ETHEREUM_MAINNET; - -@Register.ContractPositionFetcher({ appId, groupId, network }) -export class EthereumInverseFirmLoanContractPositionFetcher implements PositionFetcher { constructor( - @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, - @Inject(InverseFirmContractFactory) private readonly inverseFirmContractFactory: InverseFirmContractFactory, - ) { } + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(InverseFirmContractFactory) private readonly contractFactory: InverseFirmContractFactory, + ) { + super(appToolkit); + } - async getMarkets() { - const dbrContract = this.inverseFirmContractFactory.dbr({ address: dbr, network }); - return getMarkets(dbrContract); + getContract(address: string): SimpleMarket { + return this.contractFactory.simpleMarket({ network: this.network, address }); } - async getPositions() { - const baseTokens = await this.appToolkit.getBaseTokenPrices(network); - const dolaToken = baseTokens.find(bt => bt.address === dola.toLowerCase())!; - const dolaAsBorrowToken = borrowed(dolaToken); - - const markets = await this.getMarkets(); - - const multicall = this.appToolkit.getMulticall(network); - const dolaContract = this.appToolkit.globalContracts.erc20({ address: dola, network }); - - const marketsData = await Promise.all( - markets.map(m => { - const contract = this.inverseFirmContractFactory.simpleMarket({ address: m, network }); - return Promise.all( - [ - multicall.wrap(contract).totalDebt(), - multicall.wrap(contract).collateral(), - multicall.wrap(dolaContract).balanceOf(m), - ] - ); - }) + async getDefinitions(): Promise { + const multicall = this.appToolkit.getMulticall(this.network); + + const dolaBorrowRightContract = this.contractFactory.dbr({ + address: '0xad038eb671c44b853887a7e32528fab35dc5d710', + network: this.network, + }); + + const logs = await dolaBorrowRightContract.queryFilter(dolaBorrowRightContract.filters.AddMarket(), 16155757); + const markets = uniq(logs.map(l => l.args.market.toLowerCase())); + + const definitions = await Promise.all( + markets.map(async address => { + const simpleMarketContract = this.contractFactory.simpleMarket({ address, network: this.network }); + const collateralTokenAddressRaw = await multicall.wrap(simpleMarketContract).collateral(); + + return { + address, + suppliedTokenAddress: collateralTokenAddressRaw.toLowerCase(), + borrowedTokenAddress: '0x865377367054516e17014ccded1e7d814edc9ce4', // dola + }; + }), ); - const positions: ContractPosition[] = marketsData.map((data, i) => { - const collateralAddress = data[1]; - const dolaBalance = data[2]; - const liquidity = Number(dolaBalance) / 1e18; - const token = baseTokens.find(bt => bt.address === collateralAddress.toLowerCase())!; - const suppliedToken = supplied(token); - - const tokens = [ - { - "type": suppliedToken.type, - "network": suppliedToken.network, - "address": suppliedToken.address, - "symbol": suppliedToken.symbol, - "decimals": suppliedToken.decimals, - "price": suppliedToken.price, - metaType: MetaType.SUPPLIED, - }, - { - "type": dolaAsBorrowToken.type, - "network": dolaAsBorrowToken.network, - "address": dolaAsBorrowToken.address, - "symbol": dolaAsBorrowToken.symbol, - "decimals": dolaAsBorrowToken.decimals, - "price": dolaAsBorrowToken.price, - metaType: MetaType.BORROWED, - }, - ]; - - return { - type: ContractType.POSITION, - address: markets[i], - appId, - groupId, - network, - tokens, - dataProps: { - liquidity, - }, - displayProps: { - label: `${suppliedToken.symbol} Market`, - secondaryLabel: { - type: 'dollar', - value: suppliedToken.price, - }, - images: getImagesFromToken(suppliedToken), - }, - statsItems: [ - { - "label": "Total Liquidity", - "value": { - "type": "dollar", - "value": liquidity, - } - }, - ], - } - }) - - return _.compact(positions); + return definitions; + } + + async getTokenDefinitions({ + definition, + }: GetTokenDefinitionsParams) { + return [ + { + metaType: MetaType.SUPPLIED, + address: definition.suppliedTokenAddress, + network: this.network, + }, + { + metaType: MetaType.BORROWED, + address: definition.borrowedTokenAddress, + network: this.network, + }, + ]; + } + + async getLabel({ contractPosition }: GetDisplayPropsParams): Promise { + return `${getLabelFromToken(contractPosition.tokens[0])} Market`; + } + + async getTokenBalancesPerPosition({ + address, + contract, + multicall, + }: GetTokenBalancesParams): Promise { + const personalEscrow = await contract.escrows(address); + + const simpleEscrowContract = this.contractFactory.simpleEscrow({ + address: personalEscrow.toLowerCase(), + network: this.network, + }); + const supplied = await multicall.wrap(simpleEscrowContract).balance(); + + const borrowed = await contract.debts(address); + + return [supplied, borrowed]; } } diff --git a/src/apps/inverse-firm/inverse-firm.definition.ts b/src/apps/inverse-firm/inverse-firm.definition.ts index fa2f6342c..168380961 100644 --- a/src/apps/inverse-firm/inverse-firm.definition.ts +++ b/src/apps/inverse-firm/inverse-firm.definition.ts @@ -9,9 +9,12 @@ export const INVERSE_FIRM_DEFINITION = appDefinition({ description: `FiRM is a Fixed-Rate lending protocol by Inverse Finance which allows borrowing the DOLA stablecoin at a fixed-rate through the utility token named DOLA Borrowing Right (DBR).`, url: 'https://www.inverse.finance/firm', tags: [AppTag.YIELD_AGGREGATOR, AppTag.LENDING], - links: {}, - dola: '0x865377367054516e17014CcdED1e7d814EDC9ce4', - dbr: '0xAD038Eb671c44b853887A7E32528FaB35dC5D710', + links: { + discord: 'https://discord.com/invite/YpYJC7R5nv', + github: 'https://github.com/InverseFinance', + telegram: 'https://t.me/InverseFinance', + twitter: 'https://twitter.com/InverseFinance', + }, groups: { loan: { diff --git a/src/apps/inverse-firm/inverse-firm.module.ts b/src/apps/inverse-firm/inverse-firm.module.ts index 77bd2eae5..5724b311a 100644 --- a/src/apps/inverse-firm/inverse-firm.module.ts +++ b/src/apps/inverse-firm/inverse-firm.module.ts @@ -1,18 +1,12 @@ import { Register } from '~app-toolkit/decorators'; import { AbstractApp } from '~app/app.dynamic-module'; + import { InverseFirmContractFactory } from './contracts'; import { EthereumInverseFirmLoanContractPositionFetcher } from './ethereum/inverse-firm.loan.contract-position-fetcher'; -import { EthereumInverseFirmBalanceFetcher } from './ethereum/inverse-firm.balance-fetcher'; import { InverseFirmAppDefinition, INVERSE_FIRM_DEFINITION } from './inverse-firm.definition'; @Register.AppModule({ appId: INVERSE_FIRM_DEFINITION.id, - imports: [], - providers: [ - InverseFirmAppDefinition, - InverseFirmContractFactory, - EthereumInverseFirmLoanContractPositionFetcher, - EthereumInverseFirmBalanceFetcher, - ], + providers: [InverseFirmAppDefinition, InverseFirmContractFactory, EthereumInverseFirmLoanContractPositionFetcher], }) -export class InverseFirmAppModule extends AbstractApp() { } +export class InverseFirmAppModule extends AbstractApp() {} diff --git a/src/network-provider/network-provider.registry.ts b/src/network-provider/network-provider.registry.ts index bfc8eeab1..98ea2c7cf 100644 --- a/src/network-provider/network-provider.registry.ts +++ b/src/network-provider/network-provider.registry.ts @@ -5,7 +5,7 @@ export const DEFAULT_REGISTRY: Record, [Network.AVALANCHE_MAINNET]: 'https://avalanche.public-rpc.com', [Network.BINANCE_SMART_CHAIN_MAINNET]: 'https://bsc-dataseed.binance.org/', [Network.CELO_MAINNET]: 'https://forno.celo.org', - [Network.ETHEREUM_MAINNET]: 'https://cloudflare-eth.com', + [Network.ETHEREUM_MAINNET]: 'https://eth-mainnet-public.unifra.io', [Network.FANTOM_OPERA_MAINNET]: 'https://rpc.ftm.tools/', [Network.GNOSIS_MAINNET]: 'https://rpc.gnosischain.com/', [Network.HARMONY_MAINNET]: 'https://harmony.public-rpc.com',