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

Commit

Permalink
fix(amp): Fix staking balances (#2269)
Browse files Browse the repository at this point in the history
* fix(amp): Fix staking balances

* fix(gamma-strategies): Linting
  • Loading branch information
wpoulin authored Feb 9, 2023
1 parent 8766bdf commit 037579a
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 81 deletions.
3 changes: 2 additions & 1 deletion src/apps/amp/amp.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common';

import { AbstractApp } from '~app/app.dynamic-module';

import { AmpStakingResolver } from './common/amp.staking-resolver';
import { AmpContractFactory } from './contracts';
import { EthereumAmpFarmContractPositionFetcher } from './ethereum/amp.farm.contract-position-fetcher';

@Module({
providers: [AmpContractFactory, EthereumAmpFarmContractPositionFetcher],
providers: [AmpContractFactory, AmpStakingResolver, EthereumAmpFarmContractPositionFetcher],
})
export class AmpAppModule extends AbstractApp() {}
72 changes: 72 additions & 0 deletions src/apps/amp/common/amp.staking-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Injectable } from '@nestjs/common';
import Axios, { AxiosError } from 'axios';

import { Cache } from '~cache/cache.decorator';

interface AmpStakingApiData {
apps: {
displayName: string;
ampPartition: string;
apy: string;
}[];
}

interface AmpStakingBalanceData {
address: string;
supplyTotal: string;
rewardTotal: string;
}

@Injectable()
export class AmpStakingResolver {
@Cache({
key: `studio:amp:staking:staking-data`,
ttl: 15 * 60, // 15 minutes
})
private async getPoolApyData() {
const url = 'https://api.capacity.production.flexa.network/apps';
const { data } = await Axios.get<AmpStakingApiData>(url, {
headers: { Accept: 'application/vnd.flexa.capacity.v1+json' },
});

return data.apps;
}

@Cache({
key: (address: string) => `studio:amp:staking:balance-data:${address}`,
ttl: 5 * 60, // 15 minutes
})
private async getBalanceData(address: string) {
const url = `https://api.capacity.production.flexa.network/accounts/${address}/totals`;
const data = await Axios.get<AmpStakingBalanceData>(url, {
headers: { Accept: 'application/vnd.flexa.capacity.v1+json' },
})
.then(({ data }) => data)
.catch(err => {
if ((err as AxiosError).response?.data.error === 'Address not found')
return { address, supplyTotal: '0', rewardTotal: '0' } as AmpStakingBalanceData;
throw err;
});

return data;
}

async getBalance(address: string) {
const balance = await this.getBalanceData(address);

return {
address: balance.address,
supplyTotal: Number(balance.supplyTotal),
rewardTotal: Number(balance.rewardTotal),
};
}

async getPoolApys(poolName: string) {
const poolApys = await this.getPoolApyData();

const poolApy = poolApys.find(x => x.displayName == poolName);
if (!poolApy) return 0;

return poolApy.apy;
}
}
83 changes: 10 additions & 73 deletions src/apps/amp/ethereum/amp.farm.contract-position-fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
import { Inject } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios, { AxiosError } from 'axios';
import { BigNumberish } from 'ethers';
import { sortBy } from 'lodash';

import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface';
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator';
import { Cache } from '~cache/cache.decorator';
import { MetaType } from '~position/position.interface';
import { ContractPositionTemplatePositionFetcher } from '~position/template/contract-position.template.position-fetcher';
import {
DefaultContractPositionDefinition,
GetTokenBalancesParams,
} from '~position/template/contract-position.template.types';

import { AmpStakingResolver } from '../common/amp.staking-resolver';
import { AmpContractFactory, AmpStaking } from '../contracts';

type DepositedAmpResponse = {
supplyTotal: string;
rewardTotal: string;
};

@PositionTemplate()
export class EthereumAmpFarmContractPositionFetcher extends ContractPositionTemplatePositionFetcher<AmpStaking> {
groupLabel = 'Flexa Capacity';

constructor(
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit,
@Inject(ConfigService) protected readonly configService: ConfigService,
@Inject(AmpStakingResolver) protected readonly ampStakingResolver: AmpStakingResolver,
@Inject(AmpContractFactory) protected readonly contractFactory: AmpContractFactory,
) {
super(appToolkit);
}

getContract(address: string): AmpStaking {
return this.contractFactory.ampStaking({ address, network: this.network });
}

async getDefinitions(): Promise<DefaultContractPositionDefinition[]> {
return [{ address: '0x706d7f8b3445d8dfc790c524e3990ef014e7c578' }];
}
Expand All @@ -50,76 +46,17 @@ export class EthereumAmpFarmContractPositionFetcher extends ContractPositionTemp
metaType: MetaType.LOCKED,
address: '0xff20817765cb7f73d4bde2e66e067e58d11095c2',
network: this.network,
symbol: 'AMP Rewards',
},
];
}

