> ## 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 deployment

> Deploy EVM accounts with an intent or create a Solana Swig before spending.

A newly created EVM smart account has a counterfactual address before it has contract code. Deployment is per chain: receiving assets at that address does not deploy it, and deployment on one chain does not deploy it everywhere.

Use the account and transaction calls from [Relay a transaction](/transactions/relaying/relay-a-transaction) for the examples below.

<Tabs>
  <Tab title="Embedded wallet">
    For EVM, embedded wallets derive and store the account address and factory data during [sign-up](/wallets/embedded-wallets/sign-up-and-sign-in). Authentication does not submit an onchain deployment transaction.

    ## Automatic deployment

    Send the first real EVM transaction with `sendIntent()`. If the account is undeployed on the execution chain, the wallet includes its factory setup in the intent. You do not need to construct or submit a factory call yourself.

    ```ts {5-6} theme={null}
    import { baseSepolia } from "viem/chains";
    import { wallet } from "./wallet";

    const result = await wallet.sendIntent({
      source: { chain: baseSepolia.id, token: usdc },
      destination: { chain: baseSepolia.id, calls: [...calls] },
      sponsorshipMode: "required",
      closeOn: "completed",
    });

    if (!result.success) {
      throw new Error(result.error?.message ?? `Intent ended as ${result.status}`);
    }
    ```

    This example uses the same-chain USDC transfer from [Relay a transaction](/transactions/relaying/relay-a-transaction), including its [clear-signing review](/wallets/embedded-wallets/clear-signing). The completed intent deploys the account on Base Sepolia if necessary, then executes the calls.

    The deployment cost is part of the route. With `sponsorshipMode: "required"`, your application must sponsor it. For a user-paid route, use `"disabled"` and fund the account with enough assets for both the calls and fees. See [Sponsor fees](/transactions/sponsorship/sponsor-fees) for setup and fallback behavior.

    ## Check deployment

    Use a public RPC client for the chain you want to check. `WalletClient` does not expose an `isDeployed()` method:

    ```ts theme={null}
    import { createPublicClient, http } from "viem";

    const publicClient = createPublicClient({
      chain: baseSepolia,
      transport: http(),
    });

    const code = await publicClient.getCode({ address: accountAddress });
    const deployed = code !== undefined && code !== "0x";
    ```

    Check the authenticated account's address, not an account from another application or WebAuthn mode. See [Accounts](/wallets/embedded-wallets/accounts#account-isolation) for the namespace boundaries.

    <Note>
      For EVM, `@rhinestone/wallet` deploys with the first real intent, not a
      standalone deployment method. Managed Solana instead requires explicit
      `wallet.deploy({ chain })` before receiving funds; follow
      [Solana setup](/wallets/embedded-wallets/accounts#set-up-a-solana-wallet).
      The Custom signer methods below belong to a different SDK.
    </Note>
  </Tab>

  <Tab title="Custom signer">
    Use `account` from the [Custom signer quickstart](/wallets/custom-signer/quickstart).

    ## Automatic deployment

    For a normal EVM SDK intent, you do not need to call `deploy` first. When the target account is undeployed, `prepareTransaction` includes its setup operation in the quote. After you sign and submit that prepared intent, a relayer executes the setup and calls as part of the selected route.

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

    const prepared = await account.prepareTransaction({
      source: { token: usdc },
      destination: { chain: baseSepolia, calls: [...calls] },
    });
    const signed = await account.signTransaction(prepared);
    const submitted = await account.submitTransaction(signed);
    const result = await account.waitForExecution(submitted);
    ```

    The account is deployed only on chains where the route executes its account operations. An accepted submission is not proof of deployment; wait for completion, then call `isDeployed` when later behavior depends on the onchain code.

    <Note>
      Funding a counterfactual EVM address is safe when you preserve its exact
      account configuration, but receiving funds alone does not execute the factory call.
      Do not extend this claim to an undeployed Solana Swig.
    </Note>

    ### Pay deployment costs

    The deployment cost is part of the prepared route:

    * For a user-paid transaction, select one funding token in `source` and review its quoted input, including deployment fees.
    * For a sponsored transaction, set `sponsored` while preparing the intent. Complete [Sponsorship setup](/transactions/sponsorship/set-up) first and confirm the selected quote's fee breakdown.

    A sponsorship rejection does not fall back to the user's funds at submission time. Prepare a new user-paid route and obtain a new signature if your application offers that fallback.

    ## Check deployment

    For EVM deployment state, pass the chain object, not a chain ID. `isDeployed` does not take a VM argument:

    ```ts theme={null}
    const deployed = await account.isDeployed(baseSepolia);
    ```

    For a smart account, this checks whether contract code exists at the account address on that chain. A plain EOA is already usable and reports as deployed even though there is no smart account contract.

    ## Deploy explicitly

    Call `deploy` with the VM and chain when the account must exist before its first application transaction. For a user-paid intent deployment, select a fee token on that chain:

    ```ts theme={null}
    const didDeploy = await account.deploy("evm", baseSepolia, {
      source: { token: usdc },
    });
    ```

    The promise waits for the deployment path to complete. It returns `true` when this call performed a deployment and `false` when the account was already deployed or is a plain EOA.

    For an SDK-managed deployment through the intent path, request gas sponsorship explicitly:

    ```ts theme={null}
    const didDeploy = await account.deploy("evm", baseSepolia, {
      sponsored: true,
    });
    ```

    Depending on the attached account data and SDK configuration, an explicit deployment can use a UserOperation instead of an intent—for example, an undeployed bring-your-own account whose Intent Executor is not installed, or an account configured with a custom bundler. In that case, configure the bundler and optional paymaster through [ERC-4337](/wallets/custom-signer/configuration/erc-4337); the relayer `sponsored` option does not configure UserOperation sponsorship.

    For EIP-7702, `deploy` creates the required initialization signature automatically. Normal `prepareTransaction` calls still require an initialization signature; `signTransaction` collects the delegation proofs. Follow the EIP-7702 flow documented in [Account types: EIP-7702](/wallets/custom-signer/configuration/account-types/eip-7702).

    ## Deploy from another account

    Use the public `deploy` action when an existing smart account should submit and pay for another account's factory call:

    ```ts theme={null}
    import { deploy } from "@rhinestone/sdk/actions";

    const newAccount = await rhinestone.createAccount({
      evm: { owners: { type: "ecdsa", accounts: [newOwner] } },
    });

    const prepared = await sponsorAccount.prepareTransaction({
      source: { token: usdc },
      destination: { chain: baseSepolia, calls: [deploy(newAccount)] },
    });
    const signed = await sponsorAccount.signTransaction(prepared);
    const submitted = await sponsorAccount.submitTransaction(signed);
    await sponsorAccount.waitForExecution(submitted);
    ```

    This continuation assumes initialized `rhinestone`, `newOwner`, and `sponsorAccount`; fund the sponsor account's selected USDC balance. The target account derives its factory call from `newAccount` while `sponsorAccount` owns and authorizes the relay. This is separate from organization-level fee sponsorship; add `sponsored` to the prepared transaction only when your configured sponsorship should cover its fees.

    ## Create a Solana Swig before spending

    Solana does not use the automatic EVM deployment path. Start with [Solana setup](/wallets/custom-signer/solana-support) and the existing `rhinestone` client. For an independent Swig, generate its ID once and durably save both `id` and `swig` before deployment. Use the saved values when restoring the account; do not generate a new identity on reload.

    ```ts theme={null}
    import { createSolanaSwigId, solanaDevnet } from "@rhinestone/sdk/solana";

    const { id, swig } = createSolanaSwigId();
    // Persist id and swig together before continuing.
    const solanaAccount = await rhinestone.createAccount({
      solana: { owner: { type: "ecdsa", account: owner }, swig },
    });
    const exists = await solanaAccount.deploy("solana", solanaDevnet, {
      swigId: id,
    });
    ```

    `owner` is one viem secp256k1 local account with a public key; WebAuthn is the other supported owner type, not Ed25519. Deployment installs the configured owner as the **permanent root authority**. Verify the intended owner before creation: a wrong root cannot be repaired by recreating the Swig. Never prefund its predictable undeployed address.

    Deployment is a sponsored intent billed to your integrator's gas sponsorship, including creation rent and fees. Configure [sponsorship](/transactions/sponsorship/set-up) first. Later authority changes are also sponsored; neither operation has a user-paid deployment mode. This does not make rent for arbitrary instruction-created accounts free.

    `deploy` resolves `true` when the Swig exists, including when it already existed. It does **not** verify an existing Swig's root owner. Before spending or funding, confirm the onchain authorities match your intended signer; do not treat `exists` as authorization evidence. The EVM `isDeployed` check above is not a Solana readiness check.

    See [Solana support](/wallets/custom-signer/solana-support) for supported account and fee modes.

    ## Supported account behavior

    The following deployment behaviors are EVM-specific.

    | Account configuration | Deployment behavior |
    | - | - |
    | Nexus, Safe, Kernel, `Startale`, or HCA | The SDK can include the provider's factory setup in an intent or deploy the account explicitly. |
    | EIP-7702 Nexus account | The SDK initializes and delegates the existing EOA address instead of creating a separate account address. Normal transaction preparation requires the EIP-7702 initialization signature. |
    | Plain EOA | There is no smart account to deploy. `deploy` returns `false`. |
    | Bring your own account | A deployed account needs no factory data. An undeployed account needs the correct address, factory, factory calldata, and Intent Executor state. |

    Keep the complete account configuration unchanged across chains. Provider version, owners, salt, nonce, or factory choices can change the counterfactual address. See [Smart account providers](/wallets/custom-signer/configuration/smart-account-providers) and [Bring your own account](/wallets/custom-signer/configuration/bring-your-own-account).
  </Tab>

  <Tab title="REST API">
    Caucasus nests EVM setup under `account.evm.initData.setupOps`; see [per-VM accounts](/api-reference/api-versioning#configure-accounts-per-vm). Automatic deployment during execution is EVM-specific. Solana spending requires an existing authorized Swig: a missing spending account returns `SOLANA_ACCOUNT_NOT_CREATED`, rather than being silently created by quoting. See [capability boundaries](/api-reference/api-versioning#respect-capability-boundaries).
  </Tab>
</Tabs>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.