From 0a46d6e0bb7b561e1e73fa2ed82557badb348839 Mon Sep 17 00:00:00 2001 From: Verisana Date: Fri, 18 Aug 2023 14:11:51 +0100 Subject: [PATCH 01/11] feat: add new factory crv-usd --- src/dex/curve-v1-factory/config.ts | 26 +- src/dex/curve-v1-factory/curve-v1-factory.ts | 782 ++++++++++-------- .../functions/_calc_withdraw_one_coin.ts | 1 + .../price-handlers/functions/_dynamic_fee.ts | 1 + .../price-handlers/functions/_rates.ts | 1 + .../price-handlers/functions/_xp.ts | 1 + .../price-handlers/functions/_xp_mem.ts | 1 + .../functions/calc_token_amount.ts | 58 ++ .../functions/calc_withdraw_one_coin.ts | 1 + .../price-handlers/functions/constants.ts | 11 + .../price-handlers/functions/get_D.ts | 2 + .../price-handlers/functions/get_D_mem.ts | 1 + .../functions/get_D_precisions.ts | 1 + .../price-handlers/functions/get_dy.ts | 1 + .../functions/get_dy_underlying.ts | 1 + .../price-handlers/functions/get_y.ts | 1 + .../price-handlers/functions/get_y_D.ts | 1 + .../factory-pool-polling.ts | 3 +- src/dex/curve-v1-factory/types.ts | 4 +- 19 files changed, 520 insertions(+), 378 deletions(-) diff --git a/src/dex/curve-v1-factory/config.ts b/src/dex/curve-v1-factory/config.ts index f485b873e..5d5d392bd 100644 --- a/src/dex/curve-v1-factory/config.ts +++ b/src/dex/curve-v1-factory/config.ts @@ -13,7 +13,10 @@ import { normalizeAddress } from '../../utils'; const CurveV1FactoryConfig: DexConfigMap = { CurveV1Factory: { [Network.MAINNET]: { - factoryAddress: '0xB9fC157394Af804a3578134A6585C0dc9cc990d4', + factoryAddresses: [ + '0xB9fC157394Af804a3578134A6585C0dc9cc990d4', + '0x4f8846ae9380b90d2e71d5e3d042dff3e7ebb40d', + ], stateUpdatePeriodMs: 5 * 1000, disabledPools: new Set([ '0x28B0Cf1baFB707F2c6826d10caf6DD901a6540C5', // It is rug pool token @@ -150,6 +153,11 @@ const CurveV1FactoryConfig: DexConfigMap = { name: ImplementationNames.FACTORY_PLAIN_4_OPTIMIZED, address: '0xad4753d045d3aed5c1a6606dfb6a7d7ad67c1ad7', }, + '0xb9f861B7755Ff09cFD65CfE262D19823607b1f24': { + name: ImplementationNames.FACTORY_PLAIN_2_CRV_EMA, + address: '0xb9f861B7755Ff09cFD65CfE262D19823607b1f24', + liquidityApiSlug: '/factory-crvusd', + }, }, customPools: { '0xDcEF968d416a41Cdac0ED8702fAC8128A64241A2': { @@ -200,7 +208,7 @@ const CurveV1FactoryConfig: DexConfigMap = { }, }, [Network.POLYGON]: { - factoryAddress: '0x722272D36ef0Da72FF51c5A65Db7b870E2e8D4ee', + factoryAddresses: ['0x722272D36ef0Da72FF51c5A65Db7b870E2e8D4ee'], stateUpdatePeriodMs: 2 * 1000, disabledPools: new Set([ '0x666Dc3b4baBfd063FaF965BD020024AF0dC51B64', @@ -306,7 +314,7 @@ const CurveV1FactoryConfig: DexConfigMap = { }, }, [Network.FANTOM]: { - factoryAddress: '0x686d67265703D1f124c45E33d47d794c566889Ba', + factoryAddresses: ['0x686d67265703D1f124c45E33d47d794c566889Ba'], stateUpdatePeriodMs: 2 * 1000, disabledPools: new Set([]), disabledImplementations: new Set([]), @@ -421,7 +429,7 @@ const CurveV1FactoryConfig: DexConfigMap = { }, }, [Network.AVALANCHE]: { - factoryAddress: '0xb17b674D9c5CB2e441F8e196a2f048A81355d031', + factoryAddresses: ['0xb17b674D9c5CB2e441F8e196a2f048A81355d031'], stateUpdatePeriodMs: 2 * 1000, // FIX: This must be removed when we go for full CurveV1 event based support disabledPools: new Set(['0x16a7da911a4dd1d83f3ff066fe28f3c792c50d90']), @@ -519,7 +527,7 @@ const CurveV1FactoryConfig: DexConfigMap = { }, }, [Network.ARBITRUM]: { - factoryAddress: '0xb17b674D9c5CB2e441F8e196a2f048A81355d031', + factoryAddresses: ['0xb17b674D9c5CB2e441F8e196a2f048A81355d031'], stateUpdatePeriodMs: 2 * 1000, disabledPools: new Set([]), disabledImplementations: new Set([]), @@ -645,7 +653,7 @@ const CurveV1FactoryConfig: DexConfigMap = { }, }, [Network.OPTIMISM]: { - factoryAddress: '0x2db0E83599a91b508Ac268a6197b8B14F5e72840', + factoryAddresses: ['0x2db0E83599a91b508Ac268a6197b8B14F5e72840'], stateUpdatePeriodMs: 2 * 1000, disabledPools: new Set([]), disabledImplementations: new Set([]), @@ -869,9 +877,9 @@ const configAddressesNormalizer = ( // Unite everything into top level config const normalizedConfig: DexParams = { - factoryAddress: _config.factoryAddress - ? _config.factoryAddress.toLowerCase() - : _config.factoryAddress, + factoryAddresses: _config.factoryAddresses + ? _config.factoryAddresses.map(e => e.toLowerCase()) + : _config.factoryAddresses, stateUpdatePeriodMs: _config.stateUpdatePeriodMs, factoryPoolImplementations, customPools, diff --git a/src/dex/curve-v1-factory/curve-v1-factory.ts b/src/dex/curve-v1-factory/curve-v1-factory.ts index e365c351b..0e88729f8 100644 --- a/src/dex/curve-v1-factory/curve-v1-factory.ts +++ b/src/dex/curve-v1-factory/curve-v1-factory.ts @@ -127,7 +127,7 @@ export class CurveV1Factory private coinsTypeTemplate: AbiItem = DefaultCoinsABI, ) { super(dexHelper, dexKey); - this.logger = dexHelper.getLogger(dexKey); + this.logger = dexHelper.getLogger(`${this.dexKey}-${this.network}`); this.ifaces = { exchangeRouter: new Interface(CurveABI), factory: new Interface(FactoryCurveV1ABI as JsonFragment[]), @@ -185,6 +185,7 @@ export class CurveV1Factory } async initializeCustomPollingPools( + factoryAddresses: string[], blockNumber?: number, // We don't want to initialize state for PoolTracker. It doesn't make any sense initializeInitialState: boolean = true, @@ -193,131 +194,144 @@ export class CurveV1Factory return; } - const { factoryAddress } = this.config; - if (!factoryAddress) { - this.logger.warn( - `${this.dexKey}: No factory address specified for ${this.network}`, - ); - return; - } - await Promise.all( - Object.values(this.config.customPools).map(async customPool => { - const poolIdentifier = this.getPoolIdentifier( - customPool.address, - false, - ); - - const poolContextConstants = ImplementationConstants[customPool.name]; - const { - N_COINS: nCoins, - USE_LENDING: useLending, - isLending, - } = poolContextConstants; - - const coinsAndImplementations = - await this.dexHelper.multiWrapper.aggregate( - _.range(0, nCoins) - .map(i => ({ - target: customPool.address, - callData: this.abiCoder.encodeFunctionCall( - this._getCoinsABI(customPool.coinsInputType), - [i.toString()], - ), - decodeFunction: addressDecode, - })) - .concat([ - { - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData( - 'get_implementation_address', - [customPool.address], - ), - decodeFunction: addressDecode, - }, - ]), - ); - const COINS = coinsAndImplementations.slice(0, -1); - const implementationAddress = coinsAndImplementations.slice(-1)[0]; - - const coins_decimals = ( - await this.dexHelper.multiWrapper.tryAggregate( - true, - COINS.map(c => ({ - target: c, - callData: this.ifaces.erc20.encodeFunctionData('decimals', []), - decodeFunction: uint8ToNumber, - })), - ) - ).map(r => r.returnData); - - const poolConstants: PoolConstants = { - COINS, - coins_decimals, - rate_multipliers: this._calcRateMultipliers(coins_decimals), - lpTokenAddress: customPool.lpTokenAddress, - }; - - let newPool: PoolPollingBase; - if ( - Object.values( - CustomImplementationNames, - ).includes(customPool.name) - ) { - // We don't want custom pools to be used for pricing, unless explicitly specified - newPool = new CustomBasePoolForFactory( - this.logger, - this.dexKey, - this.dexHelper.config.data.network, - this.cacheStateKey, - customPool.name, - implementationAddress, - customPool.address, - this.config.stateUpdatePeriodMs, - poolIdentifier, - poolConstants, - poolContextConstants, - customPool.liquidityApiSlug, - customPool.lpTokenAddress, - isLending, - customPool.balancesInputType, - useLending, - customPool.useForPricing, - ); - } else { - // Use for pricing pools from factory - newPool = new CustomBasePoolForFactory( - this.logger, - this.dexKey, - this.dexHelper.config.data.network, - this.cacheStateKey, - customPool.name, - implementationAddress, - customPool.address, - this.config.stateUpdatePeriodMs, - poolIdentifier, - poolConstants, - poolContextConstants, - customPool.liquidityApiSlug, - customPool.lpTokenAddress, - isLending, - customPool.balancesInputType, - useLending, - true, + factoryAddresses.map(async factoryAddress => { + try { + await Promise.all( + Object.values(this.config.customPools).map(async customPool => { + const poolIdentifier = this.getPoolIdentifier( + customPool.address, + false, + ); + + const poolContextConstants = + ImplementationConstants[customPool.name]; + const { + N_COINS: nCoins, + USE_LENDING: useLending, + isLending, + } = poolContextConstants; + + const coinsAndImplementations = + await this.dexHelper.multiWrapper.aggregate( + _.range(0, nCoins) + .map(i => ({ + target: customPool.address, + callData: this.abiCoder.encodeFunctionCall( + this._getCoinsABI(customPool.coinsInputType), + [i.toString()], + ), + decodeFunction: addressDecode, + })) + .concat([ + { + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData( + 'get_implementation_address', + [customPool.address], + ), + decodeFunction: addressDecode, + }, + ]), + ); + const COINS = coinsAndImplementations.slice(0, -1); + const implementationAddress = + coinsAndImplementations.slice(-1)[0]; + + const coins_decimals = ( + await this.dexHelper.multiWrapper.tryAggregate( + true, + COINS.map(c => ({ + target: c, + callData: this.ifaces.erc20.encodeFunctionData( + 'decimals', + [], + ), + decodeFunction: uint8ToNumber, + })), + ) + ).map(r => r.returnData); + + const poolConstants: PoolConstants = { + COINS, + coins_decimals, + rate_multipliers: this._calcRateMultipliers(coins_decimals), + lpTokenAddress: customPool.lpTokenAddress, + }; + + let newPool: PoolPollingBase; + if ( + Object.values( + CustomImplementationNames, + ).includes(customPool.name) + ) { + // We don't want custom pools to be used for pricing, unless explicitly specified + newPool = new CustomBasePoolForFactory( + this.logger, + this.dexKey, + this.dexHelper.config.data.network, + this.cacheStateKey, + customPool.name, + implementationAddress, + customPool.address, + this.config.stateUpdatePeriodMs, + poolIdentifier, + poolConstants, + poolContextConstants, + customPool.liquidityApiSlug, + customPool.lpTokenAddress, + isLending, + customPool.balancesInputType, + useLending, + customPool.useForPricing, + ); + } else { + // Use for pricing pools from factory + newPool = new CustomBasePoolForFactory( + this.logger, + this.dexKey, + this.dexHelper.config.data.network, + this.cacheStateKey, + customPool.name, + implementationAddress, + customPool.address, + this.config.stateUpdatePeriodMs, + poolIdentifier, + poolConstants, + poolContextConstants, + customPool.liquidityApiSlug, + customPool.lpTokenAddress, + isLending, + customPool.balancesInputType, + useLending, + true, + ); + } + + this.poolManager.initializeNewPoolForState( + poolIdentifier, + newPool, + ); + + if (initializeInitialState) { + await this.poolManager.initializeIndividualPollingPoolState( + poolIdentifier, + CustomBasePoolForFactory.IS_SRC_FEE_ON_TRANSFER_SUPPORTED, + blockNumber, + ); + } + }), ); - } - - this.poolManager.initializeNewPoolForState(poolIdentifier, newPool); - - if (initializeInitialState) { - await this.poolManager.initializeIndividualPollingPoolState( - poolIdentifier, - CustomBasePoolForFactory.IS_SRC_FEE_ON_TRANSFER_SUPPORTED, - blockNumber, + } catch (e) { + this.logger.error( + `Error initializing custom polling pools for factory ${factoryAddress}: `, + e, ); + throw e; } }), ); + this.areCustomPoolsFetched = true; } @@ -332,277 +346,311 @@ export class CurveV1Factory return; } + if ( + !this.config.factoryAddresses || + this.config.factoryAddresses.length == 0 + ) { + this.logger.warn(`No factory address specified in configs`); + return; + } + // There is no scenario when we need to call initialize custom pools without factory pools // So I put it here to not forget call, because custom pools must be initialised before factory pools // This function may be called multiple times, but will execute only once await this.initializeCustomPollingPools( + this.config.factoryAddresses, blockNumber, initializeInitialState, ); - const { factoryAddress } = this.config; - if (!factoryAddress) { - this.logger.warn( - `${this.dexKey}: No factory address specified for ${this.network}`, - ); - return; - } - - const poolCountResult = await this.dexHelper.multiWrapper!.tryAggregate( - true, - [ - { - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData('pool_count'), - decodeFunction: uint256DecodeToNumber, - }, - // This is used later to request all available implementations. In particular meta implementations - { - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData('base_pool_count'), - decodeFunction: uint256DecodeToNumber, - }, - ], - ); - - const poolCount = poolCountResult[0].returnData; - const basePoolCount = poolCountResult[1].returnData; - - const calldataGetPoolAddresses = _.range(0, poolCount).map(i => ({ - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData('pool_list', [i]), - decodeFunction: addressDecode, - })); - - const calldataGetBasePoolAddresses = _.range(0, basePoolCount).map(i => ({ - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData('base_pool_list', [i]), - decodeFunction: addressDecode, - })); - - const allPoolAddresses = ( - await this.dexHelper.multiWrapper.tryAggregate( - true, - calldataGetPoolAddresses.concat(calldataGetBasePoolAddresses), - ) - ).map(e => e.returnData); - - const poolAddresses = allPoolAddresses.slice(0, poolCount); - const basePoolAddresses = allPoolAddresses.slice(poolCount); - - const customPoolAddresses = Object.values(this.config.customPools).map( - customPool => customPool.address, - ); - basePoolAddresses.forEach(basePool => { - if ( - !customPoolAddresses.includes(basePool) && - !this.config.disabledPools.has(basePool) - ) { - this._reportForUnspecifiedCustomPool(basePool); - } - }); - - let callDataFromFactoryPools: MultiCallParams< - string[] | number[] | string - >[] = poolAddresses - .map(p => [ - { - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData( - 'get_implementation_address', - [p], - ), - decodeFunction: addressDecode, - }, - { - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData('get_coins', [p]), - decodeFunction: ( - result: MultiResult | BytesLike, - ): string[] => - generalDecoder( - result, - ['address[4]'], - new Array(4).fill(NULL_ADDRESS), - parsed => parsed[0].map((p: string) => p.toLowerCase()), - ), - }, - { - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData('get_decimals', [p]), - decodeFunction: ( - result: MultiResult | BytesLike, - ): number[] => - generalDecoder( - result, - ['uint256[4]'], - [0, 0, 0, 0], - parsed => parsed[0].map((p: BigNumber) => Number(p.toString())), - ), - }, - ]) - .flat(); - - // This is divider between pools related results and implementations - const factoryResultsDivider = callDataFromFactoryPools.length; - - // Implementations must be requested from factory, but it accepts as arg basePool address - // for metaPools - callDataFromFactoryPools = callDataFromFactoryPools.concat( - ...basePoolAddresses.map(basePoolAddress => ({ - target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData( - 'metapool_implementations', - [basePoolAddress], - ), - decodeFunction: ( - result: MultiResult | BytesLike, - ): string[] => - generalDecoder( - result, - ['address[10]'], - new Array(10).fill(NULL_ADDRESS), - parsed => parsed[0].map((p: string) => p.toLowerCase()), - ), - })), - // To receive plain pool implementation address, you have to call plain_implementations - // with two variables: N_COINS and implementations_index - // N_COINS is between 2-4. Currently more than 4 coins is not supported - // as for implementation index, there are only 0-9 indexes - ..._.flattenDeep( - _.range(2, FACTORY_MAX_PLAIN_COINS + 1).map(coinNumber => - _.range(FACTORY_MAX_PLAIN_IMPLEMENTATIONS_FOR_COIN).map(implInd => ({ + await Promise.all( + this.config.factoryAddresses.map(async factoryAddress => { + try { + const poolCountResult = + await this.dexHelper.multiWrapper!.tryAggregate(true, [ + { + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData('pool_count'), + decodeFunction: uint256DecodeToNumber, + }, + // This is used later to request all available implementations. In particular meta implementations + { + target: factoryAddress, + callData: + this.ifaces.factory.encodeFunctionData('base_pool_count'), + decodeFunction: uint256DecodeToNumber, + }, + ]); + + const poolCount = poolCountResult[0].returnData; + const basePoolCount = poolCountResult[1].returnData; + + const calldataGetPoolAddresses = _.range(0, poolCount).map(i => ({ target: factoryAddress, - callData: this.ifaces.factory.encodeFunctionData( - 'plain_implementations', - [coinNumber, implInd], - ), + callData: this.ifaces.factory.encodeFunctionData('pool_list', [i]), decodeFunction: addressDecode, - })), - ), - ), - ); + })); + + const calldataGetBasePoolAddresses = _.range(0, basePoolCount).map( + i => ({ + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData( + 'base_pool_list', + [i], + ), + decodeFunction: addressDecode, + }), + ); - const allResultsFromFactory = ( - await this.dexHelper.multiWrapper.tryAggregate< - string[] | number[] | string - >(true, callDataFromFactoryPools) - ).map(r => r.returnData); + const allPoolAddresses = ( + await this.dexHelper.multiWrapper.tryAggregate( + true, + calldataGetPoolAddresses.concat(calldataGetBasePoolAddresses), + ) + ).map(e => e.returnData); + + const poolAddresses = allPoolAddresses.slice(0, poolCount); + const basePoolAddresses = allPoolAddresses.slice(poolCount); + + const customPoolAddresses = Object.values( + this.config.customPools, + ).map(customPool => customPool.address); + basePoolAddresses.forEach(basePool => { + if ( + !customPoolAddresses.includes(basePool) && + !this.config.disabledPools.has(basePool) + ) { + this._reportForUnspecifiedCustomPool(basePool); + } + }); - const resultsFromFactory = allResultsFromFactory.slice( - 0, - factoryResultsDivider, - ); + let callDataFromFactoryPools: MultiCallParams< + string[] | number[] | string + >[] = poolAddresses + .map(p => [ + { + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData( + 'get_implementation_address', + [p], + ), + decodeFunction: addressDecode, + }, + { + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData('get_coins', [ + p, + ]), + decodeFunction: ( + result: MultiResult | BytesLike, + ): string[] => + generalDecoder( + result, + ['address[4]'], + new Array(4).fill(NULL_ADDRESS), + parsed => parsed[0].map((p: string) => p.toLowerCase()), + ), + }, + { + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData( + 'get_decimals', + [p], + ), + decodeFunction: ( + result: MultiResult | BytesLike, + ): number[] => + generalDecoder( + result, + ['uint256[4]'], + [0, 0, 0, 0], + parsed => + parsed[0].map((p: BigNumber) => Number(p.toString())), + ), + }, + ]) + .flat(); + + // This is divider between pools related results and implementations + const factoryResultsDivider = callDataFromFactoryPools.length; + + // Implementations must be requested from factory, but it accepts as arg basePool address + // for metaPools + callDataFromFactoryPools = callDataFromFactoryPools.concat( + ...basePoolAddresses.map(basePoolAddress => ({ + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData( + 'metapool_implementations', + [basePoolAddress], + ), + decodeFunction: ( + result: MultiResult | BytesLike, + ): string[] => + generalDecoder( + result, + ['address[10]'], + new Array(10).fill(NULL_ADDRESS), + parsed => parsed[0].map((p: string) => p.toLowerCase()), + ), + })), + // To receive plain pool implementation address, you have to call plain_implementations + // with two variables: N_COINS and implementations_index + // N_COINS is between 2-4. Currently more than 4 coins is not supported + // as for implementation index, there are only 0-9 indexes + ..._.flattenDeep( + _.range(2, FACTORY_MAX_PLAIN_COINS + 1).map(coinNumber => + _.range(FACTORY_MAX_PLAIN_IMPLEMENTATIONS_FOR_COIN).map( + implInd => ({ + target: factoryAddress, + callData: this.ifaces.factory.encodeFunctionData( + 'plain_implementations', + [coinNumber, implInd], + ), + decodeFunction: addressDecode, + }), + ), + ), + ), + ); - const allAvailableImplementations = _.flattenDeep( - allResultsFromFactory.slice(factoryResultsDivider) as string[], - ).filter( - implementation => - implementation !== NULL_ADDRESS && - !this.config.disabledImplementations.has(implementation), - ); + const allResultsFromFactory = ( + await this.dexHelper.multiWrapper.tryAggregate< + string[] | number[] | string + >(true, callDataFromFactoryPools) + ).map(r => r.returnData); - allAvailableImplementations.forEach(implementation => { - const currentImplementation = - this.config.factoryPoolImplementations[implementation]; - if (currentImplementation === undefined) { - this._reportForUnspecifiedImplementation(implementation); - } - }); + const resultsFromFactory = allResultsFromFactory.slice( + 0, + factoryResultsDivider, + ); - const stateInitializePromises: Promise[] = []; - _.chunk(resultsFromFactory, 3).forEach((result, i) => { - if (this.config.disabledPools.has(poolAddresses[i])) { - this.logger.trace(`Filtering disabled pool ${poolAddresses[i]}`); - return; - } + const allAvailableImplementations = _.flattenDeep( + allResultsFromFactory.slice(factoryResultsDivider) as string[], + ).filter( + implementation => + implementation !== NULL_ADDRESS && + !this.config.disabledImplementations.has(implementation), + ); - let [implementationAddress, coins, coins_decimals] = result as [ - string, - string[], - number[], - ]; + allAvailableImplementations.forEach(implementation => { + const currentImplementation = + this.config.factoryPoolImplementations[implementation]; + if (currentImplementation === undefined) { + this._reportForUnspecifiedImplementation(implementation); + } + }); - implementationAddress = implementationAddress.toLowerCase(); - coins = coins.map(c => c.toLowerCase()).filter(c => c !== NULL_ADDRESS); - coins_decimals = coins_decimals.filter(cd => cd !== 0); + const stateInitializePromises: Promise[] = []; + _.chunk(resultsFromFactory, 3).forEach((result, i) => { + if (this.config.disabledPools.has(poolAddresses[i])) { + this.logger.trace(`Filtering disabled pool ${poolAddresses[i]}`); + return; + } + + let [implementationAddress, coins, coins_decimals] = result as [ + string, + string[], + number[], + ]; + + implementationAddress = implementationAddress.toLowerCase(); + coins = coins + .map(c => c.toLowerCase()) + .filter(c => c !== NULL_ADDRESS); + coins_decimals = coins_decimals.filter(cd => cd !== 0); + + const factoryImplementationFromConfig = + this.config.factoryPoolImplementations[implementationAddress]; + + if ( + factoryImplementationFromConfig === undefined && + !this.config.disabledImplementations.has(implementationAddress) + ) { + this._reportForUnspecifiedImplementation( + implementationAddress, + poolAddresses[i], + ); + return; + } + + const factoryImplementationConstants = + ImplementationConstants[factoryImplementationFromConfig.name]; + + let isMeta: boolean = false; + let basePoolStateFetcher: PoolPollingBase | undefined; + if (factoryImplementationFromConfig.basePoolAddress !== undefined) { + isMeta = true; + const basePoolIdentifier = this.getPoolIdentifier( + factoryImplementationFromConfig.basePoolAddress, + false, + ); + const basePool = this.poolManager.getPool( + basePoolIdentifier, + false, + ); + if (basePool === null) { + this.logger.error( + `${this.dexKey}_${this.dexHelper.config.data.network}: custom base pool ${basePoolIdentifier} was not initialized properly. ` + + `You must call initializeCustomPollingPools before fetching factory`, + ); + return; + } + basePoolStateFetcher = basePool; + } + + const poolConstants: PoolConstants = { + COINS: coins, + coins_decimals, + rate_multipliers: this._calcRateMultipliers(coins_decimals), + }; + + const poolIdentifier = this.getPoolIdentifier( + poolAddresses[i], + isMeta, + ); - const factoryImplementationFromConfig = - this.config.factoryPoolImplementations[implementationAddress]; + const newPool = new FactoryStateHandler( + this.logger, + this.dexKey, + this.dexHelper.config.data.network, + this.cacheStateKey, + factoryImplementationFromConfig.name, + implementationAddress.toLowerCase(), + poolAddresses[i], + this.config.stateUpdatePeriodMs, + factoryAddress, + poolIdentifier, + poolConstants, + factoryImplementationConstants, + factoryImplementationConstants.isFeeOnTransferSupported, + factoryImplementationFromConfig.liquidityApiSlug ?? '/factory', + basePoolStateFetcher, + factoryImplementationFromConfig.customGasCost, + factoryImplementationFromConfig.isStoreRateSupported, + ); - if ( - factoryImplementationFromConfig === undefined && - !this.config.disabledImplementations.has(implementationAddress) - ) { - this._reportForUnspecifiedImplementation( - implementationAddress, - poolAddresses[i], - ); - return; - } + this.poolManager.initializeNewPool(poolIdentifier, newPool); - const factoryImplementationConstants = - ImplementationConstants[factoryImplementationFromConfig.name]; + if (initializeInitialState) { + stateInitializePromises.push( + this.poolManager.initializeIndividualPollingPoolState( + poolIdentifier, + factoryImplementationConstants.isFeeOnTransferSupported, + ), + ); + } + }); - let isMeta: boolean = false; - let basePoolStateFetcher: PoolPollingBase | undefined; - if (factoryImplementationFromConfig.basePoolAddress !== undefined) { - isMeta = true; - const basePoolIdentifier = this.getPoolIdentifier( - factoryImplementationFromConfig.basePoolAddress, - false, - ); - const basePool = this.poolManager.getPool(basePoolIdentifier, false); - if (basePool === null) { + await Promise.all(stateInitializePromises); + } catch (e) { this.logger.error( - `${this.dexKey}_${this.dexHelper.config.data.network}: custom base pool ${basePoolIdentifier} was not initialized properly. ` + - `You must call initializeCustomPollingPools before fetching factory`, + `Error fetching factory pools for ${factoryAddress}: `, + e, ); - return; + throw e; } - basePoolStateFetcher = basePool; - } - - const poolConstants: PoolConstants = { - COINS: coins, - coins_decimals, - rate_multipliers: this._calcRateMultipliers(coins_decimals), - }; - - const poolIdentifier = this.getPoolIdentifier(poolAddresses[i], isMeta); - - const newPool = new FactoryStateHandler( - this.logger, - this.dexKey, - this.dexHelper.config.data.network, - this.cacheStateKey, - factoryImplementationFromConfig.name, - implementationAddress.toLowerCase(), - poolAddresses[i], - this.config.stateUpdatePeriodMs, - factoryAddress, - poolIdentifier, - poolConstants, - factoryImplementationConstants, - factoryImplementationConstants.isFeeOnTransferSupported, - basePoolStateFetcher, - factoryImplementationFromConfig.customGasCost, - factoryImplementationFromConfig.isStoreRateSupported, - ); - - this.poolManager.initializeNewPool(poolIdentifier, newPool); - - if (initializeInitialState) { - stateInitializePromises.push( - this.poolManager.initializeIndividualPollingPoolState( - poolIdentifier, - factoryImplementationConstants.isFeeOnTransferSupported, - ), - ); - } - }); - - await Promise.all(stateInitializePromises); + }), + ); this.areFactoryPoolsFetched = true; } diff --git a/src/dex/curve-v1-factory/price-handlers/functions/_calc_withdraw_one_coin.ts b/src/dex/curve-v1-factory/price-handlers/functions/_calc_withdraw_one_coin.ts index 5c9b0954e..ff85c7bd5 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/_calc_withdraw_one_coin.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/_calc_withdraw_one_coin.ts @@ -284,6 +284,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: customArbitrum2CoinBtc, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: customArbitrum2CoinBtc, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: factoryPlain2EthEma2, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: customArbitrum2CoinBtc, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/_dynamic_fee.ts b/src/dex/curve-v1-factory/price-handlers/functions/_dynamic_fee.ts index 541a47343..b0ba16915 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/_dynamic_fee.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/_dynamic_fee.ts @@ -93,6 +93,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: notExist, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: notExist, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/_rates.ts b/src/dex/curve-v1-factory/price-handlers/functions/_rates.ts index b8ab211d1..b742d20da 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/_rates.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/_rates.ts @@ -108,6 +108,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: notExist, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: notExist, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/_xp.ts b/src/dex/curve-v1-factory/price-handlers/functions/_xp.ts index 2f815afdd..f12737784 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/_xp.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/_xp.ts @@ -106,6 +106,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: notExist, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: notExist, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/_xp_mem.ts b/src/dex/curve-v1-factory/price-handlers/functions/_xp_mem.ts index c93fe7a3b..94db894b6 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/_xp_mem.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/_xp_mem.ts @@ -95,6 +95,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: factoryPlain2Basic, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: factoryPlain2Basic, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: factoryPlain2Basic, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: factoryPlain2Basic, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts b/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts index cb83a1dfe..573c44b0e 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts @@ -96,6 +96,63 @@ const customAvalanche3CoinLending: calc_token_amount = ( return (diff * state.totalSupply) / D0; }; +const customPlain2CoinCrv: calc_token_amount = ( + self: IPoolContext, + state: PoolState, + amounts: bigint[], + is_deposit: boolean, +) => { + const { N_COINS, BI_N_COINS, FEE_DENOMINATOR } = self.constants; + const amp = state.A; + const balances = [...state.balances]; + const D0 = self.get_D_mem(self, state, balances, amp); + for (const i of _.range(N_COINS)) { + if (is_deposit) balances[i] += amounts[i]; + else balances[i] -= amounts[i]; + } + const D1 = self.get_D_mem(self, state, balances, amp); + + if (state.totalSupply === undefined) { + throw new Error( + `${self.IMPLEMENTATION_NAME} customPlain3CoinThree: totalSupply is not provided`, + ); + } + + const total_supply = state.totalSupply; + let D2 = D1; + + if (total_supply > 0n) { + const base_fee = (state.fee * BI_N_COINS) / (4n * (BI_N_COINS - 1n)); + for (const i of _.range(N_COINS)) { + const ideal_balance = (D1 * state.balances[i]) / D0; + let difference = 0n; + const new_balance = balances[i]; + if (ideal_balance > new_balance) { + difference = ideal_balance - new_balance; + } else { + difference = new_balance - ideal_balance; + } + balances[i] -= (base_fee * difference) / FEE_DENOMINATOR; + } + const xp = self._xp_mem( + self, + [...state.constants.rate_multipliers], + balances, + ); + D2 = self.get_D_mem(self, state, xp, amp); + } else { + return D1; + } + + let diff = 0n; + if (is_deposit) { + diff = D2 - D0; + } else { + diff = D0 - D2; + } + return (diff * total_supply) / D0; +}; + const notImplemented: calc_token_amount = ( self: IPoolContext, state: PoolState, @@ -165,6 +222,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: customPlain3CoinThree, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: customPlain3CoinThree, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: customPlain3CoinThree, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: customPlain2CoinCrv, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/calc_withdraw_one_coin.ts b/src/dex/curve-v1-factory/price-handlers/functions/calc_withdraw_one_coin.ts index c6269ac23..4e6e06d66 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/calc_withdraw_one_coin.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/calc_withdraw_one_coin.ts @@ -82,6 +82,7 @@ export const implementations: Record< [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: customPlain3CoinThree, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: customPlain3CoinThree, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: customPlain3CoinThree, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: customPlain3CoinThree, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/constants.ts b/src/dex/curve-v1-factory/price-handlers/functions/constants.ts index e808ade22..a85483b84 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/constants.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/constants.ts @@ -573,6 +573,17 @@ const implementationConstants: Record< isFeeOnTransferSupported: true, isLending: false, + N_COINS: 2, + BI_N_COINS: 2n, + PRECISION: BI_POWS[18], + FEE_DENOMINATOR: BI_POWS[10], + A_PRECISION: 100n, + }, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: { + isWrapNative: false, + isFeeOnTransferSupported: false, + isLending: false, + N_COINS: 2, BI_N_COINS: 2n, PRECISION: BI_POWS[18], diff --git a/src/dex/curve-v1-factory/price-handlers/functions/get_D.ts b/src/dex/curve-v1-factory/price-handlers/functions/get_D.ts index c14500c6c..8ff128af1 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/get_D.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/get_D.ts @@ -321,6 +321,8 @@ const implementations: Record = { makeFuncCacheable(factoryPlain2Basic), [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: makeFuncCacheable(factoryPlain2Basic), + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: + makeFuncCacheable(factoryPlain2Basic), }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/get_D_mem.ts b/src/dex/curve-v1-factory/price-handlers/functions/get_D_mem.ts index 1178e2123..dc4d1d0fd 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/get_D_mem.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/get_D_mem.ts @@ -140,6 +140,7 @@ export const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: factoryPlain2Basic, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: factoryPlain2Basic, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: factoryPlain2EthEma2, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: factoryPlain2Basic, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/get_D_precisions.ts b/src/dex/curve-v1-factory/price-handlers/functions/get_D_precisions.ts index 178fc3232..b94501510 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/get_D_precisions.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/get_D_precisions.ts @@ -112,6 +112,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: notExist, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: notExist, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/get_dy.ts b/src/dex/curve-v1-factory/price-handlers/functions/get_dy.ts index bf69105bf..23339da18 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/get_dy.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/get_dy.ts @@ -266,6 +266,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: factoryPlain2Basic, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: factoryPlain2Basic, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: factoryPlain2EthEma2, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: factoryPlain2Basic, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/get_dy_underlying.ts b/src/dex/curve-v1-factory/price-handlers/functions/get_dy_underlying.ts index bd21d08e3..e4df36f5c 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/get_dy_underlying.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/get_dy_underlying.ts @@ -285,6 +285,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: notExist, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: notExist, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: notExist, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/get_y.ts b/src/dex/curve-v1-factory/price-handlers/functions/get_y.ts index 4fab6617e..62a323887 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/get_y.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/get_y.ts @@ -189,6 +189,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: customPlain2CoinFrax, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: customPlain2CoinFrax, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: customPlain2CoinFrax, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: customPlain2CoinFrax, }; export default implementations; diff --git a/src/dex/curve-v1-factory/price-handlers/functions/get_y_D.ts b/src/dex/curve-v1-factory/price-handlers/functions/get_y_D.ts index 7c713c3d1..65bbe1c8b 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/get_y_D.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/get_y_D.ts @@ -175,6 +175,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_PLAIN_2_BASIC_EMA]: customPlain2CoinFrax, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA]: customPlain2CoinFrax, [ImplementationNames.FACTORY_PLAIN_2_ETH_EMA2]: customPlain2CoinFrax, + [ImplementationNames.FACTORY_PLAIN_2_CRV_EMA]: customPlain2CoinFrax, }; export default implementations; diff --git a/src/dex/curve-v1-factory/state-polling-pools/factory-pool-polling.ts b/src/dex/curve-v1-factory/state-polling-pools/factory-pool-polling.ts index 42f71e2cb..7f3c3811e 100644 --- a/src/dex/curve-v1-factory/state-polling-pools/factory-pool-polling.ts +++ b/src/dex/curve-v1-factory/state-polling-pools/factory-pool-polling.ts @@ -44,6 +44,7 @@ export class FactoryStateHandler extends PoolPollingBase { readonly poolConstants: PoolConstants, readonly poolContextConstants: PoolContextConstants, readonly isSrcFeeOnTransferSupported: boolean, + liquidityApiSlug: string, baseStatePoolPolling?: PoolPollingBase, customGasCost?: number, readonly isStoredRatesSupported: boolean = false, @@ -65,7 +66,7 @@ export class FactoryStateHandler extends PoolPollingBase { poolIdentifier, poolConstants, address, - '/factory', + liquidityApiSlug, false, baseStatePoolPolling, isSrcFeeOnTransferSupported, diff --git a/src/dex/curve-v1-factory/types.ts b/src/dex/curve-v1-factory/types.ts index f5d6832ce..bb2b889f6 100644 --- a/src/dex/curve-v1-factory/types.ts +++ b/src/dex/curve-v1-factory/types.ts @@ -104,6 +104,7 @@ export enum FactoryImplementationNames { FACTORY_PLAIN_2_ETH_EMA = 'factory_plain_2_eth_ema', FACTORY_PLAIN_2_ETH_EMA2 = 'factory_plain_2_eth_ema2', FACTORY_PLAIN_2_OPTIMIZED = 'factory_plain_2_optimized', + FACTORY_PLAIN_2_CRV_EMA = 'factory_plain_2_crv_ema', FACTORY_PLAIN_3_BALANCES = 'factory_plain_3_balances', FACTORY_PLAIN_3_BASIC = 'factory_plain_3_basic', @@ -154,6 +155,7 @@ export type FactoryPoolImplementations = { basePoolAddress?: Address; customGasCost?: number; isStoreRateSupported?: boolean; + liquidityApiSlug?: string; }; export type CustomPoolConfig = { @@ -174,7 +176,7 @@ export type CustomPoolConfig = { }; export type DexParams = { - factoryAddress: string | null; + factoryAddresses: string[] | null; stateUpdatePeriodMs: number; factoryPoolImplementations: Record; customPools: Record; From d91999ba6522bba23ed956b01c43e02f24ca3a8b Mon Sep 17 00:00:00 2001 From: Verisana Date: Fri, 18 Aug 2023 14:19:33 +0100 Subject: [PATCH 02/11] fix: remove redundant token from constants --- tests/constants-e2e.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index 004aa98b0..e93aa284f 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -785,10 +785,6 @@ export const Tokens: { address: '0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f', decimals: 8, }, - USDCe: { - address: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', - decimals: 6, - }, LEX: { address: '0x6bB7A17AcC227fd1F6781D1EEDEAE01B42047eE0', decimals: 18, From c6ee53cd9edd9e13c66d7bb8c8d1574c70f24941 Mon Sep 17 00:00:00 2001 From: Verisana Date: Fri, 18 Aug 2023 15:41:06 +0100 Subject: [PATCH 03/11] fix: set right implementation address --- src/dex/curve-v1-factory/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dex/curve-v1-factory/config.ts b/src/dex/curve-v1-factory/config.ts index 5d5d392bd..f458836ab 100644 --- a/src/dex/curve-v1-factory/config.ts +++ b/src/dex/curve-v1-factory/config.ts @@ -153,9 +153,9 @@ const CurveV1FactoryConfig: DexConfigMap = { name: ImplementationNames.FACTORY_PLAIN_4_OPTIMIZED, address: '0xad4753d045d3aed5c1a6606dfb6a7d7ad67c1ad7', }, - '0xb9f861B7755Ff09cFD65CfE262D19823607b1f24': { + '0x67fe41A94e779CcFa22cff02cc2957DC9C0e4286': { name: ImplementationNames.FACTORY_PLAIN_2_CRV_EMA, - address: '0xb9f861B7755Ff09cFD65CfE262D19823607b1f24', + address: '0x67fe41A94e779CcFa22cff02cc2957DC9C0e4286', liquidityApiSlug: '/factory-crvusd', }, }, From 86394596e58071b069c39e8a8584d25ed66ab3b7 Mon Sep 17 00:00:00 2001 From: Verisana Date: Fri, 18 Aug 2023 15:42:26 +0100 Subject: [PATCH 04/11] 2.28.11-factory-crvusd.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6449d4439..58464b583 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.28.10", + "version": "2.28.11-factory-crvusd.0", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 5e2166ae82870d70c8036b4cc71ea2230f70ad7e Mon Sep 17 00:00:00 2001 From: Verisana Date: Fri, 18 Aug 2023 17:01:00 +0100 Subject: [PATCH 05/11] feat: added new test for curve pools --- .../curve-v1-factory-integration.test.ts | 153 ++++++++++++------ tests/constants-e2e.ts | 10 ++ 2 files changed, 118 insertions(+), 45 deletions(-) diff --git a/src/dex/curve-v1-factory/curve-v1-factory-integration.test.ts b/src/dex/curve-v1-factory/curve-v1-factory-integration.test.ts index 55b6fcb20..5dc723f42 100644 --- a/src/dex/curve-v1-factory/curve-v1-factory-integration.test.ts +++ b/src/dex/curve-v1-factory/curve-v1-factory-integration.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import dotenv from 'dotenv'; dotenv.config(); @@ -143,23 +144,6 @@ describe('CurveV1Factory', function () { const tokens = Tokens[network]; - const srcTokenSymbol = 'USDD'; - const destTokenSymbol = 'USDT'; - - const amountsForSell = [ - 0n, - 1n * BI_POWS[tokens[srcTokenSymbol].decimals], - 2n * BI_POWS[tokens[srcTokenSymbol].decimals], - 3n * BI_POWS[tokens[srcTokenSymbol].decimals], - 4n * BI_POWS[tokens[srcTokenSymbol].decimals], - 5n * BI_POWS[tokens[srcTokenSymbol].decimals], - 6n * BI_POWS[tokens[srcTokenSymbol].decimals], - 7n * BI_POWS[tokens[srcTokenSymbol].decimals], - 8n * BI_POWS[tokens[srcTokenSymbol].decimals], - 9n * BI_POWS[tokens[srcTokenSymbol].decimals], - 10n * BI_POWS[tokens[srcTokenSymbol].decimals], - ]; - beforeAll(async () => { blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); // @ts-expect-error for testing there is dummy blocknumber, but it is not @@ -178,39 +162,118 @@ describe('CurveV1Factory', function () { if (curveV1Factory) curveV1Factory.releaseResources(); }); - it('getPoolIdentifiers and getPricesVolume SELL', async function () { - await testPricingOnNetwork( - curveV1Factory, - network, - dexKey, - blockNumber, - srcTokenSymbol, - destTokenSymbol, - SwapSide.SELL, - amountsForSell, - ); + describe(`USDD-USDT`, () => { + const srcTokenSymbol = 'USDD'; + const destTokenSymbol = 'USDT'; + const amountsForSell = [ + 0n, + 1n * BI_POWS[tokens[srcTokenSymbol].decimals], + 2n * BI_POWS[tokens[srcTokenSymbol].decimals], + 3n * BI_POWS[tokens[srcTokenSymbol].decimals], + 4n * BI_POWS[tokens[srcTokenSymbol].decimals], + 5n * BI_POWS[tokens[srcTokenSymbol].decimals], + 6n * BI_POWS[tokens[srcTokenSymbol].decimals], + 7n * BI_POWS[tokens[srcTokenSymbol].decimals], + 8n * BI_POWS[tokens[srcTokenSymbol].decimals], + 9n * BI_POWS[tokens[srcTokenSymbol].decimals], + 10n * BI_POWS[tokens[srcTokenSymbol].decimals], + ]; + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + await testPricingOnNetwork( + curveV1Factory, + network, + dexKey, + blockNumber, + srcTokenSymbol, + destTokenSymbol, + SwapSide.SELL, + amountsForSell, + ); + }); + + it('getTopPoolsForToken', async function () { + // We have to check without calling initializePricing, because + // pool-tracker is not calling that function + const newCurveV1Factory = new CurveV1Factory( + network, + dexKey, + dexHelper, + ); + if (newCurveV1Factory.updatePoolState) { + await newCurveV1Factory.updatePoolState(); + } + const poolLiquidity = await newCurveV1Factory.getTopPoolsForToken( + tokens[srcTokenSymbol].address, + 10, + ); + console.log(`${srcTokenSymbol} Top Pools:`, poolLiquidity); + + if (!newCurveV1Factory.hasConstantPriceLargeAmounts) { + checkPoolsLiquidity( + poolLiquidity, + Tokens[network][srcTokenSymbol].address, + dexKey, + ); + } + }); }); - it('getTopPoolsForToken', async function () { - // We have to check without calling initializePricing, because - // pool-tracker is not calling that function - const newCurveV1Factory = new CurveV1Factory(network, dexKey, dexHelper); - if (newCurveV1Factory.updatePoolState) { - await newCurveV1Factory.updatePoolState(); - } - const poolLiquidity = await newCurveV1Factory.getTopPoolsForToken( - tokens[srcTokenSymbol].address, - 10, - ); - console.log(`${srcTokenSymbol} Top Pools:`, poolLiquidity); + describe(`crvUSD-GHO`, () => { + const srcTokenSymbol = 'crvUSD'; + const destTokenSymbol = 'GHO'; + const amountsForSell = [ + 0n, + 1n * BI_POWS[tokens[srcTokenSymbol].decimals], + 2n * BI_POWS[tokens[srcTokenSymbol].decimals], + 3n * BI_POWS[tokens[srcTokenSymbol].decimals], + 4n * BI_POWS[tokens[srcTokenSymbol].decimals], + 5n * BI_POWS[tokens[srcTokenSymbol].decimals], + 6n * BI_POWS[tokens[srcTokenSymbol].decimals], + 7n * BI_POWS[tokens[srcTokenSymbol].decimals], + 8n * BI_POWS[tokens[srcTokenSymbol].decimals], + 9n * BI_POWS[tokens[srcTokenSymbol].decimals], + 10n * BI_POWS[tokens[srcTokenSymbol].decimals], + ]; + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + await testPricingOnNetwork( + curveV1Factory, + network, + dexKey, + blockNumber, + srcTokenSymbol, + destTokenSymbol, + SwapSide.SELL, + amountsForSell, + ); + }); - if (!newCurveV1Factory.hasConstantPriceLargeAmounts) { - checkPoolsLiquidity( - poolLiquidity, - Tokens[network][srcTokenSymbol].address, + it('getTopPoolsForToken', async function () { + // We have to check without calling initializePricing, because + // pool-tracker is not calling that function + const newCurveV1Factory = new CurveV1Factory( + network, dexKey, + dexHelper, ); - } + if (newCurveV1Factory.updatePoolState) { + await newCurveV1Factory.updatePoolState(); + } + const poolLiquidity = await newCurveV1Factory.getTopPoolsForToken( + tokens[srcTokenSymbol].address, + 10, + ); + console.log(`${srcTokenSymbol} Top Pools:`, poolLiquidity); + + if (!newCurveV1Factory.hasConstantPriceLargeAmounts) { + checkPoolsLiquidity( + poolLiquidity, + Tokens[network][srcTokenSymbol].address, + dexKey, + ); + } + }); }); }); describe('Polygon', () => { diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index e93aa284f..3d892ae28 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -299,6 +299,14 @@ export const Tokens: { address: '0xc411db5f5eb3f7d552f9b8454b2d74097ccde6e3', decimals: 6, }, + GHO: { + address: '0x40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f', + decimals: 18, + }, + crvUSD: { + address: '0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E', + decimals: 18, + }, }, [Network.ROPSTEN]: { DAI: { @@ -904,6 +912,8 @@ export const Holders: { aEthWETH: '0x645C4c0c95C1Aa6EF25d12f4a25038cA9b0C6Cc7', dUSDC: '0x2FC2F705110A7F46Ce85F701d7217EF1018f01A3', PSP: '0xE5E5440a1CE69C5cf67BFFA74d185e57c31b43E5', + crvUSD: '0xA920De414eA4Ab66b97dA1bFE9e6EcA7d4219635', + GHO: '0x844Dc85EdD8492A56228D293cfEbb823EF3E10EC', }, [Network.ROPSTEN]: { ETH: '0x43262A12d8610AA70C15DbaeAC321d51613c9071', From 7e7bd72a1da7ec36904eadd39f7262f4e85f35c4 Mon Sep 17 00:00:00 2001 From: Verisana Date: Mon, 21 Aug 2023 13:14:13 +0100 Subject: [PATCH 06/11] fix: add new liquidity slug to array --- .../curve-v1-factory-e2e.test.ts | 19 +++++++++++++++++++ .../curve-v1-factory/curve-v1-pool-manager.ts | 5 ++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/dex/curve-v1-factory/curve-v1-factory-e2e.test.ts b/src/dex/curve-v1-factory/curve-v1-factory-e2e.test.ts index baf4d5527..2aa034745 100644 --- a/src/dex/curve-v1-factory/curve-v1-factory-e2e.test.ts +++ b/src/dex/curve-v1-factory/curve-v1-factory-e2e.test.ts @@ -111,6 +111,25 @@ describe('CurveV1Factory E2E', () => { tokenBAmount, ); }); + + describe('Mainnet crvUSD', () => { + const network = Network.MAINNET; + + const tokenASymbol: string = 'crvUSD'; + const tokenBSymbol: string = 'USDT'; + + const tokenAAmount: string = '10000000000000000000'; + const tokenBAmount: string = '10000000'; + + testForNetwork( + network, + dexKey, + tokenASymbol, + tokenBSymbol, + tokenAAmount, + tokenBAmount, + ); + }); describe('Mainnet ng pool', () => { const network = Network.MAINNET; diff --git a/src/dex/curve-v1-factory/curve-v1-pool-manager.ts b/src/dex/curve-v1-factory/curve-v1-pool-manager.ts index c240dbb09..42e2c8c20 100644 --- a/src/dex/curve-v1-factory/curve-v1-pool-manager.ts +++ b/src/dex/curve-v1-factory/curve-v1-pool-manager.ts @@ -42,7 +42,10 @@ export class CurveV1FactoryPoolManager { // It should bo considered for optimizing private coinAddressesToPoolIdentifiers: Record = {}; - private allCurveLiquidityApiSlugs: Set = new Set(['/factory']); + private allCurveLiquidityApiSlugs: Set = new Set([ + '/factory', + '/factory-crvusd', + ]); private statePollingManager = StatePollingManager; private taskScheduler: TaskScheduler; From eb21cbf581ccef27b1e0ab9d5fafb90afcedaf74 Mon Sep 17 00:00:00 2001 From: Verisana Date: Mon, 21 Aug 2023 13:15:45 +0100 Subject: [PATCH 07/11] 2.28.11-factory-crvusd.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 58464b583..863106efb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.28.11-factory-crvusd.0", + "version": "2.28.11-factory-crvusd.1", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From dd0fb03844d937383bb85900ee81b71b15ae5b0a Mon Sep 17 00:00:00 2001 From: Verisana Date: Tue, 22 Aug 2023 10:19:58 +0100 Subject: [PATCH 08/11] feat: re add cal token amount removed when revert --- .../functions/calc_token_amount.ts | 34 ++++++++++++++++++- src/dex/uniswap-v3/uniswap-v3.ts | 2 +- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts b/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts index 4121d1224..7f5708faa 100644 --- a/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts +++ b/src/dex/curve-v1-factory/price-handlers/functions/calc_token_amount.ts @@ -64,6 +64,38 @@ const customAvalanche3CoinLending: calc_token_amount = ( return (diff * state.totalSupply) / D0; }; +const factoryPlain2Basic: calc_token_amount = ( + self: IPoolContext, + state: PoolState, + amounts: bigint[], + is_deposit: boolean, +) => { + const { N_COINS } = self.constants; + const amp = state.A; + const balances = [...state.balances]; + const D0 = self.get_D(self, balances, amp); + for (const i of _.range(N_COINS)) { + if (is_deposit) balances[i] += amounts[i]; + else balances[i] -= amounts[i]; + } + const D1 = self.get_D(self, balances, amp); + + if (state.totalSupply === undefined) { + throw new Error( + `${self.IMPLEMENTATION_NAME} customPlain3CoinThree: totalSupply is not provided`, + ); + } + + const token_amount = state.totalSupply; + let diff = 0n; + if (is_deposit) { + diff = D1 - D0; + } else { + diff = D0 - D1; + } + return (diff * token_amount) / D0; +}; + const customPlain2CoinCrv: calc_token_amount = ( self: IPoolContext, state: PoolState, @@ -171,7 +203,7 @@ const implementations: Record = { [ImplementationNames.FACTORY_META_USD_BALANCES_FRAX_USDC]: notImplemented, [ImplementationNames.FACTORY_PLAIN_2_BALANCES]: notImplemented, - [ImplementationNames.FACTORY_PLAIN_2_BASIC]: notImplemented, + [ImplementationNames.FACTORY_PLAIN_2_BASIC]: factoryPlain2Basic, [ImplementationNames.FACTORY_PLAIN_2_ETH]: notImplemented, [ImplementationNames.FACTORY_PLAIN_2_OPTIMIZED]: notImplemented, diff --git a/src/dex/uniswap-v3/uniswap-v3.ts b/src/dex/uniswap-v3/uniswap-v3.ts index 8a6ae661c..b31280a11 100644 --- a/src/dex/uniswap-v3/uniswap-v3.ts +++ b/src/dex/uniswap-v3/uniswap-v3.ts @@ -279,7 +279,7 @@ export class UniswapV3 e, ); } else { - // on unkown error mark as failed and increase retryCount for retry init strategy + // on unknown error mark as failed and increase retryCount for retry init strategy // note: state would be null by default which allows to fallback this.logger.warn( `${this.dexKey}: Can not generate pool state for srcAddress=${srcAddress}, destAddress=${destAddress}, fee=${fee} pool fallback to rpc and retry every ${this.config.initRetryFrequency} times, initRetryAttemptCount=${pool.initRetryAttemptCount}`, From 750de3f06a69eb494f477c0c738bb70beeb62793 Mon Sep 17 00:00:00 2001 From: Verisana Date: Tue, 22 Aug 2023 10:21:47 +0100 Subject: [PATCH 09/11] 2.28.11-factory-crvusd.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 863106efb..4e1770966 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.28.11-factory-crvusd.1", + "version": "2.28.11-factory-crvusd.2", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 269d68374282728227039e59b49b4ad981b1717d Mon Sep 17 00:00:00 2001 From: Verisana Date: Tue, 22 Aug 2023 11:47:52 +0100 Subject: [PATCH 10/11] chore: set latest dex-lib version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e1770966..44655e19c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.28.11-factory-crvusd.2", + "version": "2.30.1", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 3e438548b267e131b59065dffb0188852378765c Mon Sep 17 00:00:00 2001 From: Verisana Date: Tue, 22 Aug 2023 11:48:52 +0100 Subject: [PATCH 11/11] 2.30.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44655e19c..3dbf23b1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "2.30.1", + "version": "2.30.2", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib",