Skip to main content
Choose your starting version: SDK v2 to v3 or SDK v1 to v2. If you use v1, complete both migrations in order.

SDK v2 to v3

SDK v3 uses API version 2026-09.caucasus. EVM-only integrations also need the changes below; adding a Solana account is optional. For HTTP request bodies, estimates and wire-level signing, see the Blanc to Caucasus API migration.
If you use intent-bound JWT sponsorship, upgrade your backend helpers first. Older sponsorship filters reject the approval contract sent by v3 clients.
Target @rhinestone/sdk@3.0.0 on both your client and backend, then run your type checker:

Nest account configuration by VM

Move your existing account configuration under evm, including account, owners, sessions, recovery, eoa, modules and initData. Keep your existing owner, nonce and account version to preserve the EVM address. The examples reuse your existing viem owner. Shared SDK authentication and provider configuration stay on RhinestoneSDK:
The top-level new RhinestoneSDK({ apiKey }) shorthand is removed and throws; use auth as shown. Keep API-key mode server-side. Browser integrations continue using JWT authentication.
Read configuration through account.config.evm, not account.config.owners. Let TypeScript infer the configuration: widening it to RhinestoneAccountConfig can hide the managed-account methods. An address-only entry such as { evm: { address } } is a receiver, not an account that can sign, fund or execute transactions.
V3 defaults to Startale 1.0.1, as do recent v2 releases. If your integration relied on the older 1.0.0 default, pin it to avoid changing the counterfactual address and signing domain. Preserve an existing v1.0.0 account with evm.account: { type: 'startale', version: '1.0.0' }. Factory-bearing initData preserves its version automatically; address-only initData does not. Verify the derived address before funding or deploying.

Use VM-specific imports

@rhinestone/sdk/utils is removed. Its helpers keep their names under @rhinestone/sdk/evm. EVM validator constants also move there; solanaMainnet moves to @rhinestone/sdk/solana.
The same move applies to experimental_getV0InitData, experimental_getRhinestoneInitData, experimental_getModuleSetup, toViewOnlyAccount, and the WebAuthn, multi-factor and smart-session constants. VM-specific configuration types live in /evm or /solana; errors stay in @rhinestone/sdk/errors. The EVM initialization and module-setup helpers still take the inner EVM configuration, not the composite { evm: ... } object.

Name one source and one destination

The SDK no longer discovers funding chains or tokens. Choose one source chain/token and one destination token per transaction. Multiple source chains, multiple source tokens and multiple delivery tokens have no equivalent in one v3 transaction.
Keep using viem chain objects, chain-specific token addresses and bigint amounts in token base units. Do not copy the HTTP API’s decimal-string amounts or destination.execution wrapper into SDK transaction inputs. Amount and funding rules:
  • destination.token with amount requests exact output. With only token, receive the maximum output the source yields. Omit both for execution without token delivery.
  • source.maxAmount caps the source balance available to the route; it does not promise to debit exactly that amount. Replace exact-input assumptions accordingly. For EVM sources, auxiliary funds are added after this cap.
  • A delivery always needs source, even when sponsored. Execution without delivery can omit it only when gas-sponsored. HyperCore, Tron and Stellar destinations still need an explicit EVM source.chain.
  • source.calls run before the claim. Their provides entries must name source.token; those amounts add to source.auxiliaryFunds. Calls formerly omitted by source selection now run or are refused.
  • An explicit destination.recipient is delivery-only: do not combine it with destination calls. To execute through the invoking managed account, omit recipient.
  • Put gasLimit alongside non-empty destination.calls, not on a delivery-only request. Cross-chain customDeadline and hyperCore options on non-HyperCore destinations are refused rather than ignored.
For same-chain calls, the old { chain: base, calls } becomes:
For a gas-sponsored, no-delivery execution, use { destination: { chain: base, calls }, sponsored: true }. If a smart session still needs enabling, name a source or enable it first; source-free preparation cannot insert its enable call.

Update deployment and setup

deploy now takes the VM first. Unsponsored intent-path deployment and setup also require a same-chain fee token:
A sponsored deployment uses account.deploy('evm', base, { sponsored: true }). UserOperation execution and funding options remain unchanged; the VM argument still applies to deploy.

Read quote requirements

Keep selecting prepared.quotes.best, or pass { intentId } to sign another quote from prepared.quotes.all. The quote now includes purpose, plan, requirements and signingRequests.
requirements is an array of erc20Approval and wrapNative requirements, replacing the chain/token map. Preparing discloses these prerequisites; it does not approve or wrap funds for you. Quote costs, plans, requirements, signing context and bridgeFill.destinationChainId use CAIP-2 strings such as 'eip155:8453', not 8453. Bridge-fill variants include fillStatusTimeout; provider-specific IDs remain separate metadata, not replacements for chain identity. SDK transaction inputs and the submission handle’s sourceChains/targetChain remain numeric/viem-based.

Migrate custom and detached signing

