Skip to content

feat(solana): sendSponsoredSignedTransaction — sponsor + co-sign + broadcast a caller-built tx#35

Merged
Zuhwa merged 2 commits into
feat/solana-devfrom
feat/solana-trade-sponsorship
Jul 17, 2026
Merged

feat(solana): sendSponsoredSignedTransaction — sponsor + co-sign + broadcast a caller-built tx#35
Zuhwa merged 2 commits into
feat/solana-devfrom
feat/solana-trade-sponsorship

Conversation

@psmiratisu

@psmiratisu psmiratisu commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

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.

  • Sponsorship-only: no self-pay fallback — missing proxy/auth or sponsor refusal throws; the caller re-plans instead of silently billing the user's SOL.
  • The tx's blockhash is fixed by the caller: retries re-run sponsor→sign→broadcast on the same bytes within the blockhash window; a stale blockhash surfaces as a terminal error to rebuild.

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

  • acp-cli `feat/20260717` (`aa49860`): routes `sponsoredSubmit`-flagged trade legs through this method.
  • trading-agent `430e1bf`: flags the leg + records the returned on-chain signature.

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 sendSponsoredSignedTransaction on PrivySolanaProviderAdapter so 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 inside withFeePayerRetry, with no self-pay fallback if sponsorship is unavailable.

Because retries rebroadcast the same bytes (fixed blockhash), confirmation uses a lastValidBlockHeight bound resolved once across attempts (optional from the builder, otherwise from the first getLatestBlockhash), and retryExpired: false so an expired blockhash fails fast for the caller to rebuild.

withFeePayerRetry gains FeePayerRetryOptions.retryExpired (default unchanged): when false, SolanaTransactionError with 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.

…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>

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ 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.

Comment thread src/providers/solana/privySolanaProviderAdapter.ts
Comment thread src/providers/solana/privySolanaProviderAdapter.ts
…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
Zuhwa merged commit f7b01c4 into feat/solana-dev Jul 17, 2026
1 check passed
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>
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