Non-custodial flows, users with existing smart accounts
Managed accounts are recommended for most integrations — simpler setup, no SDK
dependency for registration. User-owned accounts are for cases where deposits
should go to an existing smart account the user already controls.
The service creates a Nexus smart account deterministically from your API key and a salt you provide. The same API key + salt always produces the same deposit address, so you can safely re-register if needed.
1
Pick a salt
Use a stable, unique identifier per user — for example, an internal user ID. Hash it for privacy:
import { keccak256, toHex } from "viem";const salt = keccak256(toHex("user-123"));
sources lists every chain the account can receive deposits from, each with its deposit address — EVM chains share the evmDepositAddress, Solana carries the solanaDepositAddress.
For this flow, you create a Rhinestone smart account via the SDK, configure session keys so the deposit service can sign bridging transactions, and register the account with its factory data and session details.
1
Install the SDK
These examples target SDK 3.0.0, which is not released yet. The install command
below requires its publication; the current v2 package does not support these APIs.
npm install @rhinestone/sdk@3.0.0 viem
2
Create the account
Run these examples in a trusted backend with userSigner, a viem account controlled by the user. Never expose API_KEY in browser code. The following blocks continue this setup.
The deposit service uses a session key to sign bridging transactions on the user’s behalf. Build session details for every chain you accept deposits from, plus the target chain — a deposit arriving on a chain with no session can’t be bridged. See supported chains and tokens for the current set.
This unscoped setup grants the service signer unrestricted execution and
ERC-1271 signing on each listed chain, with no separate claim policies. It is
not limited to bridging. The policy override matches the service’s per-chain
session reconstruction; changing it invalidates the enable signature. Confirm
chain support and the production signer address before signing.
The example pins Nexus 1.2.1. The service also recognizes Nexus 1.2.0, Startale 1.0.0, Safe, and Kernel factories; do not assume other factories or validator configurations are compatible. Restricted swap sessions require the service’s matching stored scope and are not covered by this example.
The JSON replacer (_, v) => typeof v === "bigint" ? v.toString() : v is needed because sessionDetails.hashesAndChainIds contains bigint chain IDs that JSON.stringify can’t serialize by default.