> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avnu.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Private Swap via avnu

> Execute a sponsored, gasless private swap through Starknet's privacy pool

A **private swap** trades one token for another entirely from your shielded balance. The sell token is withdrawn from the privacy pool to avnu's executor, the swap runs through avnu's solver-optimized routing, and the bought token lands back in your private balance as a new note.

<Info>
  **Preview.** Requires `@avnu/avnu-sdk >= 4.2.0` and a proving backend: a **STRK20-capable wallet** ([starknet.js >= 10.4](https://github.com/starknet-io/starknet.js/releases/tag/v10.4.0)) or the **Starknet privacy SDK**. The sell token must already be in your **private balance** — deposit into the pool first. Questions? [Reach out on Telegram](https://t.me/avnu_developers).
</Info>

## Setup

The snippets below assume a few objects are already wired up:

| Identifier                                                                                                                        | Source                                                                   |
| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `getQuotes`, `executePrivateSwap`, `createStrk20WalletProver`, `buildPrivateSwapFee`, `submitPrivateSwap`, `PRIVACY_POOL_ADDRESS` | `@avnu/avnu-sdk`                                                         |
| `account`                                                                                                                         | a starknet.js `WalletAccountV6` — see [Wallet setup](#wallet-setup)      |
| `transfers`, `Open`, `provingBlockId`                                                                                             | the Starknet privacy SDK (only if you prove with it instead of a wallet) |

The paymaster client is built into the SDK: it targets `starknet.paymaster.avnu.fi` by default (override with `AvnuOptions.paymasterBaseUrl`, e.g. `sepolia.paymaster.avnu.fi` for testing).

## Wallet setup

Private swaps need the `WalletAccountV6` flavor of the starknet.js account — it carries the STRK20 privacy methods (`strk20PrepareInvoke`, `strk20Balances`). Build it around the connected wallet as for any SDK usage: see [Initialize your account](/get-started/first-swap#step-2-initialize-your-account).

### Detect STRK20 support

The STRK20 privacy methods (`wallet_strk20PrepareInvoke`, `wallet_strk20Balances`, ...) ship with **wallet API >= 0.10.3** — Ready and Xverse today. Probe the connected wallet before surfacing private swaps in your UI:

```typescript theme={null}
import { compareVersions, walletV6 } from 'starknet';

const versions = await walletV6.supportedWalletApi(walletProvider);
const supportsPrivateSwaps = versions.some((v) => compareVersions(v, '0.10.3') >= 0);
```

Wallets predating `wallet_supportedWalletApi` throw on the probe — catch and treat them as not capable.

## How it works

A private swap is an `apply_action` transaction relayed by avnu's paymaster. No user signature is needed, since everything settles on-chain straight from the proof. `executePrivateSwap` orchestrates the four steps:

1. **Pool fee.** The paymaster returns the pool fee to withdraw (token, recipient, amount).
2. **Private calls.** `quoteToCalls({ private: true })`: the backend sets `takerAddress = executor` and returns the inner swap `calls` plus the `executorAddress`.
3. **Proof.** Your injected `PrivateSwapProver` builds and proves the private transaction: withdraw the sell amount to the executor, withdraw the pool fee, open a note for the bought token, and invoke the executor with the serialized swap calls.
4. **Submit.** The paymaster relays the proof on-chain. The relayer pays gas; the pool fee reimburses it.

The SDK never touches keys, notes, or proofs — that's the prover's job. Both proving backends produce the same `{ call, proof }` artifact.

## Execute a private swap

```typescript theme={null}
import { getQuotes, executePrivateSwap, createStrk20WalletProver, PRIVACY_POOL_ADDRESS } from '@avnu/avnu-sdk';

const STRK = '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d';
const ETH  = '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7';

const [quote] = await getQuotes({
  sellTokenAddress: STRK,
  buyTokenAddress: ETH,
  sellAmount,
  takerAddress: account.address,
  size: 1,
});

const { transactionHash } = await executePrivateSwap({
  quote,
  slippage: 0.05, // 5%
  takerAddress: account.address,
  poolAddress: PRIVACY_POOL_ADDRESS, // SEPOLIA_PRIVACY_POOL_ADDRESS on testnet
  feeMode: { poolFeeToken: STRK, tip: 'normal' },
  prover: createStrk20WalletProver(account), // or your own PrivateSwapProver, see below
  paymasterApiKey: PAYMASTER_API_KEY, // Portal key, same as gasfree
});
```

## Implement the prover

The `prover` is where the cryptography lives. It receives a `PrivateSwapPlan` (sell/buy tokens, amounts, `executorAddress`, inner `calls`, pool fee) and returns the proven call.

### With a STRK20-capable wallet

If the connected wallet supports STRK20 (see [Detect STRK20 support](#detect-strk20-support)), the SDK ships a ready-made prover. The wallet keeps the keys and notes and generates the proof; the dapp only describes actions:

```typescript theme={null}
import { createStrk20WalletProver } from '@avnu/avnu-sdk';

const prover = createStrk20WalletProver(account); // any account exposing strk20PrepareInvoke
```

To drive `wallet_strk20PrepareInvoke` yourself instead, `buildStrk20Actions(plan)` returns the four STRK20 actions to prove; map the wallet's `{ call, proof }` artifact to `PrivateSwapCallAndProof`.

### With the Starknet privacy SDK

When you manage keys and notes yourself, describe the same four actions with the privacy SDK's `transfers` builder:

```typescript theme={null}
import { transaction } from 'starknet';
import type { PrivateSwapProver } from '@avnu/avnu-sdk';

const prover: PrivateSwapProver = {
  buildAndProve: (plan) =>
    transfers
      .build()
      .with(plan.sellTokenAddress, (t) => {
        t.withdraw({ recipient: plan.executorAddress, amount: plan.sellAmount });
        t.surplusTo(plan.takerAddress);
      })
      .with(plan.fee.token, (t) =>
        t.withdraw({ recipient: plan.fee.recipient, amount: plan.fee.amount }),
      )
      .with(plan.buyTokenAddress, (t) =>
        t.transfer({ recipient: plan.takerAddress, amount: Open }),
      )
      .invoke(({ openNotes }) => ({
        contractAddress: plan.executorAddress,
        calldata: [
          plan.buyTokenAddress,
          ...transaction.fromCallsToExecuteCalldata_cairo1(plan.executorCalls),
          openNotes[0].noteId,
        ],
      }))
      .execute({ provingBlockId }),
};
```

`Open` is a privacy SDK sentinel that opens a note for the swap output, whose amount is only known after execution. The inner calls are serialized with starknet.js's `transaction.fromCallsToExecuteCalldata_cairo1`; the executor expects `[buyToken, ...serializedCalls, openNoteId]`. Both proving backends produce the same `{ call, proof }` artifact.

## Manual control

`executePrivateSwap` wraps three functions you can also call yourself:

```typescript theme={null}
import { buildPrivateSwapFee, quoteToCalls, submitPrivateSwap, PRIVACY_POOL_ADDRESS } from '@avnu/avnu-sdk';

// 1. Pool fee from the paymaster
const fee = await buildPrivateSwapFee({
  poolAddress: PRIVACY_POOL_ADDRESS,
  feeMode: { poolFeeToken: STRK, tip: 'normal' },
  paymasterApiKey: PAYMASTER_API_KEY,
});
// { token, recipient, amount } — withdraw this inside the private transaction

// 2. Private swap calls (backend sets takerAddress = executor)
const { calls, executorAddress } = await quoteToCalls({
  quoteId: quote.quoteId,
  slippage: 0.05,
  private: true,
});

// 3. Prove with your backend (see above), then submit through the paymaster
const callAndProof = await prover.buildAndProve({ /* plan */ });
const { transactionHash } = await submitPrivateSwap({
  callAndProof,
  feeMode: { poolFeeToken: STRK, tip: 'normal' },
  paymasterApiKey: PAYMASTER_API_KEY,
});
```

## Key parameters

The SDK's private swap functions always run in the `sponsored_private` fee mode: the relayer pays gas, the user pays the pool fee from their private balance.

<ParamField path="feeMode.poolFeeToken" type="string" required>
  Token used to pay the pool fee (e.g. STRK, ETH, USDC). The paymaster converts the base STRK amount to this token via its price oracle.
</ParamField>

<ParamField path="feeMode.tip" type="'slow' | 'normal' | 'fast'" default="normal">
  Relayer priority tip.
</ParamField>

<ParamField path="poolAddress" type="string" required>
  The privacy pool contract address. The SDK exports the pools whitelisted by the paymaster: `PRIVACY_POOL_ADDRESS` (mainnet) and `SEPOLIA_PRIVACY_POOL_ADDRESS`.
</ParamField>

<ParamField path="prover" type="PrivateSwapProver" required>
  Your proving backend: `buildAndProve(plan)` returns the proven `{ call, proof }`. For STRK20 wallets, use `createStrk20WalletProver(account)`. The SDK never handles private keys, notes, or proof generation.
</ParamField>

<ParamField path="chainId" type="string">
  Optional fail-fast check: when provided, it is compared to `quote.chainId` before any paymaster or proving round-trip.
</ParamField>

<ParamField path="paymasterApiKey" type="string" required>
  [Portal](https://portal.avnu.fi) API key, the same one used for [gasfree](/docs/paymaster/gasfree) — the paymaster requires it for the sponsored modes. Server-side only: do not ship it in client code. Browser dapps should split the flow: `buildPrivateSwapFee` and `submitPrivateSwap` behind server endpoints, proving (`prover`) client-side with the user's wallet.
</ParamField>

## Related

<CardGroup cols={2}>
  <Card title="Privacy Overview" icon="shield-halved" href="/docs/privacy/index">
    Privacy pool, fees, and transaction types
  </Card>

  <Card title="Get Quotes" icon="magnifying-glass" href="/docs/swap/get-quotes">
    Fetch solver-optimized swap quotes
  </Card>
</CardGroup>
