swap
Requests a private swap — phase one of the two-transaction swap lifecycle.
Resolves the trade intent against live pool state, obtains a single-use
blinded identity and a token record, and submits the swap transition on
shield_swap_v3.aleo. The chain computes the actual output at finalize; read
it back with getSwapOutput and collect it
with claimSwapOutput. The action hits
the network for pool reads, a deadline read, a record scan, and the
transaction itself; it signs, and on the local-signer path proves locally.
The call resolves differently by signer:
- Local account — derives the blinding identity from the account's view
key, selects an unspent token record via the client's record provider,
proves locally, and returns a handle already carrying
swapIdandblindedAddress. - Wallet account — emits wallet-derived requests for the blinding slots
(
tokenRecordMUST be supplied); the wallet proves and returns a transaction id.swapIdandblindedAddressbecome recoverable only once the transaction confirms.
Execute a quote
const quote = await client.quote({ from: 'USDCx', to: 'ETH', amountIn: '1.5' })
const handle = await client.swap({ quote })
This overload selects single- or multi-hop execution and preserves quote.minOut
exactly. Quote inputs accept decimal strings in token units or bigint in raw
base units; the quote resolves decimals and rejects excess precision. Quotes expire after 60 seconds;
expiry is checked during preparation,
not after proving has begun. The transaction's block deadline remains separate.
Execution checks only route pools on chain and resolves imports automatically.
Wallet callers MUST additionally supply tokenRecord. The quote's amount,
route, slippage and program cannot be overridden. The existing manual overload
below remains supported.
Usage
Local account
import { loadNetwork } from '@provablehq/veil-aleo-sdk'
import { shieldSwapActions } from '@provablehq/shield-swap-sdk'
const aleo = await loadNetwork('testnet')
const scanner = aleo.createRemoteScanner({
url: 'https://edge.provable.com/api/scanner',
})
const { walletClient } = aleo.createAleoClient({
privateKey: PRIVATE_KEY,
networkUrl: 'https://edge.provable.com/api/v2',
provingMode: 'delegated',
proverUrl: 'https://edge.provable.com/api/prove',
records: scanner,
})
const client = walletClient.extend(shieldSwapActions())
const handle = await client.swap({
poolKey,
tokenInId,
amountIn: 10n ** 18n, // raw atomic amount, bigint
expectedOut, // a real quote, scaled to raw base units
slippageBps: 50, // 0.5%
tokenInProgram: 'ethx_5a095e.aleo',
})
// { swapId: '123…field', blindingFactor: '456…field', blindedAddress: 'aleo1…',
// tokenInId, tokenOutId, poolKey, amountIn: 1000000000000000000n,
// transactionId: 'at1…', program: 'shield_swap_v3.aleo' }
Wallet account
A wallet never exposes its records, so tokenInProgram is dropped in favor
of a record InputRequest; the wallet resolves it against its own records
and fills the blinding slots itself. The returned handle comes back
without swapId or blindedAddress — recover them from the confirmed
transaction before claiming.
import { createWalletClient } from '@provablehq/veil-core'
import { fromWalletAdapter } from '@provablehq/veil-aleo-wallet-adapter'
import { shieldSwapActions, SHIELD_SWAP_ALGORITHM_GRANTS } from '@provablehq/shield-swap-sdk'
await adapter.connect(network, decryptPermission, {
algorithmsAllowed: SHIELD_SWAP_ALGORITHM_GRANTS,
})
const { account, transport } = fromWalletAdapter(adapter)
const client = createWalletClient({ account, transport }).extend(shieldSwapActions())
const handle = await client.swap({
poolKey,
tokenInId,
amountIn: 10n ** 18n,
expectedOut,
slippageBps: 50,
tokenRecord: {
type: 'record',
program: tokenInProgram,
recordname: 'Token',
filters: { amount: { gte: '1000000000000000000u128' } },
},
})
// { swapId: undefined, blindedAddress: undefined, tokenInId, tokenOutId,
// poolKey, amountIn: 1000000000000000000n, transactionId: 'at1…',
// program: 'shield_swap_v3.aleo' }
Returns
Promise<SwapHandle>
The serializable thread between the swap's two transactions. Persist it —
claimSwapOutput consumes it, and it survives a crash or a move to another
process since it is plain JSON.
- swapId —
string | undefined. Swap id field literal, the request transition's first output. Set immediately on the local path; on the wallet path, resolve it from the confirmed transaction before claiming. - blindingFactor —
string | undefined. Secret field literal proving ownership at claim time. Set only on the local path — a wallet keeps it private and re-derives it fromblindedAddressat claim time. Treat it like a key. - blindedAddress —
string | undefined. The public single-use address the swap recorded. Set immediately on the local path; on the wallet path, recover it post-confirmation from the transition's public inputs or therecipientof the chain'sswap_outputsentry (getSwapOutput). - tokenInId —
string. Token id (field literal) that was sold. - tokenOutId —
string. Token id (field literal) that was bought. - poolKey —
string. The pool the swap executed against. - amountIn —
bigint. Raw atomic amount sold (u128). - transactionId —
string. The request transaction's id. - program —
string. The shield_swap program the swap targets.
Parameters
poolKey
- Type:
string
Pool key field literal to trade against.
tokenInId
- Type:
string
Token id (field literal) being sold. Must be one of the pool's two tokens.
amountIn
- Type:
bigint
Raw atomic amount to sell (u128). Must respect the token's no-dust rule.
slippageBps
- Type:
number - Default:
50(0.5%)
Slippage tolerance in basis points.
expectedOut
- Type:
bigint
Quoted output amount, e.g. from the DEX API's /route endpoint. Without it a
spot estimate is used, which ignores price impact and fees — pass a real
quote for anything beyond a tiny trade.
sqrtPriceLimit
- Type:
bigint - Default: the directional extreme, relying on
amount_out_minfor slippage protection.
Explicit Q64 price bound.
deadlineOffsetBlocks
- Type:
number - Default:
100
Blocks until the swap request expires.
nonce
- Type:
bigint - Default: crypto-random.
Explicit u64 nonce. Override only for reproducible ids, such as tests.
tokenInProgram
- Type:
string
Program holding the caller's token records (a wrapper program or the
registry). Required on the local-signer path unless tokenRecord is given;
unused when tokenRecord is provided.
tokenRecord
- Type:
string | InputRequest
Explicit record input: a record plaintext literal for a local signer, or a
record InputRequest for a wallet signer. REQUIRED for wallet accounts —
the client cannot guess a wallet's record shape.
blindedIdentity
- Type:
{ blindingFactor: string; blindedAddress: string } - Default: derived from the local account's view key, or wallet-side
derivedrequests for wallet accounts.
Explicit pre-derived identity literals, for a local signer that derives by its own means.
imports
- Type:
Record<string, string>
Program sources for dynamic-dispatch dependencies ({ 'token.aleo': source }). The prover cannot discover IARC20@(...) callees statically, so the
involved token programs' sources must be passed when proving locally or via
a service that requires them.
program
- Type:
string - Default:
shield_swap_v3.aleo
shield_swap program override.
Errors
Throws when the pool does not exist; when the intent violates the contract's
rules (dust, bad slippage, a foreign token); when no record covers the
amount; when a wallet account is used without tokenRecord; and on
transport or proving errors.