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: baseTokens & chainIds #43

Merged
merged 2 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
26 changes: 19 additions & 7 deletions libs/metrics/src/l1/l1MetricsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ import { AssetTvl, GasInfo } from "@zkchainhub/metrics/types";
import { IPricingService, PRICING_PROVIDER } from "@zkchainhub/pricing";
import { EvmProviderService } from "@zkchainhub/providers";
import { BatchesInfo, ChainId, Chains, ChainType, vitalikAddress } from "@zkchainhub/shared";
import { ETH_TOKEN_ADDRESS } from "@zkchainhub/shared/constants";
import {
erc20Tokens,
isNativeToken,
ETH_TOKEN_ADDRESS,
nativeToken,
tokens,
WETH,
} from "@zkchainhub/shared/tokens/tokens";
} from "@zkchainhub/shared/constants";
import { Token } from "@zkchainhub/shared/types";
import { isNativeToken } from "@zkchainhub/shared/utils";

const ONE_ETHER = parseEther("1");

Expand All @@ -59,7 +60,7 @@ export class L1MetricsService {
* @returns A Promise that resolves to an array of AssetTvl objects representing the TVL for each asset.
*/
async l1Tvl(): Promise<AssetTvl[]> {
const erc20Addresses = erc20Tokens.map((token) => token.contractAddress);
const erc20Addresses = Object.values(erc20Tokens).map((token) => token.contractAddress);

const balances = await this.fetchTokenBalances(erc20Addresses);
const pricesRecord = await this.pricingService.getTokenPrices(
Expand Down Expand Up @@ -185,7 +186,7 @@ export class L1MetricsService {
* @returns A Promise that resolves to an array of AssetTvl objects representing the TVL for each asset.
*/
async tvl(chainId: ChainId): Promise<AssetTvl[]> {
const erc20Addresses = erc20Tokens.map((token) => token.contractAddress);
const erc20Addresses = Object.values(erc20Tokens).map((token) => token.contractAddress);

const balances = await this.fetchTokenBalancesByChain(chainId, erc20Addresses);
const pricesRecord = await this.pricingService.getTokenPrices(
Expand Down Expand Up @@ -342,7 +343,7 @@ export class L1MetricsService {
* Get the base token for each chain
* @returns A map of chainId to base token address
*/
async getBaseTokens(chainIds: ChainId[]): Promise<Address[]> {
async getBaseTokens(chainIds: ChainId[]): Promise<Token<"erc20" | "native">[]> {
if (chainIds.length === 0) return [];
const baseTokens = await this.evmProviderService.multicall({
contracts: chainIds.map((chainId) => {
Expand All @@ -355,7 +356,18 @@ export class L1MetricsService {
}),
allowFailure: false,
});
return baseTokens;
return baseTokens.map((baseToken) => {
return baseToken === ETH_TOKEN_ADDRESS
? nativeToken
: erc20Tokens[baseToken] || {
contractAddress: baseToken,
decimals: 18,
name: "unknown",
type: "erc20",
symbol: "unknown",
Comment on lines +364 to +367
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can add a task to enhancements to fetch decimals, name and symbol from the contract now that i think of it but for now it's good since we know that baseTokens are permisioned and practically it'll be Ether most of the times

coingeckoId: "unknown",
};
});
}

//TODO: Implement feeParams.
Expand Down
4 changes: 2 additions & 2 deletions libs/metrics/src/types/tvl.type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TokenUnion } from "@zkchainhub/shared/tokens/tokens";
import { Token, TokenType } from "@zkchainhub/shared";

export type AssetTvl = Omit<TokenUnion, "coingeckoId"> & {
export type AssetTvl = Omit<Token<TokenType>, "coingeckoId"> & {
amount: string;
amountUsd: string;
price: string;
Expand Down
105 changes: 76 additions & 29 deletions libs/metrics/test/unit/l1/l1MetricsService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,42 +17,44 @@ import {
BatchesInfo,
ChainId,
ChainType,
erc20Tokens,
ETH_TOKEN_ADDRESS,
nativeToken,
Token,
TokenType,
vitalikAddress,
WETH,
} from "@zkchainhub/shared";
import { nativeToken, WETH } from "@zkchainhub/shared/tokens/tokens";

// Mock implementations of the dependencies
const mockEvmProviderService = createMock<EvmProviderService>();

const mockPricingService = createMock<IPricingService>();

const ONE_ETHER = parseEther("1");
jest.mock("@zkchainhub/shared/tokens/tokens", () => ({
...jest.requireActual("@zkchainhub/shared/tokens/tokens"),
get erc20Tokens() {
return [
{
name: "USDC",
symbol: "USDC",
contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
coingeckoId: "usd-coin",
imageUrl:
"https://coin-images.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
type: "erc20",
decimals: 6,
},
{
name: "Wrapped BTC",
symbol: "WBTC",
contractAddress: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
coingeckoId: "wrapped-bitcoin",
imageUrl:
"https://coin-images.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
type: "erc20",
decimals: 8,
},
];
jest.mock("@zkchainhub/shared/constants/token", () => ({
...jest.requireActual("@zkchainhub/shared/constants/token"),
erc20Tokens: {
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": {
name: "USDC",
symbol: "USDC",
contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
coingeckoId: "usd-coin",
imageUrl:
"https://coin-images.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
type: "erc20",
decimals: 6,
},
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599": {
name: "Wrapped BTC",
symbol: "WBTC",
contractAddress: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
coingeckoId: "wrapped-bitcoin",
imageUrl:
"https://coin-images.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
type: "erc20",
decimals: 8,
},
},
get tokens() {
return [
Expand Down Expand Up @@ -785,7 +787,29 @@ describe("L1MetricsService", () => {
});

describe("getBaseTokens", () => {
it("returns baseTokens", async () => {
it("returns known tokens", async () => {
const mockedChainIds = [1n, 2n];
const knownTokenAddress1 = Object.keys(erc20Tokens)[0];
const knownTokenAddress2 = Object.keys(erc20Tokens)[0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the duplicated usage of the [0] element intended?


if (!knownTokenAddress1 || !knownTokenAddress2) {
throw new Error("ERC20 tokens are not defined");
}
const mockedMulticallReturnValue = [knownTokenAddress1, knownTokenAddress2];
jest.spyOn(mockEvmProviderService, "multicall").mockResolvedValue(
mockedMulticallReturnValue,
);
const mockedReturnData: Token<TokenType>[] = [
erc20Tokens[knownTokenAddress1 as Address] as Token<"erc20">,
erc20Tokens[knownTokenAddress2 as Address] as Token<"erc20">,
];

const result = await l1MetricsService.getBaseTokens(mockedChainIds);

expect(result).toEqual(mockedReturnData);
});

it("returns unknown tokens", async () => {
const mockedChainIds = [1n, 2n];
const mockedMulticallReturnValue = [
"0x1234567890123456789012345678901234567123",
Expand All @@ -794,12 +818,30 @@ describe("L1MetricsService", () => {
jest.spyOn(mockEvmProviderService, "multicall").mockResolvedValue(
mockedMulticallReturnValue,
);
const mockedReturnData: Token<TokenType>[] = [
{
contractAddress: "0x1234567890123456789012345678901234567123",
symbol: "unknown",
name: "unknown",
decimals: 18,
type: "erc20",
coingeckoId: "unknown",
},
{
contractAddress: "0x1234567890123456789012345678901234567345",
symbol: "unknown",
name: "unknown",
decimals: 18,
type: "erc20",
coingeckoId: "unknown",
},
];

const result = await l1MetricsService.getBaseTokens(mockedChainIds);

expect(result).toEqual(mockedMulticallReturnValue);
expect(result).toEqual(mockedReturnData);
});
it("returns baseTokens", async () => {
it("returns empty array if chainIds is empty", async () => {
const mockedChainIds: ChainId[] = [];
const result = await l1MetricsService.getBaseTokens(mockedChainIds);
expect(result).toEqual([]);
Expand All @@ -809,6 +851,11 @@ describe("L1MetricsService", () => {
jest.spyOn(mockEvmProviderService, "multicall").mockRejectedValue(new Error());
await expect(l1MetricsService.getBaseTokens(mockedChainIds)).rejects.toThrow(Error);
});
it("returns eth token", async () => {
const mockedChainIds: ChainId[] = [1n, 2n];
jest.spyOn(mockEvmProviderService, "multicall").mockRejectedValue(new Error());
await expect(l1MetricsService.getBaseTokens(mockedChainIds)).rejects.toThrow(Error);
});
});

describe("feeParams", () => {
Expand Down
1 change: 1 addition & 0 deletions libs/shared/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./addresses";
export * from "./token";
export const TOKEN_CACHE_TTL_IN_SEC = 60;
export const BASE_CURRENCY = "usd";
Loading