Skip to content
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

Feat/support arb tokens on coingecko endpoint #1277

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions api/_constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ defaultRelayerFeeCapitalCostConfig["USDB"] = {
...defaultRelayerFeeCapitalCostConfig["DAI"],
};

export const coinGeckoAssetPlatformLookup: Record<string, string> = {
"0x4200000000000000000000000000000000000042": "optimistic-ethereum",
export const coinGeckoAssetPlatformLookup: Record<string, number> = {
"0x4200000000000000000000000000000000000042": CHAIN_IDs.OPTIMISM,
};

export const defaultRelayerAddressOverride: {
Expand Down Expand Up @@ -159,7 +159,10 @@ export const BLOCK_TAG_LAG = -1;
// we've decided to keep this list small for now.
export const SUPPORTED_CG_BASE_CURRENCIES = new Set(["eth", "usd"]);
// Note: this is a small set of currencies that the API will derive from the base currencies by using USD as an intermediary.
export const SUPPORTED_CG_DERIVED_CURRENCIES = new Set(["matic"]);
export const SUPPORTED_CG_DERIVED_CURRENCIES = new Set(["azero", "matic"]);
export const CG_CONTRACTS_DEFERRED_TO_ID = new Set([
TOKEN_SYMBOLS_MAP.AZERO.addresses[CHAIN_IDs.MAINNET],
]);

// 1:1 because we don't need to handle underlying tokens on FE
export const EXTERNAL_POOL_TOKEN_EXCHANGE_RATE = utils.fixedPointAdjustment;
Expand All @@ -181,6 +184,10 @@ export const ENABLED_POOLS_UNDERLYING_TOKENS = [
export const SECONDS_PER_YEAR = 365 * 24 * 60 * 60;

export const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
export const MULTICALL3_ADDRESS_OVERRIDES = {
[CHAIN_IDs.ALEPH_ZERO]: "0x3CA11702f7c0F28e0b4e03C31F7492969862C569",
[CHAIN_IDs.ZK_SYNC]: "0xF9cda624FBC7e059355ce98a31693d299FACd963",
};

export const DEFI_LLAMA_POOL_LOOKUP: Record<string, string> = {
"0x36Be1E97eA98AB43b4dEBf92742517266F5731a3":
Expand Down
37 changes: 30 additions & 7 deletions api/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@ import {
utils,
Signer,
} from "ethers";
import { define } from "superstruct";
import {
assert,
coerce,
create,
define,
Infer,
number,
string,
Struct,
} from "superstruct";

import enabledMainnetRoutesAsJson from "../src/data/routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json";
import enabledSepoliaRoutesAsJson from "../src/data/routes_11155111_0x14224e63716afAcE30C9a417E0542281869f7d9e.json";
Expand All @@ -36,7 +45,7 @@ import {
} from "./_abis";
import { BatchAccountBalanceResponse } from "./batch-account-balance";
import { StaticJsonRpcProvider } from "@ethersproject/providers";
import { VercelResponse } from "@vercel/node";
import { VercelRequestQuery, VercelResponse } from "@vercel/node";
import {
BLOCK_TAG_LAG,
CHAIN_IDs,
Expand All @@ -46,6 +55,7 @@ import {
DOMAIN_CALLDATA_DELIMITER,
EXTERNAL_POOL_TOKEN_EXCHANGE_RATE,
MULTICALL3_ADDRESS,
MULTICALL3_ADDRESS_OVERRIDES,
SECONDS_PER_YEAR,
TOKEN_SYMBOLS_MAP,
defaultRelayerAddressOverride,
Expand Down Expand Up @@ -1181,6 +1191,21 @@ export function applyMapFilter<InputType, MapType>(
}, []);
}

export const coercibleInt = coerce(number(), string(), (value) =>
parseInt(value)
);

// parses, coerces and validates query params
export function parseQuery<
Q extends VercelRequestQuery,
S extends Struct<any, any>,
>(query: Q, schema: S): Infer<S> {
const coerced = create(query, schema);

assert(coerced, schema);
return coerced;
}

/* ------------------------- superstruct validators ------------------------- */

export function parsableBigNumberString() {
Expand Down Expand Up @@ -1621,8 +1646,9 @@ export async function callViaMulticall3(
}[],
overrides: ethers.CallOverrides = {}
): Promise<ethers.utils.Result[]> {
const chainId = provider.network.chainId;
const multicall3 = new ethers.Contract(
MULTICALL3_ADDRESS,
MULTICALL3_ADDRESS_OVERRIDES[chainId] ?? MULTICALL3_ADDRESS,
MINIMAL_MULTICALL3_ABI,
provider
);
Expand Down Expand Up @@ -1905,10 +1931,7 @@ export function getCachedFillGasUsage(
);
const { nativeGasCost } = await relayerFeeCalculatorQueries.getGasCosts(
buildDepositForSimulation(deposit),
overrides?.relayerAddress,
undefined,
undefined,
true
overrides?.relayerAddress
);
return nativeGasCost;
};
Expand Down
52 changes: 36 additions & 16 deletions api/coingecko.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { VercelResponse } from "@vercel/node";
import { ethers } from "ethers";
import { object, assert, Infer, optional, string, pattern } from "superstruct";
import { object, Infer, optional, string, pattern } from "superstruct";
import { TypedVercelRequest } from "./_types";
import {
getLogger,
handleErrorCondition,
validAddress,
getBalancerV2TokenPrice,
getCachedTokenPrice,
coercibleInt,
parseQuery,
} from "./_utils";
import {
CG_CONTRACTS_DEFERRED_TO_ID,

Check warning on line 15 in api/coingecko.ts

View workflow job for this annotation

GitHub Actions / format-and-lint

'CG_CONTRACTS_DEFERRED_TO_ID' is defined but never used
CHAIN_IDs,
SUPPORTED_CG_BASE_CURRENCIES,
SUPPORTED_CG_DERIVED_CURRENCIES,
Expand All @@ -28,7 +31,9 @@
} = process.env;

const CoingeckoQueryParamsSchema = object({
l1Token: validAddress(),
l1Token: optional(validAddress()),
tokenAddress: optional(validAddress()),
chainId: optional(coercibleInt),
baseCurrency: optional(string()),
date: optional(pattern(string(), /\d{2}-\d{2}-\d{4}/)),
});
Expand All @@ -46,13 +51,27 @@
query,
});
try {
assert(query, CoingeckoQueryParamsSchema);

let { l1Token, baseCurrency, date: dateStr } = query;
const parsed = parseQuery(query, CoingeckoQueryParamsSchema);

let {
l1Token,
tokenAddress,
chainId,
baseCurrency,
date: dateStr,
} = parsed;

let address = l1Token ?? tokenAddress;
if (!address) {
throw new InvalidParamError({
message: `Token Address is undefined. You must define either "l1Token", or "tokenAddress"`,
param: "l1Token, tokenAddress",
});
}

// Format the params for consistency
baseCurrency = (baseCurrency ?? "eth").toLowerCase();
l1Token = ethers.utils.getAddress(l1Token);
address = ethers.utils.getAddress(address);

// Confirm that the base Currency is supported by Coingecko
const isDerivedCurrency = SUPPORTED_CG_DERIVED_CURRENCIES.has(baseCurrency);
Expand All @@ -74,8 +93,8 @@

// Perform a 1-deep lookup to see if the provided l1Token is
// to be "redirected" to another provided token contract address
if (redirectLookupAddresses[l1Token]) {
l1Token = redirectLookupAddresses[l1Token];
if (redirectLookupAddresses[address]) {
address = redirectLookupAddresses[address];
}

const coingeckoClient = Coingecko.get(
Expand All @@ -90,17 +109,17 @@
BALANCER_V2_TOKENS ?? "[]"
).map(ethers.utils.getAddress);

const platformId = coinGeckoAssetPlatformLookup[l1Token] ?? "ethereum";
chainId = coinGeckoAssetPlatformLookup[address] ?? chainId ?? 1;

if (balancerV2PoolTokens.includes(ethers.utils.getAddress(l1Token))) {
if (balancerV2PoolTokens.includes(ethers.utils.getAddress(address))) {
if (dateStr) {
throw new InvalidParamError({
message: "Historical price not supported for BalancerV2 tokens",
param: "date",
});
}
if (baseCurrency === "usd") {
price = await getBalancerV2TokenPrice(l1Token);
price = await getBalancerV2TokenPrice(address);
} else {
throw new InvalidParamError({
message: "Only CG base currency allowed for BalancerV2 tokens is usd",
Expand All @@ -112,19 +131,20 @@
// date is provided, fetch historical price. Otherwise, fetch
// current price.
else {
// // If derived, we need to convert to USD first.
// If derived, we need to convert to USD first.
const modifiedBaseCurrency = isDerivedCurrency ? "usd" : baseCurrency;
if (dateStr) {
price = await coingeckoClient.getContractHistoricDayPrice(
l1Token,
address,
dateStr,
modifiedBaseCurrency
modifiedBaseCurrency,
chainId
);
} else {
[, price] = await coingeckoClient.getCurrentPriceByContract(
l1Token,
address,
modifiedBaseCurrency,
platformId
chainId
);
}
}
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"private": true,
"license": "AGPL-3.0-only",
"dependencies": {
"@across-protocol/constants": "^3.1.16",
"@across-protocol/contracts": "^3.0.11",
"@across-protocol/constants": "^3.1.20",
"@across-protocol/contracts": "^3.0.16",
"@across-protocol/contracts-v3.0.6": "npm:@across-protocol/[email protected]",
"@across-protocol/sdk": "^3.2.9",
"@across-protocol/sdk": "^3.2.14",
"@amplitude/analytics-browser": "^2.3.5",
"@balancer-labs/sdk": "1.1.6-beta.16",
"@emotion/react": "^11.13.0",
Expand Down
2 changes: 1 addition & 1 deletion scripts/chain-configs/optimism-sepolia/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default {
},
chainId,
publicRpcUrl: "https://sepolia.optimism.io",
tokens: [],
tokens: ["ETH", "WETH", "USDC"],
enableCCTP: false,
swapTokens: [],
} as ChainConfig;
8 changes: 4 additions & 4 deletions scripts/generate-routes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "@across-protocol/constants";
import { utils as sdkUtils } from "@across-protocol/sdk";

import { utils } from "ethers";
import { writeFileSync } from "fs";
import * as prettier from "prettier";
Expand Down Expand Up @@ -270,12 +271,11 @@ function transformChainConfigs(
];
}

// Handle WETH Polygon
// Handle WETH Polygon & other non-eth chains
if (
tokenSymbol === "WETH" &&
[CHAIN_IDs.POLYGON, CHAIN_IDs.POLYGON_AMOY].includes(
toChainConfig.chainId
)
!toChainConfig.tokens.includes("ETH") &&
chainConfig.tokens.includes("ETH")
) {
return ["WETH", "ETH"];
}
Expand Down
48 changes: 46 additions & 2 deletions src/data/chains_11155111.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,52 @@
"logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/scripts/chain-configs/optimism/assets/logo.svg",
"spokePool": "0x4e8E101924eDE233C13e2D8622DC8aED2872d505",
"spokePoolBlock": 7762656,
"inputTokens": [],
"outputTokens": []
"inputTokens": [
{
"address": "0x4200000000000000000000000000000000000006",
"symbol": "ETH",
"name": "Ether",
"decimals": 18,
"logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/src/assets/token-logos/eth.svg"
},
{
"address": "0x4200000000000000000000000000000000000006",
"symbol": "WETH",
"name": "Wrapped Ether",
"decimals": 18,
"logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/src/assets/token-logos/weth.svg"
},
{
"address": "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
"symbol": "USDC",
"name": "USD Coin",
"decimals": 6,
"logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/src/assets/token-logos/usdc.svg"
}
],
"outputTokens": [
{
"address": "0x4200000000000000000000000000000000000006",
"symbol": "WETH",
"name": "Wrapped Ether",
"decimals": 18,
"logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/src/assets/token-logos/weth.svg"
},
{
"address": "0x4200000000000000000000000000000000000006",
"symbol": "ETH",
"name": "Ether",
"decimals": 18,
"logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/src/assets/token-logos/eth.svg"
},
{
"address": "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
"symbol": "USDC",
"name": "USD Coin",
"decimals": 6,
"logoUrl": "https://raw.githubusercontent.com/across-protocol/frontend/master/src/assets/token-logos/usdc.svg"
}
]
},
{
"chainId": 919,
Expand Down
Loading
Loading