Skip to main content
A swap scope narrows a session to one swap: it may spend one token, receive one token, deliver to one recipient, and route only through the venues you name. Every other operation reverts on-chain. This is what makes an automated conversion session — a backend that turns deposits into a stablecoin, say — grantable without handing the session key authority over the account.
Requires @rhinestone/sdk 2.6.0 or later (1.14.0 on the v1 line).

Declaring a swap scope

A swap scope is single-chain: it applies on the session’s chain.

The implied restriction

Declaring swap implies restrictToActions. The session’s only operations are to approve the sell token to a listed venue’s spender, and to call that venue’s swap entrypoint with the sell token, buy token, and recipient pinned. Two consequences:
  • The ERC-1271 signing surface defaults to { mode: 'disabled' }, so a swap-scoped key cannot sign an off-chain approval. Pass signing explicitly to opt back in.
  • A swap scope cannot be combined with crossChainPermits or claimPolicies. Those guardrails live inside the fallback the restriction drops, so the SDK rejects the combination rather than silently dropping them.

Naming venues

Venues are named, not addressed — router addresses, selectors, and calldata offsets stay inside the SDK, so you never encode a swap yourself and the SDK can follow a router deployment without a breaking change. The builders are published exports:
Each builder also accepts an optional maxSpend — a venue-level cumulative cap that overrides sell.maxTotal for that venue. Naming an aggregator authorizes every call shape its swaps can arrive in: both a direct router call by the account and the Swapper-wrapped one. That cannot be narrowed, because which shape the orchestrator emits depends on the swap’s direction and the winning quoter — both decided after the session is signed. Authorizing a single shape is how a session ends up rejecting the swap it was created for. Venue availability is per chain, and the SDK rejects a venue that isn’t available on the session’s chain.

Pinning 0x’s Settler

0x redeploys its Settler every few weeks and moves the registry token, so the SDK bundles no default address. Resolve it and pin the result:
Resolve once, at session-enable time — do not re-resolve per swap. A session that looked the address up at use time would follow 0x’s registry wherever it points, so a compromise of 0x’s upgrade key could redirect every live session’s approved funds. Pinned, the worst case of a rotation is that the session stops working until it is re-enabled.
zeroEx({ anySettler: true, maxSpend }) trades the pin for longevity, and it is an explicit security downgrade. With the target unpinned, a compromised session key can redirect the AllowanceHolder pull to its own contract, and maxSpend becomes the only bound on what it takes — which is why the SDK makes it mandatory in that form.

What the scope binds

Using the session

A same-chain swap intent, signed by the session key:
Sponsor the intent. An unsponsored intent adds the orchestrator’s fee and gas-refund operations, which are authorized by the wildcard intent-execution fallback that a swap scope drops. Without sponsored: true the intent is rejected on-chain, and the error surfaces as a signature failure rather than an authorization one.
You don’t restate the venue at transaction time. When the session names specific aggregators, the SDK derives the matching quoters pin from swap.via and sends it with the quote request; across a per-chain session set the pin is the intersection of every session’s venues. rhinestoneSwap() pins nothing, since any quoter may legitimately fill it.
Venue pinning constrains the route rather than selecting it. If no permitted venue can serve the swap, the request fails instead of falling back to a venue the session does not allow — an availability consideration, not a security one.

Pinning venues without a session

quoters is also available on any transaction, as an include or exclude filter over the orchestrator’s swap venues:
An explicit filter can only narrow a session-derived pin, never widen it. And an empty filter is not the same as omitting the field: { include: [] } means no venue may serve the swap, so the request fails at quote time rather than falling back to an unconstrained route.