From e3657601178ce311f30d949b49bdb7848bb07adb Mon Sep 17 00:00:00 2001 From: Clonescody Date: Tue, 21 Jun 2022 09:38:33 -0700 Subject: [PATCH 1/4] Fixed name, added APY/APR/TVL from API calls --- .../arbitrum/umami.compound.token-fetcher.ts | 32 +++++++++++++++++-- .../arbitrum/umami.marinate.token-fetcher.ts | 25 +++++++++++++++ src/apps/umami/umami.definition.ts | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts b/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts index 5a3009463..57e065b5d 100644 --- a/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts +++ b/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts @@ -1,4 +1,5 @@ import { Inject } from '@nestjs/common'; +import axios from 'axios'; import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; import { Register } from '~app-toolkit/decorators'; @@ -16,6 +17,19 @@ const appId = UMAMI_DEFINITION.id; const groupId = UMAMI_DEFINITION.groups.compound.id; const network = Network.ARBITRUM_MAINNET; +type UmamiMarinateApiObject = { + apy: string; +}; + +type UmamiCompounderApiObject = { + tvl: number; +}; + +export type UmamiApiDatas = { + marinate: UmamiMarinateApiObject; + mUmamiCompounder: UmamiCompounderApiObject; +}; + @Register.TokenPositionFetcher({ appId, groupId, network }) export class ArbitrumUmamiCompoundTokenFetcher implements PositionFetcher { constructor( @@ -52,6 +66,12 @@ export class ArbitrumUmamiCompoundTokenFetcher implements PositionFetcher('https://horseysauce.xyz/').then(v => v.data); + + const { marinate, mUmamiCompounder } = data; + const { apy } = marinate; + const { tvl } = mUmamiCompounder; + const supply = Number(supplyRaw) / 10 ** decimals; const reserve = Number(balanceRaw) / 10 ** decimals; const pricePerShare = reserve / supply; @@ -61,7 +81,14 @@ export class ArbitrumUmamiCompoundTokenFetcher implements PositionFetcher { constructor( @@ -43,12 +53,25 @@ export class ArbitrumUmamiMarinateTokenFetcher implements PositionFetcher v.address === UMAMI_ADDRESS); if (!underlyingToken) return []; + + const data = await axios.get('https://horseysauce.xyz/').then(v => v.data); + + const { marinate } = data; + const { apr, marinateTVL } = marinate; + const tokens = [underlyingToken]; const pricePerShare = 1.0; const price = pricePerShare * underlyingToken.price; const label = `Marinating UMAMI`; const images = getImagesFromToken(underlyingToken); const secondaryLabel = buildDollarDisplayItem(price); + const tertiaryLabel = `${apr}% APR`; + const statsItems = [ + { + label: 'TVL', + value: `${marinateTVL}`, + }, + ]; const token: AppTokenPosition = { type: ContractType.APP_TOKEN, @@ -67,6 +90,8 @@ export class ArbitrumUmamiMarinateTokenFetcher implements PositionFetcher Date: Tue, 21 Jun 2022 11:41:18 -0700 Subject: [PATCH 2/4] fix: pricePerShare and API CacheOnInterval --- .../arbitrum/umami.compound.token-fetcher.ts | 32 ++++++++++++------- .../arbitrum/umami.marinate.token-fetcher.ts | 22 ++++++++++--- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts b/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts index 57e065b5d..618197d41 100644 --- a/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts +++ b/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts @@ -5,6 +5,7 @@ import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; import { Register } from '~app-toolkit/decorators'; import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present'; import { getImagesFromToken } from '~app-toolkit/helpers/presentation/image.present'; +import { CacheOnInterval } from '~cache/cache-on-interval.decorator'; import { ContractType } from '~position/contract.interface'; import { PositionFetcher } from '~position/position-fetcher.interface'; import { AppTokenPosition } from '~position/position.interface'; @@ -37,6 +38,22 @@ export class ArbitrumUmamiCompoundTokenFetcher implements PositionFetcher('https://horseysauce.xyz/').then(v => v.data); + + const { marinate, mUmamiCompounder } = data; + const { apy } = marinate; + const { tvl } = mUmamiCompounder; + return { + apy, + tvl, + }; + } + async getPositions() { const mUMAMI_ADDRESS = '0x2AdAbD6E8Ce3e82f52d9998a7f64a90d294A92A4'.toLowerCase(); const cmUMAMI_ADDRESS = '0x1922C36F3bc762Ca300b4a46bB2102F84B1684aB'.toLowerCase(); @@ -66,19 +83,14 @@ export class ArbitrumUmamiCompoundTokenFetcher implements PositionFetcher('https://horseysauce.xyz/').then(v => v.data); - - const { marinate, mUmamiCompounder } = data; - const { apy } = marinate; - const { tvl } = mUmamiCompounder; + const { apy, tvl } = await this.getUmamiInformations(); const supply = Number(supplyRaw) / 10 ** decimals; const reserve = Number(balanceRaw) / 10 ** decimals; const pricePerShare = reserve / supply; const price = pricePerShare * underlyingToken.price; const tokens = [underlyingToken]; - - const label = `Compounding Marinated UMAMI`; + const label = `Compounding Marinating UMAMI`; const images = getImagesFromToken(underlyingToken); const secondaryLabel = buildDollarDisplayItem(price); @@ -99,12 +111,10 @@ export class ArbitrumUmamiCompoundTokenFetcher implements PositionFetcher('https://horseysauce.xyz/').then(v => v.data); + + const { marinate } = data; + const { apr, marinateTVL } = marinate; + + return { + apr, + marinateTVL, + }; + } + async getPositions() { const UMAMI_ADDRESS = '0x1622bF67e6e5747b81866fE0b85178a93C7F86e3'.toLowerCase(); const mUMAMI_ADDRESS = '0x2AdAbD6E8Ce3e82f52d9998a7f64a90d294A92A4'.toLowerCase(); @@ -54,10 +71,7 @@ export class ArbitrumUmamiMarinateTokenFetcher implements PositionFetcher v.address === UMAMI_ADDRESS); if (!underlyingToken) return []; - const data = await axios.get('https://horseysauce.xyz/').then(v => v.data); - - const { marinate } = data; - const { apr, marinateTVL } = marinate; + const { apr, marinateTVL } = await this.getUmamiInformations(); const tokens = [underlyingToken]; const pricePerShare = 1.0; From ee7176ea6f0e7a6c4b09f0a52df2556f972cb43f Mon Sep 17 00:00:00 2001 From: Clonescody Date: Tue, 21 Jun 2022 15:02:52 -0700 Subject: [PATCH 3/4] fix: cache key, formats, final object mapping, app name --- .../arbitrum/umami.compound.token-fetcher.ts | 19 ++++++++++++------- .../arbitrum/umami.marinate.token-fetcher.ts | 18 ++++++++++++------ src/apps/umami/umami.definition.ts | 2 +- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts b/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts index 618197d41..152ed3e63 100644 --- a/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts +++ b/src/apps/umami/arbitrum/umami.compound.token-fetcher.ts @@ -3,7 +3,10 @@ import axios from 'axios'; import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; import { Register } from '~app-toolkit/decorators'; -import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present'; +import { + buildDollarDisplayItem, + buildPercentageDisplayItem, +} from '~app-toolkit/helpers/presentation/display-item.present'; import { getImagesFromToken } from '~app-toolkit/helpers/presentation/image.present'; import { CacheOnInterval } from '~cache/cache-on-interval.decorator'; import { ContractType } from '~position/contract.interface'; @@ -39,7 +42,7 @@ export class ArbitrumUmamiCompoundTokenFetcher implements PositionFetcher Date: Tue, 21 Jun 2022 15:03:09 -0700 Subject: [PATCH 4/4] feature: added logo --- src/apps/umami/assets/logo.png | Bin 0 -> 15994 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/apps/umami/assets/logo.png diff --git a/src/apps/umami/assets/logo.png b/src/apps/umami/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..91e4bc0516e103a5d84c510e321cc4324637869d GIT binary patch literal 15994 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