Skip to main content
Every depositing user needs a registered smart account. You can let the service create one (managed) or bring your own (user-owned).

Choose an account type

Managed accountUser-owned account
Who creates the accountDeposit serviceYou, via SDK
Session key setupAutomaticYou sign session authorization
User-facing addressService returns deposit addressYour existing smart account address
RecipientRequired (you specify where funds go)Optional (defaults to the account itself)
Best forExisting apps, EOA users (e.g. browser wallets)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.
All examples below use these shared constants:
const DEPOSIT_SERVICE_URL =
  "https://v1.orchestrator.rhinestone.dev/deposit-processor";
const API_KEY = "YOUR_RHINESTONE_API_KEY";

const headers = {
  "Content-Type": "application/json",
  "x-api-key": API_KEY,
};

Register an account

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"));
2

Call /register-managed

const response = await fetch(`${DEPOSIT_SERVICE_URL}/register-managed`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    account: {
      salt,
      target: {
        chain: "eip155:42161", // Arbitrum
        token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC
        recipient: "0xYOUR_RECIPIENT_ADDRESS",
      },
    },
  }),
});

const { evmDepositAddress, solanaDepositAddress } = await response.json();
The response includes two deposit addresses:
FieldDescription
evmDepositAddressAccepts deposits on any supported EVM chain
solanaDepositAddressAccepts deposits on Solana
Both addresses route to the same target chain and token.
3

Verify registration

const check = await fetch(`${DEPOSIT_SERVICE_URL}/check/${evmDepositAddress}`);
const data = await check.json();
{
  "isRegistered": true,
  "targetChain": "eip155:42161",
  "targetToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
  "sourceChains": ["eip155:8453", "eip155:10", "eip155:42161"]
}

Optional: token routing

By default, all deposits are bridged to the single target token you set at registration. Token routing rules let you select the output token based on what the user deposited. When registering an account, the target object accepts two optional fields:
FieldTypeDescription
outputTokenRulesArray<Rule>Routing rules evaluated by match specificity
rejectUnmappedbooleanIf true, deposits that don’t match any rule are rejected instead of falling back to target.token
Each rule has:
FieldTypeDescription
match.chainstring (CAIP-2)Match deposits from this source chain (e.g. "eip155:1")
match.tokenstring (address)Match deposits of this source token address
match.symbolstringMatch deposits by token symbol (case-insensitive, e.g. "USDC")
outputTokenstring (address)The final token to deliver on the target chain
A rule’s match must specify at least one of chain, token, or symbol. You can combine them for more specific matches.

Rule priority

When multiple rules match a deposit, the most specific rule wins. Declaration order only matters when two rules share the same specificity.
Match typeExample
chain + tokenSpecific token from a specific chain
chain + symbolAny token with symbol X from chain Y
token (only)Specific token from any chain
symbol (only)Any token with symbol X from any chain
chain (only)Any token from a specific chain
No matchFalls back to target.token (or rejected if rejectUnmapped: true)

Example: USDC and ETH passthrough

Route USDC deposits to USDC.e and ETH deposits to WETH on Optimism, while defaulting other tokens to a fallback:
await fetch(`${DEPOSIT_SERVICE_URL}/register-managed`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    account: {
      salt,
      target: {
        chain: "eip155:10", // Optimism
        token: "0xYOUR_DEFAULT_FALLBACK_TOKEN",
        recipient: "0xYOUR_RECIPIENT_ADDRESS",
        outputTokenRules: [
          {
            match: { symbol: "USDC" },
            outputToken: "0x7f5c764cbc14f9669b88837ca1490cca17c31607", // USDC.e on Optimism
          },
          {
            match: { symbol: "ETH" },
            outputToken: "0x4200000000000000000000000000000000000006", // WETH on Optimism
          },
        ],
      },
    },
  }),
});
With this configuration:
  • User deposits USDC (from any chain) → receives USDC.e on Optimism
  • User deposits ETH (from any chain) → receives WETH on Optimism
  • User deposits any other token → receives target.token (default fallback)

Example: chain-specific overrides

Combine chain and symbol for chain-specific routing. The chain-specific rule takes priority because chain + symbol outranks symbol alone.
outputTokenRules: [
  {
    match: { chain: "eip155:1", symbol: "USDC" },
    outputToken: "0xUSCD_BRIDGED_ADDRESS",
  },
  {
    match: { symbol: "USDC" },
    outputToken: "0xUSDC_NATIVE_ADDRESS",
  },
];

Example: reject unknown tokens

Only accept specific tokens and reject everything else:
await fetch(`${DEPOSIT_SERVICE_URL}/register-managed`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    account: {
      salt,
      target: {
        chain: "eip155:10",
        token: "0xNOT_USED_AS_FALLBACK",
        recipient: "0xYOUR_RECIPIENT_ADDRESS",
        outputTokenRules: [
          { match: { symbol: "USDC" }, outputToken: "0xUSDC_ADDRESS" },
          { match: { symbol: "ETH" }, outputToken: "0xWETH_ADDRESS" },
        ],
        rejectUnmapped: true,
      },
    },
  }),
});
Deposits that don’t match USDC or ETH are ignored (not bridged).

Optional: configure post-bridge actions

Post-bridge actions execute after the bridged tokens arrive on the target chain — for example, swapping into a protocol-specific token. Set postBridgeActions in the target object at registration time. When present, target.token is the intermediate token bridged into the account, and the last action’s outputToken is the final token delivered to the recipient.
await fetch(`${DEPOSIT_SERVICE_URL}/register-managed`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    account: {
      salt,
      target: {
        chain: "eip155:8453",
        token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC bridged first
        recipient: "0xYOUR_RECIPIENT_ADDRESS",
        postBridgeActions: [
          {
            type: "orderbook-swap",
            contract: "0xSWAP_CONTRACT_ADDRESS",
            outputToken: "0xFINAL_TOKEN_ADDRESS",
          },
        ],
      },
    },
  }),
});
FieldTypeDescription
type"orderbook-swap"The action type
contractAddressSwap contract address
outputTokenAddressFinal token delivered to the recipient