diff --git a/content/wallets/pages/gas-manager-admin-api/gas-coverage-api-endpoints/paymaster-api-endpoints.mdx b/content/wallets/pages/gas-manager-admin-api/gas-coverage-api-endpoints/paymaster-api-endpoints.mdx index c366a3619..331f39631 100644 --- a/content/wallets/pages/gas-manager-admin-api/gas-coverage-api-endpoints/paymaster-api-endpoints.mdx +++ b/content/wallets/pages/gas-manager-admin-api/gas-coverage-api-endpoints/paymaster-api-endpoints.mdx @@ -18,4 +18,4 @@ The Gas Coverage API is a set of methods that allow you to sponsor gas for your | [alchemy_requestPaymasterAndData](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-paymaster-and-data) | **\[To be deprecated]** Requests gas sponsorship for a `UserOperation` on EVM networks. If approved, returns`paymaster` and `paymasterData`. | | [pm_getPaymasterStubData](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/pm-get-paymaster-stub-data) | Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation on EVM networks. | | [pm_getPaymasterData](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/pm-get-paymaster-data) | Returns values to be used in paymaster-related fields of a signed user operation on EVM networks. These are not stub values and will be included in a signed user operation as part of an `eth_sendUserOperation` call. | -| [alchemy_requestFeePayer](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-fee-payer) | Request gas sponsorship for a `transaction` on Solana. Returns the `serializedTransaction`, including the `feePayer` signature. | +| [alchemy_requestFeePayer](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-fee-payer) | Request fee sponsorship for a Solana `transaction`. Returns the updated `serializedTransaction`, fee and rent estimates, and optional CPI rent-prefunding details. | diff --git a/content/wallets/pages/transactions/solana/api-prepare.mdx b/content/wallets/pages/transactions/solana/api-prepare.mdx index 9060eada5..6e48dc8dc 100644 --- a/content/wallets/pages/transactions/solana/api-prepare.mdx +++ b/content/wallets/pages/transactions/solana/api-prepare.mdx @@ -24,7 +24,7 @@ const message = new solanaWeb3.TransactionMessage({ }).compileToV0Message(); const transaction = new solanaWeb3.VersionedTransaction(message); -export const serializedTx = Buffer.from(transaction.serialize()).toString( +export const serializedTransaction = Buffer.from(transaction.serialize()).toString( "base64", ); ``` diff --git a/content/wallets/pages/transactions/solana/api-request.mdx b/content/wallets/pages/transactions/solana/api-request.mdx index 493a94c67..0252d9236 100644 --- a/content/wallets/pages/transactions/solana/api-request.mdx +++ b/content/wallets/pages/transactions/solana/api-request.mdx @@ -35,6 +35,12 @@ export const RPC_URL = `https://solana-devnet.g.alchemy.com/v2/${ALCHEMY_API_KEY export interface AlchemyFeePayerResponse { result?: { serializedTransaction: string; // base64 + estimatedFeeLamports: number; + estimatedRentLamports: number; + prefundLamports?: number; // omitted when no prefund simulation ran + simulationSlot?: number; // omitted when no prefund simulation ran + usesDurableNonce: boolean; + lastValidBlockHeight?: number; // omitted for durable-nonce transactions }; error?: any; } diff --git a/content/wallets/pages/transactions/solana/sponsor-gas-solana.mdx b/content/wallets/pages/transactions/solana/sponsor-gas-solana.mdx index ea49f3761..5176ad5d8 100644 --- a/content/wallets/pages/transactions/solana/sponsor-gas-solana.mdx +++ b/content/wallets/pages/transactions/solana/sponsor-gas-solana.mdx @@ -4,18 +4,20 @@ description: How to sponsor fees & rent on Solana slug: wallets/transactions/solana/sponsor-gas --- -Fees and rent are a significant barrier to entry for new users of your app. Sponsor fees and rent to enable users to transact without holding SOL. +Fees and rent are a significant barrier to entry for new users of your app. Sponsor them to let users transact without holding SOL. * Works with any Solana wallet. [Check out Solana Wallet APIs](/docs/wallets/react/solana-wallets/get-started) * No need to manage SOL. Fees and rent are sponsored and added to your bill. ## How it works -When you request gas sponsorship for a transaction using a configured policy, the policy engine determines if that transaction is eligible for sponsorship. If eligible, the Gas Manager pays for the fees and rent upfront when the transaction is sent. The Gas Manager records the sponsored cost and adds it to your monthly bill. +When you request sponsorship for a transaction using a configured policy, the policy engine determines if that transaction is eligible. If eligible, the Gas Manager sets the fee payer, signs the transaction, and returns the updated serialized transaction. The sponsored cost is added to your monthly bill. -* Fees: the cost of executing transactions -* Rent: the minimum payment to store data onchain - * Rent sponsorship is supported for `createAccount` and `createAssociatedTokenAccount`. If you need support for custom programs, contact [wallets@alchemy.com](mailto:wallets@alchemy.com). +* Fees: the cost of executing transactions. Alchemy's fee payer replaces yours on every sponsored transaction. +* Rent: the minimum payment to store data onchain. + * Top-level rent sponsorship: on every sponsored transaction, any top-level `SystemProgram.createAccount` and Associated Token Program `Create`/`CreateIdempotent` instructions are rewritten so the paymaster funds them directly. You do not need to opt in — this happens for all requests. + * CPI rent sponsorship: for account creation that happens inside a cross-program invocation (CPI), opt in per request with `prefundRent: true`. See [CPI rent prefunding](#cpi-rent-prefunding) below — this mode adds a hard requirement on `payerKey`. + * We do not support sponsoring rent for `SystemProgram.createAccountWithSeed`. ## Prerequisites @@ -35,9 +37,14 @@ When you request gas sponsorship for a transaction using a configured policy, th ### Request sponsorship for the serialized transaction - To sponsor fees and rent on Solana, 1) set the `payerKey` field of the transaction to the feePayer wallet that pays for the gas, and 2) the feePayer wallet must sign the transaction. + To sponsor fees on Solana, 1) set the `payerKey` field of the transaction, and 2) call `alchemy_requestFeePayer`. - Get the `feePayer` address and the feePayer signature through [`alchemy_requestFeePayer`](/docs/reference/alchemy-requestfeepayer) using your gas policy ID and the serialized transaction. The Gas Manager updates the `feePayer` and adds the signature to the `serializedTransaction` if and only if the transaction satisfies the rules defined in your policy. + `alchemy_requestFeePayer` updates the `feePayer` and adds the fee-payer signature if the transaction satisfies the rules defined in your policy. + + What to put at `payerKey` depends on the request: + + * **Default (fees + top-level rent):** any placeholder address that is not otherwise referenced in the transaction. The paymaster replaces it as the fee payer, and any top-level `createAccount`/`createAssociatedTokenAccount` instructions are rewritten so the paymaster funds them — the identity of the placeholder does not affect sponsorship. + * **With `prefundRent: true`:** the user's wallet, specifically the account your program's CPI will debit for rent. See [CPI rent prefunding](#cpi-rent-prefunding) for the full contract. @@ -48,3 +55,47 @@ When you request gas sponsorship for a transaction using a configured policy, th + +## CPI rent prefunding + +Use `prefundRent: true` to opt into cross-program invocation (CPI) rent prefunding when a transaction may create accounts through CPI. + + + This feature is allowlisted. Contact [support@alchemy.com](mailto:support@alchemy.com) to request access. + + +```json +{ + "policyId": "", + "serializedTransaction": "", + "prefundRent": true +} +``` + +### How it works + +When `prefundRent` is enabled, the service simulates the sponsored transaction to estimate any rent that may be needed for CPI-driven account creation. If needed, it adds a `SystemProgram.transfer` from the paymaster to the user's wallet — signaled by being the original `payerKey` on the submitted transaction, before the paymaster replaces it as fee payer — so the user's wallet holds enough SOL to cover the CPI's account creation. + +When a prefund simulation runs, the response includes `prefundLamports` and `simulationSlot` alongside the normal fee sponsorship fields. Both are omitted when no simulation ran — either because `prefundRent` was not requested or because the fee payer was replaced rather than injected, leaving no user wallet to top up. + +We support sponsoring rent for inner `SystemProgram.createAccount` calls. `SystemProgram.createAccountWithSeed` is not sponsored today. + +### Requirements + +* **`payerKey` MUST be set to the user's wallet** — the same account your program's CPI will debit for rent. This is different from fee and top-level rent sponsorship, where `payerKey` can be any placeholder address. With `prefundRent: true`, the service targets `payerKey` by position: it transfers SOL to whatever account sits there, and only detects inner `createAccount` calls funded by that same account. If `payerKey` is a placeholder (or any account other than the CPI funder), prefunding silently misses and the transaction will fail on-chain for lack of rent. +* **Set `maxSpendPerTxnUsd`** on every policy used with `prefundRent`. The request is rejected if the policy does not have this cap. + +### Best practices + +* Enable `prefundRent` only for flows that may create accounts through CPI. +* Submit the returned transaction promptly from your backend after collecting signatures. +* For extra protection against stale state, when the response includes `simulationSlot`, consider passing it as `minContextSlot` when submitting the transaction. + +### Notes + +* Simulation is not the same as execution. Account state can change between sponsorship and submission, and programs can take different CPI paths at execution time. + * `minContextSlot` reduces stale-state risk, but it does not guarantee identical execution. + * If execution needs more lamports than the simulation predicted, the transaction will fail. If execution needs less, the extra SOL remains in the funded wallet and is still billed to your policy. +* **Important:** Only use `prefundRent` for payloads you control or can validate. If an attacker can influence the transaction or the programs it invokes, they may be able to grief your policy by causing failed or unnecessarily expensive prefunds. +* `prefundRent` is not transaction-shape-preserving. When prefunding is needed, the returned transaction includes an added top-level `SystemProgram.transfer` instruction before your program flow. Programs that inspect sibling instructions or depend on exact top-level instruction ordering can behave differently or fail. +* Durable nonce transactions have a simulation caveat: the service simulates a modified copy without `AdvanceNonceAccount`, then returns a nonce-preserving transaction for submission. diff --git a/content/wallets/shared/infra/api-endpoints/gas-manager-sponsorship.mdx b/content/wallets/shared/infra/api-endpoints/gas-manager-sponsorship.mdx index 474934195..a1a689e78 100644 --- a/content/wallets/shared/infra/api-endpoints/gas-manager-sponsorship.mdx +++ b/content/wallets/shared/infra/api-endpoints/gas-manager-sponsorship.mdx @@ -8,5 +8,5 @@ The Gas Coverage API is a set of methods that allow you to sponsor gas for trans | [alchemy_requestPaymasterAndData](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-paymaster-and-data) | **\[To be deprecated]** Requests gas sponsorship for a `UserOperation` on EVM networks. If approved, returns`paymaster` and `paymasterData`. | | [pm_getPaymasterStubData](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/pm-get-paymaster-stub-data) | Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation on EVM networks. | | [pm_getPaymasterData](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/pm-get-paymaster-data) | Returns values to be used in paymaster-related fields of a signed user operation on EVM networks. These are not stub values and will be included in a signed user operation as part of an `eth_sendUserOperation` call. | -| [alchemy_requestFeePayer](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-fee-payer) | Request gas sponsorship for a `transaction` on Solana. Returns the `serializedTransaction`, including the `feePayer` signature. | +| [alchemy_requestFeePayer](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-fee-payer) | Request fee sponsorship for a Solana `transaction`. Returns the updated `serializedTransaction`, fee and rent estimates, and optional CPI rent-prefunding details. | | [alchemy_requestPaymasterTokenQuote](/docs/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-paymaster-token-quote) | Requests ERC20 fee quote for a `UserOperation`. Optionally accepts fee parameter overrides to be used in the `UserOperation` | diff --git a/src/openrpc/alchemy/gas-manager-coverage/gas-manager-coverage.yaml b/src/openrpc/alchemy/gas-manager-coverage/gas-manager-coverage.yaml index ad6e13f05..b25a2a725 100644 --- a/src/openrpc/alchemy/gas-manager-coverage/gas-manager-coverage.yaml +++ b/src/openrpc/alchemy/gas-manager-coverage/gas-manager-coverage.yaml @@ -646,16 +646,20 @@ methods: - name: "alchemy_requestFeePayer" description: | - Requests fee and rent sponsorship for a Solana transaction and returns the **fully-signed** `serializedTransaction` (now containing the fee-payer signature). + Requests fee sponsorship for a Solana transaction and returns the updated `serializedTransaction` with the paymaster fee payer set and signed. - We support rent sponsorship for `createAccount` and `createAssociatedTokenAccount` programs. If you need support for sponsoring rent in custom programs, please contact us at [wallets@alchemy.com](mailto:wallets@alchemy.com). + On every sponsored request, any top-level `SystemProgram.createAccount` and Associated Token Program `Create`/`CreateIdempotent` instructions are rewritten so the paymaster funds them directly — no opt-in required. For CPI-driven account creation, opt into rent prefunding with `prefundRent: true`; policies using `prefundRent` must set `maxSpendPerTxnUsd`. + + + + CPI rent prefunding is allowlisted. Contact [support@alchemy.com](mailto:support@alchemy.com) for access. params: - name: "params[0]" required: true - description: "Object containing the policy ID and the unsigned serialized transaction." + description: "Object containing the policy ID, serialized transaction, and optional rent-prefunding settings." schema: type: "object" required: ["policyId", "serializedTransaction"] @@ -675,17 +679,41 @@ methods: type: "string" pattern: "^[A-Za-z0-9+/]+={0,2}$" description: > - The unsigned transaction produced by `tx.serialize()`. - It may already contain a `feePayer`; the service will overwrite it - with the sponsoring account and add the fee-payer signature. + The serialized transaction produced by `tx.serialize()`. + The service replaces the existing `feePayer` with the sponsoring + account and adds the fee-payer signature. For default requests, + `payerKey` can be any placeholder address. For requests with + `prefundRent: true`, `payerKey` MUST be the user's wallet — the + same account the program's CPI will debit for rent — because + prefunding targets that position. + webhookData: + type: "string" + description: Optional metadata forwarded to your policy webhook. + prefundRent: + type: "boolean" + default: false + description: > + Opts into cross-program invocation (CPI) rent prefunding. Use + this when the transaction creates accounts through CPI and the + user's wallet needs SOL prefunded first. Requires two things: + (1) the policy must set `maxSpendPerTxnUsd`, and (2) the + transaction's `payerKey` MUST be the user's wallet (the account + the CPI debits for rent). Prefunding targets `payerKey` by + position, so a placeholder `payerKey` silently misses and the + transaction fails on-chain for lack of rent. result: name: "Sponsored transaction wrapper" description: > - Object containing the **fully-signed** transaction ready for - `sendRawTransaction`. + Object containing the sponsored transaction plus fee and rent estimates. schema: type: "object" - required: ["serializedTransaction"] + required: + [ + "serializedTransaction", + "estimatedFeeLamports", + "estimatedRentLamports", + "usesDurableNonce", + ] properties: serializedTransaction: title: "base-64-encoded Solana transaction" @@ -693,6 +721,37 @@ methods: description: > The transaction after the service sets the `feePayer` field and adds the fee-payer signature. + estimatedFeeLamports: + type: "integer" + description: Estimated network fee in lamports for the returned transaction. + estimatedRentLamports: + type: "integer" + description: Estimated rent in lamports covered by sponsorship. + prefundLamports: + type: "integer" + description: > + Lamports transferred to the transaction's `payerKey` to cover + CPI-driven account creation rent. Omitted when no prefund + simulation ran — i.e., `prefundRent` was not requested, or it + was requested but the fee payer was replaced rather than + injected so there was no user wallet to top up. + simulationSlot: + type: "integer" + description: > + Slot used for rent-prefunding simulation. Omitted when no + prefund simulation ran. When present, pass it as + `minContextSlot` when submitting the transaction to reduce + stale-state risk. Simulation is only an estimate: onchain state + or CPI behavior can change before execution, so prefunding may + still end up too low or higher than what is ultimately consumed. + usesDurableNonce: + type: "boolean" + description: Whether the returned transaction uses a durable nonce. + lastValidBlockHeight: + type: "integer" + description: > + Expiry hint for recent-blockhash transactions. Omitted for durable + nonce transactions. servers: [ { @@ -705,16 +764,37 @@ methods: }, ] examples: - - name: "alchemy_requestFeePayer example" + - name: "Default request" + params: + - name: "params[0]" + value: + policyId: "69d524a7-e932-4214-8673-dcdcba31bb42" + serializedTransaction: "AgICAQMEBQYHCAkKCwwNDg8BCg==" + result: + name: "Sponsored transaction wrapper" + value: + serializedTransaction: "AQIDBAUGBwgJCgsMDQ4PEA==" + estimatedFeeLamports: 5000 + estimatedRentLamports: 890880 + usesDurableNonce: false + lastValidBlockHeight: 567890 + - name: "With CPI rent prefunding" params: - name: "params[0]" value: policyId: "69d524a7-e932-4214-8673-dcdcba31bb42" - serializedTransaction: "AgICAQMEBQYHCAkKCwwNDg8BCg==" # unsigned tx + serializedTransaction: "AgICAQMEBQYHCAkKCwwNDg8BCg==" + prefundRent: true result: name: "Sponsored transaction wrapper" value: - serializedTransaction: "AQIDBAUGBwgJCgsMDQ4PEA==" # signed tx + serializedTransaction: "AQIDBAUGBwgJCgsMDQ4PEA==" + estimatedFeeLamports: 5000 + estimatedRentLamports: 890880 + prefundLamports: 890880 + simulationSlot: 456789 + usesDurableNonce: false + lastValidBlockHeight: 567890 - name: "alchemy_requestPaymasterTokenQuote" description: | Requests ERC20 fee quote for a `UserOperation`. Optionally accepts fee parameter overrides to be used in the `UserOperation`.