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

# Quickstart

> Create a smart wallet and send your first transaction with 1auth or the Rhinestone SDK

Create a smart wallet and send your first transaction. Choose 1auth for managed passkey authentication and transaction approval, or use the SDK to control the signer and account integration yourself.

<Tabs>
  <Tab title="1auth">
    ## Prerequisites

    You need:

    * A browser app served over HTTPS. The hosted provider does not accept plain `http://localhost`; see the [local HTTPS options](https://docs.1auth.app/faq#can-i-test-against-1auth-from-localhost).
    * A 1auth app and JWT signing credentials from the [Rhinestone dashboard](https://dashboard.rhinestone.dev).
    * Two same-origin server endpoints that issue sponsorship tokens. Follow the [fee sponsorship guide](https://docs.1auth.app/guides/fee-sponsorship) to create them.

    Keep JWT signing credentials on your server. In production, authenticate these endpoints and restrict their policies, budgets, and rate limits.

    Install 1auth and viem:

    <CodeGroup>
      ```bash npm theme={null}
      npm install @rhinestone/1auth viem
      ```

      ```bash pnpm theme={null}
      pnpm add @rhinestone/1auth viem
      ```

      ```bash bun theme={null}
      bun add @rhinestone/1auth viem
      ```
    </CodeGroup>

    <Steps>
      <Step title="Initialize 1auth">
        Create a client in your browser app. Replace `my-app` with your app's client ID:

        ```ts theme={null}
        import { OneAuthClient } from "@rhinestone/1auth";
        import { encodeFunctionData, parseAbi, parseUnits } from "viem";

        const client = new OneAuthClient({
          providerUrl: "https://passkey.1auth.app",
          clientId: "my-app",
          sponsorship: {
            accessTokenUrl: "/api/sponsorship/access-token",
            extensionTokenUrl: "/api/sponsorship/extension-token",
          },
        });
        ```

        The access-token URL receives a `GET` request and returns `{ token }`. The extension-token URL receives a `POST` request containing `{ intentOp }` and returns `{ token }`. These are endpoints in your app, not hosted 1auth endpoints. Without working app credentials, `sendIntent` fails with `MISSING_APP_CREDENTIALS`.

        <Note>
          1auth uses app-origin passkeys by default. The WebAuthn ceremony runs on your
          app's exact origin, so different origins create different passkeys and smart
          accounts. Learn more in [Choose your WebAuthn
          mode](https://docs.1auth.app/guides/app-origin-passkeys#choose-your-webauthn-mode).
        </Note>
      </Step>

      <Step title="Authenticate the user">
        Call authentication from a user-facing control, such as a **Sign in** button. Do not start authentication automatically when the page loads.

        The mint function below verifies authentication before reading the session address:

        ```ts theme={null}
        async function mintMusd() {
          const auth = await client.authenticate();
          if (!auth.success) {
            console.error("Authentication failed", auth.error);
            return;
          }

          const accountAddress = auth.session.accountAddress;
        ```
      </Step>

      <Step title="Mint 1 mUSD">
        Continue the function by submitting a sponsored mint on Base Sepolia. Invoke it from a transaction button so the user chooses when to review and approve the transaction:

        ```ts theme={null}
          const musdBaseSepolia = "0x2f6fdE5E2AeAB6335d8f978B4d8B2a9c1129AcFb";

          const result = await client.sendIntent({
            accountAddress,
            targetChain: 84532,
            calls: [
              {
                to: musdBaseSepolia,
                data: encodeFunctionData({
                  abi: parseAbi(["function mint(address to, uint256 amount)"]),
                  functionName: "mint",
                  args: [accountAddress, parseUnits("1", 6)],
                }),
                label: "Mint",
                sublabel: "1 mUSD",
              },
            ],
            closeOn: "completed",
          });

          if (!result.success) {
            console.error("Intent did not complete", {
              intentId: result.intentId || undefined,
              status: result.status,
              error: result.error,
            });
            return;
          }

          console.log("Completed intent", {
            intentId: result.intentId,
            status: result.status,
            transactionHash: result.transactionHash,
          });
        }
        ```

        `sendIntent` waits for the `completed` status because `closeOn` is set explicitly. A timeout is not proof that an already submitted intent failed; keep its intent ID for status tracking instead of blindly submitting it again.
      </Step>
    </Steps>

    ## Expected outcome

    The authenticated smart account receives 1 mUSD on Base Sepolia and can start with a zero balance. This works because the call creates a faucet token and does not require tokens to be sourced or bridged first. Fee sponsorship does not make every transaction funding-free; transactions that need assets still require those assets to be available.

    ## Next steps

    <CardGroup cols={3}>
      <Card title="Send crosschain" icon="route" href="https://docs.1auth.app/guides/crosschain">
        Use the mUSD you minted in a crosschain transaction.
      </Card>

      <Card title="Add React components" icon="component" href="https://docs.1auth.app/sdk/react">
        Add managed wallet components to a React app.
      </Card>

      <Card title="Connect viem or wagmi" icon="wallet-cards" href="https://docs.1auth.app/sdk/wallet-client">
        Use a passkey wallet client in an existing web3 app.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="SDK">
    ## Prerequisites

    Run this example in a secure server-side environment. You need:

    * A `RHINESTONE_API_KEY` from the [Rhinestone dashboard](https://dashboard.rhinestone.dev).
    * A funding account with Base Sepolia testnet ETH and its private key in `FUNDING_PRIVATE_KEY`. Get testnet ETH from [QuickNode](https://faucet.quicknode.com/drip) or [Alchemy](https://www.alchemy.com/faucets).

    Keep both values in secure runtime configuration. Never expose or log API keys or private keys in browser code.

    Install the SDK:

    <CodeGroup>
      ```bash npm theme={null}
      npm install viem @rhinestone/sdk
      ```

      ```bash pnpm theme={null}
      pnpm add viem @rhinestone/sdk
      ```

      ```bash bun theme={null}
      bun add viem @rhinestone/sdk
      ```
    </CodeGroup>

    <Steps>
      <Step title="Create an account">
        Create a smart account with a single ECDSA owner:

        ```ts theme={null}
        import { RhinestoneSDK } from "@rhinestone/sdk";
        import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
        import { baseSepolia, arbitrumSepolia } from "viem/chains";
        import {
          createPublicClient,
          createWalletClient,
          encodeFunctionData,
          erc20Abi,
          type Hex,
          http,
          parseEther,
          parseUnits,
        } from "viem";

        const rhinestoneApiKey = process.env.RHINESTONE_API_KEY;
        if (!rhinestoneApiKey) {
          throw new Error("RHINESTONE_API_KEY is not set");
        }

        const fundingPrivateKey = process.env.FUNDING_PRIVATE_KEY;
        if (!fundingPrivateKey) {
          throw new Error("FUNDING_PRIVATE_KEY is not set");
        }

        const sourceChain = baseSepolia;
        const targetChain = arbitrumSepolia;

        const privateKey = generatePrivateKey();
        const account = privateKeyToAccount(privateKey);

        const rhinestone = new RhinestoneSDK({
          apiKey: rhinestoneApiKey,
        });
        const rhinestoneAccount = await rhinestone.createAccount({
          owners: {
            type: "ecdsa",
            accounts: [account],
          },
        });
        const address = rhinestoneAccount.getAddress();
        console.log(`Smart account address: ${address}`);
        ```

        Store the owner key securely if you need to access this account again. Generating a new owner key creates a different account address.

        You'll see a deterministic address printed:

        ```txt theme={null}
        Smart account address: 0x...
        ```

        Nothing is onchain yet: the account is deployed lazily on first use, on each chain it touches.
      </Step>

      <Step title="Fund the account">
        Send ETH to the smart account on the source chain. This is the only token you fund:

        ```ts theme={null}
        const publicClient = createPublicClient({
          chain: sourceChain,
          transport: http(),
        });
        const fundingAccount = privateKeyToAccount(fundingPrivateKey as Hex);
        const fundingClient = createWalletClient({
          account: fundingAccount,
          chain: sourceChain,
          transport: http(),
        });

        const txHash = await fundingClient.sendTransaction({
          to: address,
          value: parseEther("0.001"),
        });
        await publicClient.waitForTransactionReceipt({ hash: txHash });
        ```
      </Step>

      <Step title="Send a crosschain transaction">
        Transfer USDC on the target chain, sourced from the ETH you funded on the source chain:

        ```ts theme={null}
        const usdcAmount = parseUnits("0.1", 6);
        const usdc = "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d"; // USDC on Arbitrum Sepolia

        const prepared = await rhinestoneAccount.prepareTransaction({
          sourceChains: [sourceChain],
          targetChain,
          calls: [
            {
              to: usdc,
              value: 0n,
              data: encodeFunctionData({
                abi: erc20Abi,
                functionName: "transfer",
                args: ["0xd8da6bf26964af9d7eed9e03e53415d37aa96045", usdcAmount],
              }),
            },
          ],
          tokenRequests: [
            {
              address: usdc,
              amount: usdcAmount,
            },
          ],
        });
        const signed = await rhinestoneAccount.signTransaction(prepared);
        const transaction = await rhinestoneAccount.submitTransaction(signed);
        console.log("Transaction", transaction);

        const transactionResult = await rhinestoneAccount.waitForExecution(transaction);
        console.log("Result", transactionResult);
        ```

        `submitTransaction` returns an intent handle; `waitForExecution` polls until every chain's operation reaches a terminal state:

        ```txt theme={null}
        Transaction {
          type: 'intent',
          id: '0x9c…',
          traceId: '…',
          sourceChains: [ 84532 ],
          targetChain: 421614
        }
        Result {
          status: 'COMPLETED',
          accountAddress: '0x5fA3…42c1',
          operations: [
            { chain: 84532, status: 'COMPLETED', txHash: '0x…', timestamp: 1750000000 },
            { chain: 421614, status: 'COMPLETED', txHash: '0x…', timestamp: 1750000002 }
          ]
        }
        ```

        Your ETH on Base Sepolia landed as USDC on Arbitrum Sepolia in a single atomic operation: no bridging, swapping, or gas tokens to manage.
      </Step>
    </Steps>

    <Info>
      **Building a browser app?** Use the [Reown + Rhinestone
      example](https://github.com/rhinestonewtf/e2e-examples/tree/main/reown) to get
      wallet connection working with MetaMask or any WalletConnect-compatible
      wallet.
    </Info>

    ## Expected outcome

    Your account is deployed on Base Sepolia and Arbitrum Sepolia, with 0.1 USDC transferred on the target chain from the ETH funded on the source chain.

    ## Next steps

    <CardGroup cols={3}>
      <Card title="Sponsor fees" icon="fuel" href="./tutorials/sponsor-fees">
        Cover gas, bridging, and swap fees for your users across any chain.
      </Card>

      <Card title="Smart account setup" icon="settings" href="./core/create-account">
        Configure signers: passkeys, embedded wallets, multisig, and more.
      </Card>

      <Card title="Smart Sessions" icon="key" href="./smart-sessions/overview">
        Add session keys for one-click UX and automated transactions.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
