Skip to content

Commit

Permalink
feat: add fetch curve lite networks
Browse files Browse the repository at this point in the history
  • Loading branch information
fedorovdg committed Oct 30, 2024
1 parent a4a4af7 commit 85c6c2b
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 72 deletions.
130 changes: 86 additions & 44 deletions src/external-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
IDaoProposal,
IDaoProposalListItem,
IVolumeAndAPYs,
ICurveLiteNetwork,
} from "./interfaces";


Expand Down Expand Up @@ -240,48 +241,89 @@ export const _getDaoProposal = memoize(async (type: "PARAMETER" | "OWNERSHIP", i

// --- CURVE LITE ---

export const _getLiteNetworksData = memoize(async (chainId: number): Promise<any> => {
const network_name = "arbitrum-sepolia";
const native_currency_symbol = "ETH";
const wrapped_letter = native_currency_symbol[0].toLowerCase() === native_currency_symbol[0] ? "w" : "W";
const wrapped_native_token = '0x980B62Da83eFf3D4576C647993b0c1D7faf17c73'.toLowerCase();

const stable_ng_factory = "0x5eeE3091f747E60a045a2E715a4c71e600e31F6E".toLowerCase();
const twocrypto_factory = "0x98EE851a00abeE0d95D08cF4CA2BdCE32aeaAF7F".toLowerCase();
const tricrypto_factory = "0x0C9D8c7e486e822C29488Ff51BFf0167B4650953".toLowerCase();
const gauge_factory = "0xB4c6A1e8A14e9Fe74c88b06275b747145DD41206".toLowerCase();

const router = "0x148ac020221D4690457812b2AE23f6Ba5001DDCf".toLowerCase();
const deposit_and_stake = "0xFfd9A3490B5E0F4f19D917048C5362Ef80919C7B".toLowerCase();
const stable_ng_meta_zap = "0xcb38785B2CceD9B40F6C5120BC8e803d3a884977".toLowerCase();

const crv = "0x50FB01Ee521b9D22cdcb713a505019f41b8BBFf4".toLowerCase();


return {
NAME: network_name,
ALIASES: {
stable_ng_factory,
twocrypto_factory,
tricrypto_factory,
"child_gauge_factory": gauge_factory,
"root_gauge_factory": gauge_factory,

router,
deposit_and_stake,
stable_ng_meta_zap,

crv,
},
NATIVE_COIN: {
symbol: native_currency_symbol,
address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
wrappedSymbol: wrapped_letter + native_currency_symbol,
wrappedAddress: wrapped_native_token.toLowerCase(),
},
export const _getLiteNetworksData = memoize(
async (chainId: number): Promise<any> => {
try {
const url = `https://api-core.curve.fi/v1/getNetworkData/${chainId}`;
const response = await axios.get(url, { validateStatus: () => true });

if (response.status !== 200 || !response.data?.data) {
console.error('Failed to fetch network data:', response);
return null;
}

const { config, contracts } = response.data.data;

const network_name = config.network_name || 'Unknown Network';
const native_currency_symbol = config.native_currency_symbol || 'N/A';
const wrapped_native_token = config.wrapped_native_token?.toLowerCase() || '';

return {
NAME: network_name,
ALIASES: {
stable_ng_factory: contracts.amm.stableswap.factory.address.toLowerCase(),
twocrypto_factory: contracts.amm.twocryptoswap.factory.address.toLowerCase(),
tricrypto_factory: contracts.amm.tricryptoswap.factory.address.toLowerCase(),
child_gauge_factory: contracts.gauge.child_gauge.factory.address.toLowerCase(),
root_gauge_factory: contracts.gauge.child_gauge.factory.address.toLowerCase(),

router: contracts.helpers.router.address.toLowerCase(),
deposit_and_stake: contracts.helpers.deposit_and_stake_zap.address.toLowerCase(),
stable_ng_meta_zap: contracts.helpers.stable_swap_meta_zap.address.toLowerCase(),

crv: config.dao.crv.toLowerCase(),
},
NATIVE_COIN: {
symbol: native_currency_symbol,
address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
wrappedSymbol:
native_currency_symbol[0].toLowerCase() === native_currency_symbol[0]
? `w${native_currency_symbol}`
: `W${native_currency_symbol}`,
wrappedAddress: wrapped_native_token,
},
};
} catch (error) {
console.error('Error fetching network data:', error);
return null;
}
},
{
promise: true,
maxAge: 5 * 60 * 1000, // 5 minutes
}
},
{
promise: true,
maxAge: 5 * 60 * 1000, // 5m
})
);

export const _getCurveLiteNetworks = memoize(
async (): Promise<ICurveLiteNetwork[]> => {
const url = `https://api-core.curve.fi/v1/getPlatforms`;
const response = await axios.get(url, { validateStatus: () => true });

if (response.status !== 200 || !response.data?.data?.platforms) {
console.error('Failed to fetch Curve platforms:', response);
return [];
}

const { platforms, platformsMetadata } = response.data.data;

const networks: ICurveLiteNetwork[] = Object.entries(platforms)
.map(([platformId, _factories]) => {
const metadata = platformsMetadata[platformId];
if (!metadata) return null;

return {
id: platformId,
name: metadata.name,
rpcUrl: metadata.rpcUrl,
explorerUrl: metadata.explorerBaseUrl,
};
})
.filter((network): network is ICurveLiteNetwork => network !== null);

return networks;
},
{
promise: true,
maxAge: 5 * 60 * 1000, // 5 minutes
}
);
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import {
hasDepositAndStake,
hasRouter,
getBasePools,
getGasPrice,
getGasPrice, getCurveLiteNetworks,
} from "./utils.js";
import {
deployStablePlainPool,
Expand Down Expand Up @@ -184,6 +184,7 @@ const curve = {
getVolume,
hasDepositAndStake,
hasRouter,
getCurveLiteNetworks,
factory: {
fetchPools: _curve.fetchFactoryPools,
fetchNewPools: _curve.fetchNewFactoryPools,
Expand Down
7 changes: 7 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ export interface IBasePoolShortItem {
token: string,
}

export interface ICurveLiteNetwork {
id: string
name: string
rpcUrl: string
explorerUrl: string
}

export type TVoteType = "PARAMETER" | "OWNERSHIP"

export type AbiParameter = { type: string, name?:string, components?: readonly AbiParameter[] }
Expand Down
4 changes: 2 additions & 2 deletions src/pools/PoolTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export class PoolTemplate extends CorePool {
swapWrappedApprove: (inputCoin: string | number, amount: number | string) => Promise<number | number[]>,
swapWrapped: (inputCoin: string | number, outputCoin: string | number, amount: number | string, slippage: number) => Promise<number | number[]>,
};
stats: IStatsPool;
wallet: IWalletPool;
stats: StatsPool;
wallet: WalletPool;

constructor(id: string) {
super(id);
Expand Down
6 changes: 3 additions & 3 deletions src/pools/mixins/depositBalancedAmountsMixins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const depositBalancedAmountsMixin: PoolTemplate = {
// @ts-ignore
const poolBalances = await this.stats.underlyingBalances();
// @ts-ignore
const walletBalances = Object.values(await this.walletUnderlyingCoinBalances());
const walletBalances = Object.values(await this.wallet.underlyingCoinBalances());
const balancedAmountsBN = (_depositBalancedAmounts(poolBalances, walletBalances, this.underlyingDecimals));

return balancedAmountsBN.map((b, i) => BigNumber.min(BN(b), BN(walletBalances[i])).toString());
Expand All @@ -40,7 +40,7 @@ export const depositBalancedAmountsCryptoMixin: PoolTemplate = {
// @ts-ignore
const poolBalances = await this.stats.underlyingBalances();
// @ts-ignore
const walletBalances = Object.values(await this.walletUnderlyingCoinBalances());
const walletBalances = Object.values(await this.wallet.underlyingCoinBalances());
// @ts-ignore
const prices = await this._underlyingPrices();
const poolBalancesUSD = poolBalances.map((b, i) => BN(b).times(prices[i]).toString());
Expand All @@ -57,7 +57,7 @@ export const depositWrappedBalancedAmountsMixin: PoolTemplate = {
// @ts-ignore
const poolBalances = await this.stats.wrappedBalances();
// @ts-ignore
const walletBalances = Object.values(await this.walletWrappedCoinBalances());
const walletBalances = Object.values(await this.wallet.wrappedCoinBalances());
const balancedAmountsBN = (_depositBalancedAmounts(poolBalances, walletBalances, this.underlyingDecimals));

return balancedAmountsBN.map((b, i) => BigNumber.min(BN(b), BN(walletBalances[i])).toString());
Expand Down
27 changes: 14 additions & 13 deletions src/pools/mixins/poolBalancesMixin.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
import { curve } from "../../curve.js";
import { PoolTemplate } from "../PoolTemplate.js";
import { _calcExpectedAmounts, _calcExpectedUnderlyingAmountsMeta } from "./common.js";
import {IStatsPool} from "../subClasses/statsPool";


// @ts-ignore
export const poolBalancesMetaMixin: PoolTemplate = {
async statsUnderlyingBalances(): Promise<string[]> {
const swapContract = curve.contracts[this.address].multicallContract;
const contractCalls = this.wrappedCoins.map((_, i) => swapContract.balances(i));
export const poolBalancesMetaMixin: IStatsPool = {
async underlyingBalances(): Promise<string[]> {
const swapContract = curve.contracts[this.pool.address].multicallContract;
const contractCalls = this.pool.wrappedCoins.map((_, i) => swapContract.balances(i));
const _poolWrappedBalances: bigint[] = await curve.multicallProvider.all(contractCalls);
const [_poolMetaCoinBalance] = _poolWrappedBalances.splice(this.metaCoinIdx, 1);
const [_poolMetaCoinBalance] = _poolWrappedBalances.splice(this.pool.metaCoinIdx, 1);
const _poolUnderlyingBalances = _poolWrappedBalances;
const basePool = new PoolTemplate(this.basePool);
const basePool = new PoolTemplate(this.pool.basePool);
const _basePoolExpectedAmounts = basePool.isMeta ?
await _calcExpectedUnderlyingAmountsMeta.call(basePool, _poolMetaCoinBalance) :
await _calcExpectedAmounts.call(basePool, _poolMetaCoinBalance);
_poolUnderlyingBalances.splice(this.metaCoinIdx, 0, ..._basePoolExpectedAmounts);
_poolUnderlyingBalances.splice(this.pool.metaCoinIdx, 0, ..._basePoolExpectedAmounts);

return _poolUnderlyingBalances.map((_b: bigint, i: number) => curve.formatUnits(_b, this.underlyingDecimals[i]))
return _poolUnderlyingBalances.map((_b: bigint, i: number) => curve.formatUnits(_b, this.pool.underlyingDecimals[i]))
},
}

// @ts-ignore
export const poolBalancesLendingMixin: PoolTemplate = {
async statsUnderlyingBalances(): Promise<string[]> {
const swapContract = curve.contracts[this.address].multicallContract;
const contractCalls = this.wrappedCoins.map((_, i) => swapContract.balances(i));
export const poolBalancesLendingMixin: IStatsPool = {
async underlyingBalances(): Promise<string[]> {
const swapContract = curve.contracts[this.pool.address].multicallContract;
const contractCalls = this.pool.wrappedCoins.map((_, i) => swapContract.balances(i));
const _poolWrappedBalances: bigint[] = await curve.multicallProvider.all(contractCalls);

// @ts-ignore
const _rates: bigint[] = await this._getRates();
const _poolUnderlyingBalances = _poolWrappedBalances.map(
(_b: bigint, i: number) => _b * _rates[i] / curve.parseUnits(String(10**18), 0));

return _poolUnderlyingBalances.map((_b: bigint, i: number) => curve.formatUnits(_b, this.underlyingDecimals[i]))
return _poolUnderlyingBalances.map((_b: bigint, i: number) => curve.formatUnits(_b, this.pool.underlyingDecimals[i]))
},
}
23 changes: 17 additions & 6 deletions src/pools/poolConstructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,28 @@ import {
swapWrappedRequiredMixin,
} from "./mixins/swapWrappedMixins.js";
import { getCountArgsOfMethodByAbi, findAbiSignature } from "../utils.js";
import {StatsPool} from "./subClasses/statsPool";


export const getPool = (poolId: string): PoolTemplate => {
const poolDummy = new PoolTemplate(poolId);
class Pool extends PoolTemplate {}
class Pool extends PoolTemplate {
stats: StatsPool;

// statsBalances
if (poolDummy.isMeta) {
Object.assign(Pool.prototype, poolBalancesMetaMixin);
} else if (poolDummy.useLending.reduce((x, y) => x || y)) {
Object.assign(Pool.prototype, poolBalancesLendingMixin);
constructor(poolId: string) {
super(poolId);
this.stats = new StatsPool(this);

this.configureStats(poolDummy);
}

private configureStats(poolDummy: PoolTemplate) {
if (poolDummy.isMeta) {
Object.assign(this.stats, poolBalancesMetaMixin);
} else if (poolDummy.useLending.reduce((x, y) => x || y)) {
Object.assign(this.stats, poolBalancesLendingMixin);
}
}
}

// depositBalancedAmounts
Expand Down
3 changes: 2 additions & 1 deletion src/pools/subClasses/statsPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface IStatsParameters {
}

export interface IStatsPool {
pool: PoolTemplate;
parameters(): Promise<IStatsParameters>;
underlyingBalances(): Promise<string[]>;
wrappedBalances(): Promise<string[]>;
Expand All @@ -38,7 +39,7 @@ export interface IStatsPool {
}

export class StatsPool implements IStatsPool {
private pool: PoolTemplate;
pool: PoolTemplate;

constructor(pool: PoolTemplate) {
this.pool = pool;
Expand Down
14 changes: 12 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import BigNumber from 'bignumber.js';
import {
Abi, AbiFunction,
IBasePoolShortItem,
IChainId,
IChainId, ICurveLiteNetwork,
IDict,
INetworkName,
IRewardFromApi,
Expand All @@ -14,7 +14,13 @@ import {
} from './interfaces';
import { curve } from "./curve.js";
import { NETWORK_CONSTANTS } from "./constants/network_constants.js";
import {_getAllPoolsFromApi, _getFactoryAPYs, _getSubgraphData, _getVolumes} from "./external-api.js";
import {
_getAllPoolsFromApi,
_getCurveLiteNetworks,
_getFactoryAPYs,
_getSubgraphData,
_getVolumes

Check failure on line 22 in src/utils.ts

View workflow job for this annotation

GitHub Actions / test

Missing trailing comma
} from "./external-api.js";
import ERC20Abi from './constants/abis/ERC20.json' assert {type: 'json'};
import {L2Networks} from './constants/L2Networks.js';
import {volumeNetworks} from "./constants/volumeNetworks.js";
Expand Down Expand Up @@ -824,6 +830,10 @@ export function runWorker<In extends { type: string }, Out>(code: string, syncFn
});
}

export const getCurveLiteNetworks = async (): Promise<ICurveLiteNetwork[]> => {
return await _getCurveLiteNetworks()
}

export const PERIODS = {
DAY: 86400,
WEEK: 604800, // 7 * 86400
Expand Down

0 comments on commit 85c6c2b

Please sign in to comment.