The ordinary lifecycle stays the same:
If you inspect signing messages or build signatures yourself, replace role-keyed payloads and signatures with ordered requests and proofs:
Each request describes the account, authority, scope, chains, purpose, validity and payload. Keep one proof per request, in order, even when two payloads are identical. Never sort, filter or deduplicate the proof array. For custom signers, handle payload.kind explicitly:
  • eip712: honor signatureFormat; an account-validator envelope is not a raw secp256k1 signature.
  • personalSign: sign message.value as UTF-8 characters, not decoded hex.
  • eip7702: return the authorization nonce and { r, s, yParity } signature.
  • webauthn: return the assertion, not an ECDSA signature.
A smart-session origin produces one eip712 proof containing { preClaim, notarizedClaim }, not two slots. signIntent now takes SigningRequest[] instead of SignData and returns { proofs }. Detached EVM owner signing keeps the same method flow:
Collect enough owners to satisfy the configured threshold. Pass SDK-returned contributions unchanged; the runtime contribution data uses slots instead of origin, and covers all required account-authorized requests, including target execution. Do not rebuild it from the old serialized shape. Contributions must belong to the same intent and ordered request set. Independent owner signing is not supported for smart-session or Solana-origin transactions.

EIP-7702 authorizations

Normal signing now collects delegation proofs automatically. Remove the separate submission option:
When a different party signs delegations, signAuthorizations returns intent-bound, indexed proof contributions. Pass them to assembly, not submission:
Assembly rejects missing, duplicate or mismatched proof slots rather than returning an incomplete signed transaction.

Upgrade sponsorship before clients

For intent-bound JWT sponsorship, deploy the updated @rhinestone/sdk/jwt-server helpers, or your custom signer, before upgrading clients. The v3 approval identifier is sdk-3.0.0-caucasus. The updated shouldSponsor helper accepts this input and the Blanc input without a contractVersion from v2 clients, so you can upgrade the backend while v2 clients remain active. Other contract identifiers, including those from earlier v3 snapshots, are not supported by the helper. Custom policies must stop reading the old approval fields: These are approval-input fields, not SDK transaction inputs. Forward the exact intentInput supplied to getIntentExtensionToken; do not reconstruct it from your UI transaction. See the sponsorship section of the API migration guide for the wire contract and digest rules. Using the built-in policy adapter avoids hard-coding those approval fields. Its callbacks retain numeric chain IDs and bigint call values:
Apply these checks on your server before issuing the grant, not as client-side authorization. In createJwtSigner, configure shouldSponsor filters explicitly; omitting filters does not validate your sponsorship policy. For Solana-only operations, the account filter receives the Swig wallet rather than an EVM address, and a calls filter is not a Solana-instruction policy. As in current v2 releases, approval runs during preparation, not submission. Denial stops preparation without an unsponsored fallback; submitting a restored prepared transaction does not request another grant. Swap sponsorship continues to use sponsored.swaps, as in current v2 releases. If older integration code still sends sponsored.swapValue, remove it; there is no separate client switch for swap-value sponsorship:

Handle grouped status and native transaction references

PENDING, COMPLETED and FAILED remain unchanged. Replace the flat operation list with chain groups containing operation items:
Branch on the transaction reference’s vm: EVM and Stellar use txHash, Solana uses signature, and Tron uses txId. Historical unknown-chain records can retain a numeric chain ID and { vm: 'unknown', id }; do not fabricate a chain mapping. Do not infer missing account or Swig-authority evidence. An empty refund list means none were observed, not proof no refund occurred. HyperCore’s six outcomes are unchanged. On failure, inspect IntentFailedError.context.operations instead of .hyperCore. For partial, error or unknown outcomes, check the Hyperliquid account before retrying to avoid duplicate orders. Use rhinestone.getIntentStatus(intentId, { full: true }) for recorded details. Execution details describe the intended execution, not individual call-success results; use operation status and transaction evidence to establish completion.

Replace removed public types

Reconcile persisted transactions

Do not restore v2 prepared or signed transactions into v3. Reconcile any possible submission using its original intent ID before deliberately preparing a replacement; a timeout is not proof the spend failed. Persist the complete new prepared artifact, including its opaque request binding, using serialization that preserves bigint values. Do not retain only intentInput, edit the binding, or combine proofs with a fresh quote. Old-generation artifacts or artifacts missing request fail with InvalidPreparedTransactionError; the SDK does not convert or automatically re-quote them. Older Solana snapshot artifacts can fail with InvalidSolanaTransactionArtifactError.

Review smart session definitions

v3 removes these inputs. Delete the keys entirely: setting them to undefined or an empty value still throws. v2 deprecates the shorthand and claimPolicies but still accepts them. Nested API-key and JWT auth, and bundler and paymaster credentials, are unchanged. Smart session behavior changed in v2 2.17.0, and v3 carries it forward. If you are upgrading from 2.17.x, your session semantics already match; remove the inputs above, and pass an explicit source on one-time-use intents. If you are upgrading from 2.16.x or earlier:
  • Cross-chain permits are scoped by default. Open wildcard access is opt-in through fallback.
  • Omitted settlement layers select every layer that can settle the permit, and explicit lists are strict. ECO is retired; use ECO_IE.
  • A reusable permit’s maxAmount is a lifetime budget. Re-enabling an enabled session requires allowBudgetReset.
  • Sessions add permit, swap, and one-time-use windows, and a one-time-use option.
  • When signing is omitted, scoped sessions default to disabled signing; see Session signing.
