Skip to content

fix: allow full curvejs reinitialization #465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Draft
310 changes: 158 additions & 152 deletions src/boosting.ts

Large diffs are not rendered by default.

9 changes: 3 additions & 6 deletions src/cached.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import memoize from "memoizee";
import {IDict, IExtendedPoolDataFromApi, INetworkName, IPoolType} from "./interfaces.js";
import {uncached_getAllPoolsFromApi, createCrvApyDict, createUsdPricesDict} from './external-api.js'
import {curve} from "./curve";
import {createCrvApyDict, createUsdPricesDict, uncached_getAllPoolsFromApi} from './external-api.js'

/**
* This function is used to cache the data fetched from the API and the data derived from it.
Expand Down Expand Up @@ -33,14 +32,12 @@ export const _getAllPoolsFromApi = async (network: INetworkName, isLiteChain = f
return poolLists
}

export const _getUsdPricesFromApi = async (): Promise<IDict<number>> => {
const network = curve.constants.NETWORK_NAME;
export const _getUsdPricesFromApi = async (network:INetworkName): Promise<IDict<number>> => {
const {usdPrices} = await _getCachedData(network, false);
return usdPrices
}

export const _getCrvApyFromApi = async (): Promise<IDict<[number, number]>> => {
const network = curve.constants.NETWORK_NAME;
export const _getCrvApyFromApi = async (network:INetworkName): Promise<IDict<[number, number]>> => {
const {crvApy} = await _getCachedData(network, false);
return crvApy
}
29 changes: 23 additions & 6 deletions src/constants/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { curve } from "../curve.js";
import { IDict, IPoolData } from "../interfaces.js";
import {IDict, IPoolData} from "../interfaces.js";
import {BigNumberish, ethers, Numeric} from "ethers";
import memoize from "memoizee";
import {Curve} from "../curve";

export const formatUnits = (value: BigNumberish, unit?: string | Numeric): string => ethers.formatUnits(value, unit);
export const parseUnits = (value: string, unit?: string | Numeric) => ethers.parseUnits(value, unit)

export const lowerCasePoolDataAddresses = (poolsData: IDict<IPoolData>): IDict<IPoolData> => {
for (const poolId in poolsData) {
Expand Down Expand Up @@ -42,22 +46,35 @@ export const extractDecimals = (poolsData: IDict<IPoolData>): IDict<number> => {
return DECIMALS;
}

export const extractGauges = (poolsData: IDict<IPoolData>): string[] => {
export function extractGauges(this: Curve, poolsData: IDict<IPoolData>): string[] {
const GAUGES: string[] = [];
for (const poolData of Object.values(poolsData)) {
if (poolData.gauge_address === curve.constants.ZERO_ADDRESS) continue;
if (poolData.gauge_address === this.constants.ZERO_ADDRESS) continue;
GAUGES.push(poolData.gauge_address);
}

return GAUGES;
}

export const lowerCaseValues = (dict: IDict<string>): IDict<string> => {
// @ts-ignore
return Object.fromEntries(Object.entries(dict).map((entry) => [entry[0], entry[1].toLowerCase()]))
}

export const lowerCaseKeys = (dict: IDict<any>): IDict<any> => {
// @ts-ignore
return Object.fromEntries(Object.entries(dict).map((entry) => [entry[0].toLowerCase(), entry[1]]))
}

/**
* Memoizes a method of an object by binding it to this when needed.
* The memoized method will cache the result for 5 minutes.
* @param obj The object to which the method belongs.
* @param name The name of the method to memoize. It must be unique within the object.
* @param method The method to memoize. It must be a function that returns a Promise.
* @returns The memoized method.
*/
export const memoizeMethod = <Obj extends object, Method extends (this: Obj, ...params: any[]) => Promise<unknown>>(obj: Obj, name: string, method: Method) => {
if (!(name in obj)) {
(obj as any)[name] = memoize(method.bind(obj), { promise: true, maxAge: 5 * 60 * 1000 /* 5m */ });
}
return (obj as any)[name] as Method;
}
172 changes: 76 additions & 96 deletions src/curve.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,65 @@
import {
ethers,
Contract,
Networkish,
BigNumberish,
Numeric,
AbstractProvider,
BigNumberish,
BrowserProvider,
Contract,
ethers,
JsonRpcProvider,
Networkish,
Numeric,
Signer,
} from "ethers";
import { Provider as MulticallProvider, Contract as MulticallContract } from "@curvefi/ethcall";
import { NETWORK_CONSTANTS } from "./constants/network_constants.js";
import { STABLE_FACTORY_CONSTANTS, CRYPTO_FACTORY_CONSTANTS } from "./constants/factory/index.js";
import { getFactoryPoolData } from "./factory/factory.js";
import { getFactoryPoolsDataFromApi } from "./factory/factory-api.js";
import { getCryptoFactoryPoolData } from "./factory/factory-crypto.js";
import { getTricryptoFactoryPoolData } from "./factory/factory-tricrypto.js";
import {IPoolData, IDict, ICurve, IChainId, IFactoryPoolType, Abi, INetworkConstants} from "./interfaces";
import ERC20Abi from './constants/abis/ERC20.json' with { type: 'json' };
import cERC20Abi from './constants/abis/cERC20.json' with { type: 'json' };
import yERC20Abi from './constants/abis/yERC20.json' with { type: 'json' };
import childGaugeFactoryABI from './constants/abis/gauge_factory/child_gauge_factory.json' with { type: 'json' };
import minterMainnetABI from './constants/abis/minter_mainnet.json' with { type: 'json' };
import votingEscrowABI from './constants/abis/votingescrow.json' with { type: 'json' };
import anycallABI from './constants/abis/anycall.json' with { type: 'json' };
import votingEscrowOracleABI from './constants/abis/voting_escrow_oracle.json' with { type: 'json' };
import votingEscrowOracleEthABI from './constants/abis/voting_escrow_oracle_eth.json' with { type: 'json' };
import feeDistributorABI from './constants/abis/fee_distributor.json' with { type: 'json' };
import feeDistributorCrvUSDABI from './constants/abis/fee_distributor_crvusd.json' with { type: 'json' };
import gaugeControllerABI from './constants/abis/gaugecontroller.json' with { type: 'json' };
import depositAndStakeABI from './constants/abis/deposit_and_stake.json' with { type: 'json' };
import depositAndStakeNgOnlyABI from './constants/abis/deposit_and_stake_ng_only.json' with { type: 'json' };
import cryptoCalcZapABI from './constants/abis/crypto_calc.json' with { type: 'json' };
import StableCalcZapABI from './constants/abis/stable_calc.json' with { type: 'json' };
import routerABI from './constants/abis/router.json' with { type: 'json' };
import routerPolygonABI from './constants/abis/routerPolygon.json' with { type: 'json' };
import routerNgPoolsOnlyABI from './constants/abis/router-ng-pools-only.json' with { type: 'json' };
import streamerABI from './constants/abis/streamer.json' with { type: 'json' };
import factoryABI from './constants/abis/factory.json' with { type: 'json' };
import factoryEywaABI from './constants/abis/factory-eywa.json' with { type: 'json' };
import factoryAdminABI from './constants/abis/factory-admin.json' with { type: 'json' };
import cryptoFactoryABI from './constants/abis/factory-crypto.json' with { type: 'json' };
import twocryptoFactoryABI from './constants/abis/factory-twocrypto-ng.json' with { type: 'json' };
import tricryptoFactoryMainnetABI from './constants/abis/factory-tricrypto-mainnet.json' with { type: 'json' };
import tricryptoFactorySidechainABI from './constants/abis/factory-tricrypto-sidechain.json' with { type: 'json' };
import stableNgFactoryABI from './constants/abis/factory-stable-ng.json' with { type: 'json' };
import gasOracleABI from './constants/abis/gas_oracle_optimism.json' with { type: 'json' };
import gasOracleBlobABI from './constants/abis/gas_oracle_optimism_blob.json' with { type: 'json' };
import votingProposalABI from './constants/abis/voting_proposal.json' with { type: 'json' };
import circulatingSupplyABI from './constants/abis/circulating_supply.json' with { type: 'json' };
import rootGaugeFactoryABI from "./constants/abis/gauge_factory/root_gauge_factory.json" with { type: 'json' };

