Skip to content

fix(svm): repin V5 Gateway and prefunded rent refunds - #1565

Open
Reinis-FRP wants to merge 4 commits into
reinis/svm-v5-core-simplificationfrom
reinis/svm-v5-gateway-repin
Open

Reinis-FRP wants to merge 4 commits into
reinis/svm-v5-core-simplificationfrom
reinis/svm-v5-gateway-repin

Conversation

@Reinis-FRP

@Reinis-FRP Reinis-FRP commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

The SVM V5 adapter and conformance fixtures targeted the old Gateway and PrefundedAdapter deployment. Repin to e2b91eb0454136773728f941b33163346e039aa4, update program IDs and canonical PDAs/bumps, and regenerate the affected deposit-ID, signature and path fixtures.

Prefunded integration injects the payer's rent_refund PDA instead of the payer account. The real-Gateway tests verify credit closure, rent custody and a separate claim_rent transaction signed and paid for by an unrelated claimer. They also cover remainder transfers and zero-transfer recipient omission.

Restacked on #1564's final self-transfer implementation. Preserve its approval regressions and cleared-allowance assertion alongside the rent-refund coverage. Adapt the zero-transfer test to select the floor/transfer suffix after the new approval commands.

Validation after restacking: 16 Rust tests, strict TypeScript checking, and 9 focused real-Gateway tests passed, covering guarded Token-2022 funding, external fills/rent reclaim, missing/wrong approvals, separate prefunded rent claims, remainder transfers and zero transfers. Guarded SBF builds and formatting passed. This is focused local validation; the full verified-build and conformance suites were not rerun locally for the restack.

Targets #1564 (reinis/svm-v5-core-simplification).


Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from 66f8661 to 53aaca1 Compare September 25, 2026 14:19

@droplet-rl droplet-rl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving. I recomputed every regenerated constant from scratch (standalone keccak-256, secp256k1 recovery and find_program_address implementations, no repo tooling) and cross-checked the pin against across-protocol/solana-v5@e2b91eb0 — everything matches.

Independently verified

Program identity:

  • gatewayBytes and v5_gateway_path.json#executor both equal the base58 decode of pVs6PJ3ofdqPyDhCXXdVW7waG6oNnwKQBKtuM6Mi6JP.
  • GATEWAY_VAULT_AUTHORITY = PDA(["vault_authority"], gateway) → GLU32tN1sN…5NVH, bump 255 ✓. This also matches upstream programs/gateway/src/constants.rs at the pin, which moved to the same key/bump.
  • GATEWAY_DISPATCH_AUTHORITY = PDA(["dispatch_authority", svm_spoke], gateway) → 8xGrXYfSb2…ARQF, bump 255 ✓ (seed set confirmed against hardcoded_v5_authorities_match_canonical_pdas).
  • svmSpokeBytes correctly left unchanged.

Fixture regeneration (v5_adapter_v1.json):

  • syntheticNonce unchanged and still correct; depositId = keccak(gateway ‖ depositor ‖ syntheticNonce) → 0xd3a75ad8…dbe2 ✓.
  • domain = keccak(nameHash ‖ gateway) → 0xd41f3c41…98d8 ✓; digest ✓; nameHash correctly unchanged.
  • signature recovers to 0xf39Fd6e5…2266, matching jit.authority; s is canonical low-s; highSSignature is the exact n − s complement with v flipped 28→27 — so the negative-path vector is still a genuine high-s rejection, not a coincidentally-invalid blob.
  • fillStatus = PDA(["fills", depositId], spoke) → 64SUuEZc…RJFz, bump 255 ✓. depositDelegate/fillDelegate/fillPayer correctly untouched (spoke-derived, gateway-independent).
  • Wire blobs self-consistent: depositJit == newOutputAmount ‖ newExclusiveRelayer ‖ signature, fillJit embeds the new depositId, dispatch.data ends with the new depositJit and still contains depositInput.

v5_gateway_path.json: pathId, siblingPathId, sorted-pair stepRoot and PREFIX ‖ stepRoot witness all recompute exactly.

Upstream pin: commit e2b91eb0 exists; its Anchor.toml declares gateway = pVs6PJ3… and prefunded_adapter = 8kUXfjTu…, matching reference.ts. RENT_REFUND_SEED == b"rent_refund", ClaimRent's account order is exactly (payer, rent_refund, system_program) as the test builds it, and DeliveryAccounts::resolve now resolves rent_refund_address(payer) + validate_rent_refund instead of the payer — so the JIT layout credit ‖ rent_refund ‖ payer(32) and the two writable injected slots line up with the adapter's jit_data being a bare 32-byte payer key.

Hygiene: no stale reference to 34trBszX…, 7S5DKhyg…, 457cf693 or the old gatewayBytes survives anywhere in the tree. Test-side vault/executor/dispatch authorities are all derived from GATEWAY rather than hardcoded, so they follow the repin automatically. The new origin() assertions are order-safe: rentRefundBefore is snapshotted per-call, and the claim_rent test drains the PDA only after the assertion, so later prefunded tests re-baseline at zero.

Comments below are non-blocking follow-ups, mostly about what the repin silently adopts.

Comment thread programs/svm-spoke/V5_ADAPTER_SPEC.md
Comment thread test/svm-gateway/reference.ts
Comment thread test/svm-gateway/RealGateway.ts Outdated
Comment thread test/svm-gateway/README.md
@droplet-rl

Copy link
Copy Markdown
Contributor

🔎 View trace

@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from 53aaca1 to 567f7cc Compare September 25, 2026 14:46
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch 2 times, most recently from 8c8812d to 959567a Compare September 25, 2026 15:05
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from 567f7cc to a2fb97c Compare September 25, 2026 15:05
@droplet-rl

