feat(solana): sendSponsoredSignedTransaction — sponsor + co-sign + broadcast a caller-built tx#35
Merged
Merged
Conversation
…oadcast a caller-built tx EVM-paymaster parity for a SERVER-BUILT transaction (the trading planner's solana-tx legs): the caller builds the tx with the user as placeholder fee payer; this method swaps in the Alchemy fee payer (alchemy_requestFeePayer, sig + CPI-rent prefund), adds the user's Privy signature, and broadcasts + confirms — returning the on-chain signature. Reuses the existing sponsored plumbing (requestFeePayer, signTransactionViaPrivy, broadcastAndConfirm, withFeePayerRetry + minContextSlot lag handling). Sponsorship-only: no self-pay fallback — absent proxy/auth or a sponsor refusal throws, so the caller re-plans instead of silently billing user SOL. Unlike sendSponsoredTransaction(instructions), the blockhash is fixed by the caller: retries re-run sponsor→sign→broadcast on the same bytes within the blockhash window. Proven on mainnet via acp-cli feat/20260717: tx 38NSvXFJ…ouUw, fee payer DmxEUYwg… (Alchemy), 2 sigs, user paid 0 SOL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a3a955f. Configure here.
…tes sponsored sends (bugbot) - withFeePayerRetry gains retryExpired (default true, preserving the instruction path): sendSponsoredSignedTransaction opts out, because an "expired" confirmation on unchangeable tx bytes can never land on a retry — it now propagates immediately for the caller to rebuild. - The confirmation bound is resolved ONCE (caller-supplied lastValidBlockHeight, else the first attempt's tip window as a strict upper bound) and held across retries; re-reading the tip per attempt slid the expiry window forward each retry and kept polling a provably dropped transaction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Zuhwa
approved these changes
Jul 17, 2026
Zuhwa
added a commit
that referenced
this pull request
Jul 18, 2026
* feat: add solana support * update solana IDLs and codama clients * merge dev * feat: add PrivySolanaProviderAdapter and update SolanaSigner type in provider interfaces * chore: update dependencies in package.json and package-lock.json, remove unused generated files from Solana ACP client * feat: refactor AcpAgent to support multiple clients and enhance job session handling * refactor: remove signTypedData method and related agentAuth functionality; update authentication flow to use signMessage instead * feat: implement new fund transfer encoding functions and update related parameters for Solana integration * feat(solana): full multi-hook-router + subscription support * feat: add signTypedData, sponsored flag for privySolanaProviderAdapter * fix(solana): address commitment and slot issues * fix(solana): poll ALT readiness in createLut instead of fixed sleep * fix(solana): retry fee-payer sponsorship on sponsor-node simulation lag * refactor(solana): enhance fee-payer retry logic for sponsored ACP transactions * fix(solana): support no-evaluator fund-hook submit via delegate approve and per-action hook routing * feat(solana): parameterized fund-request and escrow intents for fund-transfer hook * feat(solana): over-budget fund-request intents via typed proposal and client approve bracket * feat(solana): harden sponsored fee-payer retries and add no-evaluator sentinel * feat(solana): encode reason and deliverable into 32-byte on-chain slots * feat(solana): confirm submitted transactions and harden sponsored fee-payer retries * refactor(solana)!: drop keypair adapter export * feat(solana): fail fast on expired jobs and format sponsored-send errors * fix(solana): fail fast on expired jobs for fund and complete sends * fix(solana): re-prepare and resend when a prepared transaction goes stale * feat(solana)!: derive hook intent PDAs from job id * fix(solana): gate setBudget retries on job expiry * chore(solana): update devnet program ids for fund-transfer-hook, multi hook router, and subscription-hook * feat(solana)!: regenerate clients for updated programs with last-issued job_counter, optional claimRefund accounts, and afterAction caller * chore(solana): update devnet program ids for acp and all three hooks * feat: add solana examples, update contracts * feat(solana): sendSponsoredSignedTransaction — sponsor + co-sign + broadcast a caller-built tx (#35) * feat(solana): sendSponsoredSignedTransaction — sponsor + co-sign + broadcast a caller-built tx EVM-paymaster parity for a SERVER-BUILT transaction (the trading planner's solana-tx legs): the caller builds the tx with the user as placeholder fee payer; this method swaps in the Alchemy fee payer (alchemy_requestFeePayer, sig + CPI-rent prefund), adds the user's Privy signature, and broadcasts + confirms — returning the on-chain signature. Reuses the existing sponsored plumbing (requestFeePayer, signTransactionViaPrivy, broadcastAndConfirm, withFeePayerRetry + minContextSlot lag handling). Sponsorship-only: no self-pay fallback — absent proxy/auth or a sponsor refusal throws, so the caller re-plans instead of silently billing user SOL. Unlike sendSponsoredTransaction(instructions), the blockhash is fixed by the caller: retries re-run sponsor→sign→broadcast on the same bytes within the blockhash window. Proven on mainnet via acp-cli feat/20260717: tx 38NSvXFJ…ouUw, fee payer DmxEUYwg… (Alchemy), 2 sigs, user paid 0 SOL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(solana): terminal expiry + stable confirmation bound for fixed-bytes sponsored sends (bugbot) - withFeePayerRetry gains retryExpired (default true, preserving the instruction path): sendSponsoredSignedTransaction opts out, because an "expired" confirmation on unchangeable tx bytes can never land on a retry — it now propagates immediately for the caller to rebuild. - The confirmation bound is resolved ONCE (caller-supplied lastValidBlockHeight, else the first attempt's tip window as a strict upper bound) and held across retries; re-reading the tip per attempt slid the expiry window forward each retry and kept polling a provably dropped transaction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * feat(solana)!: support mainnet and devnet --------- Co-authored-by: ariessa <ariessa@users.noreply.github.com> Co-authored-by: Zuhwa <zuhwa@virtuals.io> Co-authored-by: Andrew Khor <andrew@virtuals.io> Co-authored-by: miratisu_virtuals <89718498+psmiratisu@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Adds a public `sendSponsoredSignedTransaction(serializedTxBase64)` to `PrivySolanaProviderAdapter` — EVM-paymaster parity for a server-built transaction (the trading planner's `solana-tx` legs).
Why
The existing sponsored path (`sendInstructions` → `sendSponsoredTransaction`) gates on `isAcpAction` (ACP program ids) and rebuilds the tx from instructions, so it can't sponsor a swap/bridge tx the trading planner already built. This method takes the prebuilt tx (user as placeholder fee payer) and runs the same sponsorship plumbing: `alchemy_requestFeePayer` (fee-payer swap + sig + CPI-rent prefund) → Privy user co-sign → broadcast + confirm, wrapped in the existing `withFeePayerRetry` / `minContextSlot` lag hardening.
Proven live (mainnet)
tx `38NSvXFJsWkcRSA7xXBwVmXEY13vKwSjbm8qFmg6hMmkRqF1aYRfV6m4GzN6XnDhssy7uLkBUE93jcJsujSrouUw` — 2 sigs, fee payer `DmxEUYwgTX2qYnsf29LXBBScxYkKp1yn1Doodqf95SP7` (Alchemy), user paid 0 SOL, driven through acp-cli `feat/20260717` + the prod ACP proxy (`/wallets/solana-rpc/501`), which forwards `alchemy_requestFeePayer` for non-ACP txs.
Consumers
Merge this first — both consumers depend on it.
🤖 Generated with Claude Code
Note
Medium Risk
New public submission path for sponsored Solana trades touches fee-payer RPC, signing, and confirmation/retry semantics; behavior is constrained by sponsorship-only errors and explicit non-retry on expiry for fixed transactions.
Overview
Adds
sendSponsoredSignedTransactiononPrivySolanaProviderAdapterso a caller-built serialized transaction (planner swap/bridge legs with the user as placeholder fee payer) can go through the same sponsorship pipeline as instruction-based sends:alchemy_requestFeePayer→ Privy co-sign → broadcast/confirm insidewithFeePayerRetry, with no self-pay fallback if sponsorship is unavailable.Because retries rebroadcast the same bytes (fixed blockhash), confirmation uses a
lastValidBlockHeightbound resolved once across attempts (optional from the builder, otherwise from the firstgetLatestBlockhash), andretryExpired: falseso an expired blockhash fails fast for the caller to rebuild.withFeePayerRetrygainsFeePayerRetryOptions.retryExpired(default unchanged): whenfalse,SolanaTransactionErrorwith phase"expired"is no longer retried—needed for fixed-blockhash senders while the instruction path keeps rebuilding with fresh blockhashes.Reviewed by Cursor Bugbot for commit 41fe171. Bugbot is set up for automated code reviews on this repo. Configure here.