Skip to content

Commit

Permalink
feat(revert-fi): Track compoundor claimable fees (Zapper-fi#1087)
Browse files Browse the repository at this point in the history
  • Loading branch information
Clonescody committed Aug 8, 2022
1 parent 920d0f1 commit e1b6c7a
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/apps/revert-finance/arbitrum/revert-finance.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Inject } from '@nestjs/common';
import { getAddress } from 'ethers/lib/utils';

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

import { accountBalancesQuery, CompoundorUserPosition } from '../graphql/accountBalancesQuery';
import { generateGraphUrlForNetwork } from '../graphql/graphUrlGenerator';
import { getCompoundorContractPosition } from '../helpers/contractPositionParser';
import { REVERT_FINANCE_DEFINITION } from '../revert-finance.definition';

const network = Network.ARBITRUM_MAINNET;

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

async getAccumulatedCompoundorRewards(address: string) {
const graphHelper = this.appToolkit.helpers.theGraphHelper;
const data = await graphHelper.requestGraph<CompoundorUserPosition>({
endpoint: generateGraphUrlForNetwork(network),
query: accountBalancesQuery,
variables: { address: getAddress(address) },
});
if (!data) return [];
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const rewards: Array<ContractPositionBalance> = [];
data.accountBalances.map(({ token, balance }) => {
const existingToken = baseTokens.find(item => item.address === token)!;
if (!token) return [];

rewards.push(getCompoundorContractPosition(network, existingToken, balance));
});
return rewards;
}

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

return presentBalanceFetcherResponse([
{
label: 'Compoundor rewards',
assets: accumulatedCompoundorRewards,
},
]);
}
}
14 changes: 14 additions & 0 deletions src/apps/revert-finance/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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 RevertFinanceContractFactory extends ContractFactory {
constructor(@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit) {
super((network: Network) => appToolkit.getNetworkProvider(network));
}
}
51 changes: 51 additions & 0 deletions src/apps/revert-finance/ethereum/revert-finance.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Inject } from '@nestjs/common';
import { getAddress } from 'ethers/lib/utils';

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

import { accountBalancesQuery, CompoundorUserPosition } from '../graphql/accountBalancesQuery';
import { generateGraphUrlForNetwork } from '../graphql/graphUrlGenerator';
import { getCompoundorContractPosition } from '../helpers/contractPositionParser';
import { REVERT_FINANCE_DEFINITION } from '../revert-finance.definition';

const network = Network.ETHEREUM_MAINNET;

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

async getAccumulatedCompoundorRewards(address: string) {
const graphHelper = this.appToolkit.helpers.theGraphHelper;
const data = await graphHelper.requestGraph<CompoundorUserPosition>({
endpoint: generateGraphUrlForNetwork(network),
query: accountBalancesQuery,
variables: { address: getAddress(address) },
});
if (!data) return [];
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const rewards: Array<ContractPositionBalance> = [];
data.accountBalances.map(({ token, balance }) => {
const existingToken = baseTokens.find(item => item.address === token)!;
if (!token) return [];

rewards.push(getCompoundorContractPosition(network, existingToken, balance));
});
return rewards;
}

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

return presentBalanceFetcherResponse([
{
label: 'Compoundor rewards',
assets: accumulatedCompoundorRewards,
},
]);
}
}
23 changes: 23 additions & 0 deletions src/apps/revert-finance/graphql/accountBalancesQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { gql } from 'graphql-request';

type AccountBalance = {
id: string;
token: string;
account: string;
balance: string;
};

export type CompoundorUserPosition = {
accountBalances: Array<AccountBalance>;
};

export const accountBalancesQuery = gql`
query getUserPositions($address: String!) {
accountBalances(where: { account: $address }) {
id
account
token
balance
}
}
`;
12 changes: 12 additions & 0 deletions src/apps/revert-finance/graphql/graphUrlGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const generateGraphUrlForNetwork = (network: string): string => {
let parsedNetwork = '';
switch (network) {
case 'ethereum':
parsedNetwork = 'mainnet';
break;
default:
parsedNetwork = network;
break;
}
return `https://api.thegraph.com/subgraphs/name/revert-finance/compoundor-${parsedNetwork}`;
};
41 changes: 41 additions & 0 deletions src/apps/revert-finance/helpers/contractPositionParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getAddress } from 'ethers/lib/utils';

import { drillBalance } from '~app-toolkit';
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 { ContractPositionBalance } from '~position/position-balance.interface';
import { claimable } from '~position/position.utils';
import { BaseToken } from '~position/token.interface';
import { Network } from '~types';

import REVERT_FINANCE_DEFINITION from '../revert-finance.definition';

const CompoundorContractAddress = getAddress('0x5411894842e610c4d0f6ed4c232da689400f94a1');

export const getCompoundorContractPosition = (
network: Network,
existingToken: BaseToken,
balanceRaw: string,
): ContractPositionBalance => {
const balance = [drillBalance(claimable(existingToken), balanceRaw)];
const dataProps = {};
const displayProps = {
label: `Claimable ${existingToken.symbol}`,
secondaryLabel: buildDollarDisplayItem(existingToken.price),
images: [getTokenImg(existingToken.address, network)],
statsItems: [],
};

return {
type: ContractType.POSITION,
address: CompoundorContractAddress,
network,
appId: REVERT_FINANCE_DEFINITION.id,
groupId: REVERT_FINANCE_DEFINITION.groups.compoundorRewards.id,
tokens: balance,
balanceUSD: balance[0].balanceUSD,
dataProps,
displayProps,
};
};
3 changes: 3 additions & 0 deletions src/apps/revert-finance/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { REVERT_FINANCE_DEFINITION, RevertFinanceAppDefinition } from './revert-finance.definition';
export { RevertFinanceAppModule } from './revert-finance.module';
export { RevertFinanceContractFactory } from './contracts';
51 changes: 51 additions & 0 deletions src/apps/revert-finance/optimism/revert-finance.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Inject } from '@nestjs/common';
import { getAddress } from 'ethers/lib/utils';

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

