Skip to content
This repository has been archived by the owner on Jan 24, 2024. It is now read-only.

feat(sturdy): Adds Ethereum version of Sturdy Finance #574

Merged
merged 25 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7876a04
pika-protocol: create app
theonepichael Jun 3, 2022
b88dd8d
wip pika-protocol implementation
theonepichael Jun 3, 2022
4f27706
wip-balance-fetcher
theonepichael Jun 3, 2022
724abe7
updates getFarmBalances
theonepichael Jun 3, 2022
09cf7dd
removes unused variable
theonepichael Jun 3, 2022
74e9418
reverts to untouched state
theonepichael Jun 3, 2022
429ba9a
implements contract-position-fetcher
theonepichael Jun 3, 2022
49ecc0a
implements balance-fetcher
theonepichael Jun 3, 2022
bb2d4b3
Merge branch 'main' into main
theonepichael Jun 3, 2022
5518d88
implements TVL fetcher
theonepichael Jun 3, 2022
b812f9f
Merge branch 'main' of https://github.com/theonepichael/studio
theonepichael Jun 3, 2022
0523de4
fix(getFarmBalances): now deformalizes staked balances to human-readable
theonepichael Jun 4, 2022
51b22ac
resolves merge conflicts
theonepichael Jun 4, 2022
14b7b2c
refactor(sturdy): adds filter to getBalances to ensure only ftm vault…
theonepichael Jun 4, 2022
01ae6bb
fixes linting errors
theonepichael Jun 4, 2022
2694ada
removes unnecessary await
theonepichael Jun 4, 2022
9571359
fixes import/order
theonepichael Jun 4, 2022
a6006e4
Merge branch 'Zapper-fi:main' into main
theonepichael Jun 5, 2022
bf41c0f
feat(sturdy): Adds Ethereum version of Sturdy Finance
theonepichael Jun 5, 2022
f5bc607
Merge branch 'Zapper-fi:main' into main
theonepichael Jun 7, 2022
6b0c10f
defines helpers
theonepichael Jun 7, 2022
97f8254
implements caching to reduce the # of API calls to the /getVaultMonit…
theonepichael Jun 7, 2022
1ddc4a8
fixes linting errors
theonepichael Jun 7, 2022
09e75d1
fixes linting import/order errors
theonepichael Jun 7, 2022
213cdf9
fixes linting import/order errors
theonepichael Jun 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/apps/sturdy/ethereum/sturdy.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Inject } from '@nestjs/common';