Copy link
Copy Markdown
Contributor

🔎 View trace

@Reinis-FRP

Copy link
Copy Markdown
Contributor Author

@droplet-rl please recheck this PR with the latest review responses

@droplet-rl

Copy link
Copy Markdown
Contributor

Rechecked at 7970a2b. All four review points are addressed, so I've resolved the threads. Approval stands and no code changes are needed from me.

Rebase: after the rebase onto the updated #1564, git range-diff shows a2fb97c has the same patch as the 53aaca1 I verified before. The program IDs, PDAs and bumps, and regenerated fixture values I checked independently are unchanged.

Follow-ups in 7970a2b:

  • Pinning the Gateway ID: assert_eq!(GATEWAY_PROGRAM_ID.to_bytes(), …"/executor") ties v5_gateway_path.json to the runtime constant. Since PathVectors.ts already checks reference.ts#GATEWAY against that fixture, the TS reference is now tied to the constant too. GATEWAY_PROGRAM_ID was already imported in tests.rs.

  • Permissionless claim_rent: a fresh keypair is the only signer and pays the fee, and owner is a writable non-signer. A legacy Transaction would refuse to serialize if owner still had to sign, so this now proves the claim needs no payer signature. payerBefore is read after funding the claimer, so payerBefore + refund is exact. 1,000,000 lamports stays above the ~890,880 rent-exempt minimum even after the fee.

  • Remainder test: this checks out because beforeEach creates a new mint, which means vault and recipientAta start at 0. The first two cases (reserve above and at the balance) resolve to zero and do nothing. Only the amount − 123 case moves tokens, so the running totals work. It would also fail on the old Gateway, which rejects bips ≠ 0 with a non-sentinel raw (InvalidAmountEncoding), so it genuinely tests the new behaviour.

  • Zero-transfer test: the three-step sequence works well:

    1. On an empty vault, the transfer succeeds even though the recipient ATA doesn't exist.
    2. The canonical floor rejects the empty vault with BalanceRequirementNotMet.
    3. After funding, the same transfer fails with MissingRequiredAccount.

    Step 3 is what proves step 1 passed because of the zero no-op rather than because the lookup isn't required. Each path() gets a random salt, so running it again doesn't hit root-reuse rejection.

  • Docs: the spec and README text on max(balance − raw, 0) and the zero-transfer early return is accurate. So is the point that a zero floor gives no positive-delivery guarantee. The rescue scope note holds up too: tests/gateway.ts#L9520 at the pin is "rescue parks rent for its payer and rejects another payer's credit."

Optional nit: connection.confirmTransaction(fundingSignature, "confirmed") uses web3.js's deprecated string-signature overload. It still works and I wouldn't change it just for that.

I couldn't run the real-Gateway suite here, so the 25 passing are from your run. I checked the new tests by reading them against the fixture setup and the upstream Gateway source.

@droplet-rl

Copy link
Copy Markdown
Contributor

🔎 View trace

@Reinis-FRP

Copy link
Copy Markdown
Contributor Author

Fixed the optional confirmation-overload nit from the latest recheck in 9b82336. Claimer funding now uses sendAndConfirmTransaction at confirmed, which confirms with the transaction's blockhash and last-valid block height, without the deprecated string-signature overload.

The affected real-Gateway prefunded integration test, test type-checking, formatting and commit hooks pass. The commit includes DCO sign-off.


Sent from Reinis Martinsons's Codex Agent using gpt-6-astra 🤖

@Reinis-FRP
Reinis-FRP marked this pull request as ready for review September 28, 2026 10:35
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 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-28T10:37:19.482644Z 9b82336 Draft marked ready
ℹ️ 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.

@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from 9b82336 to cd0fd1a Compare September 28, 2026 11:04
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch 2 times, most recently from c0c1ebd to 982580e Compare September 28, 2026 13:09
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch 2 times, most recently from b062034 to d4faa63 Compare September 28, 2026 18:42
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 982580e to e915cf5 Compare September 28, 2026 18:42
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from d4faa63 to cad6931 Compare September 29, 2026 09:47
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from e915cf5 to 58b936f Compare September 29, 2026 09:47
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from eff487b to 344b1ba Compare September 30, 2026 08:21
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from f8dea0e to 248223d Compare September 30, 2026 08:21
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 344b1ba to 714af9c Compare September 30, 2026 08:46
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch 2 times, most recently from 720446e to a9c09e9 Compare September 30, 2026 09:48
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 714af9c to 00cba19 Compare September 30, 2026 09:48
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from a9c09e9 to 2594146 Compare September 30, 2026 10:27
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 00cba19 to 0e000a9 Compare September 30, 2026 10:27
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 0e000a9 to 31ace75 Compare September 30, 2026 13:48
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from 2594146 to 7f3e357 Compare September 30, 2026 13:48
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 31ace75 to 966a064 Compare September 30, 2026 15:57
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from 7f3e357 to 3b9fec2 Compare September 30, 2026 15:57
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 966a064 to 0299d0c Compare September 30, 2026 16:41
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch 4 times, most recently from ccd106c to 81a2211 Compare October 1, 2026 07:00
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 9d7898a to 0a6b5ab Compare October 1, 2026 07:00
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-core-simplification branch from 0a6b5ab to bb37ba1 Compare October 5, 2026 07:48
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch 2 times, most recently from 8861526 to 48ef30e Compare October 5, 2026 11:27
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-v5-gateway-repin branch from 48ef30e to e210d80 Compare October 5, 2026 12:04
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