import { accountBalancesQuery, CompoundorUserPosition } from '../graphql/accountBalancesQuery';
import { generateGraphUrlForNetwork } from '../graphql/graphUrlGenerator';
import { getCompoundorContractPosition } from '../helpers/contractPositionParser';
import { REVERT_FINANCE_DEFINITION } from '../revert-finance.definition';

const network = Network.OPTIMISM_MAINNET;

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

async getAccumulatedCompoundorRewards(address: string) {
const graphHelper = this.appToolkit.helpers.theGraphHelper;
const data = await graphHelper.requestGraph<CompoundorUserPosition>({
endpoint: generateGraphUrlForNetwork(network),
query: accountBalancesQuery,
variables: { address: getAddress(address) },
});
if (!data) return [];
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const rewards: Array<ContractPositionBalance> = [];
data.accountBalances.map(({ token, balance }) => {
const existingToken = baseTokens.find(item => item.address === token)!;
if (!token) return [];

rewards.push(getCompoundorContractPosition(network, existingToken, balance));
});
return rewards;
}

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

return presentBalanceFetcherResponse([
{
label: 'Compoundor rewards',
assets: accumulatedCompoundorRewards,
},
]);
}
}
51 changes: 51 additions & 0 deletions src/apps/revert-finance/polygon/revert-finance.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Inject } from '@nestjs/common';
import { getAddress } from 'ethers/lib/utils';

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

import { accountBalancesQuery, CompoundorUserPosition } from '../graphql/accountBalancesQuery';
import { generateGraphUrlForNetwork } from '../graphql/graphUrlGenerator';
import { getCompoundorContractPosition } from '../helpers/contractPositionParser';
import { REVERT_FINANCE_DEFINITION } from '../revert-finance.definition';

const network = Network.POLYGON_MAINNET;

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

async getAccumulatedCompoundorRewards(address: string) {
const graphHelper = this.appToolkit.helpers.theGraphHelper;
const data = await graphHelper.requestGraph<CompoundorUserPosition>({
endpoint: generateGraphUrlForNetwork(network),
query: accountBalancesQuery,
variables: { address: getAddress(address) },
});
if (!data) return [];
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const rewards: Array<ContractPositionBalance> = [];
data.accountBalances.map(({ token, balance }) => {
const existingToken = baseTokens.find(item => item.address === token)!;
if (!token) return [];

rewards.push(getCompoundorContractPosition(network, existingToken, balance));
});
return rewards;
}

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

return presentBalanceFetcherResponse([
{
label: 'Compoundor rewards',
assets: accumulatedCompoundorRewards,
},
]);
}
}
41 changes: 41 additions & 0 deletions src/apps/revert-finance/revert-finance.definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Register } from '~app-toolkit/decorators';
import { appDefinition, AppDefinition } from '~app/app.definition';
import { AppAction, AppTag, GroupType } from '~app/app.interface';
import { Network } from '~types/network.interface';

export const REVERT_FINANCE_DEFINITION = appDefinition({
id: 'revert-finance',
name: 'Revert finance',
description: 'Revert develops analytics and management tools for liquidity providers in AMM protocols. ',
url: 'https://revert.finance/',

groups: {
compoundorRewards: {
id: 'compoundor-rewards',
type: GroupType.POSITION,
label: 'Compoundor rewards',
},
},

tags: [AppTag.ASSET_MANAGEMENT],
keywords: [],
links: {},

supportedNetworks: {
[Network.ETHEREUM_MAINNET]: [AppAction.VIEW],
[Network.POLYGON_MAINNET]: [AppAction.VIEW],
[Network.OPTIMISM_MAINNET]: [AppAction.VIEW],
[Network.ARBITRUM_MAINNET]: [AppAction.VIEW],
},

primaryColor: '#fff',
});

@Register.AppDefinition(REVERT_FINANCE_DEFINITION.id)
export class RevertFinanceAppDefinition extends AppDefinition {
constructor() {
super(REVERT_FINANCE_DEFINITION);
}
}

export default REVERT_FINANCE_DEFINITION;
22 changes: 22 additions & 0 deletions src/apps/revert-finance/revert-finance.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Register } from '~app-toolkit/decorators';
import { AbstractApp } from '~app/app.dynamic-module';

import { ArbitrumRevertFinanceBalanceFetcher } from './arbitrum/revert-finance.balance-fetcher';
import { RevertFinanceContractFactory } from './contracts';
import { EthereumRevertFinanceBalanceFetcher } from './ethereum/revert-finance.balance-fetcher';
import { OptimismRevertFinanceBalanceFetcher } from './optimism/revert-finance.balance-fetcher';
import { PolygonRevertFinanceBalanceFetcher } from './polygon/revert-finance.balance-fetcher';
import { RevertFinanceAppDefinition, REVERT_FINANCE_DEFINITION } from './revert-finance.definition';

@Register.AppModule({
appId: REVERT_FINANCE_DEFINITION.id,
providers: [
ArbitrumRevertFinanceBalanceFetcher,
EthereumRevertFinanceBalanceFetcher,
OptimismRevertFinanceBalanceFetcher,
PolygonRevertFinanceBalanceFetcher,
RevertFinanceAppDefinition,
RevertFinanceContractFactory,
],
})
export class RevertFinanceAppModule extends AbstractApp() {}

0 comments on commit e1b6c7a

Please sign in to comment.