Skip to content

Commit

Permalink
fix: Handle L2 USDC symbol lookup (#1509)
Browse files Browse the repository at this point in the history
* fix: Handle L2 USDC symbol lookup

Handle case where an L2 token for chain ID can map to more than one TOKEN_SYMBOLS_MAP entry. For example, L2 Bridged USDC maps to both the USDC and USDC.e/USDbC entries in TOKEN_SYMBOLS_MAP.

This PR is a temporary patch that handles the known USDC issue where both USDC.e/USDbC contain the same chain-address combinations as the USDC entry. This can be reverted once we remove the USDC entry in the TOKEN_SYMBOLS_MAP.

* Update InventoryClient.ts

* fix tests
  • Loading branch information
nicholaspai authored May 10, 2024
1 parent fa3dc49 commit 58046e6
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 8 deletions.
10 changes: 8 additions & 2 deletions src/clients/HubPoolClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { clients, interfaces } from "@across-protocol/sdk-v2";
import { Contract } from "ethers";
import winston from "winston";
import { MakeOptional, EventSearchConfig, getTokenInfo, getL1TokenInfo } from "../utils";
import { MakeOptional, EventSearchConfig, getTokenInfo, getL1TokenInfo, getUsdcSymbol } from "../utils";
import { IGNORED_HUB_EXECUTED_BUNDLES, IGNORED_HUB_PROPOSED_BUNDLES } from "../common";
import { L1Token } from "../interfaces";

Expand Down Expand Up @@ -41,7 +41,13 @@ export class HubPoolClient extends clients.HubPoolClient {
* @returns Token info for the given token address on the L2 chain including symbol and decimal.
*/
getTokenInfoForAddress(tokenAddress: string, chain: number): L1Token {
return getTokenInfo(tokenAddress, chain);
const tokenInfo = getTokenInfo(tokenAddress, chain);
// @dev Temporarily handle case where an L2 token for chain ID can map to more than one TOKEN_SYMBOLS_MAP
// entry. For example, L2 Bridged USDC maps to both the USDC and USDC.e/USDbC entries in TOKEN_SYMBOLS_MAP.
if (tokenInfo.symbol.toLowerCase() === "usdc" && chain !== this.chainId) {
tokenInfo.symbol = getUsdcSymbol(tokenAddress, chain);
}
return tokenInfo;
}

/**
Expand Down
12 changes: 7 additions & 5 deletions src/clients/InventoryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,10 +818,10 @@ export class InventoryClient {
for (const [_chainId, rebalances] of Object.entries(groupedRebalances)) {
const chainId = Number(_chainId);
mrkdwn += `*Rebalances sent to ${getNetworkName(chainId)}:*\n`;
for (const { l1Token, l2Token, amount, targetPct, thresholdPct, cumulativeBalance, hash } of rebalances) {
const tokenInfo = this.hubPoolClient.getTokenInfoForL1Token(l1Token);
for (const { l2Token, amount, targetPct, thresholdPct, cumulativeBalance, hash, chainId } of rebalances) {
const tokenInfo = this.hubPoolClient.getTokenInfoForAddress(l2Token, chainId);
if (!tokenInfo) {
throw new Error(`InventoryClient::rebalanceInventoryIfNeeded no L1 token info for token ${l1Token}`);
`InventoryClient::rebalanceInventoryIfNeeded no token info for L2 token ${l2Token} on chain ${chainId}`;
}
const { symbol, decimals } = tokenInfo;
const formatter = createFormatFunction(2, 4, false, decimals);
Expand All @@ -842,9 +842,11 @@ export class InventoryClient {
const chainId = Number(_chainId);
mrkdwn += `*Insufficient amount to rebalance to ${getNetworkName(chainId)}:*\n`;
for (const { l1Token, l2Token, balance, cumulativeBalance, amount } of rebalances) {
const tokenInfo = this.hubPoolClient.getTokenInfoForL1Token(l1Token);
const tokenInfo = this.hubPoolClient.getTokenInfoForAddress(l2Token, chainId);
if (!tokenInfo) {
throw new Error(`InventoryClient::rebalanceInventoryIfNeeded no L1 token info for token ${l1Token}`);
throw new Error(
`InventoryClient::rebalanceInventoryIfNeeded no token info for L2 token ${l2Token} on chain ${chainId}`
);
}
const { symbol, decimals } = tokenInfo;
const formatter = createFormatFunction(2, 4, false, decimals);
Expand Down
2 changes: 1 addition & 1 deletion src/relayer/Relayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@ export class Relayer {
.div(deposit.inputAmount);
const totalFeePct = formatFeePct(_totalFeePct);
const { symbol: outputTokenSymbol, decimals: outputTokenDecimals } =
this.clients.hubPoolClient.getTokenInfoForDeposit(deposit);
this.clients.hubPoolClient.getTokenInfoForAddress(deposit.outputToken, deposit.destinationChainId);
const _outputAmount = createFormatFunction(2, 4, false, outputTokenDecimals)(deposit.outputAmount.toString());
msg +=
` and output ${_outputAmount} ${outputTokenSymbol}, with depositor ${depositor}.` +
Expand Down
11 changes: 11 additions & 0 deletions src/utils/AddressUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ export function getTokenAddressWithCCTP(
return getTokenAddress(l1Token, hubChainId, l2ChainId);
}

/**
* Get the USDC symbol for the given token address and chain ID.
* @param l2Token A Web3 token address (not case sensitive)
* @param chainId A chain Id to reference
* @returns Either USDC (if native) or USDbC/USDC.e (if bridged) or undefined if the token address is not recognized.
*/
export function getUsdcSymbol(l2Token: string, chainId: number): string | undefined {
const compareToken = (token?: string) => isDefined(token) && compareAddressesSimple(l2Token, token);
return ["_USDC", "USDbC", "USDC.e"].find((token) => compareToken(TOKEN_SYMBOLS_MAP[token].addresses[chainId]));
}

export function checkAddressChecksum(tokenAddress: string): boolean {
return ethers.utils.getAddress(tokenAddress) === tokenAddress;
}
8 changes: 8 additions & 0 deletions test/InventoryClient.InventoryRebalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ describe("InventoryClient: Rebalancing inventory", async function () {
await configStoreClient.update();

hubPoolClient = new MockHubPoolClient(spyLogger, hubPool, configStoreClient);
enabledChainIds.forEach((chainId) => {
hubPoolClient.mapTokenInfo(l2TokensForWeth[chainId], "WETH", 18);
hubPoolClient.mapTokenInfo(l2TokensForUsdc[chainId], "USDC", 6);
});
await hubPoolClient.update();

adapterManager = new MockAdapterManager(null, null, null, null);
Expand Down Expand Up @@ -349,6 +353,10 @@ describe("InventoryClient: Rebalancing inventory", async function () {
beforeEach(async function () {
// Sub in a nested USDC config for the existing USDC single-token config.
inventoryConfig.tokenConfig[mainnetUsdc] = usdcConfig;

enabledChainIds.forEach((chainId) => {
hubPoolClient.mapTokenInfo(nativeUSDC[chainId], "USDC", 6);
});
});

it("Correctly resolves 1:many token mappings", async function () {
Expand Down

0 comments on commit 58046e6

Please sign in to comment.