From 22900ac1480ca0f939706085eb563fe6303778e5 Mon Sep 17 00:00:00 2001 From: nigiri <168690269+0xnigir1@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:06:18 -0300 Subject: [PATCH] feat: add whitelist tokens (#13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 🤖 Linear Closes GIT-111 GIT-66 ## Description - copies Tokens whitelist per chain to Shared package - modifies `PricingProvider` interface to use `TokenCode` in place of (addreess, chainId) - updates internal CoingeckoProvider to map `TokenCode` to `CoingeckoID` - completes TODO in `PoolCreatedHandler` to fetch Token given the Chain and TokenAddress from Event ## Checklist before requesting a review - [x] I have conducted a self-review of my code. - [x] I have conducted a QA. - [ ] If it is a core feature, I have included comprehensive tests. --- packages/pricing/src/exceptions/index.ts | 1 + .../exceptions/unsupportedToken.exception.ts | 7 + packages/pricing/src/external.ts | 1 + .../src/interfaces/pricing.interface.ts | 13 +- .../src/providers/coingecko.provider.ts | 93 +-- .../test/providers/coingecko.provider.spec.ts | 69 +- .../src/allo/handlers/poolCreated.handler.ts | 24 +- .../allo/handlers/poolCreated.handler.spec.ts | 84 ++- packages/shared/src/external.ts | 3 + packages/shared/src/internal.ts | 1 + packages/shared/src/tokens/tokens.ts | 608 ++++++++++++++++++ 11 files changed, 760 insertions(+), 144 deletions(-) create mode 100644 packages/pricing/src/exceptions/unsupportedToken.exception.ts create mode 100644 packages/shared/src/tokens/tokens.ts diff --git a/packages/pricing/src/exceptions/index.ts b/packages/pricing/src/exceptions/index.ts index 63732e0..1ea09ef 100644 --- a/packages/pricing/src/exceptions/index.ts +++ b/packages/pricing/src/exceptions/index.ts @@ -1,3 +1,4 @@ export * from "./network.exception.js"; export * from "./unsupportedChain.exception.js"; export * from "./unknownPricing.exception.js"; +export * from "./unsupportedToken.exception.js"; diff --git a/packages/pricing/src/exceptions/unsupportedToken.exception.ts b/packages/pricing/src/exceptions/unsupportedToken.exception.ts new file mode 100644 index 0000000..e071acb --- /dev/null +++ b/packages/pricing/src/exceptions/unsupportedToken.exception.ts @@ -0,0 +1,7 @@ +import { TokenCode } from "@grants-stack-indexer/shared"; + +export class UnsupportedToken extends Error { + constructor(tokenCode: TokenCode) { + super(`Unsupported token: ${tokenCode}`); + } +} diff --git a/packages/pricing/src/external.ts b/packages/pricing/src/external.ts index 0585585..ef38d90 100644 --- a/packages/pricing/src/external.ts +++ b/packages/pricing/src/external.ts @@ -5,4 +5,5 @@ export { UnsupportedChainException, NetworkException, UnknownPricingException, + UnsupportedToken, } from "./internal.js"; diff --git a/packages/pricing/src/interfaces/pricing.interface.ts b/packages/pricing/src/interfaces/pricing.interface.ts index ab3e214..c9f22d1 100644 --- a/packages/pricing/src/interfaces/pricing.interface.ts +++ b/packages/pricing/src/interfaces/pricing.interface.ts @@ -1,27 +1,24 @@ -import { Address } from "@grants-stack-indexer/shared"; +import { TokenCode } from "@grants-stack-indexer/shared"; import { TokenPrice } from "../internal.js"; /** * Represents a pricing service that retrieves token prices. - * @dev is service responsibility to map address to their internal ID - * @dev for native token (eg. ETH), use the one address + * @dev is service responsibility to map token code to their internal platform ID */ export interface IPricingProvider { /** * Retrieves the price of a token at a timestamp range. - * @param chainId - The ID of the blockchain network. - * @param tokenAddress - The address of the token. + * @param tokenCode - The code of the token. * @param startTimestampMs - The start timestamp for which to retrieve the price. * @param endTimestampMs - The end timestamp for which to retrieve the price. * @returns A promise that resolves to the price of the token at the specified timestamp or undefined if no price is found. - * @throws {UnsupportedChainException} if the chain ID is not supported by the pricing provider. + * @throws {UnsupportedToken} if the token is not supported by the pricing provider. * @throws {NetworkException} if the network is not reachable. * @throws {UnknownFetchException} if the pricing provider returns an unknown error. */ getTokenPrice( - chainId: number, - tokenAddress: Address, + tokenCode: TokenCode, startTimestampMs: number, endTimestampMs: number, ): Promise; diff --git a/packages/pricing/src/providers/coingecko.provider.ts b/packages/pricing/src/providers/coingecko.provider.ts index 2221740..2ff78e4 100644 --- a/packages/pricing/src/providers/coingecko.provider.ts +++ b/packages/pricing/src/providers/coingecko.provider.ts @@ -1,18 +1,16 @@ import { isNativeError } from "util/types"; import axios, { AxiosInstance, isAxiosError } from "axios"; -import { Address, isNativeToken } from "@grants-stack-indexer/shared"; +import { TokenCode } from "@grants-stack-indexer/shared"; import { IPricingProvider } from "../interfaces/index.js"; import { - CoingeckoPlatformId, CoingeckoPriceChartData, - CoingeckoSupportedChainId, CoingeckoTokenId, NetworkException, TokenPrice, UnknownPricingException, - UnsupportedChainException, + UnsupportedToken, } from "../internal.js"; type CoingeckoOptions = { @@ -25,32 +23,33 @@ const getApiTypeConfig = (apiType: "demo" | "pro"): { baseURL: string; authHeade ? { baseURL: "https://api.coingecko.com/api/v3", authHeader: "x-cg-demo-api-key" } : { baseURL: "https://pro-api.coingecko.com/api/v3/", authHeader: "x-cg-pro-api-key" }; -const platforms: { [key in CoingeckoSupportedChainId]: CoingeckoPlatformId } = { - 1: "ethereum" as CoingeckoPlatformId, - 10: "optimistic-ethereum" as CoingeckoPlatformId, - 100: "xdai" as CoingeckoPlatformId, - 250: "fantom" as CoingeckoPlatformId, - 42161: "arbitrum-one" as CoingeckoPlatformId, - 43114: "avalanche" as CoingeckoPlatformId, - 713715: "sei-network" as CoingeckoPlatformId, - 1329: "sei-network" as CoingeckoPlatformId, - 42: "lukso" as CoingeckoPlatformId, - 42220: "celo" as CoingeckoPlatformId, - 1088: "metis" as CoingeckoPlatformId, -}; - -const nativeTokens: { [key in CoingeckoSupportedChainId]: CoingeckoTokenId } = { - 1: "ethereum" as CoingeckoTokenId, - 10: "ethereum" as CoingeckoTokenId, - 100: "xdai" as CoingeckoTokenId, - 250: "fantom" as CoingeckoTokenId, - 42161: "ethereum" as CoingeckoTokenId, - 43114: "avalanche-2" as CoingeckoTokenId, - 713715: "sei-network" as CoingeckoTokenId, - 1329: "sei-network" as CoingeckoTokenId, - 42: "lukso-token" as CoingeckoTokenId, - 42220: "celo" as CoingeckoTokenId, - 1088: "metis-token" as CoingeckoTokenId, +const TokenMapping: { [key: string]: CoingeckoTokenId | undefined } = { + USDC: "usd-coin" as CoingeckoTokenId, + DAI: "dai" as CoingeckoTokenId, + ETH: "ethereum" as CoingeckoTokenId, + eBTC: "ebtc" as CoingeckoTokenId, + USDGLO: "glo-dollar" as CoingeckoTokenId, + GIST: "dai" as CoingeckoTokenId, + OP: "optimism" as CoingeckoTokenId, + LYX: "lukso-token-2" as CoingeckoTokenId, + WLYX: "wrapped-lyx-universalswaps" as CoingeckoTokenId, + XDAI: "xdai" as CoingeckoTokenId, + MATIC: "polygon-ecosystem-token" as CoingeckoTokenId, + DATA: "streamr" as CoingeckoTokenId, + FTM: "fantom" as CoingeckoTokenId, + GcV: undefined, + USDT: "tether" as CoingeckoTokenId, + LUSD: "liquity-usd" as CoingeckoTokenId, + MUTE: "mute" as CoingeckoTokenId, + GTC: "gitcoin" as CoingeckoTokenId, + METIS: "metis" as CoingeckoTokenId, + SEI: "sei-network" as CoingeckoTokenId, + ARB: "arbitrum" as CoingeckoTokenId, + CELO: "celo" as CoingeckoTokenId, + CUSD: "celo-dollar" as CoingeckoTokenId, + AVAX: "avalanche-2" as CoingeckoTokenId, + MTK: undefined, + WSEI: "wrapped-sei" as CoingeckoTokenId, }; /** @@ -80,13 +79,13 @@ export class CoingeckoProvider implements IPricingProvider { /* @inheritdoc */ async getTokenPrice( - chainId: number, - tokenAddress: Address, + tokenCode: TokenCode, startTimestampMs: number, endTimestampMs: number, ): Promise { - if (!this.isSupportedChainId(chainId)) { - throw new UnsupportedChainException(chainId); + const tokenId = TokenMapping[tokenCode]; + if (!tokenId) { + throw new UnsupportedToken(tokenCode); } if (startTimestampMs > endTimestampMs) { @@ -96,7 +95,7 @@ export class CoingeckoProvider implements IPricingProvider { const startTimestampSecs = Math.floor(startTimestampMs / 1000); const endTimestampSecs = Math.floor(endTimestampMs / 1000); - const path = this.getApiPath(chainId, tokenAddress, startTimestampSecs, endTimestampSecs); + const path = `/coins/${tokenId}/market_chart/range?vs_currency=usd&from=${startTimestampSecs}&to=${endTimestampSecs}&precision=full`; //TODO: handle retries try { @@ -130,28 +129,4 @@ export class CoingeckoProvider implements IPricingProvider { ); } } - - /* - * @returns Whether the given chain ID is supported by the Coingecko API. - */ - private isSupportedChainId(chainId: number): chainId is CoingeckoSupportedChainId { - return chainId in platforms; - } - - /* - * @returns The API endpoint path for the given parameters. - */ - private getApiPath( - chainId: CoingeckoSupportedChainId, - tokenAddress: Address, - startTimestampSecs: number, - endTimestampSecs: number, - ): string { - const platform = platforms[chainId]; - const nativeTokenId = nativeTokens[chainId]; - - return isNativeToken(tokenAddress) - ? `/coins/${nativeTokenId}/market_chart/range?vs_currency=usd&from=${startTimestampSecs}&to=${endTimestampSecs}&precision=full` - : `/coins/${platform}/contract/${tokenAddress.toLowerCase()}/market_chart/range?vs_currency=usd&from=${startTimestampSecs}&to=${endTimestampSecs}&precision=full`; - } } diff --git a/packages/pricing/test/providers/coingecko.provider.spec.ts b/packages/pricing/test/providers/coingecko.provider.spec.ts index ee7a5a3..07f4f2a 100644 --- a/packages/pricing/test/providers/coingecko.provider.spec.ts +++ b/packages/pricing/test/providers/coingecko.provider.spec.ts @@ -1,13 +1,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { Address, NATIVE_TOKEN_ADDRESS } from "@grants-stack-indexer/shared"; +import { TokenCode } from "@grants-stack-indexer/shared"; import type { TokenPrice } from "../../src/external.js"; -import { - CoingeckoProvider, - NetworkException, - UnsupportedChainException, -} from "../../src/external.js"; +import { CoingeckoProvider, NetworkException, UnsupportedToken } from "../../src/external.js"; const mock = vi.hoisted(() => ({ get: vi.fn(), @@ -53,32 +49,7 @@ describe("CoingeckoProvider", () => { mock.get.mockResolvedValueOnce({ status: 200, data: mockResponse }); const result = await provider.getTokenPrice( - 1, - "0x1234567890123456789012345678901234567890" as Address, - 1609459200000, - 1609545600000, - ); - - const expectedPrice: TokenPrice = { - timestampMs: 1609459200000, - priceUsd: 100, - }; - - expect(result).toEqual(expectedPrice); - expect(mock.get).toHaveBeenCalledWith( - "/coins/ethereum/contract/0x1234567890123456789012345678901234567890/market_chart/range?vs_currency=usd&from=1609459200&to=1609545600&precision=full", - ); - }); - - it("return token price for a supported chain and native token", async () => { - const mockResponse = { - prices: [[1609459200000, 100]], - }; - mock.get.mockResolvedValueOnce({ status: 200, data: mockResponse }); - - const result = await provider.getTokenPrice( - 10, - NATIVE_TOKEN_ADDRESS, + "ETH" as TokenCode, 1609459200000, 1609545600000, ); @@ -101,8 +72,7 @@ describe("CoingeckoProvider", () => { mock.get.mockResolvedValueOnce({ status: 200, data: mockResponse }); const result = await provider.getTokenPrice( - 1, - "0x1234567890123456789012345678901234567890" as Address, + "ETH" as TokenCode, 1609459200000, 1609545600000, ); @@ -112,8 +82,7 @@ describe("CoingeckoProvider", () => { it("return undefined when endTimestamp is greater than startTimestamp", async () => { const result = await provider.getTokenPrice( - 1, - "0x1234567890123456789012345678901234567890" as Address, + "ETH" as TokenCode, 1609545600000, // startTimestamp 1609459200000, // endTimestamp ); @@ -129,8 +98,7 @@ describe("CoingeckoProvider", () => { }); const result = await provider.getTokenPrice( - 1, - "0x1234567890123456789012345678901234567890" as Address, + "ETH" as TokenCode, 1609459200000, 1609545600000, ); @@ -138,15 +106,10 @@ describe("CoingeckoProvider", () => { expect(result).toBeUndefined(); }); - it("throw UnsupportedChainException for unsupported chain", async () => { + it("throw UnsupportedTokenException for unsupported token", async () => { await expect(() => - provider.getTokenPrice( - 999999, // Unsupported chain ID - "0x1234567890123456789012345678901234567890" as Address, - 1609459200000, - 1609545600000, - ), - ).rejects.toThrow(UnsupportedChainException); + provider.getTokenPrice("UNSUPPORTED" as TokenCode, 1609459200000, 1609545600000), + ).rejects.toThrow(UnsupportedToken); }); it("throws NetworkException for 500 family errors", async () => { @@ -156,12 +119,7 @@ describe("CoingeckoProvider", () => { isAxiosError: true, }); await expect( - provider.getTokenPrice( - 1, - "0x1234567890123456789012345678901234567890" as Address, - 1609459200000, - 1609545600000, - ), + provider.getTokenPrice("ETH" as TokenCode, 1609459200000, 1609545600000), ).rejects.toThrow(NetworkException); }); @@ -173,12 +131,7 @@ describe("CoingeckoProvider", () => { }); await expect( - provider.getTokenPrice( - 1, - "0x1234567890123456789012345678901234567890" as Address, - 1609459200000, - 1609545600000, - ), + provider.getTokenPrice("ETH" as TokenCode, 1609459200000, 1609545600000), ).rejects.toThrow(NetworkException); }); }); diff --git a/packages/processors/src/allo/handlers/poolCreated.handler.ts b/packages/processors/src/allo/handlers/poolCreated.handler.ts index 392b587..de6a6a8 100644 --- a/packages/processors/src/allo/handlers/poolCreated.handler.ts +++ b/packages/processors/src/allo/handlers/poolCreated.handler.ts @@ -1,8 +1,9 @@ -import { Address, getAddress, parseUnits, zeroAddress } from "viem"; +import { getAddress, parseUnits, zeroAddress } from "viem"; import type { Changeset, NewRound, PendingRoundRole } from "@grants-stack-indexer/repository"; -import type { ChainId, ProtocolEvent } from "@grants-stack-indexer/shared"; +import type { ChainId, ProtocolEvent, Token } from "@grants-stack-indexer/shared"; import { isAlloNativeToken } from "@grants-stack-indexer/shared"; +import { getToken } from "@grants-stack-indexer/shared/dist/src/internal.js"; import type { IEventHandler, ProcessorDependencies, StrategyTimings } from "../../internal.js"; import { getRoundRoles } from "../../helpers/roles.js"; @@ -17,7 +18,7 @@ type Dependencies = Pick< >; // sometimes coingecko returns no prices for 1 hour range, 2 hours works better -const TIMESTAMP_DELTA_RANGE = 2 * 60 * 60 * 1000; +export const TIMESTAMP_DELTA_RANGE = 2 * 60 * 60 * 1000; /** /** @@ -62,13 +63,7 @@ export class PoolCreatedHandler implements IEventHandler<"Allo", "PoolCreated"> const strategy = extractStrategyFromId(strategyId); - // TODO: get token for the chain - const token = { - address: matchTokenAddress, - decimals: 18, //TODO: get decimals from token - symbol: "USDC", //TODO: get symbol from token - name: "USDC", //TODO: get name from token - }; + const token = getToken(this.chainId, matchTokenAddress); let strategyTimings: StrategyTimings = { applicationsStartTime: null, @@ -87,7 +82,7 @@ export class PoolCreatedHandler implements IEventHandler<"Allo", "PoolCreated"> if ( strategy.name === "allov2.DonationVotingMerkleDistributionDirectTransferStrategy" && parsedRoundMetadata.success && - token !== null + token ) { matchAmount = parseUnits( parsedRoundMetadata.data.quadraticFundingConfig.matchingFundsAvailable.toString(), @@ -104,7 +99,7 @@ export class PoolCreatedHandler implements IEventHandler<"Allo", "PoolCreated"> let fundedAmountInUsd = "0"; - if (token !== null && fundedAmount > 0n) { + if (token && fundedAmount > 0n) { fundedAmountInUsd = await this.getTokenAmountInUsd( token, fundedAmount, @@ -206,14 +201,13 @@ export class PoolCreatedHandler implements IEventHandler<"Allo", "PoolCreated"> } private async getTokenAmountInUsd( - token: { address: Address; decimals: number }, + token: Token, amount: bigint, timestamp: number, ): Promise { const { pricingProvider } = this.dependencies; const tokenPrice = await pricingProvider.getTokenPrice( - this.chainId, - token.address, + token.priceSourceCode, timestamp, timestamp + TIMESTAMP_DELTA_RANGE, ); diff --git a/packages/processors/test/allo/handlers/poolCreated.handler.spec.ts b/packages/processors/test/allo/handlers/poolCreated.handler.spec.ts index 49b3d43..26acf4c 100644 --- a/packages/processors/test/allo/handlers/poolCreated.handler.spec.ts +++ b/packages/processors/test/allo/handlers/poolCreated.handler.spec.ts @@ -5,10 +5,13 @@ import type { EvmProvider } from "@grants-stack-indexer/chain-providers"; import type { IMetadataProvider } from "@grants-stack-indexer/metadata"; import type { IPricingProvider } from "@grants-stack-indexer/pricing"; import type { IRoundReadRepository, Round } from "@grants-stack-indexer/repository"; -import type { ChainId, DeepPartial, ProtocolEvent } from "@grants-stack-indexer/shared"; +import type { ChainId, DeepPartial, ProtocolEvent, TokenCode } from "@grants-stack-indexer/shared"; import { mergeDeep } from "@grants-stack-indexer/shared"; -import { PoolCreatedHandler } from "../../../src/allo/handlers/poolCreated.handler.js"; +import { + PoolCreatedHandler, + TIMESTAMP_DELTA_RANGE, +} from "../../../src/allo/handlers/poolCreated.handler.js"; // Function to create a mock event with optional overrides function createMockEvent( @@ -419,6 +422,79 @@ describe("PoolCreatedHandler", () => { ); }); - it.skip("handles a native token"); - it.skip("handles an unknown token"); + it("handles a native token", async () => { + const mockEvent = createMockEvent({ + params: { token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" }, + }); + + vi.spyOn(mockMetadataProvider, "getMetadata").mockResolvedValue({ + round: { + name: "Test Round", + roundType: "private", + quadraticFundingConfig: { + matchingFundsAvailable: 1, + }, + }, + application: { + version: "1.0.0", + }, + }); + vi.spyOn(mockPricingProvider, "getTokenPrice").mockResolvedValue({ + priceUsd: 2500, + timestampMs: 1708369911, + }); + vi.spyOn(mockEvmProvider, "multicall").mockResolvedValue([ + 1609459200n, + 1609459200n, + 1609459200n, + 1609459200n, + ]); + + vi.spyOn(mockRoundRepository, "getPendingRoundRoles").mockResolvedValue([]); + + const handler = new PoolCreatedHandler(mockEvent, 10 as ChainId, { + evmProvider: mockEvmProvider, + pricingProvider: mockPricingProvider, + metadataProvider: mockMetadataProvider, + roundRepository: mockRoundRepository, + }); + + await handler.handle(); + + expect(mockPricingProvider.getTokenPrice).toHaveBeenCalledWith( + "ETH" as TokenCode, + 1708369911, + 1708369911 + TIMESTAMP_DELTA_RANGE, + ); + }); + + it("handles an unknown token", async () => { + const fundedAmount = parseUnits("10", 18); + const mockEvent = createMockEvent({ + params: { + amount: fundedAmount, + token: "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", + strategyId: "0xunknown", + }, + }); + + vi.spyOn(mockRoundRepository, "getPendingRoundRoles").mockResolvedValue([]); + + const handler = new PoolCreatedHandler(mockEvent, 10 as ChainId, { + evmProvider: mockEvmProvider, + pricingProvider: mockPricingProvider, + metadataProvider: mockMetadataProvider, + roundRepository: mockRoundRepository, + }); + + const result = await handler.handle(); + + const changeset = result[0] as { type: "InsertRound"; args: { round: Round } }; + expect(changeset.type).toBe("InsertRound"); + expect(changeset.args.round).toMatchObject({ + fundedAmount: fundedAmount, + fundedAmountInUsd: "0", //since it's an unknown token + }); + expect(mockPricingProvider.getTokenPrice).not.toHaveBeenCalled(); + }); }); diff --git a/packages/shared/src/external.ts b/packages/shared/src/external.ts index 9b1b0af..05c7b32 100644 --- a/packages/shared/src/external.ts +++ b/packages/shared/src/external.ts @@ -13,3 +13,6 @@ export type { ILogger, Logger } from "./internal.js"; export { BigNumber } from "./internal.js"; export type { BigNumberType } from "./internal.js"; + +export type { TokenCode, Token } from "./internal.js"; +export { TOKENS } from "./tokens/tokens.js"; diff --git a/packages/shared/src/internal.ts b/packages/shared/src/internal.ts index a6a96fb..8a37a26 100644 --- a/packages/shared/src/internal.ts +++ b/packages/shared/src/internal.ts @@ -4,3 +4,4 @@ export * from "./types/index.js"; export * from "./constants/index.js"; export * from "./utils/testing.js"; export * from "./logger/index.js"; +export * from "./tokens/tokens.js"; diff --git a/packages/shared/src/tokens/tokens.ts b/packages/shared/src/tokens/tokens.ts new file mode 100644 index 0000000..3167001 --- /dev/null +++ b/packages/shared/src/tokens/tokens.ts @@ -0,0 +1,608 @@ +import { Branded } from "viem"; + +import { Address } from "../internal.js"; + +export type TokenCode = Branded; + +export type Token = { + code: TokenCode; + priceSourceCode: TokenCode; + address: Address; + decimals: number; + voteAmountCap?: bigint; +}; + +export const TOKENS: { + [chainId: number]: { + [tokenAddress: Address]: Token; + }; +} = { + "1": { + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + decimals: 6, + }, + "0x6B175474E89094C44Da98b954EedeAC495271d0F": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", + decimals: 18, + }, + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x661c70333AA1850CcDBAe82776Bb436A0fCfeEfB": { + code: "eBTC" as TokenCode, + priceSourceCode: "eBTC" as TokenCode, + address: "0x661c70333AA1850CcDBAe82776Bb436A0fCfeEfB", + decimals: 18, + }, + }, + "10": { + "0x7F5c764cBc14f9669B88837ca1490cCa17c31607": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", + decimals: 6, + }, + "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", + decimals: 18, + }, + "0x4F604735c1cF31399C6E711D5962b2B3E0225AD3": { + code: "USDGLO" as TokenCode, + priceSourceCode: "USDGLO" as TokenCode, + address: "0x4F604735c1cF31399C6E711D5962b2B3E0225AD3", + decimals: 18, + }, + "0x93A5347036f69BC6f37Ed2b59CBcDDa927719217": { + code: "GIST" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0x93A5347036f69BC6f37Ed2b59CBcDDa927719217", + decimals: 18, + voteAmountCap: 10000000000000000000n, + }, + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x4200000000000000000000000000000000000042": { + code: "OP" as TokenCode, + priceSourceCode: "OP" as TokenCode, + address: "0x4200000000000000000000000000000000000042", + decimals: 18, + }, + }, + "42": { + "0x0000000000000000000000000000000000000000": { + code: "LYX" as TokenCode, + priceSourceCode: "LYX" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "LYX" as TokenCode, + priceSourceCode: "LYX" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x2dB41674F2b882889e5E1Bd09a3f3613952bC472": { + code: "WLYX" as TokenCode, + priceSourceCode: "WLYX" as TokenCode, + address: "0x2dB41674F2b882889e5E1Bd09a3f3613952bC472", + decimals: 18, + }, + }, + "100": { + "0x0000000000000000000000000000000000000000": { + code: "XDAI" as TokenCode, + priceSourceCode: "XDAI" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "XDAI" as TokenCode, + priceSourceCode: "XDAI" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", + decimals: 6, + }, + }, + "137": { + "0x0000000000000000000000000000000000000000": { + code: "MATIC" as TokenCode, + priceSourceCode: "MATIC" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "MATIC" as TokenCode, + priceSourceCode: "MATIC" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + decimals: 6, + }, + "0x3a9A81d576d83FF21f26f325066054540720fC34": { + code: "DATA" as TokenCode, + priceSourceCode: "DATA" as TokenCode, + address: "0x3a9A81d576d83FF21f26f325066054540720fC34", + decimals: 18, + }, + "0x4F604735c1cF31399C6E711D5962b2B3E0225AD3": { + code: "USDGLO" as TokenCode, + priceSourceCode: "USDGLO" as TokenCode, + address: "0x4F604735c1cF31399C6E711D5962b2B3E0225AD3", + decimals: 18, + }, + }, + "250": { + "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75", + decimals: 6, + }, + "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E", + decimals: 18, + }, + "0x0000000000000000000000000000000000000000": { + code: "FTM" as TokenCode, + priceSourceCode: "FTM" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "FTM" as TokenCode, + priceSourceCode: "FTM" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x83791638da5EB2fAa432aff1c65fbA47c5D29510": { + code: "GcV" as TokenCode, + priceSourceCode: "GcV" as TokenCode, + address: "0x83791638da5EB2fAa432aff1c65fbA47c5D29510", + decimals: 18, + voteAmountCap: 10000000000000000000n, + }, + }, + "300": { + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + }, + "324": { + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4", + decimals: 6, + }, + "0x493257fD37EDB34451f62EDf8D2a0C418852bA4C": { + code: "USDT" as TokenCode, + priceSourceCode: "USDT" as TokenCode, + address: "0x493257fD37EDB34451f62EDf8D2a0C418852bA4C", + decimals: 6, + }, + "0x4B9eb6c0b6ea15176BBF62841C6B2A8a398cb656": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0x4B9eb6c0b6ea15176BBF62841C6B2A8a398cb656", + decimals: 18, + }, + "0x503234F203fC7Eb888EEC8513210612a43Cf6115": { + code: "LUSD" as TokenCode, + priceSourceCode: "LUSD" as TokenCode, + address: "0x503234F203fC7Eb888EEC8513210612a43Cf6115", + decimals: 18, + }, + "0x0e97C7a0F8B2C9885C8ac9fC6136e829CbC21d42": { + code: "MUTE" as TokenCode, + priceSourceCode: "MUTE" as TokenCode, + address: "0x0e97C7a0F8B2C9885C8ac9fC6136e829CbC21d42", + decimals: 18, + }, + }, + "424": { + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x7c6b91D9Be155A6Db01f749217d76fF02A7227F2": { + code: "GTC" as TokenCode, + priceSourceCode: "GTC" as TokenCode, + address: "0x7c6b91D9Be155A6Db01f749217d76fF02A7227F2", + decimals: 18, + }, + "0x6C121674ba6736644A7e73A8741407fE8a5eE5BA": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0x6C121674ba6736644A7e73A8741407fE8a5eE5BA", + decimals: 18, + }, + }, + "1088": { + "0x0000000000000000000000000000000000000000": { + code: "METIS" as TokenCode, + priceSourceCode: "METIS" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "METIS" as TokenCode, + priceSourceCode: "METIS" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + }, + "1329": { + "0x0000000000000000000000000000000000000000": { + code: "SEI" as TokenCode, + priceSourceCode: "SEI" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "SEI" as TokenCode, + priceSourceCode: "SEI" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x3894085Ef7Ff0f0aeDf52E2A2704928d1Ec074F1": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x3894085Ef7Ff0f0aeDf52E2A2704928d1Ec074F1", + decimals: 6, + }, + }, + "4201": { + "0x0000000000000000000000000000000000000000": { + code: "LYX" as TokenCode, + priceSourceCode: "LYX" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "LYX" as TokenCode, + priceSourceCode: "LYX" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + }, + "8453": { + "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + decimals: 6, + }, + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + }, + "42161": { + "0xaf88d065e77c8cC2239327C5EDb3A432268e5831": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + decimals: 6, + }, + "0x912ce59144191c1204e64559fe8253a0e49e6548": { + code: "ARB" as TokenCode, + priceSourceCode: "ARB" as TokenCode, + address: "0x912ce59144191c1204e64559fe8253a0e49e6548", + decimals: 18, + }, + "0x4f604735c1cf31399c6e711d5962b2b3e0225ad3": { + code: "USDGLO" as TokenCode, + priceSourceCode: "USDGLO" as TokenCode, + address: "0x4f604735c1cf31399c6e711d5962b2b3e0225ad3", + decimals: 18, + }, + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x7f9a7db853ca816b9a138aee3380ef34c437dee0": { + code: "GTC" as TokenCode, + priceSourceCode: "GTC" as TokenCode, + address: "0x7f9a7db853ca816b9a138aee3380ef34c437dee0", + decimals: 18, + }, + }, + "42220": { + "0x0000000000000000000000000000000000000000": { + code: "CELO" as TokenCode, + priceSourceCode: "CELO" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "CELO" as TokenCode, + priceSourceCode: "CELO" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x765de816845861e75a25fca122bb6898b8b1282a": { + code: "CUSD" as TokenCode, + priceSourceCode: "CUSD" as TokenCode, + address: "0x765de816845861e75a25fca122bb6898b8b1282a", + decimals: 18, + }, + "0x4f604735c1cf31399c6e711d5962b2b3e0225ad3": { + code: "USDGLO" as TokenCode, + priceSourceCode: "USDGLO" as TokenCode, + address: "0x4f604735c1cf31399c6e711d5962b2b3e0225ad3", + decimals: 18, + }, + }, + "43113": { + "0x0000000000000000000000000000000000000000": { + code: "AVAX" as TokenCode, + priceSourceCode: "AVAX" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "AVAX" as TokenCode, + priceSourceCode: "AVAX" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", + decimals: 6, + }, + }, + "43114": { + "0x0000000000000000000000000000000000000000": { + code: "AVAX" as TokenCode, + priceSourceCode: "AVAX" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "AVAX" as TokenCode, + priceSourceCode: "AVAX" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", + decimals: 6, + }, + }, + "44787": { + "0x0000000000000000000000000000000000000000": { + code: "CELO" as TokenCode, + priceSourceCode: "CELO" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "CELO" as TokenCode, + priceSourceCode: "CELO" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + }, + "58008": { + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x5fbdb2315678afecb367f032d93f642f64180aa3": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", + decimals: 18, + }, + }, + "80001": { + "0x0000000000000000000000000000000000000000": { + code: "MATIC" as TokenCode, + priceSourceCode: "MATIC" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "MATIC" as TokenCode, + priceSourceCode: "MATIC" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x9999f7Fea5938fD3b1E26A12c3f2fb024e194f97": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x9999f7Fea5938fD3b1E26A12c3f2fb024e194f97", + decimals: 6, + }, + }, + "534351": { + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0xc2332031de487f430fae3290c05465d907785eda": { + code: "MTK" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0xc2332031de487f430fae3290c05465d907785eda", + decimals: 18, + }, + }, + "534352": { + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4", + decimals: 6, + }, + }, + "713715": { + "0x0000000000000000000000000000000000000000": { + code: "SEI" as TokenCode, + priceSourceCode: "SEI" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "SEI" as TokenCode, + priceSourceCode: "SEI" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x26841a0A5D958B128209F4ea9a1DD7E61558c330": { + code: "WSEI" as TokenCode, + priceSourceCode: "WSEI" as TokenCode, + address: "0x26841a0A5D958B128209F4ea9a1DD7E61558c330", + decimals: 18, + }, + }, + "11155111": { + "0x8db0F9eE54753B91ec1d81Bf68074Be82ED30fEb": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0x8db0F9eE54753B91ec1d81Bf68074Be82ED30fEb", + decimals: 18, + }, + "0xa9dd7983B57E1865024d27110bAB098B66087e8F": { + code: "DAI" as TokenCode, + priceSourceCode: "DAI" as TokenCode, + address: "0xa9dd7983B57E1865024d27110bAB098B66087e8F", + decimals: 18, + }, + "0x0000000000000000000000000000000000000000": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0x0000000000000000000000000000000000000000", + decimals: 18, + }, + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": { + code: "ETH" as TokenCode, + priceSourceCode: "ETH" as TokenCode, + address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + decimals: 18, + }, + "0x78e0D07C4A08adFfe610113310163b40E7e47e81": { + code: "USDC" as TokenCode, + priceSourceCode: "USDC" as TokenCode, + address: "0x78e0D07C4A08adFfe610113310163b40E7e47e81", + decimals: 6, + }, + }, +} as const; + +export const getToken = (chainId: number, tokenAddress: Address): Token | undefined => { + return TOKENS[chainId]?.[tokenAddress]; +};