getContract(address: string): AmpStaking {
return this.contractFactory.ampStaking({ address, network: this.network });
}
@Cache({
instance: 'business',
key: (address: string) => `apps-v3:balance:ethereum:amp:api-data:${address}`,
ttl: 15 * 60, // 15 minutes
})
async getAddressBalances(address: string) {
const axiosInstance = axios.create({
baseURL: 'https://api.capacity.production.flexa.network',
headers: {
Accept: 'application/vnd.flexa.capacity.v1+json',
},
});
return axiosInstance
.get<DepositedAmpResponse>(`/accounts/${address}/totals`)
.then(({ data }) => data)
.catch(err => {
if ((err as AxiosError).response?.data.error === 'Address not found')
return { supplyTotal: '0', rewardTotal: '0' } as DepositedAmpResponse;
throw err;
});
}

async getTokenBalancesPerPosition({ address }: GetTokenBalancesParams<AmpStaking>): Promise<BigNumberish[]> {
const { supplyTotal, rewardTotal } = await this.getAddressBalances(address);
return rewardTotal > supplyTotal ? [] : [supplyTotal, rewardTotal];
}
@Cache({
instance: 'business',
key: () => `apps-v3:balance:ethereum:amp:api-data`,
ttl: 15 * 60, // 15 minutes
})
async getPoolApys() {
const capacityResponse = axios.create({
baseURL: 'https://api.capacity.production.flexa.network',
headers: {
Accept: 'application/vnd.flexa.capacity.v1+json',
},
});
return capacityResponse
.get('/apps')
.then(function (response) {
let apyRangeLabel;
const data = response.data;
if (data.apps) {
for (const i in data.apps) {
data.apps[i].numApy = Number(data.apps[i].apy);
}
const sortedApps = sortBy(data.apps, 'numApy');
const lowestApy = sortedApps[0].apy === '0' ? sortedApps[1].apy : sortedApps[0].apy;
const highestApy = sortedApps[sortedApps.length - 1].apy;
apyRangeLabel = 'Staked Amp (' + highestApy + '% to ' + lowestApy + '% APY)';
} else {
apyRangeLabel = 'Staked Amp';
}
return apyRangeLabel;
})
.catch(function () {
return 'Staked Amp';
});
const { supplyTotal, rewardTotal } = await this.ampStakingResolver.getBalance(address);
return rewardTotal > supplyTotal ? [0, 0] : [supplyTotal, rewardTotal];
}

async getLabel() {
return await this.getPoolApys();
const apy = await this.ampStakingResolver.getPoolApys(this.network);
return `Amp Staking ${apy}%`;
}
}
16 changes: 9 additions & 7 deletions src/apps/gamma-strategies/helpers/gamma-strategies.api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import Axios from 'axios';
import { map, merge } from 'lodash'
import { map, merge } from 'lodash';

import { Cache } from '~cache/cache.decorator';
import { Network } from '~types/network.interface';
Expand All @@ -9,17 +9,19 @@ export type GammaApiTokensResponse = Record<string, object>;

@Injectable()
export class GammaApiHelper {
constructor() { }
constructor() {}

@Cache({
key: (network: Network) => `studio:gamma:${network}:vaults`,
ttl: 60 * 60, // 12 hours
})
async getVaultDefinitionsData(urls: string[]) {
const data = await Promise.all(map(urls, async (url) => {
const { data } = await Axios.get<GammaApiTokensResponse>(url);
return data
}))
return merge(...data as [GammaApiTokensResponse, GammaApiTokensResponse])
const data = await Promise.all(
map(urls, async url => {
const { data } = await Axios.get<GammaApiTokensResponse>(url);
return data;
}),
);
return merge(...(data as [GammaApiTokensResponse, GammaApiTokensResponse]));
}
}

0 comments on commit 037579a

Please sign in to comment.