Skip to content

fix(inventory): Fix cross-chain refund under-counting in BundleDataApproxClient#3172

Merged
nicholaspai merged 6 commits intomasterfrom
fix/bundle-approx-cross-chain-refund-counting
Apr 9, 2026
Merged

fix(inventory): Fix cross-chain refund under-counting in BundleDataApproxClient#3172
nicholaspai merged 6 commits intomasterfrom
fix/bundle-approx-cross-chain-refund-counting

Conversation

@nicholaspai
Copy link
Copy Markdown
Member

Summary

  • Bug: BundleDataApproxClient.getApproximateRefundsForToken filtered fills using fromBlocks[fillChain], which is derived from the last executed bundle on the fill chain. When a bundle was executed on the fill chain but the root bundle relay had not yet propagated to the repayment chain (normal cross-chain delay of ~10-20 minutes), fills with cross-chain repayment were incorrectly excluded from upcoming refunds. This caused the relayer to under-estimate pending refunds on the repayment chain during the relay propagation window.
  • Fix: getUnexecutedBundleStartBlocks now returns a 2D block mapping (result[referenceChain][fillChain]) containing the matched bundle's end blocks for all chains, not just the reference chain. Refund filtering uses fromBlocks[repaymentChainId][fillChain] so fills are only excluded when the repayment chain's refund leaf has actually been executed. Deposit filtering uses the diagonal (fromBlocks[chainId][chainId]), preserving existing behavior.
  • Test: Added regression test that creates a fill on chain A with repayment on chain B, where chain A has executed the bundle but chain B has not received the relay. Verifies the fill is correctly counted as an upcoming refund on chain B.

Root cause

Each bundle cycle, the dataworker executes a bundle on the hub chain and then relays root bundles to each spoke chain. On the hub chain (Ethereum), execution is immediate since it's same-chain. On L2s, the relay takes 10-20+ minutes to propagate. The BundleDataApproxClient typically runs shortly after a new bundle is proposed — at which point the previous bundle's relay may not have reached all spoke chains yet.

The old code computed a 1D fromBlocks[chainId] per chain based on that chain's own spoke pool events. A fill on chain X with repaymentChainId = Y was filtered by fromBlocks[X]. When chain X advanced (bundle executed there) but chain Y hadn't received the relay, the fill was excluded even though the relayer hadn't received the refund on chain Y.

Changes

File Change
src/clients/BundleDataApproxClient.ts getUnexecutedBundleStartBlocks returns 2D mapping; getApproximateRefundsForToken indexes by [repaymentChainId][fillChainId]; getApproximateDepositsForToken uses diagonal
test/BundleDataApproxClient.ts New regression test for cross-chain relay delay scenario; updated existing tests for 2D structure
test/mocks/MockSpokePoolClient.ts Added addRelayerRefundExecution method (replaces as any cast)
test/mocks/MockBundleDataApproxClient.ts Updated override signatures to match 2D types

Test plan

  • All existing BundleDataApproxClient tests pass (10/10)
  • New regression test verifies cross-chain refund counting with relay delay
  • Verify no impact on inventory rebalancing behavior in staging

🤖 Generated with Claude Code

…proxClient

When a bundle is executed on chain A but the root bundle relay has not
yet propagated to chain B (normal cross-chain delay of ~10-20 minutes),
fills on chain A with repaymentChainId = B were incorrectly excluded
from upcoming refunds. The filter used fromBlocks[fillChain] which
advanced when the fill chain processed the bundle, even though the
repayment chain's refund leaf had not been executed yet.

Fix: expand getUnexecutedBundleStartBlocks to return a 2D block mapping
(result[referenceChain][fillChain]) so that refund filtering uses the
repayment chain's execution state (fromBlocks[repaymentChainId][fillChain])
rather than the fill chain's own state. Deposit filtering uses the
diagonal (fromBlocks[chainId][chainId]) preserving existing behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@pxrl
Copy link
Copy Markdown
Collaborator

pxrl commented Apr 9, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ 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 merged commit 9b5caed into master Apr 9, 2026
4 checks passed
@nicholaspai nicholaspai deleted the fix/bundle-approx-cross-chain-refund-counting branch April 9, 2026 12:46
pxrl pushed a commit that referenced this pull request Apr 9, 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.

3 participants