import { lowerCasePoolDataAddresses, extractDecimals, extractGauges } from "./constants/utils.js";
import {Contract as MulticallContract, Provider as MulticallProvider} from "@curvefi/ethcall";
import {NETWORK_CONSTANTS} from "./constants/network_constants.js";
import {CRYPTO_FACTORY_CONSTANTS, STABLE_FACTORY_CONSTANTS} from "./constants/factory/index.js";
import {getFactoryPoolData} from "./factory/factory.js";
import {getFactoryPoolsDataFromApi} from "./factory/factory-api.js";
import {getCryptoFactoryPoolData} from "./factory/factory-crypto.js";
import {getTricryptoFactoryPoolData} from "./factory/factory-tricrypto.js";
import {Abi, IChainId, ICurve, IDict, IFactoryPoolType, INetworkConstants, IPoolData} from "./interfaces";
import ERC20Abi from './constants/abis/ERC20.json' with {type: 'json'};
import cERC20Abi from './constants/abis/cERC20.json' with {type: 'json'};
import yERC20Abi from './constants/abis/yERC20.json' with {type: 'json'};
import childGaugeFactoryABI from './constants/abis/gauge_factory/child_gauge_factory.json' with {type: 'json'};
import minterMainnetABI from './constants/abis/minter_mainnet.json' with {type: 'json'};
import votingEscrowABI from './constants/abis/votingescrow.json' with {type: 'json'};
import anycallABI from './constants/abis/anycall.json' with {type: 'json'};
import votingEscrowOracleABI from './constants/abis/voting_escrow_oracle.json' with {type: 'json'};
import votingEscrowOracleEthABI from './constants/abis/voting_escrow_oracle_eth.json' with {type: 'json'};
import feeDistributorABI from './constants/abis/fee_distributor.json' with {type: 'json'};
import feeDistributorCrvUSDABI from './constants/abis/fee_distributor_crvusd.json' with {type: 'json'};
import gaugeControllerABI from './constants/abis/gaugecontroller.json' with {type: 'json'};
import depositAndStakeABI from './constants/abis/deposit_and_stake.json' with {type: 'json'};
import depositAndStakeNgOnlyABI from './constants/abis/deposit_and_stake_ng_only.json' with {type: 'json'};
import cryptoCalcZapABI from './constants/abis/crypto_calc.json' with {type: 'json'};
import StableCalcZapABI from './constants/abis/stable_calc.json' with {type: 'json'};
import routerABI from './constants/abis/router.json' with {type: 'json'};
import routerPolygonABI from './constants/abis/routerPolygon.json' with {type: 'json'};
import routerNgPoolsOnlyABI from './constants/abis/router-ng-pools-only.json' with {type: 'json'};
import streamerABI from './constants/abis/streamer.json' with {type: 'json'};
import factoryABI from './constants/abis/factory.json' with {type: 'json'};
import factoryEywaABI from './constants/abis/factory-eywa.json' with {type: 'json'};
import factoryAdminABI from './constants/abis/factory-admin.json' with {type: 'json'};
import cryptoFactoryABI from './constants/abis/factory-crypto.json' with {type: 'json'};
import twocryptoFactoryABI from './constants/abis/factory-twocrypto-ng.json' with {type: 'json'};
import tricryptoFactoryMainnetABI from './constants/abis/factory-tricrypto-mainnet.json' with {type: 'json'};
import tricryptoFactorySidechainABI from './constants/abis/factory-tricrypto-sidechain.json' with {type: 'json'};
import stableNgFactoryABI from './constants/abis/factory-stable-ng.json' with {type: 'json'};
import gasOracleABI from './constants/abis/gas_oracle_optimism.json' with {type: 'json'};
import gasOracleBlobABI from './constants/abis/gas_oracle_optimism_blob.json' with {type: 'json'};
import votingProposalABI from './constants/abis/voting_proposal.json' with {type: 'json'};
import circulatingSupplyABI from './constants/abis/circulating_supply.json' with {type: 'json'};
import rootGaugeFactoryABI from "./constants/abis/gauge_factory/root_gauge_factory.json" with {type: "json"};
import {
extractDecimals,
extractGauges,
formatUnits,
lowerCasePoolDataAddresses,
parseUnits,
} from "./constants/utils.js";
import {_getHiddenPools} from "./external-api.js";
import { L2Networks } from "./constants/L2Networks.js";
import { getTwocryptoFactoryPoolData } from "./factory/factory-twocrypto.js";
import {L2Networks} from "./constants/L2Networks.js";
import {getTwocryptoFactoryPoolData} from "./factory/factory-twocrypto.js";
import {getNetworkConstants} from "./utils.js";


Expand Down Expand Up @@ -90,7 +95,7 @@ export const memoizedMulticallContract = (): (address: string, abi: any) => Mult

export type ContractItem = { contract: Contract, multicallContract: MulticallContract, abi: Abi };

class Curve implements ICurve {
export class Curve implements ICurve {
provider: ethers.BrowserProvider | ethers.JsonRpcProvider;
isNoRPC: boolean;
multicallProvider: MulticallProvider;
Expand All @@ -106,16 +111,13 @@ class Curve implements ICurve {
constants: INetworkConstants;

constructor() {
// @ts-ignore
this.provider = null;
// @ts-ignore
this.provider = null!;
this.signer = null;
this.isNoRPC = false;
this.signerAddress = '';
this.chainId = 1;
this.isLiteChain = false;
// @ts-ignore
this.multicallProvider = null;
this.multicallProvider = null!;
this.contracts = {};
this.feeData = {}
this.constantOptions = { gasLimit: 12000000 }
Expand Down Expand Up @@ -149,15 +151,12 @@ class Curve implements ICurve {
providerSettings: { url?: string, privateKey?: string, batchMaxCount? : number } | { externalProvider: ethers.Eip1193Provider } | { network?: Networkish, apiKey?: string } | 'NoRPC',
options: { gasPrice?: number, maxFeePerGas?: number, maxPriorityFeePerGas?: number, chainId?: number } = {} // gasPrice in Gwei
): Promise<void> {
// @ts-ignore
this.provider = null;
// @ts-ignore
this.provider = null!;
this.signer = null;
this.isNoRPC = false;
this.signerAddress = '';
this.chainId = 1;
// @ts-ignore
this.multicallProvider = null;
this.multicallProvider = null!;
this.contracts = {};
this.feeData = {}
this.constantOptions = { gasLimit: 12000000 }
Expand Down Expand Up @@ -210,7 +209,7 @@ class Curve implements ICurve {
} else if (!providerSettings.url?.startsWith("https://rpc.gnosischain.com")) {
try {
this.signer = await this.provider.getSigner();
} catch (e) {
} catch {
this.signer = null;
}
}
Expand Down Expand Up @@ -245,7 +244,7 @@ class Curve implements ICurve {

this.isLiteChain = !(this.chainId in NETWORK_CONSTANTS);

const network_constants = await getNetworkConstants(this.chainId);
const network_constants = await getNetworkConstants.call(this, this.chainId);
this.constants.NATIVE_TOKEN = network_constants.NATIVE_COIN;
this.constants.NETWORK_NAME = network_constants.NAME;
this.constants.ALIASES = network_constants.ALIASES;
Expand All @@ -258,7 +257,7 @@ class Curve implements ICurve {
this.constants.DECIMALS = extractDecimals({...this.constants.POOLS_DATA, ...this.constants.LLAMMAS_DATA});
this.constants.DECIMALS[this.constants.NATIVE_TOKEN.address] = 18;
this.constants.DECIMALS[this.constants.NATIVE_TOKEN.wrappedAddress] = 18;
this.constants.GAUGES = extractGauges(this.constants.POOLS_DATA);
this.constants.GAUGES = extractGauges.call(this, this.constants.POOLS_DATA);

if(this.isLiteChain) {
this.constants.API_CONSTANTS = network_constants.API_CONSTANTS
Expand Down Expand Up @@ -287,7 +286,7 @@ class Curve implements ICurve {
if (this.signer) {
try {
this.signerAddress = await this.signer.getAddress();
} catch (err) {
} catch {
this.signer = null;
}
} else {
Expand Down Expand Up @@ -438,47 +437,29 @@ class Curve implements ICurve {
curveInstance.setContract(curveInstance.constants.ALIASES.gas_oracle, gasOracleABI);
curveInstance.setContract(curveInstance.constants.ALIASES.gas_oracle_blob, gasOracleBlobABI);

// @ts-ignore
if(AbstractProvider.prototype.originalEstimate) {
// @ts-ignore
AbstractProvider.prototype.estimateGas = AbstractProvider.prototype.originalEstimate;
if('originalEstimate' in AbstractProvider.prototype) {
AbstractProvider.prototype.estimateGas = AbstractProvider.prototype.originalEstimate as any;
}

const originalEstimate = AbstractProvider.prototype.estimateGas;

const oldEstimate = async function(arg: any) {
// @ts-ignore
const originalEstimateFunc = originalEstimate.bind(this);

const gas = await originalEstimateFunc(arg);

return gas;
}
const oldEstimate = originalEstimate.bind(this)

//Override
const newEstimate = async function(arg: any) {
// @ts-ignore
const L2EstimateGas = originalEstimate.bind(this);

const L2EstimateGas = oldEstimate;
const L1GasUsed = await curveInstance.contracts[curveInstance.constants.ALIASES.gas_oracle_blob].contract.getL1GasUsed(arg.data);
const L1Fee = await curveInstance.contracts[curveInstance.constants.ALIASES.gas_oracle_blob].contract.getL1Fee(arg.data);

curveInstance.L1WeightedGasPrice = Number(L1Fee)/Number(L1GasUsed);

const L2GasUsed = await L2EstimateGas(arg);

return [L2GasUsed,L1GasUsed];
}

// @ts-ignore
AbstractProvider.prototype.estimateGas = newEstimate;
// @ts-ignore
AbstractProvider.prototype.originalEstimate = oldEstimate;
AbstractProvider.prototype.estimateGas = newEstimate as any;
(AbstractProvider.prototype as any).originalEstimate = oldEstimate;
} else {
// @ts-ignore
if(AbstractProvider.prototype.originalEstimate) {
// @ts-ignore
AbstractProvider.prototype.estimateGas = AbstractProvider.prototype.originalEstimate;
if('originalEstimate' in AbstractProvider.prototype) {
AbstractProvider.prototype.estimateGas = AbstractProvider.prototype.originalEstimate as any;
}
}
}
Expand Down Expand Up @@ -514,13 +495,12 @@ class Curve implements ICurve {

async _filterHiddenPools(pools: IDict<IPoolData>): Promise<IDict<IPoolData>> {
const hiddenPools = (await _getHiddenPools())[this.constants.NETWORK_NAME] || [];
// @ts-ignore
return Object.fromEntries(Object.entries(pools).filter(([id]) => !hiddenPools.includes(id)));
return Object.fromEntries(Object.entries(pools).filter(([id]) => !hiddenPools.includes(id))) as IDict<IPoolData>;
}

_updateDecimalsAndGauges(pools: IDict<IPoolData>): void {
this.constants.DECIMALS = { ...this.constants.DECIMALS, ...extractDecimals(pools) };
this.constants.GAUGES = [ ...this.constants.GAUGES, ...extractGauges(pools) ];
this.constants.GAUGES = [ ...this.constants.GAUGES, ...extractGauges.call(this, pools) ];
}

fetchFactoryPools = async (useApi = true): Promise<void> => {
Expand Down Expand Up @@ -808,11 +788,11 @@ class Curve implements ICurve {
}

formatUnits(value: BigNumberish, unit?: string | Numeric): string {
return ethers.formatUnits(value, unit);
return formatUnits(value, unit);
}

parseUnits(value: string, unit?: string | Numeric): bigint {
return ethers.parseUnits(value, unit);
return parseUnits(value, unit);
}

async updateFeeData(): Promise<void> {
Expand Down
Loading
Loading