> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Account registration

> Register managed or user-owned smart accounts for cross-chain deposit processing.

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 account                                 | User-owned account                                      |
| ----------------------- | ----------------------------------------------- | ------------------------------------------------------- |
| Who creates the account | Deposit service                                 | You, via SDK                                            |
| Session key setup       | Automatic                                       | You sign session authorization                          |
| User-facing address     | Service returns deposit address                 | Your existing smart account address                     |
| Recipient               | Required (you specify where funds go)           | Optional (defaults to the account itself)               |
| Best for                | Existing apps, EOA users (e.g. browser wallets) | Non-custodial flows, users with existing smart accounts |

<Info>
  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.
</Info>

All examples below use these shared constants:

```ts theme={null}
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

<Tabs>
  <Tab title="Managed 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.

    <Steps>
      <Step title="Pick a salt">
        Use a stable, unique identifier per user — for example, an internal user ID. Hash it for privacy:

        ```ts theme={null}
        import { keccak256, toHex } from "viem";

        const salt = keccak256(toHex("user-123"));
        ```
      </Step>

      <Step title="Call /register-managed">
        ```ts theme={null}
        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:

        | Field                  | Description                                 |
        | ---------------------- | ------------------------------------------- |
        | `evmDepositAddress`    | Accepts deposits on any supported EVM chain |
        | `solanaDepositAddress` | Accepts deposits on Solana                  |

        Both addresses route to the same target chain and token.
      </Step>

      <Step title="Verify registration">
        ```ts theme={null}
        const check = await fetch(`${DEPOSIT_SERVICE_URL}/check/${evmDepositAddress}`);
        const data = await check.json();
        ```

        ```json theme={null}
        {
          "isRegistered": true,
          "targetChain": "eip155:42161",
          "targetToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
          "sources": [
            { "chain": "eip155:8453", "depositAddress": "<evmDepositAddress>" },
            { "chain": "eip155:42161", "depositAddress": "<evmDepositAddress>" },
            {
              "chain": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
              "depositAddress": "<solanaDepositAddress>"
            }
          ]
        }
        ```

        `sources` lists every chain the account can receive deposits from, each with its deposit address — EVM chains share the `evmDepositAddress`, Solana carries the `solanaDepositAddress`.
      </Step>
    </Steps>
  </Tab>

  <Tab title="User-owned account">
    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.

    <Steps>
      <Step title="Install the SDK">
        ```bash theme={null}
        npm install @rhinestone/sdk viem
        ```
      </Step>

      <Step title="Create the account">
        ```ts theme={null}
        import { RhinestoneSDK, type RhinestoneAccountConfig } from "@rhinestone/sdk";

        const rhinestone = new RhinestoneSDK();

        const config: RhinestoneAccountConfig = {
          owners: {
            type: "ecdsa",
            accounts: [userSigner],
          },
          sessions: { enabled: true },
        };

        const account = await rhinestone.createAccount(config);
        const address = account.getAddress();
        const { factory, factoryData } = account.getInitData();
        ```
      </Step>

      <Step title="Build session details">
        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](/deposits/overview/resources/supported-chains-and-tokens) for the current set.

        ```ts theme={null}
        import { toViewOnlyAccount } from "@rhinestone/sdk/utils";
        import { base, optimism, arbitrum } from "viem/chains";

        const RHINESTONE_SIGNER_ADDRESS = "0x177bfcdd15bc01e99013dcc5d2b09cd87a18ce9c";
        const sessionSigner = toViewOnlyAccount(RHINESTONE_SIGNER_ADDRESS);

        const sourceChains = [base, optimism, arbitrum]; // extend to every chain you accept
        const sessions = sourceChains.map((chain) => ({
          owners: { type: "ecdsa" as const, accounts: [sessionSigner] },
          chain,
        }));

        const sessionDetails = await account.getSessionDetails(sessions);
        const signature = await account.signEnableSession(sessionDetails);

        const enableSessionDetails = {
          hashesAndChainIds: sessionDetails.hashesAndChainIds,
          signature,
        };
        ```
      </Step>

      <Step title="Call /register">
        ```ts theme={null}
        const response = await fetch(`${DEPOSIT_SERVICE_URL}/register`, {
          method: "POST",
          headers,
          body: JSON.stringify(
            {
              account: {
                address,
                accountParams: {
                  factory,
                  factoryData,
                  sessionDetails: enableSessionDetails,
                },
                target: {
                  chain: "eip155:42161", // Arbitrum
                  token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC
                },
              },
            },
            (_, v) => (typeof v === "bigint" ? v.toString() : v),
          ),
        });

        const { evmDepositAddress, solanaDepositAddress } = await response.json();
        ```

        <Note>
          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.
        </Note>
      </Step>

      <Step title="Verify registration">
        ```ts theme={null}
        const check = await fetch(`${DEPOSIT_SERVICE_URL}/check/${address}`);
        const data = await check.json();
        // { isRegistered: true, targetChain: "eip155:42161", sources: [...] }
        ```
      </Step>
    </Steps>

    ### Adding source chains

    To accept deposits from chains that weren't included in the original registration, add new session details:

    ```ts theme={null}
    import { polygon } from "viem/chains";

    const newSessions = [
      {
        owners: { type: "ecdsa" as const, accounts: [sessionSigner] },
        chain: polygon,
      },
    ];
    const newSessionDetails =
      await account.getSessionDetails(newSessions);
    const newSignature =
      await account.signEnableSession(newSessionDetails);

    await fetch(`${DEPOSIT_SERVICE_URL}/account/${address}/session`, {
      method: "POST",
      headers,
      body: JSON.stringify(
        {
          sessionDetails: {
            hashesAndChainIds: newSessionDetails.hashesAndChainIds,
            signature: newSignature,
          },
        },
        (_, v) => (typeof v === "bigint" ? v.toString() : v),
      ),
    });
    ```

    <Note>
      Managed accounts automatically support all available source chains — this
      step is only needed for user-owned accounts.
    </Note>
  </Tab>
</Tabs>

To select the destination token from the source chain, token, or symbol, configure [token routing](/deposits/headless/setup/token-routing) during registration.
