@merkl/api 0.16.6 → 0.16.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,10 @@
1
1
  import { Redis } from "../../cache";
2
2
  import { campaignsToOldFormat } from "../../libs/deprecated-merklv3";
3
- import { AprService, CampaignService, OpportunityService } from "../../modules/v4";
3
+ import { AprService } from "../../modules/v4/apr";
4
4
  import { CacheService } from "../../modules/v4/cache";
5
5
  import { TTLPresets } from "../../modules/v4/cache/cache.model";
6
+ import { CampaignService } from "../../modules/v4/campaign";
7
+ import { OpportunityService } from "../../modules/v4/opportunity";
6
8
  import { OpportunityRepository } from "../../modules/v4/opportunity/opportunity.repository";
7
9
  import { RewardService } from "../../modules/v4/reward";
8
10
  import { TvlService } from "../../modules/v4/tvl";
@@ -1,4 +1,4 @@
1
- import { TokenService } from "../../modules/v4";
1
+ import { TokenService } from "../../modules/v4/token";
2
2
  import { log } from "../../utils/logger";
3
3
  import { Pricer } from "../../utils/pricer";
4
4
  export const updatePrices = async () => {
@@ -1316,6 +1316,7 @@ declare const eden: {
1316
1316
  headers?: Record<string, unknown> | undefined;
1317
1317
  query: {
1318
1318
  tokenAddress?: string | undefined;
1319
+ verified?: boolean | undefined;
1319
1320
  additionalTokenAddresses?: string[] | undefined;
1320
1321
  chainId: number;
1321
1322
  userAddress: string;
@@ -2066,7 +2067,7 @@ declare const eden: {
2066
2067
  proofs: string[];
2067
2068
  }, "breakdowns"> & {
2068
2069
  breakdowns: {
2069
- opportunity: import("../modules/v4").Opportunity["model"];
2070
+ opportunity: import("../modules/v4/opportunity").Opportunity["model"];
2070
2071
  claimed: bigint;
2071
2072
  amount: bigint;
2072
2073
  pending: bigint;
@@ -2780,7 +2781,7 @@ declare const eden: {
2780
2781
  fetch?: RequestInit | undefined;
2781
2782
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
2782
2783
  200: (import("../modules/v4/claims").ClaimModel & {
2783
- token?: import("../modules/v4").Token["model"];
2784
+ token?: import("../modules/v4/token").Token["model"];
2784
2785
  })[];
2785
2786
  }>>;
2786
2787
  }) & {};
@@ -4427,6 +4428,7 @@ declare const eden: {
4427
4428
  headers?: Record<string, unknown> | undefined;
4428
4429
  query: {
4429
4430
  tokenAddress?: string | undefined;
4431
+ verified?: boolean | undefined;
4430
4432
  additionalTokenAddresses?: string[] | undefined;
4431
4433
  chainId: number;
4432
4434
  userAddress: string;
@@ -5177,7 +5179,7 @@ declare const eden: {
5177
5179
  proofs: string[];
5178
5180
  }, "breakdowns"> & {
5179
5181
  breakdowns: {
5180
- opportunity: import("../modules/v4").Opportunity["model"];
5182
+ opportunity: import("../modules/v4/opportunity").Opportunity["model"];
5181
5183
  claimed: bigint;
5182
5184
  amount: bigint;
5183
5185
  pending: bigint;
@@ -5891,7 +5893,7 @@ declare const eden: {
5891
5893
  fetch?: RequestInit | undefined;
5892
5894
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
5893
5895
  200: (import("../modules/v4/claims").ClaimModel & {
5894
- token?: import("../modules/v4").Token["model"];
5896
+ token?: import("../modules/v4/token").Token["model"];
5895
5897
  })[];
5896
5898
  }>>;
5897
5899
  }) & {};
@@ -8448,6 +8450,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
8448
8450
  params: {};
8449
8451
  query: {
8450
8452
  tokenAddress?: string | undefined;
8453
+ verified?: boolean | undefined;
8451
8454
  additionalTokenAddresses?: string[] | undefined;
8452
8455
  chainId: number;
8453
8456
  userAddress: string;
@@ -9493,7 +9496,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
9493
9496
  proofs: string[];
9494
9497
  }, "breakdowns"> & {
9495
9498
  breakdowns: {
9496
- opportunity: import("../modules/v4").Opportunity["model"];
9499
+ opportunity: import("../modules/v4/opportunity").Opportunity["model"];
9497
9500
  claimed: bigint;
9498
9501
  amount: bigint;
9499
9502
  pending: bigint;
@@ -10344,7 +10347,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
10344
10347
  headers: unknown;
10345
10348
  response: {
10346
10349
  200: (import("../modules/v4/claims").ClaimModel & {
10347
- token?: import("../modules/v4").Token["model"];
10350
+ token?: import("../modules/v4/token").Token["model"];
10348
10351
  })[];
10349
10352
  };
10350
10353
  };
@@ -13000,6 +13003,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
13000
13003
  headers?: Record<string, unknown> | undefined;
13001
13004
  query: {
13002
13005
  tokenAddress?: string | undefined;
13006
+ verified?: boolean | undefined;
13003
13007
  additionalTokenAddresses?: string[] | undefined;
13004
13008
  chainId: number;
13005
13009
  userAddress: string;
@@ -13750,7 +13754,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
13750
13754
  proofs: string[];
13751
13755
  }, "breakdowns"> & {
13752
13756
  breakdowns: {
13753
- opportunity: import("../modules/v4").Opportunity["model"];
13757
+ opportunity: import("../modules/v4/opportunity").Opportunity["model"];
13754
13758
  claimed: bigint;
13755
13759
  amount: bigint;
13756
13760
  pending: bigint;
@@ -14464,7 +14468,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
14464
14468
  fetch?: RequestInit | undefined;
14465
14469
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
14466
14470
  200: (import("../modules/v4/claims").ClaimModel & {
14467
- token?: import("../modules/v4").Token["model"];
14471
+ token?: import("../modules/v4/token").Token["model"];
14468
14472
  })[];
14469
14473
  }>>;
14470
14474
  }) & {};
@@ -16111,6 +16115,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
16111
16115
  headers?: Record<string, unknown> | undefined;
16112
16116
  query: {
16113
16117
  tokenAddress?: string | undefined;
16118
+ verified?: boolean | undefined;
16114
16119
  additionalTokenAddresses?: string[] | undefined;
16115
16120
  chainId: number;
16116
16121
  userAddress: string;
@@ -16861,7 +16866,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
16861
16866
  proofs: string[];
16862
16867
  }, "breakdowns"> & {
16863
16868
  breakdowns: {
16864
- opportunity: import("../modules/v4").Opportunity["model"];
16869
+ opportunity: import("../modules/v4/opportunity").Opportunity["model"];
16865
16870
  claimed: bigint;
16866
16871
  amount: bigint;
16867
16872
  pending: bigint;
@@ -17575,7 +17580,7 @@ export declare const MerklApi: (domain: string | import("elysia").default<"", fa
17575
17580
  fetch?: RequestInit | undefined;
17576
17581
  }) => Promise<import("@elysiajs/eden").Treaty.TreatyResponse<{
17577
17582
  200: (import("../modules/v4/claims").ClaimModel & {
17578
- token?: import("../modules/v4").Token["model"];
17583
+ token?: import("../modules/v4/token").Token["model"];
17579
17584
  })[];
17580
17585
  }>>;
17581
17586
  }) & {};
@@ -1528,6 +1528,7 @@ declare const app: Elysia<"", false, {
1528
1528
  params: {};
1529
1529
  query: {
1530
1530
  tokenAddress?: string | undefined;
1531
+ verified?: boolean | undefined;
1531
1532
  additionalTokenAddresses?: string[] | undefined;
1532
1533
  chainId: number;
1533
1534
  userAddress: string;
@@ -2573,7 +2574,7 @@ declare const app: Elysia<"", false, {
2573
2574
  proofs: string[];
2574
2575
  }, "breakdowns"> & {
2575
2576
  breakdowns: {
2576
- opportunity: import("./modules/v4").Opportunity["model"];
2577
+ opportunity: import("./modules/v4/opportunity").Opportunity["model"];
2577
2578
  claimed: bigint;
2578
2579
  amount: bigint;
2579
2580
  pending: bigint;
@@ -3424,7 +3425,7 @@ declare const app: Elysia<"", false, {
3424
3425
  headers: unknown;
3425
3426
  response: {
3426
3427
  200: (import("./modules/v4/claims").ClaimModel & {
3427
- token?: import("./modules/v4").Token["model"];
3428
+ token?: import("./modules/v4/token").Token["model"];
3428
3429
  })[];
3429
3430
  };
3430
3431
  };
@@ -14,14 +14,4 @@ export interface Token {
14
14
  export interface TokenInfoListType {
15
15
  [symbol: string]: Token;
16
16
  }
17
- export interface TokenList {
18
- [chainId: number]: TokenInfoListType;
19
- }
20
17
  export declare const getTokensListWithCache: () => Promise<TokenList>;
21
- export type MerklTokens = {
22
- [address: string]: {
23
- symbol: string;
24
- decimals: number;
25
- };
26
- };
27
- export declare function getMerklTokenList(chainId: number): Promise<MerklTokens>;
@@ -1,5 +1,4 @@
1
1
  import { Redis } from "../cache";
2
- import { engineDbClient } from "../utils/prisma";
3
2
  import axios from "axios";
4
3
  import { log } from "../utils/logger";
5
4
  async function getTokensList() {
@@ -14,24 +13,5 @@ async function getTokensList() {
14
13
  }
15
14
  return res;
16
15
  }
16
+ // Deprecated
17
17
  export const getTokensListWithCache = async () => await Redis.getOrSet("TokenList", getTokensList);
18
- export async function getMerklTokenList(chainId) {
19
- const tokensRaws = await engineDbClient.tokens.findMany({
20
- select: {
21
- address: true,
22
- decimals: true,
23
- symbol: true,
24
- },
25
- where: {
26
- chainId,
27
- },
28
- });
29
- const tokens = {};
30
- for (const token of tokensRaws) {
31
- tokens[token.address] = {
32
- decimals: token.decimals,
33
- symbol: token.symbol,
34
- };
35
- }
36
- return tokens;
37
- }
@@ -1,8 +1,8 @@
1
1
  import { Redis } from "../cache";
2
+ import { TokenService } from "../modules/v4/token";
2
3
  import { Interface } from "@ethersproject/abi";
3
4
  import { BN2Number, DistributionCreator__factory, Distributor__factory, NETWORK_LABELS, NULL_ADDRESS, registry, } from "@sdk";
4
5
  import { batchMulticallCallWithRetry } from "../utils/generic";
5
- import { getMerklTokenList, getTokensListWithCache } from "./getTokensList";
6
6
  const DistributorInterface = Distributor__factory.createInterface();
7
7
  const DistributorCreatorInterface = DistributionCreator__factory.createInterface();
8
8
  const MTW_INTERFACE = new Interface([
@@ -10,7 +10,7 @@ const MTW_INTERFACE = new Interface([
10
10
  "function token() external view returns (address)",
11
11
  ]);
12
12
  export async function merklChainData(chainId) {
13
- const tokens = await getMerklTokenList(chainId);
13
+ const tokens = await TokenService.findManyObjectPerAddress({ chainId, verified: true });
14
14
  const distributorAddress = registry(chainId)?.Merkl?.Distributor;
15
15
  const distributionCreatorAddress = registry(chainId)?.Merkl?.DistributionCreator;
16
16
  if (!distributorAddress || !distributionCreatorAddress) {
@@ -54,7 +54,6 @@ export async function merklChainData(chainId) {
54
54
  },
55
55
  ];
56
56
  const result = await batchMulticallCallWithRetry(chainId, { calls });
57
- const tokenList = await getTokensListWithCache();
58
57
  let i = 0;
59
58
  const merkleRoot = DistributorInterface.decodeFunctionResult("getMerkleRoot", result[i++].returnData)[0]?.toString();
60
59
  const treeRoot = DistributorInterface.decodeFunctionResult("tree", result[i++].returnData)[0].toString();
@@ -67,16 +66,8 @@ export async function merklChainData(chainId) {
67
66
  }
68
67
  catch { }
69
68
  const validRewardTokens = DistributorCreatorInterface.decodeFunctionResult("getValidRewardTokens()", result[i++].returnData)[0].map((t) => {
70
- const decimals = tokens[t.token]?.decimals
71
- ? tokens[t.token]?.decimals
72
- : tokenList[chainId]?.[t.token]?.decimals
73
- ? tokenList[chainId][t.token].decimals
74
- : undefined;
75
- const symbol = tokens[t.token]?.symbol
76
- ? tokens[t.token]?.symbol
77
- : tokenList[chainId]?.[t.token]?.symbol
78
- ? tokenList[chainId][t.token].symbol
79
- : undefined;
69
+ const decimals = tokens[t.token]?.decimals ? tokens[t.token]?.decimals : undefined;
70
+ const symbol = tokens[t.token]?.symbol ? tokens[t.token]?.symbol : undefined;
80
71
  return {
81
72
  decimals: decimals,
82
73
  minimumAmountPerEpoch: decimals
@@ -86,17 +77,6 @@ export async function merklChainData(chainId) {
86
77
  token: t.token,
87
78
  };
88
79
  });
89
- const missingSymbols = validRewardTokens.filter(t => !t.symbol || !t.decimals);
90
- for (const token of missingSymbols) {
91
- if (!tokenList[chainId])
92
- continue;
93
- const tokenInfo = tokenList[chainId][token.token];
94
- if (!tokenInfo)
95
- continue;
96
- token.symbol = tokenInfo.symbol;
97
- token.decimals = tokenInfo.decimals;
98
- token.minimumAmountPerEpoch = BN2Number(token.minimumAmountPerEpoch, tokenInfo.decimals);
99
- }
100
80
  const validRewardTokensCalls = validRewardTokens.reduce((acc, t) => {
101
81
  acc.push({
102
82
  allowFailure: true,
@@ -1,6 +1,6 @@
1
+ import { TokenService } from "../../modules/v4/token";
1
2
  import { ChainId, ChainInteractionService, ETH_ADDRESS, ETH_ZKSYNC_ADDRESS, Erc20__factory, EthOnZKSync_INTERFACE, NETWORK_LABELS, } from "@sdk";
2
3
  import { log } from "../../utils/logger";
3
- import { getTokensListWithCache } from "../getTokensList";
4
4
  export async function getOnlyUserBalance(chainId, userAddress, tokenAddresses) {
5
5
  const calls = [];
6
6
  const ERC20_Interface = Erc20__factory.createInterface();
@@ -40,7 +40,7 @@ export async function getOnlyUserBalance(chainId, userAddress, tokenAddresses) {
40
40
  return res;
41
41
  }
42
42
  export async function getUserBalances(user, chainId, tokenAddresses) {
43
- const tokens = (await getTokensListWithCache())?.[chainId];
43
+ const tokens = await TokenService.findManyObjectPerAddress({ chainId, verified: true });
44
44
  if (!tokens) {
45
45
  log.debug("❌ Chain not supported");
46
46
  return {
@@ -1,5 +1,5 @@
1
1
  import type { Resource } from "../prisma";
2
- import { type Token } from "../token";
2
+ import { type Token } from "../token/token.model";
3
3
  import type { Campaign as CampaignEnum, CampaignParameters } from "@sdk";
4
4
  import { type Chain } from "../chain/chain.model";
5
5
  import { type Status } from "../status/status.model";
@@ -1,4 +1,4 @@
1
- import { TokenResourceDto } from "../token";
1
+ import { TokenResourceDto } from "../token/token.model";
2
2
  import { t } from "elysia";
3
3
  import { ChainResourceDto } from "../chain/chain.model";
4
4
  import { CampaignStatusResourceDto } from "../status/status.model";
@@ -25,7 +25,7 @@ export declare const ClaimController: Elysia<"/claims", false, {
25
25
  headers: unknown;
26
26
  response: {
27
27
  200: (import("./claims.model").ClaimModel & {
28
- token?: import("..").Token["model"];
28
+ token?: import("../token").Token["model"];
29
29
  })[];
30
30
  };
31
31
  };
@@ -1,5 +1,5 @@
1
1
  import type { Resource } from "../prisma";
2
- import { type Token } from "../token";
2
+ import { type Token } from "../token/token.model";
3
3
  /**
4
4
  * Rewards Record
5
5
  * @description Describes one rewards value snapshot for an opportunity
@@ -1,5 +1,5 @@
1
1
  import { t } from "elysia";
2
- import { TokenResourceDto } from "../token";
2
+ import { TokenResourceDto } from "../token/token.model";
3
3
  // ─── DTOs ────────────────────────────────────────────────────────────────────
4
4
  export const DailyRewardsBreakdownRecordResourceDto = t.Object({
5
5
  id: t.Number(),
@@ -1398,6 +1398,7 @@ export declare const v4: Elysia<"/v4", false, {
1398
1398
  params: {};
1399
1399
  query: {
1400
1400
  tokenAddress?: string | undefined;
1401
+ verified?: boolean | undefined;
1401
1402
  additionalTokenAddresses?: string[] | undefined;
1402
1403
  chainId: number;
1403
1404
  userAddress: string;
@@ -113,6 +113,7 @@ export declare const TokenController: Elysia<"/tokens", false, {
113
113
  params: {};
114
114
  query: {
115
115
  tokenAddress?: string | undefined;
116
+ verified?: boolean | undefined;
116
117
  additionalTokenAddresses?: string[] | undefined;
117
118
  chainId: number;
118
119
  userAddress: string;
@@ -47,6 +47,7 @@ export declare const TokenDto: import("@sinclair/typebox").TObject<{
47
47
  export declare const GetTokenBalanceDto: import("@sinclair/typebox").TObject<{
48
48
  chainId: import("@sinclair/typebox").TNumber;
49
49
  userAddress: import("@sinclair/typebox").TString;
50
+ verified: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
50
51
  tokenAddress: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
51
52
  additionalTokenAddresses: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
52
53
  }>;
@@ -36,6 +36,7 @@ export const TokenDto = t.Object({
36
36
  export const GetTokenBalanceDto = t.Object({
37
37
  chainId: t.Numeric(),
38
38
  userAddress: t.String(),
39
+ verified: t.Optional(t.Boolean()),
39
40
  tokenAddress: t.Optional(t.String({ description: "If provided, the default verified token balances won't be included" })),
40
41
  additionalTokenAddresses: t.Optional(t.Array(t.String())),
41
42
  });
@@ -163,6 +163,19 @@ export declare abstract class TokenService {
163
163
  } & {
164
164
  price?: number | null | undefined;
165
165
  })[]>;
166
+ static findManyObjectPerAddress(query: GetTokenQueryModel): Promise<Record<string, {
167
+ symbol: string;
168
+ name: string | null;
169
+ id: string;
170
+ icon: string;
171
+ chainId: number;
172
+ address: string;
173
+ decimals: number;
174
+ verified: boolean;
175
+ isTest: boolean;
176
+ } & {
177
+ price?: number | null | undefined;
178
+ }>>;
166
179
  /**
167
180
  * Get value of tokens
168
181
  * @param tokenAmounts address/chain + amount of token
@@ -139,23 +139,6 @@ export class TokenService {
139
139
  catch (e) {
140
140
  console.error(e);
141
141
  }
142
- try {
143
- const res = await TokenRepository.create({
144
- id: TokenService.hashId({ chainId: Number.parseInt(chain), address: token.address }),
145
- chainId: Number.parseInt(chain),
146
- address: token.address,
147
- name: token.name,
148
- symbol: token.symbol,
149
- verified: true,
150
- decimals: token.decimals,
151
- icon: token.logoURI,
152
- isTest: false,
153
- });
154
- log.local(`Token created: ${res?.symbol} on ${NETWORK_LABELS[Number.parseInt(chain)]}`);
155
- }
156
- catch (e) {
157
- console.error(e);
158
- }
159
142
  }
160
143
  try {
161
144
  await apiDbClient.token.update({
@@ -236,6 +219,12 @@ export class TokenService {
236
219
  static async findMany(query) {
237
220
  return (await TokenRepository.findMany(query)).map(TokenService.format);
238
221
  }
222
+ static async findManyObjectPerAddress(query) {
223
+ return (await TokenService.findMany(query)).reduce((acc, token) => {
224
+ acc[token.address] = token;
225
+ return acc;
226
+ }, {});
227
+ }
239
228
  /**
240
229
  * Get value of tokens
241
230
  * @param tokenAmounts address/chain + amount of token
@@ -198,7 +198,7 @@ export declare const UserController: Elysia<"/users", false, {
198
198
  proofs: string[];
199
199
  }, "breakdowns"> & {
200
200
  breakdowns: {
201
- opportunity: import("..").Opportunity["model"];
201
+ opportunity: import("../opportunity").Opportunity["model"];
202
202
  claimed: bigint;
203
203
  amount: bigint;
204
204
  pending: bigint;
@@ -1,11 +1,11 @@
1
1
  import { Redis } from "../../cache";
2
+ import { TokenService } from "../../modules/v4/token";
2
3
  import { getLastBlockBeforeWithCache } from "../../utils/lastBlockBefore";
3
4
  import { BN2Number, ChainId, DAY, ERC20Interface } from "@sdk";
4
5
  import { t } from "elysia";
5
6
  import { utils } from "ethers";
6
7
  import moment from "moment";
7
8
  import checkQueryAddressValidity from "../../hooks/checkQueryAddressValidity";
8
- import { getTokensListWithCache } from "../../libs/getTokensList";
9
9
  import { batchMulticallCallWithRetry } from "../../utils/generic";
10
10
  import { log } from "../../utils/logger";
11
11
  import { providers } from "../../utils/providers";
@@ -32,13 +32,16 @@ const CONSTANTS = {
32
32
  ],
33
33
  };
34
34
  const getLostYield = async (user) => {
35
- const tokenList = await getTokensListWithCache();
36
35
  const result = {};
37
36
  const now = moment().unix();
38
37
  const LOOKBACK = 30 * DAY;
39
38
  const AVERAGING_PERIOD = 2 * DAY;
40
39
  const promises = Object.keys(CONSTANTS).map(chainId => (async () => {
41
40
  try {
41
+ const tokenList = await TokenService.findManyObjectPerAddress({
42
+ chainId: Number.parseInt(chainId),
43
+ verified: true,
44
+ });
42
45
  result[chainId] = {};
43
46
  for (const token of CONSTANTS[Number.parseInt(chainId)]) {
44
47
  result[chainId][token] = { total: 0, yield: 0 };
@@ -64,7 +67,7 @@ const getLostYield = async (user) => {
64
67
  const results = await batchMulticallCallWithRetry(Number.parseInt(chainId), { calls, blockNumber });
65
68
  let i = 0;
66
69
  for (const token of CONSTANTS[Number.parseInt(chainId)]) {
67
- const tokenDetails = tokenList[Number.parseInt(chainId)][utils.getAddress(token)];
70
+ const tokenDetails = tokenList[utils.getAddress(token)];
68
71
  const balance = ERC20Interface.decodeFunctionResult("balanceOf", results[i++].returnData)[0];
69
72
  result[chainId][token].total += BN2Number(balance, tokenDetails.decimals);
70
73
  }
@@ -263,7 +263,7 @@ export default class PriceService {
263
263
  this._prices[`r${xUpper}`] = this._prices[x];
264
264
  this._prices[`variableDebt${xUpper}`] = this._prices[x];
265
265
  }
266
- /** Fill all BorrowStaker prices */
266
+ /** Deprecated - Fill all BorrowStaker prices */
267
267
  const tokens = await getTokensListWithCache();
268
268
  for (const chainIdString of Object.keys(tokens)) {
269
269
  const chainId = Number.parseInt(chainIdString);