Recreate affected definitions with createSession, inspect validateSession output, and persist the result. Sessions you already stored stay usable for enable, sign, and disable: pass the stored Session directly and do not rebuild it from a definition. An installed grant keeps its installed policies, so revoke it or authorize a replacement deliberately. See Store the created session. These session changes do not change account addresses or factory data.

What stays the same for EVM-only integrations

  • You do not need a Solana account, signer or dependency. Configure only evm.
  • Existing EVM account identities remain stable with the same configuration. Preserve any older Startale version explicitly, as described above.
  • The prepare → sign → submit → wait lifecycle, quote selection and terminal status names stay the same.
  • Managed EVM accounts retain reads, message signing, modules, recovery and UserOperation methods. UserOperation inputs remain flat { chain, calls, ... }; do not apply the intent input migration to them.
  • Shared authentication/providers, appFees, protocolFees, settlement-layer/quoter filters, and HyperCore order builders retain their roles. Move only the transaction-specific fields described above.
  • Preparation neither signs, spends nor deploys.
If you add managed Solana, configure its owner and explicit Swig state address separately. getAddress('solana') returns the asset-holding wallet, not the Swig state account; construction alone does not deploy or verify it. EVM-to-Solana delivery does not require a managed Solana account when you provide the recipient. It does not authorize destination instructions. Solana-to-EVM calls remain restricted to a backend-compatible EVM-derived Swig pairing; unrelated pairs support delivery only. Before switching traffic, verify derived EVM addresses, explicit funding and amount behavior, custom signing, sponsored preparation, status rendering, and your existing UserOperation path.

SDK v1 to v2

This section is historical v2 guidance. For v3, also apply the migration above. This guide targets @rhinestone/sdk 2.16.1. Upgrade and run your type checker before changing behavior:
The 2.x package is ESM-only. Use import syntax and only published entry points such as @rhinestone/sdk, @rhinestone/sdk/utils, @rhinestone/sdk/actions/*, and @rhinestone/sdk/smart-sessions.

Initialize the SDK and account

createRhinestoneAccount is removed. Create one SDK instance for shared authentication, RPC, bundler, and paymaster configuration, then create accounts from it.
Keep API-key mode server-side. If the SDK runs in a browser, migrate to short-lived JWTs as described in security.
The default account is Nexus 1.2.1. If a 1.x integration derived an unfunded Nexus 1.2.0 address that you must preserve, set account: { type: 'nexus', version: '1.2.0' }. Verify the expected address before funding or deploying.

Replace transaction shortcuts

account.sendTransaction is removed for intents. Use the explicit lifecycle:
Update the surrounding transaction code as follows:
  • Pass token addresses, not symbols such as USDC or WETH.
  • Read the recommended route from prepared.quotes.best; all routes are in prepared.quotes.all.
  • To select another route, call signTransaction(prepared, { intentId }) with an ID from quotes.all.
  • Change settlementLayers arrays to { include: [...] } or { exclude: [...] }.
  • Pass EIP-7702 authorizations as submitTransaction(signed, { authorizations }).
  • Remove the second acceptsPreconfirmations argument from waitForExecution.
  • Store intent IDs as strings before passing them to rhinestone.getIntentStatus(intentId).
A valid 2.x token request uses the token address for the target chain:

Migrate session keys

The 1.x experimental session names became stable in 2.x. Replace experimental_sessions and type: 'experimental_session' with sessions and type: 'session', then remove the experimental_ prefix from session account methods. Do not copy a 1.x resolved Session.actions object into 2.x. Recreate the permission with the current ABI-driven configuration and verify its permission ID before enabling it. Continue with create a session for the current session workflow.

Update account and provider configuration

  • Alchemy’s { type: 'alchemy', apiKey } provider configuration is removed. Supply complete per-chain URLs with { type: 'custom', urls }; see JSON-RPC providers.
  • passport accounts are removed.
  • Nexus supports 1.2.0 and 1.2.1; Kernel supports 3.3. Remove older pinned versions.
  • submitTransaction options are now an object, including EIP-7702 authorizations.
  • PortfolioToken.decimals and aggregate balances moved to per-chain entries as chains[].decimals and chains[].amount.

Update imports and removed helpers

Use these published 2.x locations:
The bundled token registry and its helpers are removed. Obtain supported chain and token metadata from Rhinestone’s chain catalog through the SDK-backed flows, and keep token addresses chain-specific. The Compact action package, Permit2 batch-signing helpers, deployAccountsForOwners, and checkERC20AllowanceDirect are also removed. Replace allowance reads with viem readContract; use the current account deployment methods and orchestrator-provided signing data for the other flows. After migration, verify derived account addresses, session permission IDs, token decimals per chain, and both intent and UserOperation paths in a test environment.