import { 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 { Network } from '~types/network.interface';

import { STURDY_DEFINITION } from '../sturdy.definition';

const network = Network.ETHEREUM_MAINNET;
const appId = STURDY_DEFINITION.id;

@Register.BalanceFetcher(STURDY_DEFINITION.id, network)
export class EthereumSturdyBalanceFetcher implements BalanceFetcher {
constructor(@Inject(TokenBalanceHelper) private readonly tokenBalanceHelper: TokenBalanceHelper) {}

private async getLendingTokenBalances(address: string) {
return this.tokenBalanceHelper.getTokenBalances({
address,
appId,
groupId: STURDY_DEFINITION.groups.lending.id,
network,
});
}

async getBalances(address: string) {
const [lendingTokenBalances] = await Promise.all([this.getLendingTokenBalances(address)]);

return presentBalanceFetcherResponse([
{
label: 'Lending',
assets: lendingTokenBalances,
},
]);
}
}
99 changes: 99 additions & 0 deletions src/apps/sturdy/ethereum/sturdy.lending.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Inject } from '@nestjs/common';
import axios from 'axios';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { ZERO_ADDRESS } from '~app-toolkit/constants/address';
import { Register } from '~app-toolkit/decorators';
import {
buildNumberDisplayItem,
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';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { BaseToken } from '~position/token.interface';
import { Network } from '~types/network.interface';

import { SturdyContractFactory } from '../contracts';
import { VaultMonitoringResponse, cacheOnIntervalKeyCreationHelper, TIMEOUT_DURATION } from '../helpers/constants';
import { STURDY_DEFINITION } from '../sturdy.definition';

const appId = STURDY_DEFINITION.id;
const groupId = STURDY_DEFINITION.groups.lending.id;
const network = Network.ETHEREUM_MAINNET;

@Register.TokenPositionFetcher({ appId, groupId, network })
export class EthereumSturdyLendingTokenFetcher implements PositionFetcher<AppTokenPosition> {
constructor(
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
@Inject(SturdyContractFactory) private readonly sturdyContractFactory: SturdyContractFactory,
) {}

@CacheOnInterval({
key: cacheOnIntervalKeyCreationHelper(appId, groupId, network),
timeout: TIMEOUT_DURATION,
})
private async getVaultMonitoringData() {
const endpoint = 'https://us-central1-stu-dashboard-a0ba2.cloudfunctions.net/getVaultMonitoring?chain=ethereum';
const data = await axios.get<VaultMonitoringResponse>(endpoint).then(res => res.data);
return data;
}

async getPositions() {
const multicall = this.appToolkit.getMulticall(network);
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const ethToken = baseTokens.find(t => t.address === ZERO_ADDRESS);
if (!ethToken) return [];

const tokenData = await this.getVaultMonitoringData();

const tokens = tokenData.map(async data => {
const symbol = data.tokens;
const underlyingTokens: BaseToken[] = [];

const contract = this.sturdyContractFactory.sturdyToken({ address: data.address, network });
const underlyingTokenAddress = await multicall
.wrap(contract)
.UNDERLYING_ASSET_ADDRESS()
.then(v => v.toLowerCase());
const underlyingToken = baseTokens.find(t => t.address === underlyingTokenAddress);
if (underlyingToken) underlyingTokens.push(underlyingToken);

const token: AppTokenPosition = {
type: ContractType.APP_TOKEN,
appId,
groupId,
address: data.address,
network,
symbol,
decimals: data.decimals,
supply: data.supply,
pricePerShare: 1,
price: underlyingTokens[0].price,
tokens: underlyingTokens,
dataProps: {
apy: data.base,
tvl: data.tvl,
},
displayProps: {
label: symbol,
images: getImagesFromToken(underlyingTokens[0]),
statsItems: [
{
label: 'APY',
value: buildPercentageDisplayItem(data.base),
},
{
label: 'Liquidity',
value: buildNumberDisplayItem(data.tvl),
},
],
},
};
return token;
});
return Promise.all(tokens);
}
}
26 changes: 26 additions & 0 deletions src/apps/sturdy/ethereum/sturdy.tvl-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Inject } from '@nestjs/common';
import { sumBy } from 'lodash';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { TvlFetcher } from '~stats/tvl/tvl-fetcher.interface';
import { Network } from '~types/network.interface';

import { STURDY_DEFINITION } from '../sturdy.definition';

const appId = STURDY_DEFINITION.id;
const network = Network.ETHEREUM_MAINNET;

@Register.TvlFetcher({ appId, network })
export class EthereumSturdyTvlFetcher implements TvlFetcher {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}

async getTvl() {
const lendingTokens = await this.appToolkit.getAppTokenPositions({
appId,
groupIds: [STURDY_DEFINITION.groups.lending.id],
network,
});
return sumBy(lendingTokens, v => v.supply * v.price);
}
}
37 changes: 14 additions & 23 deletions src/apps/sturdy/fantom/sturdy.lending.token-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,45 @@ import {
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';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { BaseToken } from '~position/token.interface';
import { Network } from '~types/network.interface';

import { SturdyContractFactory } from '../contracts';
import { VaultMonitoringResponse, cacheOnIntervalKeyCreationHelper, TIMEOUT_DURATION } from '../helpers/constants';
import { STURDY_DEFINITION } from '../sturdy.definition';

const appId = STURDY_DEFINITION.id;
const groupId = STURDY_DEFINITION.groups.lending.id;
const network = Network.FANTOM_OPERA_MAINNET;

type VaultMonitoringResponse = {
chain: string;
tokens: string;
decimals: number;
address: string;
supply: number;
price: number;
base: number;
reward: number;
rewards: {
CRV: number;
CVX: number;
};
url: number;
tvl: number;
active: boolean;
}[];

@Register.TokenPositionFetcher({ appId, groupId, network })
export class FantomSturdyLendingTokenFetcher implements PositionFetcher<AppTokenPosition> {
constructor(
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
@Inject(SturdyContractFactory) private readonly sturdyContractFactory: SturdyContractFactory,
) {}

@CacheOnInterval({
key: cacheOnIntervalKeyCreationHelper(appId, groupId, network),
timeout: TIMEOUT_DURATION,
})
private async getVaultMonitoringData() {
const endpoint = 'https://us-central1-stu-dashboard-a0ba2.cloudfunctions.net/getVaultMonitoring';
const data = await axios.get<VaultMonitoringResponse>(endpoint).then(res => res.data);
return data;
}

async getPositions() {
const multicall = this.appToolkit.getMulticall(network);
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const ethToken = baseTokens.find(t => t.address === ZERO_ADDRESS);
if (!ethToken) return [];

const endpoint = 'https://us-central1-stu-dashboard-a0ba2.cloudfunctions.net/getVaultMonitoring';
const tokenData = (await axios.get<VaultMonitoringResponse>(endpoint).then(v => v.data)).filter(
data => data.chain === 'ftm',
);
const tokenData = await this.getVaultMonitoringData();

const tokens = tokenData.map(async data => {
const symbol = data.tokens;
Expand Down Expand Up @@ -91,7 +82,7 @@ export class FantomSturdyLendingTokenFetcher implements PositionFetcher<AppToken
images: getImagesFromToken(underlyingTokens[0]),
statsItems: [
{
label: 'apy',
label: 'APY',
value: buildPercentageDisplayItem(data.base),
},
{
Expand Down
32 changes: 32 additions & 0 deletions src/apps/sturdy/helpers/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Network } from '~types';

export type VaultMonitoringResponse = {
chain: string;
tokens: string;
decimals: number;
address: string;
supply: number;
price: number;
base: number;
reward: number;
rewards: {
CRV: number;
CVX: number;
};
url: number;
tvl: number;
active: boolean;
}[];

export const TIMEOUT_DURATION = 15 * 60 * 1000;

/**
* Creates a unique primary key used for caching API responses
* @param appId The application's unique identifier
* @param groupId The application's unique group identifier
* @param network Network where the application is deployed
* @returns string
*/
export function cacheOnIntervalKeyCreationHelper(appId: string, groupId: string, network: Network): string {
return `studio:${appId}:${groupId}:${network}:addresses`;
}
1 change: 1 addition & 0 deletions src/apps/sturdy/sturdy.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const STURDY_DEFINITION = appDefinition({
},

supportedNetworks: {
[Network.ETHEREUM_MAINNET]: [AppAction.VIEW],
[Network.FANTOM_OPERA_MAINNET]: [AppAction.VIEW],
},

Expand Down
6 changes: 6 additions & 0 deletions src/apps/sturdy/sturdy.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Register } from '~app-toolkit/decorators';
import { AbstractApp } from '~app/app.dynamic-module';

import { SturdyContractFactory } from './contracts';
import { EthereumSturdyBalanceFetcher } from './ethereum/sturdy.balance-fetcher';
import { EthereumSturdyLendingTokenFetcher } from './ethereum/sturdy.lending.token-fetcher';
import { EthereumSturdyTvlFetcher } from './ethereum/sturdy.tvl-fetcher';
import { FantomSturdyBalanceFetcher } from './fantom/sturdy.balance-fetcher';
import { FantomSturdyLendingTokenFetcher } from './fantom/sturdy.lending.token-fetcher';
import { FantomSturdyTvlFetcher } from './fantom/sturdy.tvl-fetcher';
Expand All @@ -10,6 +13,9 @@ import { SturdyAppDefinition, STURDY_DEFINITION } from './sturdy.definition';
@Register.AppModule({
appId: STURDY_DEFINITION.id,
providers: [
EthereumSturdyBalanceFetcher,
EthereumSturdyLendingTokenFetcher,
EthereumSturdyTvlFetcher,
FantomSturdyBalanceFetcher,
FantomSturdyLendingTokenFetcher,
FantomSturdyTvlFetcher,
Expand Down