Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions concepts/policies/examples/solana.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ sidebarTitle: "Solana"

Note: see the [language section](/concepts/policies/language#solana) for various approaches on writing Solana policies.

<Note>
For sponsored Solana transactions, start with
[Solana Rent Sponsorship](/networks/solana-rent-refunds) before applying the
examples below. That guide covers account-creation risk, rent refunds, and
mitigation strategy for sponsored flows.
</Note>

#### Allow IDL-specific program instructions

See [here](../../../concepts/policies/smart-contract-interfaces) for more information and examples.
Expand Down
3 changes: 2 additions & 1 deletion concepts/transaction-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ import SendTxConcepts from "/snippets/shared/send-tx-concepts.mdx";

For implementation guides, see:

- [Sending Sponsored Transactions (React)](/embedded-wallets/code-examples/sending-sponsored-transactions) - Using `@turnkey/react-wallet-kit`
- [Sending Sponsored EVM Transactions (React)](/embedded-wallets/code-examples/sending-sponsored-transactions) - Using `@turnkey/react-wallet-kit`
- [Sending Sponsored Solana Transactions (React)](/embedded-wallets/code-examples/sending-sponsored-solana-transactions) - Using `@turnkey/react-wallet-kit`
- [Sending Sponsored Transactions](/company-wallets/code-examples/sending-sponsored-transactions) - Using `@turnkey/core` directly
3 changes: 3 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@
"embedded-wallets/code-examples/authenticate-user-passkey",
"embedded-wallets/code-examples/create-passkey-session",
"embedded-wallets/code-examples/sending-sponsored-transactions",
"embedded-wallets/code-examples/sending-sponsored-solana-transactions",
"reference/tron-gasless-transactions",
"embedded-wallets/code-examples/client-side-signing",
"embedded-wallets/code-examples/create-user-email",
Expand Down Expand Up @@ -231,6 +232,8 @@
"pages": [
"networks/ethereum",
"networks/solana",
"networks/solana-transaction-construction",
"networks/solana-rent-refunds",
"networks/bitcoin",
"networks/hyperliquid",
"networks/cosmos",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
title: "Sending Sponsored Solana Transactions"
description: "Send sponsored Solana transactions using Turnkey's solSendTransaction API"
---

import SendSolanaTxSdkOverview from "/snippets/shared/send-solana-tx-sdk-overview.mdx";

<SendSolanaTxSdkOverview />

<Note>
This example shows how to submit a sponsored Solana transaction, but your
application is still responsible for validating transaction contents. If the
transaction may create accounts that require rent, ensure `Sponsor Solana
Rent` has been enabled in the dashboard first. Review whether the unsigned
transaction creates accounts, closes accounts, or routes rent refunds back to
the signer. See
[Solana Rent Sponsorship](/networks/solana-rent-refunds) for rent setup and
refund-path guidance, and [Solana transaction construction for sponsored
flows](/networks/solana-transaction-construction) for payer-model and
account-creation caveats.
</Note>

## Using `handleSendTransaction` (React)

This handler wraps everything: intent creation, signing, Turnkey submission, polling, modal UX, and final success UI.

### Step 1 — Configure the Provider
```tsx
import { TurnkeyProvider } from "@turnkey/react-wallet-kit";

const turnkeyConfig = {
apiBaseUrl: "https://api.turnkey.com",
defaultOrganizationId: process.env.NEXT_PUBLIC_TURNKEY_ORG_ID,
rpId: window.location.hostname,
iframeUrl: "https://auth.turnkey.com",
};

export default function App({ children }) {
return (
<TurnkeyProvider config={turnkeyConfig}>
{children}
</TurnkeyProvider>
);
}
```

### Step 2 — Use `handleSendTransaction` for Solana
```tsx
const { handleSendTransaction, wallets } = useTurnkey();

const walletAccount = wallets
.flatMap((w) => w.accounts)
.find((a) => a.addressFormat === "ADDRESS_FORMAT_SOLANA");

if (!walletAccount) {
throw new Error("No Solana wallet account found");
}

await handleSendTransaction({
transaction: {
signWith: walletAccount.address,
unsignedTransaction: "<base64-serialized-unsigned-solana-tx>",
caip2: "solana:mainnet",
sponsor: true,
},
});
```

This automatically:
- Opens the Turnkey modal
- Shows the chain logo
- Polls until `INCLUDED`
- Displays success page + explorer link

## Checking Gas Usage

You can configure gas limits for both sub-orgs and all orgs. We recommend checking sub-org gas usage against the limit on the client side so your application can handle edge cases when approaching or exceeding the gas limit.
```tsx
const resp = await httpClient?.getGasUsage({});
if (resp?.usageUsd! > resp?.windowLimitUsd!) {
console.error("Gas usage limit exceeded for sponsored transactions");
return;
}
```

For additional references leveraging these endpoints, check out our [Swapping Example](/cookbook/jupiter) and [Sweeping Example](/cookbook/sweeping).
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "Sending Sponsored Transactions"
title: "Sending Sponsored EVM Transactions"
description: "In this guide, we’ll walk through the process of setting up sponsored transactions, with abstractions for transaction construction, broadcast, and gas management, using React."
---

Expand Down
6 changes: 4 additions & 2 deletions faq.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ title: "FAQ"

This limit is on a **per IP address** basis: if you have multiple servers making requests to the turnkey API under a different IP address, each server is subject to the 60 RPS limit individually.

In addition, the **broadcast transaction endpoints** (`ethSendTransaction` and `solSendTransaction`) are subject to a separate global limit of **10 RPS**, applied uniformly across all plans.

Please get in touch with us ([help@turnkey.com](mailto:help@turnkey.com)) if you need this limit adjusted for your use-case.
</Accordion>
</AccordionGroup>
Expand Down Expand Up @@ -116,9 +118,9 @@ title: "FAQ"

We handle gassing and our battle-tested broadcast logic ensures inclusion even under adverse network conditions. You and your users never touch gas tokens or deal with stuck transactions.

While we'e abstracted gas sponsorship, you're always able to bring in your own gas service, and we can handle the rest.
While we've abstracted gas sponsorship, you're always able to bring in your own gas service, and we can handle the rest.

Please note that this functionality is supported for a limited number of select EVM chains, however are are aggressively expanding the coverage area. For more detail visit [Transaction Management](/concepts/transaction-management) overview page.
This functionality is currently supported for EVM chains (Base, Polygon, Ethereum) and Solana. We're actively expanding coverage — reach out if you need a specific chain. For more detail visit the [Transaction Management](/concepts/transaction-management) overview page.
</Accordion>
<Accordion title="What does `HASH_FUNCTION_NO_OP` mean?">
In the ECDSA context, messages are hashed before signing. Turnkey can perform this hashing for you, as we support two hash functions: `HASH_FUNCTION_KECCAK256` and `HASH_FUNCTION_SHA256` (for Ethereum and Bitcoin ecosystems respectively). If your message had already been hashed, you should use the `HASH_FUNCTION_NO_OP` option to sign the raw hash, in which case Turnkey will sign the payload as is. `HASH_FUNCTION_NO_OP` also has privacy implications: if a raw hashed message is passed in, Turnkey has no knowledge of the underlying pre-image.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions networks/ethereum.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,42 @@ To construct and sign an EVM transaction with Turnkey, we offer:
- [@turnkey/viem](https://github.com/tkhq/sdk/tree/main/packages/viem): contains a `createAccount` method to create a Turnkey-powered [custom account](https://viem.sh/docs/accounts/local) which [Viem](https://viem.sh/) can use seamlessly.
- [@turnkey/ethers](https://github.com/tkhq/sdk/tree/main/packages/ethers): contains a `TurnkeySigner` which implements Ethers' `AbstractSigner` interface. See [Ethers docs](https://docs.ethers.org/v6/api/providers/abstract-signer/#AbstractSigner).

## Transaction management & Gas sponsorship

Turnkey's [Transaction Management](/concepts/transaction-management) handles the full lifecycle of EVM transactions — construction, broadcast, nonce management, and status monitoring — down to a few API calls.

### What Turnkey auto-fills

When you submit a transaction via `ethSendTransaction`, Turnkey automatically manages:

- **Nonce**: set correctly to order transactions and prevent conflicts
- **Gas estimation**: calculated to ensure inclusion under current network conditions
- **Priority fee (tip)**: set to target timely block inclusion per EIP-1559

### Gas sponsorship (fee abstraction)

Set `sponsor: true` to enable fee sponsorship — your users never need to hold native tokens to pay gas. Turnkey covers fees on your behalf and passes costs through as a monthly line item.

**Supported networks:**
- Base (eip155:8453)
- Polygon (eip155:137)
- Ethereum (eip155:1)
- Base Sepolia, Polygon Amoy, Ethereum Sepolia (testnets)

<Note>
To enable gas sponsorship, ensure it is activated in your Turnkey dashboard before setting `sponsor: true`.
</Note>

### Non-sponsored transactions

Set `sponsor: false` to have gas paid by the sender's wallet. Turnkey still manages nonce, gas estimation, tip fees, broadcast, and status monitoring — you just don't get fee abstraction.

### Transaction status

After broadcast, Turnkey monitors your transaction until it is included in a block or fails, with structured error decoding for smart contract reverts. Query status via the [Get Send Transaction Status](/api-reference/queries/get-send-transaction-status) endpoint.

For a full walkthrough, see [Sending Sponsored EVM Transactions](/embedded-wallets/code-examples/sending-sponsored-transactions).

## Transaction parsing, policies, and signing

Turnkey has built an EVM parser which runs in a secure enclave, to parse unsigned EVM transactions and extract useful metadata: transaction source, destination, amount, chain ID, and more. See the `EthereumTransaction` struct in our [policy language](/concepts/policies/language) page for a full list.
Expand Down
156 changes: 156 additions & 0 deletions networks/solana-rent-refunds.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
title: "Solana Rent Sponsorship"
description: "Understand how Solana rent sponsorship works, when rent is pre-funded, and how to reduce rent-refund leakage in sponsored flows."
sidebarTitle: "Rent Sponsorship"
---

## Overview

On Solana, fee sponsorship and rent sponsorship are separate.

Fee sponsorship covers the network fee for a transaction. Rent sponsorship covers the rent-exempt lamports needed when instructions create new accounts.

If an instruction creates a new account and the user signer is the payer, Turnkey pre-funds that signer for the rent-exempt amount. The amount is based on the size of the new account data and is intended to make the account rent exempt.

When enabled, sponsored rent is added to your monthly gas bill and counts toward the same spend limits used for sponsored transaction fees. See [Spend limits](/concepts/transaction-management#spend-limits).

Rent sponsorship is disabled by default and must be enabled separately in the Turnkey dashboard.

For the broader transaction-construction model behind sponsored Solana flows, including payer behavior and account-creation caveats, see [Solana transaction construction for sponsored flows](/networks/solana-transaction-construction).

## Enable rent sponsorship

If your sponsored Solana transactions may create accounts, you must explicitly enable rent sponsorship in the Turnkey dashboard.

1. Enable gas sponsorship.
2. Turn on `Sponsor Solana Rent`.
3. Click `Save Configuration`.

<img
src="/images/concepts/network/enable_rent_sponsorship.png"
alt="Turnkey dashboard showing Sponsor Solana Rent enabled"
/>

<Note>
Refunded rent from later-closed accounts follows Solana account rules. It
does not automatically return to the sponsor.
</Note>

## Rent extraction risk

On Solana, when an account is later closed, the rent previously deposited into that account is returned according to Solana account rules, usually to the configured destination for the close operation.

That destination is usually the signer or account owner, not the sponsor that originally funded the rent.

This creates a rent extraction risk for sponsored Solana transactions: the sponsor covers the rent needed to create the account, but the refunded rent can later flow back to the signer instead.

This is not necessarily malicious. It can happen in ordinary product flows.

A common example is a swap that temporarily wraps SOL into wSOL, uses that account during execution, and then closes the temporary account before the transaction completes.

In a self-funded flow, the rent simply returns to the user. In a sponsored flow, that same rent may have been pre-funded by the sponsor and then refunded back to the signer.

## Mitigations and guardrails

The best mitigation depends on how much transaction flexibility your product allows.

- Reuse a constrained set of token accounts where possible instead of creating and closing them repeatedly.
- Avoid or strip `CloseAccount` patterns from sponsored flows when that works for your product.
- Prefer backend-generated or backend-validated transactions for higher-control flows.
- Use spend caps, rate limits, monitoring, and alerts to bound and detect repeated leakage.
- Treat account creation and account closure as first-class review criteria for sponsored Solana transactions.

### Strip `CloseAccount` instructions before submission

One practical guardrail is to inspect the transaction payload before submission and remove SPL Token `CloseAccount` instructions.

This is especially useful when you receive a prebuilt transaction from a routing service such as Jupiter and want to keep your sponsored flow from immediately refunding rent back to the signer.

In the SPL Token program, `CloseAccount` is instruction discriminator `9`. A simple filter can remove those instructions before you hand the transaction to Turnkey:

```ts
const SPL_TOKEN_PROGRAM_IDS = new Set([
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
]);

const CLOSE_ACCOUNT_INSTRUCTION_DISCRIMINATOR = 9;

function isCloseAccountInstruction(ix: TransactionInstruction): boolean {
return (
SPL_TOKEN_PROGRAM_IDS.has(ix.programId.toBase58()) &&
ix.data[0] === CLOSE_ACCOUNT_INSTRUCTION_DISCRIMINATOR
);
}

const filteredInstructions = txMessage.instructions.filter(
(ix) => !isCloseAccountInstruction(ix),
);
```

This does not eliminate all rent-related risk, but it removes one of the most common leakage paths in sponsored swap flows.

### Disable Jupiter auto wrap and unwrap for SOL flows

Another useful guardrail is to disable Jupiter's automatic SOL wrapping and unwrapping behavior when you request the swap transaction.

When `wrapAndUnwrapSol` is enabled, Jupiter may create a temporary wSOL account for the transaction and then close it before completion. In a sponsored flow, that pattern can create a rent refund path back to the signer.

To prevent that, set `wrapAndUnwrapSol` to `false` in the Jupiter API request:

```ts
body: JSON.stringify({
quoteResponse,
userPublicKey: request.signWith,
wrapAndUnwrapSol: false,
dynamicComputeUnitLimit: true,
prioritizationFeeLamports: "auto",
})
```

With this setting disabled, your application should manage wSOL explicitly instead of relying on Jupiter to create and close a temporary account on the user's behalf.

This higher-control approach is often a better fit for sponsored flows because it lets you:

- reuse a persistent wSOL account instead of creating a fresh one per swap
- avoid automatic close-account behavior in the routed transaction
- review account lifecycle decisions on the backend before submission

For products with tighter controls, the strongest approach is usually to combine both mitigations: disable auto wrap and unwrap where possible, and still validate or sanitize the final instruction payload before sending it.

## Key custody matters

These mitigations are weaker if end users can export or independently control the signer key.

Even if your application removes account-close instructions from the original sponsored flow, a user who later controls the signer can submit a separate transaction to close previously created accounts and reclaim rent.

If you need stronger protection, keep transaction submission behind your backend and avoid giving end users independent signer control.

## Policy guidance

These example deny policies can help reduce common Solana rent-leakage patterns in sponsored flows.

### Deny sponsored Solana `CloseAccount`

```json
{
"effect": "EFFECT_DENY",
"condition": "solana.tx.instructions.any(i, (i.program_key == 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' || i.program_key == 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb') && i.instruction_data_hex == '09')"
}
```

### Deny sponsored Solana account lifecycle ops

```json
{
"effect": "EFFECT_DENY",
"condition": "solana.tx.instructions.any(i, i.program_key == 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL') || solana.tx.instructions.any(i, (i.program_key == 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' || i.program_key == 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb') && (i.instruction_data_hex == '09' || i.instruction_data_hex == '11'))"
}
```

## Next steps

- See [Solana (SVM) support on Turnkey](/networks/solana)
- See [Solana transaction construction for sponsored flows](/networks/solana-transaction-construction)
- See [Sending Sponsored Solana Transactions](/embedded-wallets/code-examples/sending-sponsored-solana-transactions)
- See [Solana policy examples](/concepts/policies/examples/solana)
Loading