Skip to content

feat: add CloseLamportsPda recovery for stranded sponsored transfers - #139

Merged
GabrielePicco merged 1 commit into
mainfrom
feat/close-lamports-pda
Sep 14, 2026
Merged

GabrielePicco merged 1 commit into
mainfrom
feat/close-lamports-pda

Conversation

@GabrielePicco

@GabrielePicco GabrielePicco commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Why

When the post-delegation actions of a SponsoredLamportsTransfer fail on the ER (for example when ER and base disagree on rent parameters), the validator rescue-undelegates the lamports PDA on base with the principal still inside. The program had no way to return it: the only close path is the post-undelegate Magic intent, which needs the DLP escrow signer, so the funds stayed locked in the PDA.

What

New public instruction 33 CloseLamportsPda, permissionless, base-layer only:

  • accounts: payer (w, receives principal), rent_pda (w), lamports_pda (w), destination (derivation only)
  • data: salt: [u8; 32]
  • refunds balance - min_balance(0) to the payer from the [b"lamports", payer, destination, salt] derivation, returns the rent share to the global rent PDA, closes the account

Safety properties:

  • a delegated (in-flight) PDA is owned by the delegation program on base, so the owner check rejects it; creation and delegation are atomic, so only rescued PDAs qualify
  • both recipients are bound to canonical addresses (derivation + validate_rent_pda), so the caller cannot redirect funds
  • payer == rent_pda (queue-refill PDAs) works: the two credits are applied sequentially on the aliased account
  • on the ER the rent PDA is a non-delegated writable account, so the transaction is rejected there

Tests: refund + close, delegated PDA rejected, substituted payer rejected. Full cargo test-sbf suite passes.

Out of scope / follow-ups

  • close_lamports_pda_intent.rs does not bind the escrow authority to the PDA payer, so a foreign program's Magic action could push a rescued PDA's balance into the rent PDA (griefing, not theft). Suggested fix is escrow_authority == payer; separate PR.
  • transfer_lamports_pda.rs:51 still requires the ER's own rent minimum; dropping the rent term would prevent the failure mode entirely. Separate PR.
  • IDL not regenerated (it has not tracked instructions 29–32 either).

When the post-delegation actions of a SponsoredLamportsTransfer fail on
the ER (e.g. a rent-parameter mismatch after a SIMD-0437 step), the
validator rescue-undelegates the lamports PDA with the principal still
inside and there was no base-layer path to recover it.

Add public instruction 33, CloseLamportsPda: a permissionless base-only
recovery that refunds everything above the sponsored rent to the payer
from the PDA derivation, returns the rent share to the global rent PDA
and closes the PDA. Delegated (in-flight) PDAs are rejected by the
owner check, and the recipients are bound to the derivation so a caller
cannot redirect funds.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T19:56:57.390504Z 02420de PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 7cb0351a-5e5b-4e4c-a4a8-030bb286209e

📥 Commits

Reviewing files that changed from the base of the PR and between e240d72 and 02420de.

📒 Files selected for processing (5)
  • e-token-api/src/instruction.rs
  • e-token/src/entrypoint.rs
  • e-token/src/processor/close_lamports_pda.rs
  • e-token/src/processor/mod.rs
  • e-token/tests/lamports_pda.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

Adds the CloseLamportsPda instruction and dispatcher entry. The processor validates the PDA, refunds principal and rent, and closes the account. Tests cover successful recovery and invalid owner or seed cases.

Changes

Lamports PDA recovery

Layer / File(s) Summary
Instruction contract and dispatch
e-token-api/src/instruction.rs, e-token/src/entrypoint.rs
Defines instruction value 33 and routes CloseLamportsPda to its processor.
Recovery processor and module wiring
e-token/src/processor/close_lamports_pda.rs, e-token/src/processor/mod.rs
Validates the salt, account owner, account data, and PDA seeds. It refunds principal to the payer, credits rent to the rent PDA, and closes the lamports PDA.
Recovery integration tests
e-token/tests/lamports_pda.rs
Tests successful refunds and closure, delegated PDA rejection, and substituted payer rejection.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Suggested reviewers: snawaz

Merge Risk: ⚪ Minimal · up to 02420

The recovery instruction validates its canonical accounts and closes the stranded PDA as intended. No actionable merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the new CloseLamportsPda recovery instruction and its purpose for stranded sponsored transfers.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/close-lamports-pda

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@GabrielePicco
GabrielePicco merged commit b8b5de1 into main Sep 14, 2026
8 checks passed
@GabrielePicco
GabrielePicco deleted the feat/close-lamports-pda branch September 14, 2026 03:47
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.

2 participants