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

feat(tenderize): integrate tenderTokens and swapTokens #882

Merged
merged 3 commits into from Jul 18, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/apps/tenderize/arbitrum/tenderize.balance-fetcher.ts
@@ -0,0 +1,52 @@
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 { TENDERIZE_DEFINITION } from '../tenderize.definition';

const network = Network.ARBITRUM_MAINNET;

@Register.BalanceFetcher(TENDERIZE_DEFINITION.id, network)
export class ArbitrumTenderizeBalanceFetcher implements BalanceFetcher {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}

async getTenderTokenBalances(address: string) {
return this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
address,
appId: TENDERIZE_DEFINITION.id,
groupId: TENDERIZE_DEFINITION.groups.tendertokens.id,
network,
});
}

async getSwapTokenBalances(address: string) {
return this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
address,
appId: TENDERIZE_DEFINITION.id,
groupId: TENDERIZE_DEFINITION.groups.swaptokens.id,
network,
});
}

async getBalances(address: string) {
const [tenderTokenBalances, swapTokenBalances] = await Promise.all([
this.getTenderTokenBalances(address),
this.getSwapTokenBalances(address),
]);

return presentBalanceFetcherResponse([
{
label: 'TenderTokens',
assets: tenderTokenBalances,
},
{
label: 'SwapTokens',
assets: swapTokenBalances,
},
]);
}
}
75 changes: 75 additions & 0 deletions src/apps/tenderize/arbitrum/tenderize.swaptokens.token-fetcher.ts
@@ -0,0 +1,75 @@
import { Inject } from '@nestjs/common';

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 { ContractType } from '~position/contract.interface';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { Network } from '~types/network.interface';

import { TenderizeContractFactory } from '../contracts';
import { arbitrumEndpoint } from '../helpers/constants';
import { ConfigResponse, configQuery } from '../helpers/queries';
import { TENDERIZE_DEFINITION } from '../tenderize.definition';

const appId = TENDERIZE_DEFINITION.id;
const groupId = TENDERIZE_DEFINITION.groups.swaptokens.id;
const network = Network.ARBITRUM_MAINNET;

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

async getPosition(address: string, steak: string) {
const multicall = this.appToolkit.getMulticall(network);
const contract = this.tenderizeContractFactory.erc20({ address, network });
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const underlyingToken = baseTokens.find(v => v.address === steak)!;

const [symbol, decimals, totalSupply] = await Promise.all([
multicall.wrap(contract).symbol(),
multicall.wrap(contract).decimals(),
multicall.wrap(contract).totalSupply(),
]);
const supply = Number(totalSupply) / 10 ** decimals;
const price = underlyingToken.price;
const token: AppTokenPosition = {
address,
network,
appId,
groupId,
symbol,
decimals,
supply,
tokens: [],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put the underlyingToken in this array

dataProps: {},
pricePerShare: 1,
price,
type: ContractType.APP_TOKEN,
displayProps: {
label: symbol,
secondaryLabel: buildDollarDisplayItem(price),
images: [],
statsItems: [],
},
};

return token;
}

async getPositions() {
const data = await this.appToolkit.helpers.theGraphHelper.request<ConfigResponse>({
endpoint: arbitrumEndpoint,
query: configQuery,
});

const positions = await Promise.all(
data.configs.map(async config => await this.getPosition(config.lpToken, config.steak, config.symbol)),
);
return positions;
}
}
@@ -0,0 +1,85 @@
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';
import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present';
import { ContractType } from '~position/contract.interface';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { Network } from '~types/network.interface';

import { TenderizeContractFactory } from '../contracts';
import { arbitrumEndpoint } from '../helpers/constants';
import { tenderTokenFetcherQuery, TenderTokenFetcherResponse } from '../helpers/queries';
import { APYResponse } from '../helpers/types';
import { TENDERIZE_DEFINITION } from '../tenderize.definition';

const appId = TENDERIZE_DEFINITION.id;
const groupId = TENDERIZE_DEFINITION.groups.tendertokens.id;
const network = Network.ARBITRUM_MAINNET;

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

async getPosition(address: string, steak: string, virtualPrice: string, apy: string) {
const multicall = this.appToolkit.getMulticall(network);
const contract = this.tenderizeContractFactory.erc20({ address, network });
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const underlyingToken = baseTokens.find(v => v.address === steak)!;

const [symbol, decimals, totalSupply] = await Promise.all([
multicall.wrap(contract).symbol(),
multicall.wrap(contract).decimals(),
multicall.wrap(contract).totalSupply(),
]);
const supply = Number(totalSupply) / 10 ** decimals;
const price = underlyingToken.price * (Number(virtualPrice) / 10 ** decimals);
const token: AppTokenPosition = {
address,
network,
appId,
groupId,
symbol,
decimals,
supply,
tokens: [],
dataProps: {},
pricePerShare: 1,
price,
type: ContractType.APP_TOKEN,
displayProps: {
label: symbol,
secondaryLabel: buildDollarDisplayItem(price),
tertiaryLabel: `${apy}% APY`,
images: [`https://app.tenderize.me/tender${symbol}.svg`],
statsItems: [],
},
};

return token;
}

async getPositions() {
const data = await this.appToolkit.helpers.theGraphHelper.request<TenderTokenFetcherResponse>({
endpoint: arbitrumEndpoint,
query: tenderTokenFetcherQuery,
});

const { data: apyData } = await Axios.get<APYResponse>('https://www.tenderize.me/api/apy');
const apyArr = Object.values(apyData);
const positions = await Promise.all(
data.configs.map(async config => {
const apy = apyArr.find(item => item.subgraphId === config.id)?.apy;
const virtualPrice =
data.tenderSwaps.find(item => item.id === config.id)?.virtualPrice ?? '1000000000000000000';
return await this.getPosition(config.tenderToken, config.steak, virtualPrice, apy ?? '0');
}),
);
return positions;
}
}
15 changes: 15 additions & 0 deletions src/apps/tenderize/contracts/index.ts
@@ -0,0 +1,15 @@
import { Injectable, Inject } from '@nestjs/common';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { ContractFactory } from '~contract/contracts';
import { Network } from '~types/network.interface';

// eslint-disable-next-line
type ContractOpts = { address: string; network: Network };

@Injectable()
export class TenderizeContractFactory extends ContractFactory {
constructor(@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit) {
super((network: Network) => appToolkit.getNetworkProvider(network));
}
}
52 changes: 52 additions & 0 deletions src/apps/tenderize/ethereum/tenderize.balance-fetcher.ts
@@ -0,0 +1,52 @@
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 { TENDERIZE_DEFINITION } from '../tenderize.definition';

const network = Network.ETHEREUM_MAINNET;

@Register.BalanceFetcher(TENDERIZE_DEFINITION.id, network)
export class EthereumTenderizeBalanceFetcher implements BalanceFetcher {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}

async getTenderTokenBalances(address: string) {
return this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
address,
appId: TENDERIZE_DEFINITION.id,
groupId: TENDERIZE_DEFINITION.groups.tendertokens.id,
network,
});
}

async getSwapTokenBalances(address: string) {
return this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
address,
appId: TENDERIZE_DEFINITION.id,
groupId: TENDERIZE_DEFINITION.groups.swaptokens.id,
network,
});
}

async getBalances(address: string) {
const [tenderTokenBalances, swapTokenBalances] = await Promise.all([
this.getTenderTokenBalances(address),
this.getSwapTokenBalances(address),
]);

return presentBalanceFetcherResponse([
{
label: 'TenderTokens',
assets: tenderTokenBalances,
},
{
label: 'SwapTokens',
assets: swapTokenBalances,
},
]);
}
}
74 changes: 74 additions & 0 deletions src/apps/tenderize/ethereum/tenderize.swaptokens.token-fetcher.ts
@@ -0,0 +1,74 @@
import { Inject } from '@nestjs/common';

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 { ContractType } from '~position/contract.interface';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { Network } from '~types/network.interface';

import { TenderizeContractFactory } from '../contracts';
import { ethereumEndpoint } from '../helpers/constants';
import { ConfigResponse, configQuery } from '../helpers/queries';
import { TENDERIZE_DEFINITION } from '../tenderize.definition';

const appId = TENDERIZE_DEFINITION.id;
const groupId = TENDERIZE_DEFINITION.groups.swaptokens.id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename group to swap, and rename the file to tenderize.swap.token-fetcher.ts (the additional tokens is redundant)

const network = Network.ETHEREUM_MAINNET;

@Register.TokenPositionFetcher({ appId, groupId, network })
export class EthereumTenderizeSwapTokensTokenFetcher implements PositionFetcher<AppTokenPosition> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the above changes, rename this to EthereumTenderizeSwapTokenFetcher.

constructor(
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
@Inject(TenderizeContractFactory) private readonly tenderizeContractFactory: TenderizeContractFactory,
) {}

async getPosition(address: string, steak: string) {
const multicall = this.appToolkit.getMulticall(network);
const contract = this.tenderizeContractFactory.erc20({ address, network });
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const underlyingToken = baseTokens.find(v => v.address === steak)!;
const [symbol, decimals, totalSupply] = await Promise.all([
multicall.wrap(contract).symbol(),
multicall.wrap(contract).decimals(),
multicall.wrap(contract).totalSupply(),
]);
const supply = Number(totalSupply) / 10 ** decimals;
const price = underlyingToken.price;
const token: AppTokenPosition = {
address,
network,
appId,
groupId,
symbol,
decimals,
supply,
tokens: [],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing as mentioned earlier, put the underlyingToken in this array

dataProps: {},
pricePerShare: 1,
price,
type: ContractType.APP_TOKEN,
displayProps: {
label: symbol,
secondaryLabel: buildDollarDisplayItem(price),
images: [],
statsItems: [],
},
};

return token;
}

async getPositions() {
const data = await this.appToolkit.helpers.theGraphHelper.request<ConfigResponse>({
endpoint: ethereumEndpoint,
query: configQuery,
});

const positions = await Promise.all(
data.configs.map(async config => await this.getPosition(config.lpToken, config.steak)),
);
return positions;
}
}