Skip to content

improve: parameterize Arbitrum_RescueAdapter L2 recipient - #1496

Open
droplet-rl wants to merge 2 commits into
masterfrom
droplet/C0BHMM63D9Q-1784437086-818009
Open

improve: parameterize Arbitrum_RescueAdapter L2 recipient#1496
droplet-rl wants to merge 2 commits into
masterfrom
droplet/C0BHMM63D9Q-1784437086-818009

Conversation

@droplet-rl

Copy link
Copy Markdown
Contributor

Motivation

~13.92 ETH of gas-refund dust has accrued at the HubPool's aliased address on Arbitrum (0xd297fA914353c44B2e33EBE05F21846f1048CFeB), because relayTokensl1ERC20GatewayRouter.outboundTransfer gives no way to direct L2 refunds. The existing mainnet Arbitrum_RescueAdapter deployment (0xC6fA0a4EBd802c01157d6E7fB1bbd2ae196ae375, Dec 2022) can't direct the rescue: its recipient is immutable and set to msg.sender at construction (the deployer EOA), and its deployed bytecode was compiled against the pre-Nitro inbox interface (createRetryableTicketNoRefundAliasRewrite). A fresh deployment is needed to send the rescued ETH to the intended recipient (the Risk Labs relayer 0x07aE…70E67, matching where the production Arbitrum_Adapter already sends its L2 gas refunds).

Context: Slack thread C0BHMM63D9Q/1784437086.818009 (Arbitrum orphaned funds recovery).

Changes

  • Arbitrum_RescueAdapter.sol: take the L2 recipient as an explicit constructor arg (l2Recipient) instead of msg.sender; import the inbox interface directly from interfaces/ArbitrumBridge.sol; drop unused SafeERC20/IERC20 imports; refresh classic-era comments (the source already targets post-Nitro unsafeCreateRetryableTicket). Gas parameters are unchanged from the battle-tested 2022 values — overprovisioning is refunded to the recipient on L2.
  • ArbitrumMocks.sol: add unsafeCreateRetryableTicket to the mock Inbox with the same call-tracking pattern as createRetryableTicket.
  • DeployArbitrumRescueAdapter.s.sol: new Foundry deploy script mirroring DeployArbitrumAdapter.s.sol, recipient defaulted to the Risk Labs relayer.
  • Arbitrum_RescueAdapter.t.sol: new tests covering the HubPool delegatecall flow (correct inbox args and L1 call value from HubPool balance), insufficient-ETH revert (both wrapped and direct), and relayTokens revert.

Intended usage

Owner multisig executes atomically: setCrossChainContracts(42161, rescueAdapter, spoke)relaySpokePoolAdminFunction(42161, abi.encode(amount)) → restore production adapter. The amount is funded from the aliased address's L2 balance; the 0.02 ETH L1 call value comes from the HubPool's existing ETH.

Test

yarn test-evm-foundry -- --match-path "*Arbitrum*" → 31 passed, 0 failed (4 new). Deploy script compiles. Prettier/solhint clean (remaining solhint warnings match pre-existing file style).

No repo docs reference the rescue adapter; contract natspec updated in place, so no separate doc updates needed.

🤖 Generated with Claude Code

The deployed (Dec 2022) Arbitrum_RescueAdapter hardcodes its L2 recipient
to the deployer address and was compiled against the pre-Nitro inbox
interface. This updates the adapter to take the recipient as an explicit
constructor arg (instead of msg.sender) so a fresh deployment can direct
rescued ETH to a chosen address, refreshes the stale classic-era comments
(the source already targets the post-Nitro unsafeCreateRetryableTicket),
and adds a Foundry deploy script plus tests covering the HubPool
delegatecall flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: droplet-rl <284132418+droplet-rl@users.noreply.github.com>
@droplet-rl
droplet-rl force-pushed the droplet/C0BHMM63D9Q-1784437086-818009 branch from 12da511 to 38df356 Compare July 19, 2026 05:46

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

Copy link
Copy Markdown

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: 12da511cc3

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

l1Inbox = _l1ArbitrumInbox;

l2RefundL2Address = msg.sender;
l2Recipient = _l2Recipient;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject a zero L2 rescue recipient

When _l2Recipient is accidentally passed as address(0), the immutable recipient cannot be corrected and relayMessage will send the rescued l2CallValue plus all retryable refunds to the zero address, permanently burning the ETH during the one-off rescue. Since this constructor now parameterizes the fund recipient, add a nonzero check before storing it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — added if (_l2Recipient == address(0)) revert InvalidL2Recipient(); in the constructor, plus a Foundry test (test_constructor_RevertsOnZeroRecipient) covering it.

Addresses Codex review feedback: a zero _l2Recipient would permanently
burn the rescued ETH and all retryable refunds, and the immutable cannot
be corrected after deployment. Revert with InvalidL2Recipient in the
constructor and add a test covering it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@droplet-rl

Copy link
Copy Markdown
Contributor Author

Addressed the Codex review feedback: the constructor now reverts with InvalidL2Recipient() when _l2Recipient is address(0), so a misdeployment can't burn the rescued ETH. Added test_constructor_RevertsOnZeroRecipient to the Foundry test suite — all 5 tests pass.

@codex review — please re-review the updated diff.

@droplet-rl

Copy link
Copy Markdown
Contributor Author

🔎 View trace

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

Copy link
Copy Markdown

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: 38df356044

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

Comment on lines +49 to +51
constructor(ArbitrumL1InboxLike _l1ArbitrumInbox, address _l2Recipient) {
l1Inbox = _l1ArbitrumInbox;

l2RefundL2Address = msg.sender;
l2Recipient = _l2Recipient;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject a zero L2 recipient

When a deployment supplies address(0) for _l2Recipient, this immutable is accepted and later used as the retryable ticket destination plus both refund addresses, so the rescue call would send the aliased HubPool ETH and all refunds to the zero address on Arbitrum. This regresses from the previous msg.sender recipient, which could not be zero, and makes a misdeployment able to permanently lose the funds this adapter is meant to recover.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This review ran against the pre-fix commit 38df356 (race with the push). Already addressed in 1f283ab: the constructor reverts with InvalidL2Recipient() on a zero _l2Recipient, with a Foundry test (test_constructor_RevertsOnZeroRecipient) covering it.

@droplet-rl

Copy link
Copy Markdown
Contributor Author

This second Codex review ran against the pre-fix commit 38df356 (it raced with the push of the fix), so its only finding — the zero L2 recipient check — is already addressed at the current PR head 1f283ab. No further changes needed; I replied on the inline thread pointing to the fix.

@droplet-rl

Copy link
Copy Markdown
Contributor Author

🔎 View trace

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