improve: generic inventory accounting for L2-only equivalence-remapped tokens (pathUSD, USDH)#3073
improve: generic inventory accounting for L2-only equivalence-remapped tokens (pathUSD, USDH)#3073nicholaspai wants to merge 23 commits intomasterfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f3cbe062b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
In aggregate balance mode, getCurrentAllocationPct reflects the combined balance across all contributor tokens, but withdrawExcessBalances only withdraws the canonical l2Token. Cap the withdrawal amount at the actual l2Token balance to avoid submitting failing transactions when the excess is mostly held in non-canonical contributor tokens (e.g. pathUSD). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update getL2ToL1TokenMap to discover L2 tokens via TOKEN_EQUIVALENCE_REMAPPING in addition to hub chain address matching. This makes pathUSD show up under the USDC row for Tempo in the relayer balance report. Also switch l2TokenAmountToL1TokenAmountConverter to use getInventoryEquivalentL1TokenAddress so it can resolve tokens like pathUSD that have no direct L1 mapping. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rows in monitor - Bump @across-protocol/constants to ^3.1.101 which adds pathUSD to TOKEN_EQUIVALENCE_REMAPPING, enabling generic resolution without hardcoded mappings. - Generalize monitor balance report to display L2-only equivalence- remapped tokens (pathUSD, USDH) as their own rows, matching how USDC.e is displayed, rather than folding them into the parent token. - Fix RefundChain test: Tempo deposit was spreading uninitialized sampleDepositData (set in a sibling describe's beforeEach). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tokens L2-only equivalence-remapped tokens (pathUSD, USDH) share the same L1 address as their parent (USDC). The monitor's updateLatestAndFutureRelayerRefunds and updatePendingL2Withdrawals were querying by L1 address for each report row, causing the same refunds/withdrawals to be counted under USDC, pathUSD, and USDH rows simultaneously. Skip these child rows since their refunds are already captured under the parent USDC row. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pathUSD and USDH were initialized with balance entries for every chain in the report, showing zeroes on chains where they don't exist. Now L2-only equivalence-remapped tokens are initialized and displayed only for chains where they have an address in TOKEN_SYMBOLS_MAP, matching the pattern used for L2_ONLY_TOKENS. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This reverts commit ec31f7f.
- Add doc comments to getInventoryEquivalentL1TokenAddress and getInventoryBalanceContributorTokens in TokenUtils. - Add isL2OnlyEquivalentToken helper to TokenUtils and use it in Monitor instead of raw TOKEN_EQUIVALENCE_REMAPPING lookups. - Use getInventoryBalanceContributorTokens in Monitor's getL2ToL1TokenMap for consistent L2 token discovery. - Simplify TokenClient catch to return empty array with original comment. - Use getInventoryBalanceContributorTokens in InventoryClient's getRemoteTokensForL1Token for non-alias config to include L2-only equivalence-remapped tokens in cumulative balances. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: nicholaspai <9457025+nicholaspai@users.noreply.github.com>
L2-only tokens (pathUSD, USDH) are now discovered generically via TOKEN_EQUIVALENCE_REMAPPING in getL1TokensForRelayerBalancesReport and getL2ToL1TokenMap. The L2_ONLY_TOKENS env var, L2Token interface, and all associated parsing/initialization/reporting logic are no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace direct TOKEN_EQUIVALENCE_REMAPPING iteration in getL1TokensForRelayerBalancesReport with getInventoryBalanceContributorTokens and isL2OnlyEquivalentToken. - Simplify getL2ToL1TokenMap catch to return empty array instead of falling back to getRemoteTokenForL1Token which also fails for unknown tokens. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
relayer/src/clients/BundleDataApproxClient.ts
Lines 147 to 148 in f694d39
After this commit, fills for equivalence-remapped assets like pathUSD/USDH are folded into their parent L1 token via getInventoryEquivalentL1TokenAddress(), but this boundary check still looks up only one HubPool-mapped l2Token when deciding whether a relayer-refund bundle has already executed. src/dataworker/DataworkerUtils.ts:253-303 builds refund leaves by the actual l2TokenAddress, including tokens that have no pool-rebalance route, so bundles that paid pathUSD/USDH refunds will not satisfy this check and the same fills keep being counted as “upcoming.” On chains using remapped tokens, that inflates BundleDataApproxClient refunds/deposits and can skew refund-chain selection and monitor pending-refund totals.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (const contributor of getInventoryBalanceContributorTokens(l1Token.address, chainId, hubChainId)) { | ||
| const { symbol } = getTokenInfo(contributor, chainId); | ||
| if (isL2OnlyEquivalentToken(symbol, hubChainId)) { | ||
| l2OnlySymbols.add(symbol); |
There was a problem hiding this comment.
Preserve monitor rows configured through
L2_ONLY_TOKENS
This new discovery path only keeps contributor symbols that satisfy isL2OnlyEquivalentToken(...), and the matching L2_ONLY_TOKENS parsing/update logic was removed from MonitorConfig/updateCurrentRelayerBalances(). If an existing monitor deployment uses L2_ONLY_TOKENS for any L2-only asset that is not in TOKEN_EQUIVALENCE_REMAPPING, reportRelayerBalances() will now silently drop that token entirely instead of continuing to report its balances.
Useful? React with 👍 / 👎.
Is this still an issue for you assuming pathUSD and USDH cannot ever be @codex address this feedback |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f5c94511e9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| protected getL1TokenAddress(l2Token: Address, chainId: number): Address | undefined { | ||
| try { | ||
| return getL1TokenAddress(l2Token, chainId); | ||
| return getInventoryEquivalentL1TokenAddress(l2Token, chainId, this.hubPoolClient.chainId); |
There was a problem hiding this comment.
Preserve origin-chain refund matching for remapped tokens
This is still reachable when the remapped asset is the origin input token. depositForcesOriginChainRepayment() forces origin-chain repayment for tokens with no pool rebalance route (src/utils/FillUtils.ts:70-76), and _buildRelayerRefundRoot() then emits the refund leaf under that actual l2TokenAddress (src/dataworker/DataworkerUtils.ts:257-274). After this change, getL1TokenAddress() folds that fill into its parent L1 token, but getUnexecutedBundleStartBlocks() still considers a bundle executed only if it finds an execution for getL2TokenForL1TokenAtBlock(l1Token, chainId) (src/clients/BundleDataApproxClient.ts:145-160). For a pathUSD/USDH-origin refund, that lookup returns the canonical USDC token, not the executed pathUSD/USDH leaf, so the boundary never advances and the same refunds keep being counted as upcoming.
Useful? React with 👍 / 👎.
This reverts commit 986c2b9.
Summary
Replaces hardcoded pathUSD and USDH handling across the codebase with generic resolution via
TOKEN_EQUIVALENCE_REMAPPINGfrom@across-protocol/constants.New TokenUtils helpers (
src/utils/TokenUtils.ts)getInventoryEquivalentL1TokenAddress— resolves an L2-only token to its inventory-equivalent L1 token (e.g. pathUSD → USDC) viaTOKEN_EQUIVALENCE_REMAPPINGgetInventoryBalanceContributorTokens— discovers all L2 tokens that contribute to a given L1 token's balance on a chain (e.g. USDC on Tempo → [USDC.e, pathUSD])isL2OnlyEquivalentToken— checks if a token symbol is an L2-only equivalence-remapped tokenInventoryClient (
src/clients/InventoryClient.ts)getL1TokenAddress— replaced hardcoded["pathUSD"]check withgetInventoryEquivalentL1TokenAddressgetRemoteTokensForL1Token— non-alias config branch now usesgetInventoryBalanceContributorTokensso pathUSD/USDH are included ingetCumulativeBalanceTokenClient (
src/clients/TokenClient.ts)resolveRemoteTokens— usesgetInventoryBalanceContributorTokensto discover tokens to fetch balances for, replacing hardcoded USDC.e/USDbC/USDzC/pathUSD listMonitor (
src/monitor/Monitor.ts)getInventoryBalanceContributorTokensandisL2OnlyEquivalentTokenL2_ONLY_TOKENSenv var,L2Tokeninterface, and all associatedl2OnlyTokenslogic — no longer neededOther
BaseChainAdapter— replaced hardcoded pathUSD check withgetInventoryEquivalentL1TokenAddress@across-protocol/constantsto ^3.1.102 (includes pathUSD and USDH inTOKEN_EQUIVALENCE_REMAPPING)Test plan
TokenUtilsunit tests verify L1 token resolution, contributor discovery, and L2-only detection🤖 Generated with Claude Code