From d9d5c04cba9d542d9ca439999e10a2c95ef221c5 Mon Sep 17 00:00:00 2001 From: Papa Sougou Wele Date: Tue, 7 Jun 2022 15:36:49 -0400 Subject: [PATCH] fix(curve): Use virtual price in v2 Pools price calculation (#592) --- src/apps/curve/curve.module.ts | 3 ++ .../curve.crypto-factory-pool.token-helper.ts | 14 +++++--- ...ve.liquidity-and-virtual.price-strategy.ts | 34 +++++++++++++++++++ .../helpers/curve.v2-pool.token-helper.ts | 14 +++++--- 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 src/apps/curve/helpers/curve.liquidity-and-virtual.price-strategy.ts diff --git a/src/apps/curve/curve.module.ts b/src/apps/curve/curve.module.ts index ed06ac41f..b6c0f7dfe 100644 --- a/src/apps/curve/curve.module.ts +++ b/src/apps/curve/curve.module.ts @@ -34,6 +34,7 @@ import { CurveFactoryPoolTokenHelper } from './helpers/curve.factory-pool.token- import { CurveFactoryPoolDefinitionStrategy } from './helpers/curve.factory.pool-definition-strategy'; import { CurveGaugeIsActiveStrategy } from './helpers/curve.gauge.is-active-strategy'; import { CurveGaugeRoiStrategy } from './helpers/curve.gauge.roi-strategy'; +import { CurveLiquidityAndVirtualPriceStrategy } from './helpers/curve.liquidity-and-virtual.price-strategy'; import { CurveLiquidityPriceStrategy } from './helpers/curve.liquidity.price-strategy'; import { CurveOnChainCoinStrategy } from './helpers/curve.on-chain.coin-strategy'; import { CurveOnChainReserveStrategy } from './helpers/curve.on-chain.reserve-strategy'; @@ -107,6 +108,7 @@ import { PolygonCurvePoolTokenFetcher } from './polygon/curve.pool.token-fetcher CurveOnChainReserveStrategy, CurveVirtualPriceStrategy, CurveLiquidityPriceStrategy, + CurveLiquidityAndVirtualPriceStrategy, CurveFactoryPoolDefinitionStrategy, CurveCryptoFactoryPoolDefinitionStrategy, // Gauge Helper Strategies @@ -141,6 +143,7 @@ import { PolygonCurvePoolTokenFetcher } from './polygon/curve.pool.token-fetcher CurveOnChainReserveStrategy, CurveVirtualPriceStrategy, CurveLiquidityPriceStrategy, + CurveLiquidityAndVirtualPriceStrategy, CurveFactoryPoolDefinitionStrategy, CurveCryptoFactoryPoolDefinitionStrategy, // Gauge Helper Strategies diff --git a/src/apps/curve/helpers/curve.crypto-factory-pool.token-helper.ts b/src/apps/curve/helpers/curve.crypto-factory-pool.token-helper.ts index cab1d9137..e85338a50 100644 --- a/src/apps/curve/helpers/curve.crypto-factory-pool.token-helper.ts +++ b/src/apps/curve/helpers/curve.crypto-factory-pool.token-helper.ts @@ -7,7 +7,7 @@ import { Network } from '~types/network.interface'; import { CurveContractFactory, CurveFactoryPool } from '../contracts'; import { CurveCryptoFactoryPoolDefinitionStrategy } from './curve.crypto-factory.pool-definition-strategy'; -import { CurveLiquidityPriceStrategy } from './curve.liquidity.price-strategy'; +import { CurveLiquidityAndVirtualPriceStrategy } from './curve.liquidity-and-virtual.price-strategy'; import { CurveOnChainCoinStrategy } from './curve.on-chain.coin-strategy'; import { CurveOnChainReserveStrategy } from './curve.on-chain.reserve-strategy'; import { CurveOnChainVolumeStrategy } from './curve.on-chain.volume-strategy'; @@ -34,8 +34,8 @@ export class CurveCryptoFactoryPoolTokenHelper { private readonly curveOnChainReserveStrategy: CurveOnChainReserveStrategy, @Inject(CurveCryptoFactoryPoolDefinitionStrategy) private readonly curveCryptoFactoryPoolDefinitionStrategy: CurveCryptoFactoryPoolDefinitionStrategy, - @Inject(CurveLiquidityPriceStrategy) - private readonly curveLiquidityPriceStrategy: CurveLiquidityPriceStrategy, + @Inject(CurveLiquidityAndVirtualPriceStrategy) + private readonly curveLiquidityAndVirtualPriceStrategy: CurveLiquidityAndVirtualPriceStrategy, @Inject(CurveContractFactory) private readonly curveContractFactory: CurveContractFactory, @Inject(CurveOnChainVolumeStrategy) @@ -71,7 +71,13 @@ export class CurveCryptoFactoryPoolTokenHelper { .wrap(poolContract) .fee() .catch(() => '0'), - resolvePoolTokenPrice: this.curveLiquidityPriceStrategy.build(), + resolvePoolTokenPrice: this.curveLiquidityAndVirtualPriceStrategy.build({ + resolveVirtualPrice: ({ multicall, poolContract }) => + multicall + .wrap(poolContract) + .get_virtual_price() + .catch(() => '0'), + }), resolvePoolTokenSymbol: ({ multicall, poolTokenContract }) => multicall.wrap(poolTokenContract).symbol(), resolvePoolTokenSupply: ({ multicall, poolTokenContract }) => multicall.wrap(poolTokenContract).totalSupply(), }); diff --git a/src/apps/curve/helpers/curve.liquidity-and-virtual.price-strategy.ts b/src/apps/curve/helpers/curve.liquidity-and-virtual.price-strategy.ts new file mode 100644 index 000000000..39aaf4bc5 --- /dev/null +++ b/src/apps/curve/helpers/curve.liquidity-and-virtual.price-strategy.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@nestjs/common'; +import { BigNumberish } from 'ethers'; + +import { EthersMulticall as Multicall } from '~multicall/multicall.ethers'; +import { Token } from '~position/position.interface'; + +@Injectable() +export class CurveLiquidityAndVirtualPriceStrategy { + build({ + resolveVirtualPrice, + }: { + resolveVirtualPrice: (opts: { multicall: Multicall; poolContract: T }) => Promise; + }) { + return async ({ + tokens, + reserves, + supply, + multicall, + poolContract, + }: { + tokens: Token[]; + reserves: number[]; + supply: number; + multicall: Multicall; + poolContract: T; + }) => { + const virtualPriceRaw = await resolveVirtualPrice({ multicall, poolContract }); + const virtualPrice = Number(virtualPriceRaw) / 10 ** 18; + const reservesUSD = tokens.map((t, i) => reserves[i] * t.price); + const liquidity = reservesUSD.reduce((total, r) => total + r, 0); + return virtualPrice > 0 ? virtualPrice * (liquidity / supply) : liquidity / supply; + }; + } +} diff --git a/src/apps/curve/helpers/curve.v2-pool.token-helper.ts b/src/apps/curve/helpers/curve.v2-pool.token-helper.ts index f56b593e6..586719a7e 100644 --- a/src/apps/curve/helpers/curve.v2-pool.token-helper.ts +++ b/src/apps/curve/helpers/curve.v2-pool.token-helper.ts @@ -8,7 +8,7 @@ import { CurveContractFactory, CurveV2Pool } from '../contracts'; import { CurvePoolDefinition } from '../curve.types'; import { CurveApiVolumeStrategy } from './curve.api.volume-strategy'; -import { CurveLiquidityPriceStrategy } from './curve.liquidity.price-strategy'; +import { CurveLiquidityAndVirtualPriceStrategy } from './curve.liquidity-and-virtual.price-strategy'; import { CurveOnChainCoinStrategy } from './curve.on-chain.coin-strategy'; import { CurveOnChainReserveStrategy } from './curve.on-chain.reserve-strategy'; import { CurvePoolTokenHelper } from './curve.pool.token-helper'; @@ -34,8 +34,8 @@ export class CurveV2PoolTokenHelper { private readonly curveOnChainReserveStrategy: CurveOnChainReserveStrategy, @Inject(CurveApiVolumeStrategy) private readonly curveApiVolumeStrategy: CurveApiVolumeStrategy, - @Inject(CurveLiquidityPriceStrategy) - private readonly curveLiquidityPriceStrategy: CurveLiquidityPriceStrategy, + @Inject(CurveLiquidityAndVirtualPriceStrategy) + private readonly curveLiquidityAndVirtualPriceStrategy: CurveLiquidityAndVirtualPriceStrategy, @Inject(CurveContractFactory) private readonly curveContractFactory: CurveContractFactory, ) {} @@ -64,7 +64,13 @@ export class CurveV2PoolTokenHelper { resolvePoolReserves: this.curveOnChainReserveStrategy.build(), resolvePoolVolume: this.curveApiVolumeStrategy.build({ statsUrl }), resolvePoolFee: ({ multicall, poolContract }) => multicall.wrap(poolContract).fee(), - resolvePoolTokenPrice: this.curveLiquidityPriceStrategy.build(), + resolvePoolTokenPrice: this.curveLiquidityAndVirtualPriceStrategy.build({ + resolveVirtualPrice: ({ multicall, poolContract }) => + multicall + .wrap(poolContract) + .get_virtual_price() + .catch(() => 0), + }), resolvePoolTokenSymbol: ({ multicall, poolTokenContract }) => multicall.wrap(poolTokenContract).symbol(), resolvePoolTokenSupply: ({ multicall, poolTokenContract }) => multicall.wrap(poolTokenContract).totalSupply(), });