Skip to content

Commit

Permalink
fix(mean-finance): Fix liquidity for Mean Finance positions (Zapper-f…
Browse files Browse the repository at this point in the history
  • Loading branch information
FiboApe committed Jul 23, 2022
1 parent 9eb2478 commit f8128d8
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 186 deletions.
65 changes: 2 additions & 63 deletions src/app-toolkit/helpers/the-graph/the-graph.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,6 @@ type RequestGraphParams = {
};

interface gqlFetchAllParams<T> {
query: string;
endpoint: string;
variables?: Variables;
dataToSearch: string;
offset?: number;
first?: number;
prevResults?: T;
}

interface gqlFetchAllStableParams<T> {
query: string;
endpoint: string;
variables?: Variables;
Expand All @@ -52,65 +42,14 @@ export class TheGraphHelper {
}

async gqlFetchAll<T = any>({
query,
endpoint,
variables = {},
dataToSearch,
offset = 0,
first = 1000,
prevResults,
}: gqlFetchAllParams<T>): Promise<T> {
const results = await this.requestGraph<T>({
endpoint,
query,
variables: {
...variables,
first: first,
skip: offset,
},
});

if (results[dataToSearch].length === first) {
let newPrevResults = results;
if (prevResults) {
newPrevResults = {
...prevResults,
...results,
[dataToSearch]: [...prevResults[dataToSearch], ...results[dataToSearch]],
};
}

return this.gqlFetchAll({
query,
endpoint,
variables,
dataToSearch,
first: first,
offset: offset + first,
prevResults: newPrevResults,
});
}

if (prevResults) {
return {
...prevResults,
...results,
[dataToSearch]: [...prevResults[dataToSearch], ...results[dataToSearch]],
};
} else {
return results;
}
}

async gqlFetchAllStable<T = any>({
query,
endpoint,
variables = {},
dataToSearch,
first = 1000,
lastId = '',
prevResults,
}: gqlFetchAllStableParams<T>): Promise<T> {
}: gqlFetchAllParams<T>): Promise<T> {
const results = await this.requestGraph<T>({
endpoint,
query,
Expand All @@ -131,7 +70,7 @@ export class TheGraphHelper {
};
}

return this.gqlFetchAllStable({
return this.gqlFetchAll({
query,
endpoint,
variables,
Expand Down
4 changes: 2 additions & 2 deletions src/apps/ethereum/ethereum/ethereum.balance-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const network = Network.ETHEREUM_MAINNET;

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

async getStakedBalances(address: string) {
return this.appToolkit.helpers.contractPositionBalanceHelper.getContractPositionBalances({
Expand All @@ -46,7 +46,7 @@ export class EthereumEthereumBalanceFetcher implements BalanceFetcher {
network,
resolveBalances: async ({ contractPosition }) => {
const token = contractPosition.tokens[0];
const data = await this.appToolkit.helpers.theGraphHelper.gqlFetchAllStable<Eth2DepositsResponse>({
const data = await this.appToolkit.helpers.theGraphHelper.gqlFetchAll<Eth2DepositsResponse>({
endpoint: GQL_ENDPOINT,
query: ETH2_DEPOSITS_QUERY,
dataToSearch: 'deposits',
Expand Down
4 changes: 2 additions & 2 deletions src/apps/mean-finance/graphql/getPairs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { gql } from 'graphql-request';

export const GET_PAIRS = gql`
query getAvailablePairs {
pairs(first: $first, skip: $skip, orderDirection: desc, orderBy: createdAtTimestamp) {
query getAvailablePairs($first: Int, $lastId: String) {
pairs(first: $first, where: { id_gt: $lastId }) {
id
tokenA {
address: id
Expand Down
7 changes: 2 additions & 5 deletions src/apps/mean-finance/graphql/getPositions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { gql } from 'graphql-request';

export const GET_POSITIONS = gql`
query getPositions($first: Int, $skip: Int) {
query getPositions($first: Int, $lastId: String) {
positions(
where: { status_in: [ACTIVE, COMPLETED] }
orderDirection: desc
orderBy: createdAtTimestamp
where: { status_in: [ACTIVE, COMPLETED], id_gt: $lastId }
first: $first
skip: $skip
) {
id
executedSwaps
Expand Down
7 changes: 2 additions & 5 deletions src/apps/mean-finance/graphql/getUserPositions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { gql } from 'graphql-request';

export const GET_USER_POSITIONS = gql`
query getUserPositions($address: String!, $first: Int, $skip: Int) {
query getUserPositions($address: String!, $first: Int, $lastId: String) {
positions(
where: { user: $address, status_in: [ACTIVE, COMPLETED] }
where: { user: $address, status_in: [ACTIVE, COMPLETED], id_gt: $lastId }
first: $first
skip: $skip
orderDirection: desc
orderBy: createdAtTimestamp
) {
id
executedSwaps
Expand Down
11 changes: 11 additions & 0 deletions src/apps/mean-finance/helpers/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const sortTokens = (tokenA: string, tokenB: string) => {
let token0 = tokenA;
let token1 = tokenB;

if (tokenA > tokenB) {
token0 = tokenB;
token1 = tokenA;
}

return [token0, token1];
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Inject } from '@nestjs/common';
import { sumBy } from 'lodash';
import { BigNumber } from 'ethers';
import { sumBy, keyBy } from 'lodash';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
Expand All @@ -10,97 +11,128 @@ import { WithMetaType } from '~position/display.interface';
import { BaseTokenBalance } from '~position/position-balance.interface';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { ContractPosition } from '~position/position.interface';
import { claimable } from '~position/position.utils';
import { BaseToken } from '~position/token.interface';
import { Network } from '~types/network.interface';

import { getPositions } from '../helpers/graph';
import { STRING_SWAP_INTERVALS } from '../helpers/intervals';
import { getPairs, getPositions } from '../helpers/graph';
import { sortTokens } from '../helpers/tokens';
import { MEAN_FINANCE_DEFINITION } from '../mean-finance.definition';

const appId = MEAN_FINANCE_DEFINITION.id;
const groupId = MEAN_FINANCE_DEFINITION.groups.dcaPosition.id;
const network = Network.OPTIMISM_MAINNET;

interface ExtendedContractPosition extends ContractPosition {
tokenA?: BaseToken;
tokenB?: BaseToken;
tokenALiquidity: BigNumber;
tokenBLiquidity: BigNumber;
positions: number;
id: string;
}
@Register.ContractPositionFetcher({ appId, groupId, network })
export class OptimismMeanFinanceDcaPositionContractPositionFetcher implements PositionFetcher<ContractPosition> {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) { }

async getPositions() {
const graphHelper = this.appToolkit.helpers.theGraphHelper;
const data = await getPositions(network, graphHelper);
const positionsData = await getPositions(network, graphHelper);
const pairsData = await getPairs(network, graphHelper);
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const positions = data.positions;
const pairs = pairsData.pairs;
const positions = positionsData.positions;
const dcaHubAddress = '0x059d306a25c4ce8d7437d25743a8b94520536bd5';

const contractPositions: ContractPosition[] = positions.map(dcaPosition => {
const contractPositionsByKey: Record<string, ExtendedContractPosition> = keyBy(pairs.map(pair => {
const tokenA = baseTokens.find(v => v.address === pair.tokenA.address);
const tokenB = baseTokens.find(v => v.address === pair.tokenB.address);

let images: string[] = [];

if (tokenA) {
tokenA.network = network;
images = [...images, ...getImagesFromToken(tokenA)];
}
if (tokenB) {
tokenB.network = network;
images = [...images, ...getImagesFromToken(tokenB)];
}
const position: ExtendedContractPosition = {
type: ContractType.POSITION,
address: dcaHubAddress,
appId,
groupId,
network,
tokens: [],
id: pair.id,
tokenA,
tokenB,
tokenALiquidity: BigNumber.from('0'),
tokenBLiquidity: BigNumber.from('0'),
positions: 0,
dataProps: {
id: pair.id,
},
displayProps: {
label: `${pair.tokenA.symbol}:${pair.tokenB.symbol} pair`,
images,
},
};

position.key = this.appToolkit.getPositionKey(position, ['id']);
return position;
}), 'id');

positions.forEach(dcaPosition => {
const [tokenAAddress, tokenBAddress] = sortTokens(dcaPosition.from.address, dcaPosition.to.address);

const toWithdraw = dcaPosition.current.idleSwapped;
const remainingLiquidity = dcaPosition.current.remainingLiquidity;
const remainingSwaps = Number(dcaPosition.current.remainingSwaps);
const swapInterval = Number(dcaPosition.swapInterval.interval) as keyof typeof STRING_SWAP_INTERVALS;
const rawRate = dcaPosition.current.rate;
const rate = Number(rawRate) / 10 ** Number(dcaPosition.from.decimals);
let formattedRate = rate.toFixed(3);

if (rate < 0.001) {
formattedRate = '<0.001';
}

const from = baseTokens.find(v => v.address === dcaPosition.from.address);
const to = baseTokens.find(v => v.address === dcaPosition.to.address);
contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].positions += 1;

const tokens: WithMetaType<BaseTokenBalance>[] = [];
let images: string[] = [];
if (from) {
from.network = network;
tokens.push(drillBalance(from, remainingLiquidity));
images = [...images, ...getImagesFromToken(from)];
if (dcaPosition.from.address === tokenAAddress) {
contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenALiquidity = contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenALiquidity.add(BigNumber.from(remainingLiquidity));
} else {
contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenBLiquidity = contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenBLiquidity.add(BigNumber.from(remainingLiquidity));
}
if (to) {
to.network = network;
tokens.push(drillBalance(claimable(to), toWithdraw));
images = [...images, ...getImagesFromToken(to)];
if (dcaPosition.to.address === tokenAAddress) {
contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenALiquidity = contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenALiquidity.add(BigNumber.from(toWithdraw));
} else {
contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenBLiquidity = contractPositionsByKey[`${tokenAAddress}-${tokenBAddress}`].tokenBLiquidity.add(BigNumber.from(toWithdraw));
}
});

const balanceUSD = sumBy(tokens, t => t.balanceUSD);
const swapIntervalAdverb = STRING_SWAP_INTERVALS[swapInterval].adverb;
let label = '';
const contractPositions: ContractPosition[] = Object.values(contractPositionsByKey).map(position => {
const tokens: WithMetaType<BaseTokenBalance>[] = [];

if (remainingSwaps > 0) {
label = `Swapping ~${formattedRate} ${from?.symbol || dcaPosition.from.symbol} ${swapIntervalAdverb} to ${
to?.symbol || dcaPosition.from.symbol
}`;
} else {
label = `Swapping ${from?.symbol || dcaPosition.from.symbol} to ${to?.symbol || dcaPosition.from.symbol}`;
if (position.tokenA) {
tokens.push(drillBalance(position.tokenA, position.tokenALiquidity.toString()));
}
if (position.tokenB) {
tokens.push(drillBalance(position.tokenB, position.tokenBLiquidity.toString()));
}

const secondaryLabel =
remainingSwaps && STRING_SWAP_INTERVALS[swapInterval]
? `${STRING_SWAP_INTERVALS[swapInterval].plural(remainingSwaps)} left`
: 'Position finished';
const balanceUSD = sumBy(tokens, t => t.balanceUSD);

const position: ContractPosition = {
return {
type: ContractType.POSITION,
address: dcaHubAddress,
appId,
groupId,
network,
tokens,
dataProps: {
id: dcaPosition.id,
toWithdraw,
remainingLiquidity,
remainingSwaps,
id: position.dataProps.id,
liquidity: balanceUSD,
tokenALiquidity: position.tokenALiquidity.toString(),
tokenBLiquidity: position.tokenBLiquidity.toString(),
},
displayProps: {
label,
secondaryLabel,
images,
...position.displayProps,
secondaryLabel: `${position.positions} active positions`
},
};

position.key = this.appToolkit.getPositionKey(position, ['id']);
return position;
}
});

return contractPositions;
Expand Down

0 comments on commit f8128d8

Please sign in to comment.