From 74df37882dfb6971d3710da8d36f2703c5ff5a5d Mon Sep 17 00:00:00 2001 From: immasandwich Date: Sun, 27 Nov 2022 10:38:08 -0500 Subject: [PATCH] feat(gmx): Migrate to templates (#1801) --- .github/workflows/ci.yml | 2 + src/apps/gmx/arbitrum/gmx.balance-fetcher.ts | 110 --------------- .../gmx/arbitrum/gmx.es-gmx.token-fetcher.ts | 29 ++-- .../gmx.farm.contract-position-fetcher.ts | 102 ++++++-------- .../gmx/arbitrum/gmx.glp.token-fetcher.ts | 32 ++--- .../gmx.option.contract-position-fetcher.ts | 26 ---- .../gmx.perp.contract-position-fetcher.ts | 10 ++ src/apps/gmx/avalanche/gmx.balance-fetcher.ts | 109 --------------- .../gmx/avalanche/gmx.es-gmx.token-fetcher.ts | 29 ++-- .../gmx.farm.contract-position-fetcher.ts | 102 ++++++-------- .../gmx/avalanche/gmx.glp.token-fetcher.ts | 31 ++--- .../gmx.option.contract-position-fetcher.ts | 26 ---- .../gmx.perp.contract-position-fetcher.ts | 10 ++ .../gmx/common/gmx.es-gmx.token-fetcher.ts | 35 +++++ .../gmx.farm.contract-position-fetcher.ts | 62 +++++++++ src/apps/gmx/common/gmx.glp.token-fetcher.ts | 73 ++++++++++ .../gmx.perp.contract-position-fetcher.ts | 128 ++++++++++++++++++ src/apps/gmx/gmx.definition.ts | 8 +- src/apps/gmx/gmx.module.ts | 22 +-- .../gmx/helpers/gmx.es-gmx.token-helper.ts | 75 ---------- src/apps/gmx/helpers/gmx.glp.token-helper.ts | 99 -------------- .../gmx/helpers/gmx.option.balance-helper.ts | 88 ------------ .../gmx.option.contract-position-helper.ts | 105 -------------- 23 files changed, 444 insertions(+), 869 deletions(-) delete mode 100644 src/apps/gmx/arbitrum/gmx.balance-fetcher.ts delete mode 100644 src/apps/gmx/arbitrum/gmx.option.contract-position-fetcher.ts create mode 100644 src/apps/gmx/arbitrum/gmx.perp.contract-position-fetcher.ts delete mode 100644 src/apps/gmx/avalanche/gmx.balance-fetcher.ts delete mode 100644 src/apps/gmx/avalanche/gmx.option.contract-position-fetcher.ts create mode 100644 src/apps/gmx/avalanche/gmx.perp.contract-position-fetcher.ts create mode 100644 src/apps/gmx/common/gmx.es-gmx.token-fetcher.ts create mode 100644 src/apps/gmx/common/gmx.farm.contract-position-fetcher.ts create mode 100644 src/apps/gmx/common/gmx.glp.token-fetcher.ts create mode 100644 src/apps/gmx/common/gmx.perp.contract-position-fetcher.ts delete mode 100644 src/apps/gmx/helpers/gmx.es-gmx.token-helper.ts delete mode 100644 src/apps/gmx/helpers/gmx.glp.token-helper.ts delete mode 100644 src/apps/gmx/helpers/gmx.option.balance-helper.ts delete mode 100644 src/apps/gmx/helpers/gmx.option.contract-position-helper.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cd9f0074..def908a6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,8 @@ jobs: run_install: true - name: Studio Background Check 🚓 + env: + NODE_OPTIONS: '--max_old_space_size=4096' run: pnpm build unit: diff --git a/src/apps/gmx/arbitrum/gmx.balance-fetcher.ts b/src/apps/gmx/arbitrum/gmx.balance-fetcher.ts deleted file mode 100644 index e3c741f25..000000000 --- a/src/apps/gmx/arbitrum/gmx.balance-fetcher.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { Inject } from '@nestjs/common'; - -import { SingleStakingContractPositionBalanceHelper, TokenBalanceHelper } from '~app-toolkit'; -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 { isSupplied } from '~position/position.utils'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory, GmxRewardTracker } from '../contracts'; -import { GMX_DEFINITION } from '../gmx.definition'; -import { GmxOptionBalanceHelper } from '../helpers/gmx.option.balance-helper'; - -import { FARMS } from './gmx.farm.contract-position-fetcher'; - -const network = Network.ARBITRUM_MAINNET; - -@Register.BalanceFetcher(GMX_DEFINITION.id, network) -export class ArbitrumGmxBalanceFetcher implements BalanceFetcher { - constructor( - @Inject(SingleStakingContractPositionBalanceHelper) - private readonly singleStakingContractPositionBalanceHelper: SingleStakingContractPositionBalanceHelper, - @Inject(GmxContractFactory) private readonly gmxContractFactory: GmxContractFactory, - @Inject(TokenBalanceHelper) private readonly tokenBalanceHelper: TokenBalanceHelper, - @Inject(GmxOptionBalanceHelper) private readonly gmxOptionBalanceHelper: GmxOptionBalanceHelper, - ) {} - - private async getGlpTokenBalances(address: string) { - return this.tokenBalanceHelper.getTokenBalances({ - address, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.glp.id, - network, - }); - } - - private async getEsGmxTokenBalances(address: string) { - return this.tokenBalanceHelper.getTokenBalances({ - address, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.esGmx.id, - network, - }); - } - - private async getStakedBalances(address: string) { - return this.singleStakingContractPositionBalanceHelper.getBalances({ - address, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.farm.id, - network, - resolveContract: ({ address, network }) => this.gmxContractFactory.gmxRewardTracker({ address, network }), - resolveStakedTokenBalance: async ({ contractPosition, address, multicall, network }) => { - const stakedToken = contractPosition.tokens.find(isSupplied)!; - const readerAddress = '0xe725ad0ce3ecf68a7b93d8d8091e83043ff12e9a'; - const readerContract = this.gmxContractFactory.gmxRewardReader({ address: readerAddress, network }); - - const depositBalances = await multicall - .wrap(readerContract) - .getDepositBalances(address, [stakedToken.address], [contractPosition.address]); - return depositBalances[0]; - }, - resolveRewardTokenBalances: async ({ contractPosition, address, multicall, network }) => { - const stakedToken = contractPosition.tokens.find(isSupplied)!; - - const farmDefinition = FARMS.find(v => v.stakedTokenAddress === stakedToken.address); - const rewardTrackers = farmDefinition?.rewardTrackerAddresses ?? []; - if (!rewardTrackers.length) return []; - - const readerAddress = '0xe725ad0ce3ecf68a7b93d8d8091e83043ff12e9a'; - const readerContract = this.gmxContractFactory.gmxRewardReader({ address: readerAddress, network }); - const stakingInfo = await multicall.wrap(readerContract).getStakingInfo(address, rewardTrackers); - return [stakingInfo[0].toString(), stakingInfo[5].toString()]; - }, - }); - } - - private async getOptionBalances(address: string) { - const vaultAddress = '0x489ee077994b6658eafa855c308275ead8097c4a'; - return this.gmxOptionBalanceHelper.getBalance({ address, network, vaultAddress }); - } - - async getBalances(address: string) { - const [glpTokenBalances, esGmxTokenBalances, stakedBalances, optionBalances] = await Promise.all([ - this.getGlpTokenBalances(address), - this.getEsGmxTokenBalances(address), - this.getStakedBalances(address), - this.getOptionBalances(address), - ]); - - return presentBalanceFetcherResponse([ - { - label: 'GLP', - assets: [...glpTokenBalances], - }, - { - label: 'esGMX', - assets: [...esGmxTokenBalances], - }, - { - label: 'Options', - assets: [...optionBalances], - }, - { - label: 'Farms', - assets: [...stakedBalances], - }, - ]); - } -} diff --git a/src/apps/gmx/arbitrum/gmx.es-gmx.token-fetcher.ts b/src/apps/gmx/arbitrum/gmx.es-gmx.token-fetcher.ts index 0448678ac..f49c6cecb 100644 --- a/src/apps/gmx/arbitrum/gmx.es-gmx.token-fetcher.ts +++ b/src/apps/gmx/arbitrum/gmx.es-gmx.token-fetcher.ts @@ -1,25 +1,12 @@ -import { Inject } from '@nestjs/common'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { AppTokenPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; +import { GmxEsGmxTokenFetcher } from '../common/gmx.es-gmx.token-fetcher'; -import { GMX_DEFINITION } from '../gmx.definition'; -import { GmxEsGmxTokenHelper } from '../helpers/gmx.es-gmx.token-helper'; +@PositionTemplate() +export class ArbitrumGmxEsGmxTokenFetcher extends GmxEsGmxTokenFetcher { + groupLabel = 'esGMX'; + isExcludedFromTvl = true; -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.esGmx.id; -const network = Network.ARBITRUM_MAINNET; - -@Register.TokenPositionFetcher({ appId, groupId, network, options: { excludeFromTvl: true } }) -export class ArbitrumGmxEsGmxTokenFetcher implements PositionFetcher { - constructor(@Inject(GmxEsGmxTokenHelper) private readonly gmxEsGmxTokenHelper: GmxEsGmxTokenHelper) {} - - async getPositions() { - return this.gmxEsGmxTokenHelper.getTokens({ - esGmxTokenAddress: '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca', - network, - }); - } + esGmxAddress = '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca'; + gmxAddress = '0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a'; } diff --git a/src/apps/gmx/arbitrum/gmx.farm.contract-position-fetcher.ts b/src/apps/gmx/arbitrum/gmx.farm.contract-position-fetcher.ts index 1e96cbde1..66c3bb01b 100644 --- a/src/apps/gmx/arbitrum/gmx.farm.contract-position-fetcher.ts +++ b/src/apps/gmx/arbitrum/gmx.farm.contract-position-fetcher.ts @@ -1,62 +1,44 @@ -import { Inject } from '@nestjs/common'; - -import { SingleStakingFarmContractPositionHelper } from '~app-toolkit'; -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { ContractPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory, GmxRewardTracker } from '../contracts'; -import GMX_DEFINITION from '../gmx.definition'; - -export const GMX_FARM = { - address: '0x908c4d94d34924765f1edc22a1dd098397c59dd4', - stakedTokenAddress: '0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a', - rewardTokenAddresses: ['0x82af49447d8a07e3bd95bd0d56f35241523fbab1', '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca'], - rewardTrackerAddresses: ['0xd2d1162512f927a7e282ef43a362659e4f2a728f', '0x908c4d94d34924765f1edc22a1dd098397c59dd4'], -}; - -export const ES_GMX_FARM = { - address: '0x908c4d94d34924765f1edc22a1dd098397c59dd4', - stakedTokenAddress: '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca', - rewardTokenAddresses: ['0x82af49447d8a07e3bd95bd0d56f35241523fbab1', '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca'], - rewardTrackerAddresses: [], -}; - -export const GLP_FARM = { - address: '0x4e971a87900b931ff39d1aad67697f49835400b6', - stakedTokenAddress: '0x4277f8f2c384827b5273592ff7cebd9f2c1ac258', - rewardTokenAddresses: ['0x82af49447d8a07e3bd95bd0d56f35241523fbab1', '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca'], - rewardTrackerAddresses: ['0x4e971a87900b931ff39d1aad67697f49835400b6', '0x1addd80e6039594ee970e5872d247bf0414c8903'], -}; - -export const FARMS = [GMX_FARM, ES_GMX_FARM, GLP_FARM]; - -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.farm.id; -const network = Network.ARBITRUM_MAINNET; - -@Register.ContractPositionFetcher({ appId, groupId, network }) -export class ArbitrumGmxFarmContractPositionFetcher implements PositionFetcher { - constructor( - @Inject(SingleStakingFarmContractPositionHelper) - private readonly singleStakingFarmContractPositionHelper: SingleStakingFarmContractPositionHelper, - @Inject(GmxContractFactory) - private readonly gmxContractFactory: GmxContractFactory, - ) {} - - async getPositions() { - return this.singleStakingFarmContractPositionHelper.getContractPositions({ - appId, - groupId, - network, - dependencies: [ - { appId: GMX_DEFINITION.id, groupIds: [GMX_DEFINITION.groups.esGmx.id, GMX_DEFINITION.groups.glp.id], network }, +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; + +import { GmxFarmContractPositionFetcher } from '../common/gmx.farm.contract-position-fetcher'; + +@PositionTemplate() +export class ArbitrumGmxFarmContractPositionFetcher extends GmxFarmContractPositionFetcher { + groupLabel = 'Farms'; + readerAddress = '0xe725ad0ce3ecf68a7b93d8d8091e83043ff12e9a'; + farms = [ + { + address: '0x908c4d94d34924765f1edc22a1dd098397c59dd4', + stakedTokenAddress: '0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a', // GMX + rewardTokenAddresses: [ + '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca', + ], + rewardTrackerAddresses: [ + '0xd2d1162512f927a7e282ef43a362659e4f2a728f', + '0x908c4d94d34924765f1edc22a1dd098397c59dd4', + ], + }, + { + address: '0x908c4d94d34924765f1edc22a1dd098397c59dd4', + stakedTokenAddress: '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca', // esGMX + rewardTokenAddresses: [ + '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca', + ], + rewardTrackerAddresses: [], + }, + { + address: '0x4e971a87900b931ff39d1aad67697f49835400b6', + stakedTokenAddress: '0x4277f8f2c384827b5273592ff7cebd9f2c1ac258', // GLP + rewardTokenAddresses: [ + '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + '0xf42ae1d54fd613c9bb14810b0588faaa09a426ca', + ], + rewardTrackerAddresses: [ + '0x4e971a87900b931ff39d1aad67697f49835400b6', + '0x1addd80e6039594ee970e5872d247bf0414c8903', ], - resolveFarmDefinitions: async () => FARMS, - resolveFarmContract: ({ network, address }) => this.gmxContractFactory.gmxRewardTracker({ network, address }), - resolveIsActive: () => true, - resolveRois: () => ({ dailyROI: 0, weeklyROI: 0, yearlyROI: 0 }), - }); - } + }, + ]; } diff --git a/src/apps/gmx/arbitrum/gmx.glp.token-fetcher.ts b/src/apps/gmx/arbitrum/gmx.glp.token-fetcher.ts index 780033c00..67af7c2cc 100644 --- a/src/apps/gmx/arbitrum/gmx.glp.token-fetcher.ts +++ b/src/apps/gmx/arbitrum/gmx.glp.token-fetcher.ts @@ -1,27 +1,11 @@ -import { Inject } from '@nestjs/common'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { AppTokenPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; +import { GmxGlpTokenFetcher } from '../common/gmx.glp.token-fetcher'; +@PositionTemplate() +export class ArbitrumGmxGlpTokenFetcher extends GmxGlpTokenFetcher { + groupLabel = 'GLP'; -import GMX_DEFINITION from '../gmx.definition'; -import { GmxGlpTokenHelper } from '../helpers/gmx.glp.token-helper'; - -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.glp.id; -const network = Network.ARBITRUM_MAINNET; - -@Register.TokenPositionFetcher({ appId, groupId, network }) -export class ArbitrumGmxGlpTokenFetcher implements PositionFetcher { - constructor(@Inject(GmxGlpTokenHelper) private readonly gmxGlpTokenHelper: GmxGlpTokenHelper) {} - - async getPositions() { - return this.gmxGlpTokenHelper.getTokens({ - glmManagerAddress: '0x321f653eed006ad1c29d174e17d96351bde22649', - glpTokenAddress: '0x4277f8f2c384827b5273592ff7cebd9f2c1ac258', - network, - blockedTokenAddresses: ['0xfea7a6a0b346362bf88a9e4a88416b77a57d6c2a'], - }); - } + glmManagerAddress = '0x321f653eed006ad1c29d174e17d96351bde22649'; + glpTokenAddress = '0x4277f8f2c384827b5273592ff7cebd9f2c1ac258'; + blockedTokenAddresses = ['0xfea7a6a0b346362bf88a9e4a88416b77a57d6c2a']; } diff --git a/src/apps/gmx/arbitrum/gmx.option.contract-position-fetcher.ts b/src/apps/gmx/arbitrum/gmx.option.contract-position-fetcher.ts deleted file mode 100644 index ee2553a51..000000000 --- a/src/apps/gmx/arbitrum/gmx.option.contract-position-fetcher.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Inject } from '@nestjs/common'; - -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { ContractPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; - -import GMX_DEFINITION from '../gmx.definition'; -import { GmxOptionContractPositionHelper } from '../helpers/gmx.option.contract-position-helper'; - -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.option.id; -const network = Network.ARBITRUM_MAINNET; - -@Register.ContractPositionFetcher({ appId, groupId, network }) -export class ArbitrumOptionsFarmContractPositionFetcher implements PositionFetcher { - constructor( - @Inject(GmxOptionContractPositionHelper) - private readonly gmxOptionContractPositionHelper: GmxOptionContractPositionHelper, - ) {} - - async getPositions() { - const vaultAddress = '0x489ee077994b6658eafa855c308275ead8097c4a'; - return this.gmxOptionContractPositionHelper.getPosition({ network, vaultAddress }); - } -} diff --git a/src/apps/gmx/arbitrum/gmx.perp.contract-position-fetcher.ts b/src/apps/gmx/arbitrum/gmx.perp.contract-position-fetcher.ts new file mode 100644 index 000000000..430ce3145 --- /dev/null +++ b/src/apps/gmx/arbitrum/gmx.perp.contract-position-fetcher.ts @@ -0,0 +1,10 @@ +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; + +import { GmxPerpContractPositionFetcher } from '../common/gmx.perp.contract-position-fetcher'; + +@PositionTemplate() +export class ArbitrumGmxPerpContractPositionFetcher extends GmxPerpContractPositionFetcher { + groupLabel = 'Perpetuals'; + vaultAddress = '0x489ee077994b6658eafa855c308275ead8097c4a'; + usdcAddress = '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8'; +} diff --git a/src/apps/gmx/avalanche/gmx.balance-fetcher.ts b/src/apps/gmx/avalanche/gmx.balance-fetcher.ts deleted file mode 100644 index 75a75df48..000000000 --- a/src/apps/gmx/avalanche/gmx.balance-fetcher.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Inject } from '@nestjs/common'; - -import { SingleStakingContractPositionBalanceHelper, TokenBalanceHelper } from '~app-toolkit'; -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 { isSupplied } from '~position/position.utils'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory, GmxRewardTracker } from '../contracts'; -import { GMX_DEFINITION } from '../gmx.definition'; -import { GmxOptionBalanceHelper } from '../helpers/gmx.option.balance-helper'; - -import { FARMS } from './gmx.farm.contract-position-fetcher'; - -const network = Network.AVALANCHE_MAINNET; - -@Register.BalanceFetcher(GMX_DEFINITION.id, network) -export class AvalancheGmxBalanceFetcher implements BalanceFetcher { - constructor( - @Inject(SingleStakingContractPositionBalanceHelper) - private readonly singleStakingContractPositionBalanceHelper: SingleStakingContractPositionBalanceHelper, - @Inject(GmxContractFactory) private readonly gmxContractFactory: GmxContractFactory, - @Inject(TokenBalanceHelper) private readonly tokenBalanceHelper: TokenBalanceHelper, - @Inject(GmxOptionBalanceHelper) private readonly gmxOptionBalanceHelper: GmxOptionBalanceHelper, - ) {} - - private async getGlpTokenBalances(address: string) { - return this.tokenBalanceHelper.getTokenBalances({ - address, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.glp.id, - network, - }); - } - - private async getEsGmxTokenBalances(address: string) { - return this.tokenBalanceHelper.getTokenBalances({ - address, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.esGmx.id, - network, - }); - } - - private async getStakedBalances(address: string) { - return this.singleStakingContractPositionBalanceHelper.getBalances({ - address, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.farm.id, - network, - resolveContract: ({ address, network }) => this.gmxContractFactory.gmxRewardTracker({ address, network }), - resolveStakedTokenBalance: async ({ contractPosition, address, multicall, network }) => { - const stakedToken = contractPosition.tokens.find(isSupplied)!; - const readerAddress = '0x956d63dd6540230487eb7e599ef8b0c6fdca4ab8'; - const readerContract = this.gmxContractFactory.gmxRewardReader({ address: readerAddress, network }); - - const depositBalances = await multicall - .wrap(readerContract) - .getDepositBalances(address, [stakedToken.address], [contractPosition.address]); - return depositBalances[0]; - }, - resolveRewardTokenBalances: async ({ contractPosition, address, multicall, network }) => { - const stakedToken = contractPosition.tokens.find(isSupplied)!; - const farmDefinition = FARMS.find(v => v.stakedTokenAddress === stakedToken.address); - const rewardTrackers = farmDefinition?.rewardTrackerAddresses ?? []; - if (!rewardTrackers.length) return []; - - const readerAddress = '0x956d63dd6540230487eb7e599ef8b0c6fdca4ab8'; - const readerContract = this.gmxContractFactory.gmxRewardReader({ address: readerAddress, network }); - const stakingInfo = await multicall.wrap(readerContract).getStakingInfo(address, rewardTrackers); - return [stakingInfo[0].toString(), stakingInfo[5].toString()]; - }, - }); - } - - private async getOptionBalances(address: string) { - const vaultAddress = '0x9ab2de34a33fb459b538c43f251eb825645e8595'; - return this.gmxOptionBalanceHelper.getBalance({ address, network, vaultAddress }); - } - - async getBalances(address: string) { - const [glpTokenBalances, esGmxTokenBalances, stakedBalances, optionBalances] = await Promise.all([ - this.getGlpTokenBalances(address), - this.getEsGmxTokenBalances(address), - this.getStakedBalances(address), - this.getOptionBalances(address), - ]); - - return presentBalanceFetcherResponse([ - { - label: 'GLP', - assets: [...glpTokenBalances], - }, - { - label: 'esGMX', - assets: [...esGmxTokenBalances], - }, - { - label: 'Options', - assets: [...optionBalances], - }, - { - label: 'Farms', - assets: [...stakedBalances], - }, - ]); - } -} diff --git a/src/apps/gmx/avalanche/gmx.es-gmx.token-fetcher.ts b/src/apps/gmx/avalanche/gmx.es-gmx.token-fetcher.ts index 18a40b756..b725767ba 100644 --- a/src/apps/gmx/avalanche/gmx.es-gmx.token-fetcher.ts +++ b/src/apps/gmx/avalanche/gmx.es-gmx.token-fetcher.ts @@ -1,25 +1,12 @@ -import { Inject } from '@nestjs/common'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { AppTokenPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; +import { GmxEsGmxTokenFetcher } from '../common/gmx.es-gmx.token-fetcher'; -import { GMX_DEFINITION } from '../gmx.definition'; -import { GmxEsGmxTokenHelper } from '../helpers/gmx.es-gmx.token-helper'; +@PositionTemplate() +export class AvalancheGmxEsGmxTokenFetcher extends GmxEsGmxTokenFetcher { + groupLabel = 'esGMX'; + isExcludedFromTvl = true; -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.esGmx.id; -const network = Network.AVALANCHE_MAINNET; - -@Register.TokenPositionFetcher({ appId, groupId, network, options: { excludeFromTvl: true } }) -export class AvalancheGmxEsGmxTokenFetcher implements PositionFetcher { - constructor(@Inject(GmxEsGmxTokenHelper) private readonly gmxEsGmxTokenHelper: GmxEsGmxTokenHelper) {} - - async getPositions() { - return this.gmxEsGmxTokenHelper.getTokens({ - esGmxTokenAddress: '0xff1489227bbaac61a9209a08929e4c2a526ddd17', - network, - }); - } + esGmxAddress = '0xff1489227bbaac61a9209a08929e4c2a526ddd17'; + gmxAddress = '0x62edc0692bd897d2295872a9ffcac5425011c661'; } diff --git a/src/apps/gmx/avalanche/gmx.farm.contract-position-fetcher.ts b/src/apps/gmx/avalanche/gmx.farm.contract-position-fetcher.ts index 72761e593..e5ed78e9c 100644 --- a/src/apps/gmx/avalanche/gmx.farm.contract-position-fetcher.ts +++ b/src/apps/gmx/avalanche/gmx.farm.contract-position-fetcher.ts @@ -1,62 +1,44 @@ -import { Inject } from '@nestjs/common'; - -import { SingleStakingFarmContractPositionHelper } from '~app-toolkit'; -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { ContractPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory, GmxRewardTracker } from '../contracts'; -import GMX_DEFINITION from '../gmx.definition'; - -export const GMX_FARM = { - address: '0x2bd10f8e93b3669b6d42e74eeedc65dd1b0a1342', - stakedTokenAddress: '0x62edc0692bd897d2295872a9ffcac5425011c661', - rewardTokenAddresses: ['0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', '0xff1489227bbaac61a9209a08929e4c2a526ddd17'], - rewardTrackerAddresses: ['0x4d268a7d4c16ceb5a606c173bd974984343fea13', '0x2bd10f8e93b3669b6d42e74eeedc65dd1b0a1342'], -}; - -export const ES_GMX_FARM = { - address: '0x2bd10f8e93b3669b6d42e74eeedc65dd1b0a1342', - stakedTokenAddress: '0xff1489227bbaac61a9209a08929e4c2a526ddd17', - rewardTokenAddresses: ['0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', '0xff1489227bbaac61a9209a08929e4c2a526ddd17'], - rewardTrackerAddresses: [], -}; - -export const GLP_FARM = { - address: '0xd2d1162512f927a7e282ef43a362659e4f2a728f', - stakedTokenAddress: '0x01234181085565ed162a948b6a5e88758cd7c7b8', - rewardTokenAddresses: ['0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', '0xff1489227bbaac61a9209a08929e4c2a526ddd17'], - rewardTrackerAddresses: ['0xd2d1162512f927a7e282ef43a362659e4f2a728f', '0x9e295b5b976a184b14ad8cd72413ad846c299660'], -}; - -export const FARMS = [GMX_FARM, ES_GMX_FARM, GLP_FARM]; - -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.farm.id; -const network = Network.AVALANCHE_MAINNET; - -@Register.ContractPositionFetcher({ appId, groupId, network }) -export class AvalancheGmxFarmContractPositionFetcher implements PositionFetcher { - constructor( - @Inject(SingleStakingFarmContractPositionHelper) - private readonly singleStakingFarmContractPositionHelper: SingleStakingFarmContractPositionHelper, - @Inject(GmxContractFactory) - private readonly gmxContractFactory: GmxContractFactory, - ) {} - - async getPositions() { - return this.singleStakingFarmContractPositionHelper.getContractPositions({ - appId, - groupId, - network, - dependencies: [ - { appId: GMX_DEFINITION.id, groupIds: [GMX_DEFINITION.groups.esGmx.id, GMX_DEFINITION.groups.glp.id], network }, +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; + +import { GmxFarmContractPositionFetcher } from '../common/gmx.farm.contract-position-fetcher'; + +@PositionTemplate() +export class AvalancheGmxFarmContractPositionFetcher extends GmxFarmContractPositionFetcher { + groupLabel = 'Farms'; + readerAddress = '0x956d63dd6540230487eb7e599ef8b0c6fdca4ab8'; + farms = [ + { + address: '0x2bd10f8e93b3669b6d42e74eeedc65dd1b0a1342', + stakedTokenAddress: '0x62edc0692bd897d2295872a9ffcac5425011c661', // GMX + rewardTokenAddresses: [ + '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', + '0xff1489227bbaac61a9209a08929e4c2a526ddd17', + ], + rewardTrackerAddresses: [ + '0x4d268a7d4c16ceb5a606c173bd974984343fea13', + '0x2bd10f8e93b3669b6d42e74eeedc65dd1b0a1342', + ], + }, + { + address: '0x2bd10f8e93b3669b6d42e74eeedc65dd1b0a1342', + stakedTokenAddress: '0xff1489227bbaac61a9209a08929e4c2a526ddd17', // esGMX + rewardTokenAddresses: [ + '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', + '0xff1489227bbaac61a9209a08929e4c2a526ddd17', + ], + rewardTrackerAddresses: [], + }, + { + address: '0xd2d1162512f927a7e282ef43a362659e4f2a728f', + stakedTokenAddress: '0x01234181085565ed162a948b6a5e88758cd7c7b8', // GLP + rewardTokenAddresses: [ + '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', + '0xff1489227bbaac61a9209a08929e4c2a526ddd17', + ], + rewardTrackerAddresses: [ + '0xd2d1162512f927a7e282ef43a362659e4f2a728f', + '0x9e295b5b976a184b14ad8cd72413ad846c299660', ], - resolveFarmDefinitions: async () => FARMS, - resolveFarmContract: ({ network, address }) => this.gmxContractFactory.gmxRewardTracker({ network, address }), - resolveIsActive: () => true, - resolveRois: () => ({ dailyROI: 0, weeklyROI: 0, yearlyROI: 0 }), - }); - } + }, + ]; } diff --git a/src/apps/gmx/avalanche/gmx.glp.token-fetcher.ts b/src/apps/gmx/avalanche/gmx.glp.token-fetcher.ts index 277913628..f567f59f9 100644 --- a/src/apps/gmx/avalanche/gmx.glp.token-fetcher.ts +++ b/src/apps/gmx/avalanche/gmx.glp.token-fetcher.ts @@ -1,27 +1,12 @@ -import { Inject } from '@nestjs/common'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { AppTokenPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; +import { GmxGlpTokenFetcher } from '../common/gmx.glp.token-fetcher'; -import GMX_DEFINITION from '../gmx.definition'; -import { GmxGlpTokenHelper } from '../helpers/gmx.glp.token-helper'; +@PositionTemplate() +export class AvalancheGmxGlpTokenFetcher extends GmxGlpTokenFetcher { + groupLabel = 'GLP'; -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.glp.id; -const network = Network.AVALANCHE_MAINNET; - -@Register.TokenPositionFetcher({ appId, groupId, network }) -export class AvalancheGmxGlpTokenFetcher implements PositionFetcher { - constructor(@Inject(GmxGlpTokenHelper) private readonly gmxGlpTokenHelper: GmxGlpTokenHelper) {} - - async getPositions() { - return this.gmxGlpTokenHelper.getTokens({ - glmManagerAddress: '0xe1ae4d4b06a5fe1fc288f6b4cd72f9f8323b107f', - glpTokenAddress: '0x01234181085565ed162a948b6a5e88758cd7c7b8', - network, - blockedTokenAddresses: ['0x130966628846bfd36ff31a822705796e8cb8c18d'], - }); - } + glmManagerAddress = '0xe1ae4d4b06a5fe1fc288f6b4cd72f9f8323b107f'; + glpTokenAddress = '0x01234181085565ed162a948b6a5e88758cd7c7b8'; + blockedTokenAddresses = ['0x130966628846bfd36ff31a822705796e8cb8c18d']; } diff --git a/src/apps/gmx/avalanche/gmx.option.contract-position-fetcher.ts b/src/apps/gmx/avalanche/gmx.option.contract-position-fetcher.ts deleted file mode 100644 index 450b96c34..000000000 --- a/src/apps/gmx/avalanche/gmx.option.contract-position-fetcher.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Inject } from '@nestjs/common'; - -import { Register } from '~app-toolkit/decorators'; -import { PositionFetcher } from '~position/position-fetcher.interface'; -import { ContractPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; - -import GMX_DEFINITION from '../gmx.definition'; -import { GmxOptionContractPositionHelper } from '../helpers/gmx.option.contract-position-helper'; - -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.option.id; -const network = Network.AVALANCHE_MAINNET; - -@Register.ContractPositionFetcher({ appId, groupId, network }) -export class AvalancheOptionsFarmContractPositionFetcher implements PositionFetcher { - constructor( - @Inject(GmxOptionContractPositionHelper) - private readonly gmxOptionContractPositionHelper: GmxOptionContractPositionHelper, - ) {} - - async getPositions() { - const vaultAddress = '0x9ab2de34a33fb459b538c43f251eb825645e8595'; - return this.gmxOptionContractPositionHelper.getPosition({ network, vaultAddress }); - } -} diff --git a/src/apps/gmx/avalanche/gmx.perp.contract-position-fetcher.ts b/src/apps/gmx/avalanche/gmx.perp.contract-position-fetcher.ts new file mode 100644 index 000000000..2adac937a --- /dev/null +++ b/src/apps/gmx/avalanche/gmx.perp.contract-position-fetcher.ts @@ -0,0 +1,10 @@ +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; + +import { GmxPerpContractPositionFetcher } from '../common/gmx.perp.contract-position-fetcher'; + +@PositionTemplate() +export class AvalancheGmxPerpContractPositionFetcher extends GmxPerpContractPositionFetcher { + groupLabel = 'Perpetuals'; + vaultAddress = '0x9ab2de34a33fb459b538c43f251eb825645e8595'; + usdcAddress = '0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e'; +} diff --git a/src/apps/gmx/common/gmx.es-gmx.token-fetcher.ts b/src/apps/gmx/common/gmx.es-gmx.token-fetcher.ts new file mode 100644 index 000000000..fd3f563d7 --- /dev/null +++ b/src/apps/gmx/common/gmx.es-gmx.token-fetcher.ts @@ -0,0 +1,35 @@ +import { Inject } from '@nestjs/common'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { Erc20 } from '~contract/contracts'; +import { AppTokenTemplatePositionFetcher } from '~position/template/app-token.template.position-fetcher'; + +import { GmxContractFactory } from '../contracts'; + +export abstract class GmxEsGmxTokenFetcher extends AppTokenTemplatePositionFetcher { + abstract esGmxAddress: string; + abstract gmxAddress: string; + + constructor( + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(GmxContractFactory) protected readonly contractFactory: GmxContractFactory, + ) { + super(appToolkit); + } + + getContract(address: string): Erc20 { + return this.contractFactory.erc20({ address, network: this.network }); + } + + async getAddresses() { + return [this.esGmxAddress]; + } + + async getUnderlyingTokenAddresses() { + return [this.gmxAddress]; + } + + async getPricePerShare() { + return [1]; + } +} diff --git a/src/apps/gmx/common/gmx.farm.contract-position-fetcher.ts b/src/apps/gmx/common/gmx.farm.contract-position-fetcher.ts new file mode 100644 index 000000000..f27270baa --- /dev/null +++ b/src/apps/gmx/common/gmx.farm.contract-position-fetcher.ts @@ -0,0 +1,62 @@ +import { Inject } from '@nestjs/common'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { isClaimable, isSupplied } from '~position/position.utils'; +import { GetDataPropsParams, GetTokenBalancesParams } from '~position/template/contract-position.template.types'; +import { SingleStakingFarmTemplateContractPositionFetcher } from '~position/template/single-staking.template.contract-position-fetcher'; + +import { GmxContractFactory, GmxRewardTracker } from '../contracts'; + +type GmxFarmType = { + address: string; + stakedTokenAddress: string; + rewardTokenAddresses: string[]; + rewardTrackerAddresses: string[]; +}; + +export abstract class GmxFarmContractPositionFetcher extends SingleStakingFarmTemplateContractPositionFetcher { + abstract farms: GmxFarmType[]; + abstract readerAddress: string; + + constructor( + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(GmxContractFactory) protected readonly contractFactory: GmxContractFactory, + ) { + super(appToolkit); + } + + getContract(address: string): GmxRewardTracker { + return this.contractFactory.gmxRewardTracker({ address, network: this.network }); + } + + async getFarmDefinitions() { + return this.farms; + } + + async getRewardRates({ contractPosition }: GetDataPropsParams) { + return contractPosition.tokens.filter(isClaimable).map(() => 0); + } + + async getStakedTokenBalance({ address, contractPosition, multicall }: GetTokenBalancesParams) { + const stakedToken = contractPosition.tokens.find(isSupplied)!; + const readerContract = this.contractFactory.gmxRewardReader({ address: this.readerAddress, network: this.network }); + + const depositBalances = await multicall + .wrap(readerContract) + .getDepositBalances(address, [stakedToken.address], [contractPosition.address]); + + return depositBalances[0]; + } + + async getRewardTokenBalances({ address, contractPosition, multicall }: GetTokenBalancesParams) { + const stakedToken = contractPosition.tokens.find(isSupplied)!; + + const farmDefinition = this.farms.find(v => v.stakedTokenAddress === stakedToken.address); + const rewardTrackers = farmDefinition?.rewardTrackerAddresses ?? []; + if (!rewardTrackers.length) return []; + + const readerContract = this.contractFactory.gmxRewardReader({ address: this.readerAddress, network: this.network }); + const stakingInfo = await multicall.wrap(readerContract).getStakingInfo(address, rewardTrackers); + return [stakingInfo[0].toString(), stakingInfo[5].toString()]; + } +} diff --git a/src/apps/gmx/common/gmx.glp.token-fetcher.ts b/src/apps/gmx/common/gmx.glp.token-fetcher.ts new file mode 100644 index 000000000..5d361f987 --- /dev/null +++ b/src/apps/gmx/common/gmx.glp.token-fetcher.ts @@ -0,0 +1,73 @@ +import { Inject } from '@nestjs/common'; +import { range } from 'lodash'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { getTokenImg } from '~app-toolkit/helpers/presentation/image.present'; +import { Erc20 } from '~contract/contracts'; +import { AppTokenTemplatePositionFetcher } from '~position/template/app-token.template.position-fetcher'; +import { + GetUnderlyingTokensParams, + GetPricePerShareParams, + GetDisplayPropsParams, +} from '~position/template/app-token.template.types'; + +import { GmxContractFactory } from '../contracts'; + +export abstract class GmxGlpTokenFetcher extends AppTokenTemplatePositionFetcher { + abstract glmManagerAddress: string; + abstract glpTokenAddress: string; + abstract blockedTokenAddresses: string[]; + + constructor( + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(GmxContractFactory) protected readonly contractFactory: GmxContractFactory, + ) { + super(appToolkit); + } + + getContract(address: string): Erc20 { + return this.contractFactory.erc20({ address, network: this.network }); + } + + async getAddresses() { + return [this.glpTokenAddress]; + } + + async getUnderlyingTokenAddresses({ multicall }: GetUnderlyingTokensParams) { + const glpManagerContract = this.contractFactory.gmxAumManager({ + address: this.glmManagerAddress, + network: this.network, + }); + + const vaultAddress = await multicall.wrap(glpManagerContract).vault(); + const vault = this.contractFactory.gmxVault({ address: vaultAddress, network: this.network }); + + const tokenCount = await multicall.wrap(vault).allWhitelistedTokensLength(); + const tokenRange = range(0, Number(tokenCount)); + const tokenAddresses = await Promise.all(tokenRange.map(i => multicall.wrap(vault).allWhitelistedTokens(i))); + + return tokenAddresses.filter(v => !this.blockedTokenAddresses.includes(v.toLowerCase())); + } + + async getPricePerShare({ appToken, multicall }: GetPricePerShareParams) { + const glpManagerContract = this.contractFactory.gmxAumManager({ + address: this.glmManagerAddress, + network: this.network, + }); + + const vaultAddress = await multicall.wrap(glpManagerContract).vault(); + const vault = this.contractFactory.gmxVault({ address: vaultAddress, network: this.network }); + const reserves = await Promise.all( + appToken.tokens.map(async token => { + const reserveRaw = await multicall.wrap(vault).getRedemptionCollateral(token.address); + return Number(reserveRaw) / 10 ** token.decimals; + }), + ); + + return reserves.map(v => v / appToken.supply); + } + + async getImages({ appToken }: GetDisplayPropsParams) { + return [getTokenImg(appToken.address, this.network)]; + } +} diff --git a/src/apps/gmx/common/gmx.perp.contract-position-fetcher.ts b/src/apps/gmx/common/gmx.perp.contract-position-fetcher.ts new file mode 100644 index 000000000..fdb1293e8 --- /dev/null +++ b/src/apps/gmx/common/gmx.perp.contract-position-fetcher.ts @@ -0,0 +1,128 @@ +import { Inject, Injectable } from '@nestjs/common'; +import _ from 'lodash'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { getImagesFromToken, getLabelFromToken } from '~app-toolkit/helpers/presentation/image.present'; +import { MetaType } from '~position/position.interface'; +import { ContractPositionTemplatePositionFetcher } from '~position/template/contract-position.template.position-fetcher'; +import { + GetDefinitionsParams, + GetTokenDefinitionsParams, + GetDisplayPropsParams, + GetDataPropsParams, + GetTokenBalancesParams, + DefaultContractPositionDefinition, +} from '~position/template/contract-position.template.types'; + +import { GmxContractFactory, GmxVault } from '../contracts'; + +export type GmxOptionContractPositionDefinition = { + address: string; + collateralTokenAddress: string; + indexTokenAddress: string; + isLong: boolean; +}; + +export type GmxOptionContractPositionDataProps = { + isLong: boolean; +}; + +@Injectable() +export abstract class GmxPerpContractPositionFetcher extends ContractPositionTemplatePositionFetcher< + GmxVault, + GmxOptionContractPositionDataProps, + GmxOptionContractPositionDefinition +> { + abstract vaultAddress: string; + abstract usdcAddress: string; + + constructor( + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(GmxContractFactory) protected readonly contractFactory: GmxContractFactory, + ) { + super(appToolkit); + } + + getContract(address: string): GmxVault { + return this.contractFactory.gmxVault({ address, network: this.network }); + } + + async getDefinitions({ multicall }: GetDefinitionsParams): Promise { + const vaultContract = this.contractFactory.gmxVault({ address: this.vaultAddress, network: this.network }); + const tokensCount = await multicall.wrap(vaultContract).allWhitelistedTokensLength(); + const tokensRange = _.range(0, Number(tokensCount)); + + const whitelistedTokens = await Promise.all( + tokensRange.map(async tokenIndex => multicall.wrap(vaultContract).allWhitelistedTokens(tokenIndex)), + ); + + return whitelistedTokens.flatMap(v => + whitelistedTokens.flatMap(t => [ + { address: this.vaultAddress, indexTokenAddress: v, collateralTokenAddress: t, isLong: true }, + { address: this.vaultAddress, indexTokenAddress: v, collateralTokenAddress: t, isLong: false }, + ]), + ); + } + + async getTokenDefinitions({ definition }: GetTokenDefinitionsParams) { + return [ + { metaType: MetaType.SUPPLIED, address: definition.collateralTokenAddress, network: this.network }, + { metaType: MetaType.SUPPLIED, address: definition.indexTokenAddress, network: this.network }, + { metaType: MetaType.SUPPLIED, address: this.usdcAddress, network: this.network }, + ]; + } + + async getDataProps({ + definition, + }: GetDataPropsParams) { + return { isLong: definition.isLong }; + } + + async getLabel({ + contractPosition, + }: GetDisplayPropsParams< + GmxVault, + GmxOptionContractPositionDataProps, + GmxOptionContractPositionDefinition + >): Promise { + const [collateralToken, indexToken] = contractPosition.tokens; + const marketLabel = [indexToken, collateralToken].map(v => getLabelFromToken(v)).join(' / '); + return `${contractPosition.dataProps.isLong ? 'Long' : 'Short'} ${marketLabel}`; + } + + async getImages({ + contractPosition, + }: GetDisplayPropsParams) { + const [collateralToken, indexToken] = contractPosition.tokens; + return [indexToken, collateralToken].flatMap(v => getImagesFromToken(v)); + } + + async getTokenBalancesPerPosition({ + address, + contractPosition, + contract, + }: GetTokenBalancesParams) { + const [collateralToken, indexToken, usdcToken] = contractPosition.tokens; + const isLong = contractPosition.dataProps.isLong; + + const position = await contract.getPosition(address, collateralToken.address, indexToken.address, isLong); + // non existing position returns size and collateral = 0 + if (Number(position[0]) == 0 && Number(position[1]) == 0) return [0, 0, 0]; + + // const leverage = await contract.getPositionLeverage(address, collateralToken.address, indexToken.address, isLong); + const delta = await contract.getPositionDelta(address, collateralToken.address, indexToken.address, isLong); + + const initialCollateralRaw = position[1]; + const initialCollateral = Number(initialCollateralRaw) / 10 ** 30; + const deltaBalanceRaw = delta[1]; + const deltaBalance = Number(deltaBalanceRaw) / 10 ** 30; + + const hasProfit = delta[0]; + const balanceUSD = hasProfit == true ? initialCollateral + deltaBalance : initialCollateral - deltaBalance; + + const profitToken = isLong ? indexToken : usdcToken; + const balanceInProfitToken = balanceUSD / profitToken.price; + const balanceInProfitTokenRaw = balanceInProfitToken * 10 ** profitToken.decimals; + return isLong ? [0, balanceInProfitTokenRaw, 0] : [0, 0, balanceInProfitTokenRaw]; + } +} diff --git a/src/apps/gmx/gmx.definition.ts b/src/apps/gmx/gmx.definition.ts index 23a56462e..c906f13d2 100644 --- a/src/apps/gmx/gmx.definition.ts +++ b/src/apps/gmx/gmx.definition.ts @@ -29,10 +29,10 @@ export const GMX_DEFINITION = appDefinition({ label: 'Farms', }, - option: { - id: 'option', + perp: { + id: 'perp', type: GroupType.POSITION, - label: 'Options', + label: 'Perpetuals', }, }, tags: [AppTag.MARGIN_TRADING], @@ -63,5 +63,3 @@ export class GmxAppDefinition extends AppDefinition { super(GMX_DEFINITION); } } - -export default GMX_DEFINITION; diff --git a/src/apps/gmx/gmx.module.ts b/src/apps/gmx/gmx.module.ts index bd86a5160..f8ebb1f47 100644 --- a/src/apps/gmx/gmx.module.ts +++ b/src/apps/gmx/gmx.module.ts @@ -1,44 +1,32 @@ import { Register } from '~app-toolkit/decorators'; import { AbstractApp } from '~app/app.dynamic-module'; -import { ArbitrumGmxBalanceFetcher } from './arbitrum/gmx.balance-fetcher'; import { ArbitrumGmxEsGmxTokenFetcher } from './arbitrum/gmx.es-gmx.token-fetcher'; import { ArbitrumGmxFarmContractPositionFetcher } from './arbitrum/gmx.farm.contract-position-fetcher'; import { ArbitrumGmxGlpTokenFetcher } from './arbitrum/gmx.glp.token-fetcher'; -import { ArbitrumOptionsFarmContractPositionFetcher } from './arbitrum/gmx.option.contract-position-fetcher'; -import { AvalancheGmxBalanceFetcher } from './avalanche/gmx.balance-fetcher'; +import { ArbitrumGmxPerpContractPositionFetcher } from './arbitrum/gmx.perp.contract-position-fetcher'; import { AvalancheGmxEsGmxTokenFetcher } from './avalanche/gmx.es-gmx.token-fetcher'; import { AvalancheGmxFarmContractPositionFetcher } from './avalanche/gmx.farm.contract-position-fetcher'; import { AvalancheGmxGlpTokenFetcher } from './avalanche/gmx.glp.token-fetcher'; -import { AvalancheOptionsFarmContractPositionFetcher } from './avalanche/gmx.option.contract-position-fetcher'; +import { AvalancheGmxPerpContractPositionFetcher } from './avalanche/gmx.perp.contract-position-fetcher'; import { GmxContractFactory } from './contracts'; -import GMX_DEFINITION, { GmxAppDefinition } from './gmx.definition'; -import { GmxEsGmxTokenHelper } from './helpers/gmx.es-gmx.token-helper'; -import { GmxGlpTokenHelper } from './helpers/gmx.glp.token-helper'; -import { GmxOptionBalanceHelper } from './helpers/gmx.option.balance-helper'; -import { GmxOptionContractPositionHelper } from './helpers/gmx.option.contract-position-helper'; +import { GmxAppDefinition, GMX_DEFINITION } from './gmx.definition'; @Register.AppModule({ appId: GMX_DEFINITION.id, providers: [ GmxAppDefinition, GmxContractFactory, - GmxEsGmxTokenHelper, - GmxGlpTokenHelper, - GmxOptionContractPositionHelper, - GmxOptionBalanceHelper, // Arbitrum - ArbitrumGmxBalanceFetcher, ArbitrumGmxEsGmxTokenFetcher, ArbitrumGmxFarmContractPositionFetcher, ArbitrumGmxGlpTokenFetcher, - ArbitrumOptionsFarmContractPositionFetcher, + ArbitrumGmxPerpContractPositionFetcher, // Avalanche - AvalancheGmxBalanceFetcher, AvalancheGmxEsGmxTokenFetcher, AvalancheGmxFarmContractPositionFetcher, AvalancheGmxGlpTokenFetcher, - AvalancheOptionsFarmContractPositionFetcher, + AvalancheGmxPerpContractPositionFetcher, ], }) export class GmxAppModule extends AbstractApp() {} diff --git a/src/apps/gmx/helpers/gmx.es-gmx.token-helper.ts b/src/apps/gmx/helpers/gmx.es-gmx.token-helper.ts deleted file mode 100644 index c60b4bcfb..000000000 --- a/src/apps/gmx/helpers/gmx.es-gmx.token-helper.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; - -import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; -import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present'; -import { getTokenImg } from '~app-toolkit/helpers/presentation/image.present'; -import { ContractType } from '~position/contract.interface'; -import { AppTokenPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory } from '../contracts'; -import { GMX_DEFINITION } from '../gmx.definition'; - -type GetGmxEsGmxTokenParams = { - network: Network; - esGmxTokenAddress: string; -}; - -@Injectable() -export class GmxEsGmxTokenHelper { - constructor( - @Inject(GmxContractFactory) private readonly contractFactory: GmxContractFactory, - @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, - ) {} - - async getTokens({ network, esGmxTokenAddress }: GetGmxEsGmxTokenParams) { - const multicall = this.appToolkit.getMulticall(network); - const baseTokens = await this.appToolkit.getBaseTokenPrices(network); - const underlyingToken = baseTokens.find(p => p.symbol === 'GMX')!; - - const esGmxContract = this.contractFactory.erc20({ address: esGmxTokenAddress, network }); - const [symbol, decimals, supplyRaw] = await Promise.all([ - multicall.wrap(esGmxContract).symbol(), - multicall.wrap(esGmxContract).decimals(), - multicall.wrap(esGmxContract).totalSupply(), - ]); - - // Data Props - const pricePerShare = 1; // Minted 1:1 - const supply = Number(supplyRaw) / 10 ** 18; - const price = pricePerShare * underlyingToken.price; - const tokens = [underlyingToken]; - const liquidity = price * supply; - - // Display Props - const label = symbol; - const secondaryLabel = buildDollarDisplayItem(price); - const images = [getTokenImg(underlyingToken.address, network)]; - - const vaultToken: AppTokenPosition = { - type: ContractType.APP_TOKEN, - address: esGmxTokenAddress, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.esGmx.id, - network, - symbol, - decimals, - supply, - price, - pricePerShare, - tokens, - - dataProps: { - liquidity, - }, - - displayProps: { - label, - secondaryLabel, - images, - }, - }; - - return [vaultToken]; - } -} diff --git a/src/apps/gmx/helpers/gmx.glp.token-helper.ts b/src/apps/gmx/helpers/gmx.glp.token-helper.ts deleted file mode 100644 index 9e3b82e17..000000000 --- a/src/apps/gmx/helpers/gmx.glp.token-helper.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import _ from 'lodash'; -import { range } from 'lodash'; - -import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; -import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present'; -import { getTokenImg } from '~app-toolkit/helpers/presentation/image.present'; -import { ContractType } from '~position/contract.interface'; -import { AppTokenPosition } from '~position/position.interface'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory } from '../contracts'; -import { GMX_DEFINITION } from '../gmx.definition'; - -type GetGmxGlpTokenParams = { - network: Network; - glmManagerAddress: string; - glpTokenAddress: string; - blockedTokenAddresses: string[]; -}; - -@Injectable() -export class GmxGlpTokenHelper { - constructor( - @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, - @Inject(GmxContractFactory) private readonly contractFactory: GmxContractFactory, - ) {} - - async getTokens({ network, glmManagerAddress, glpTokenAddress, blockedTokenAddresses }: GetGmxGlpTokenParams) { - const multicall = this.appToolkit.getMulticall(network); - const baseTokens = await this.appToolkit.getBaseTokenPrices(network); - - const glpManagerContract = this.contractFactory.gmxAumManager({ address: glmManagerAddress, network }); - const glpTokenContract = this.contractFactory.erc20({ address: glpTokenAddress, network }); - const [symbol, decimals, supplyRaw, vaultAddressRaw] = await Promise.all([ - multicall.wrap(glpTokenContract).symbol(), - multicall.wrap(glpTokenContract).decimals(), - multicall.wrap(glpTokenContract).totalSupply(), - multicall.wrap(glpManagerContract).vault(), - ]); - - // Tokens - const vaultAddress = vaultAddressRaw.toLowerCase(); - const vaultContract = this.contractFactory.gmxVault({ address: vaultAddress, network }); - const mcVault = multicall.wrap(vaultContract); - const numTokens = await mcVault.allWhitelistedTokensLength(); - const tokenAddressesRaw = await Promise.all(range(0, Number(numTokens)).map(i => mcVault.allWhitelistedTokens(i))); - const tokensRaw = tokenAddressesRaw.map(t1 => baseTokens.find(t2 => t2.address === t1.toLowerCase())); - const tokensUnfiltered = _.compact(tokensRaw); - const tokens = tokensUnfiltered.filter(x => !blockedTokenAddresses.includes(x.address)); - - // Reserves - const reserves = await Promise.all( - tokens.map(async token => { - const reserveRaw = await multicall.wrap(vaultContract).getRedemptionCollateral(token.address); - return Number(reserveRaw) / 10 ** token.decimals; - }), - ); - - // Liquidity - const liquidity = tokens.reduce((acc, t, i) => acc + reserves[i] * t.price, 0); - const supply = Number(supplyRaw) / 10 ** decimals; - const price = liquidity / supply; - const pricePerShare = reserves.map(r => r / supply); - - // Display Props - const label = symbol; - const secondaryLabel = buildDollarDisplayItem(price); - const images = [getTokenImg(glpTokenAddress, network)]; - const statsItems = [{ label: 'Liquidity', value: buildDollarDisplayItem(liquidity) }]; - - const glpToken: AppTokenPosition = { - type: ContractType.APP_TOKEN, - address: glpTokenAddress, - appId: GMX_DEFINITION.id, - groupId: GMX_DEFINITION.groups.glp.id, - network, - symbol, - decimals, - supply, - price, - pricePerShare, - tokens, - - dataProps: { - liquidity, - }, - - displayProps: { - label, - secondaryLabel, - images, - statsItems, - }, - }; - - return [glpToken]; - } -} diff --git a/src/apps/gmx/helpers/gmx.option.balance-helper.ts b/src/apps/gmx/helpers/gmx.option.balance-helper.ts deleted file mode 100644 index 790f6efb8..000000000 --- a/src/apps/gmx/helpers/gmx.option.balance-helper.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import _ from 'lodash'; - -import { drillBalance } from '~app-toolkit'; -import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; -import { ContractPositionBalance } from '~position/position-balance.interface'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory } from '../contracts'; -import GMX_DEFINITION from '../gmx.definition'; - -export type GmxOptionContractPositionDataProps = { - collateralTokenAddress: string; - indexTokenAddress: string; - isLong: boolean; -}; - -type GetOptionContractPositionHelperParams = { - network: Network; - vaultAddress: string; - address: string; -}; - -@Injectable() -export class GmxOptionBalanceHelper { - constructor( - @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, - @Inject(GmxContractFactory) private readonly contractFactory: GmxContractFactory, - ) {} - - async getBalance({ address, network, vaultAddress }: GetOptionContractPositionHelperParams) { - const multicall = this.appToolkit.getMulticall(network); - const baseTokens = await this.appToolkit.getBaseTokenPrices(network); - - const vaultContract = this.contractFactory.gmxVault({ address: vaultAddress, network }); - - const contractPositions = await this.appToolkit.getAppContractPositions({ - appId: GMX_DEFINITION.id, - groupIds: [GMX_DEFINITION.groups.option.id], - network, - }); - - const optionBalances = await Promise.all( - contractPositions.map(async contractPosition => { - const collateralTokenAddress = contractPosition.dataProps.collateralTokenAddress; - const indexTokenAddress = contractPosition.dataProps.indexTokenAddress; - const isLong = contractPosition.dataProps.isLong; - - const position = await multicall - .wrap(vaultContract) - .getPosition(address, collateralTokenAddress, indexTokenAddress, isLong); - // non existing position returns size and collateral = 0 - if (Number(position[0]) == 0 && Number(position[1]) == 0) return null; - - const positionDelta = await multicall - .wrap(vaultContract) - .getPositionDelta(address, collateralTokenAddress, indexTokenAddress, isLong); - - // Long profit in index token and short profit in usdc - const profitToken = - isLong == true - ? baseTokens.find(x => x.address == indexTokenAddress) - : baseTokens.find(x => x.symbol == 'USDC'); - if (!profitToken) return null; - - const initialCollateralRaw = position[1]; - const initialCollateral = Number(initialCollateralRaw) / 10 ** 30; - const deltaRaw = positionDelta[1]; - const delta = Number(deltaRaw) / 10 ** 30; - const hasProfit = positionDelta[0]; - const balanceUSD = hasProfit == true ? initialCollateral + delta : initialCollateral - delta; - const balanceInProfitToken = balanceUSD / profitToken.price; - const balanceInProfitTokenRaw = balanceInProfitToken * 10 ** profitToken.decimals; - - const tokenBalance = [drillBalance(profitToken, balanceInProfitTokenRaw.toString())]; - - const contractPositionBalance: ContractPositionBalance = { - ...contractPosition, - tokens: tokenBalance, - balanceUSD: balanceUSD, - }; - return contractPositionBalance; - }), - ); - - return _.compact(optionBalances); - } -} diff --git a/src/apps/gmx/helpers/gmx.option.contract-position-helper.ts b/src/apps/gmx/helpers/gmx.option.contract-position-helper.ts deleted file mode 100644 index c5e538e60..000000000 --- a/src/apps/gmx/helpers/gmx.option.contract-position-helper.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import _ from 'lodash'; - -import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; -import { getTokenImg } from '~app-toolkit/helpers/presentation/image.present'; -import { ContractType } from '~position/contract.interface'; -import { ContractPosition } from '~position/position.interface'; -import { supplied } from '~position/position.utils'; -import { Network } from '~types/network.interface'; - -import { GmxContractFactory } from '../contracts'; -import GMX_DEFINITION from '../gmx.definition'; - -export type GmxOptionContractPositionDataProps = { - collateralTokenAddress: string; - indexTokenAddress: string; - isLong: boolean; -}; - -type GetOptionContractPositionHelperParams = { - network: Network; - vaultAddress: string; -}; - -const appId = GMX_DEFINITION.id; -const groupId = GMX_DEFINITION.groups.option.id; - -@Injectable() -export class GmxOptionContractPositionHelper { - constructor( - @Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, - @Inject(GmxContractFactory) private readonly contractFactory: GmxContractFactory, - ) {} - - async getPosition({ network, vaultAddress }: GetOptionContractPositionHelperParams) { - const multicall = this.appToolkit.getMulticall(network); - const baseTokens = await this.appToolkit.getBaseTokenPrices(network); - - const vaultContract = this.contractFactory.gmxVault({ address: vaultAddress, network }); - const whitelistedTokenLengthRaw = await multicall.wrap(vaultContract).allWhitelistedTokensLength(); - - const whitelistedTokens = await Promise.all( - _.range(0, Number(whitelistedTokenLengthRaw)).map(async tokenIndex => { - return (await multicall.wrap(vaultContract).allWhitelistedTokens(tokenIndex)).toLowerCase(); - }), - ); - - const positions = await Promise.all( - whitelistedTokens.map(async collateralTokenAddress => { - const positionsForGivenPair = await Promise.all( - whitelistedTokens.map(indexTokenAddress => { - const collateralToken = baseTokens.find(x => x.address === collateralTokenAddress); - const indexToken = baseTokens.find(x => x.address === indexTokenAddress); - if (!collateralToken || !indexToken) return null; - - const shortPosition: ContractPosition = { - type: ContractType.POSITION, - appId, - groupId, - address: vaultAddress, - key: `${collateralToken.symbol}:${indexToken.symbol}:short`, - network, - tokens: [supplied(collateralToken), indexToken], - dataProps: { - collateralTokenAddress: collateralToken.address, - indexTokenAddress: indexToken.address, - isLong: false, - }, - displayProps: { - label: `Short ${indexToken.symbol}`, - images: [getTokenImg(collateralToken.address, network), getTokenImg(indexToken.address, network)], - statsItems: [], - }, - }; - const longPosition: ContractPosition = { - type: ContractType.POSITION, - appId, - groupId, - address: vaultAddress, - key: `${collateralToken.symbol}:${indexToken.symbol}:long`, - network, - tokens: [supplied(collateralToken), indexToken], - dataProps: { - collateralTokenAddress: collateralToken.address, - indexTokenAddress: indexToken.address, - isLong: true, - }, - displayProps: { - label: `Long ${indexToken.symbol}`, - images: [getTokenImg(collateralToken.address, network), getTokenImg(indexToken.address, network)], - statsItems: [], - }, - }; - - return [shortPosition, longPosition]; - }), - ); - - return _.compact(positionsForGivenPair).flat(); - }), - ); - - return positions.flat(); - } -}