Skip to content

refactor: Generalize inventory accounting and fix Binance CEX pending withdrawal logic#3073

Closed
nicholaspai wants to merge 80 commits intomasterfrom
pathusd-cumulative-balances
Closed

refactor: Generalize inventory accounting and fix Binance CEX pending withdrawal logic#3073
nicholaspai wants to merge 80 commits intomasterfrom
pathusd-cumulative-balances

Conversation

@nicholaspai
Copy link
Copy Markdown
Member

@nicholaspai nicholaspai commented Mar 20, 2026

Summary

Inventory equivalence-remapping generalization

  • New TokenUtils functions (getInventoryEquivalentL1TokenAddress, getInventoryBalanceContributorTokens, isL2OnlyEquivalentToken) that use TOKEN_EQUIVALENCE_REMAPPING to generically resolve L2-only tokens (e.g. pathUSD, USDH) to their inventory-equivalent L1 token and vice versa — replacing scattered hardcoded symbol checks
  • Extracted RunningBalanceUtils.getLatestRunningBalances from InventoryClient into a shared utility so both InventoryClient and Monitor can compute approximate running balances without duplicating logic
  • Rewrote Monitor.reportRelayerBalances to use getInventoryBalanceContributorTokens for per-chain L2 token discovery, batch-fetch all balances in parallel, and produce a single aligned table per token — replacing the old multi-pass RelayerBalanceReport / BalanceType / TokenTransferClient approach
  • BundleDataApproxClient now returns values in L1 token decimals and groups deposits/refunds across all inventory-equivalent L2 tokens per chain, removing the need for callers to do decimal conversion
  • Deleted TokenTransferClient (no longer needed), BalanceType/RelayerBalanceReport/TokenTransfer interfaces, and L2Token/l2OnlyTokens config — all replaced by the generic equivalence-remapping utilities
  • Replaced hubPoolClient.getTokenInfoForAddress calls with getTokenInfo utility throughout InventoryClient and Monitor, with protected getTokenInfo override pattern for test mocking

Binance CEX Bridge pending withdrawal fix

  • Rewrote BinanceCEXBridge.getL2PendingWithdrawalAmount to correctly compute outstanding deposits when the Binance finalizer batches deposits from multiple L2s into a single L1 withdrawal. The old approach (total deposits minus total withdrawals per-chain) would incorrectly show zero outstanding when a withdrawal for a different L2's deposit exceeded the current chain's deposit amount
  • Extracted getOutstandingBinanceDeposits into BinanceUtils as a pure, testable function. The algorithm computes net outstanding deposit volume across all chains, then attributes it to the newest deposits (newest-first iteration) since the finalizer processes older deposits first
  • Fixed floatToBN to use string manipulation instead of floating-point arithmetic, preventing MAX_SAFE_INTEGER overflow for numbers with many decimal digits (e.g. 5654.8610313399695 * 10^13). Also handles scientific notation for very small numbers
  • Exported BinanceDeposit type from BinanceUtils for use in tests

Cross-chain adapter cleanup

  • BaseChainAdapter.getOutstandingCrossChainTransfers now skips zero-amount entries instead of always assigning them to the result object
  • Updated mock contracts (MockLineaEvents.sol) and generic adapter tests to use non-zero amounts and expect empty results when no transfers are outstanding

Test improvements

  • New unit tests for getOutstandingBinanceDeposits, floatToBN, and getInventoryBalanceContributorTokens
  • Updated InventoryClient tests to use alias config for USDC (matching production config shape)
  • Updated InventoryClient.RefundChain tests to pass upcoming refunds in L1 decimals (matching new BundleDataApproxClient return format)
  • Updated generic adapter tests (Linea, Polygon, Scroll, zkSync) to expect correct empty-state behavior

Test plan

  • BinanceUtils unit tests pass — outstanding deposit matching, partial amounts, cross-chain filtering
  • BNUtils unit tests pass — floatToBN with large decimals, scientific notation, string inputs
  • BundleDataApproxClient unit tests pass with L1-decimal-denominated return values
  • TokenUtils unit tests pass for getInventoryBalanceContributorTokens and getInventoryEquivalentL1TokenAddress
  • InventoryClient tests pass (55/55) with alias config and L1-decimal refunds
  • Monitor tests pass (3/3) with MockMonitor override
  • Generic adapter tests pass (85/85) — Linea, Polygon, Scroll, zkSync, OpStack, Arbitrum, USDC, OFT, SplitBridge
  • Run relayer + monitor against staging to confirm balance reports render correctly

🤖 Generated with Claude Code

@nicholaspai nicholaspai marked this pull request as ready for review March 20, 2026 03:10
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 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".

nicholaspai and others added 10 commits March 20, 2026 09:44
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>
- 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>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ac0a1ebc0

ℹ️ 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".

@nicholaspai
Copy link
Copy Markdown
Member Author

@codex can you respond to all outstanding threads that you started

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@nicholaspai
Copy link
Copy Markdown
Member Author

@codex

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 223e51f0fd

ℹ️ 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".

@nicholaspai
Copy link
Copy Markdown
Member Author

@codex

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b5c00cbd4

ℹ️ 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".

@nicholaspai
Copy link
Copy Markdown
Member Author

@codex review for security regressions

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6f34ddf45

ℹ️ 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".

@nicholaspai nicholaspai closed this Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant