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

# Quickstart

> Learn how to create users, embedded wallets, and send transactions in a Ruby app using the Privy Ruby SDK.

## 0. Prerequisites

This guide assumes the [setup](/basics/ruby/setup) guide is complete and a Privy client instance is available.

## 1. Creating a wallet

First, create a wallet. The wallet's `id` is used in future calls to sign messages and send transactions.

<Tabs>
  <Tab title="Ethereum">
    ```ruby theme={"system"}
    begin
      wallet = client.wallets.create(
        wallet_create_params: {chain_type: :ethereum}
      )
      wallet_id = wallet.id
    rescue Privy::Errors::APIStatusError => e
      # Non-2xx HTTP status codes (e.g. 400, 401, 404, 429, 5xx)
      puts(e.status)
      puts(e.message)
    rescue Privy::Errors::APIConnectionError => e
      # Network-level errors raised by `net/http`
      puts(e.cause)
    end
    ```
  </Tab>

  <Tab title="Solana">
    ```ruby theme={"system"}
    begin
      wallet = client.wallets.create(
        wallet_create_params: {chain_type: :solana}
      )
      wallet_id = wallet.id
    rescue Privy::Errors::APIStatusError => e
      puts(e.status)
      puts(e.message)
    rescue Privy::Errors::APIConnectionError => e
      puts(e.cause)
    end
    ```
  </Tab>
</Tabs>

<Tip>[Learn more](/wallets/wallets/create/create-a-wallet) about creating wallets.</Tip>

<Note>
  When using the `PrivyClient` to interact with the API, all errors raised inherit from
  `Privy::Errors::APIError`. Catch `Privy::Errors::APIStatusError` for non-2xx responses (with
  `status` and `message` available) or more specific subclasses such as `RateLimitError`,
  `BadRequestError`, or `NotFoundError`.
</Note>

### User wallets

Create a self-custodial user wallet by first creating a user, then provisioning a wallet for that user.

<Tabs>
  <Tab title="Ethereum">
    ```ruby theme={"system"}
    begin
      user = client.users.create(
        user_create_params: {
          linked_accounts: [{type: :email, address: "batman@privy.io"}]
        }
      )

      wallet = client.wallets.create(
        wallet_create_params: {
          chain_type: :ethereum,
          owner: {user_id: user.id}
        }
      )
    rescue Privy::Errors::APIError => e
      puts(e.message)
    end
    ```
  </Tab>

  <Tab title="Solana">
    ```ruby theme={"system"}
    begin
      user = client.users.create(
        user_create_params: {
          linked_accounts: [{type: :email, address: "batman@privy.io"}]
        }
      )

      wallet = client.wallets.create(
        wallet_create_params: {
          chain_type: :solana,
          owner: {user_id: user.id}
        }
      )
    rescue Privy::Errors::APIError => e
      puts(e.message)
    end
    ```
  </Tab>
</Tabs>

<Info>
  When creating a user wallet, specify the user ID as the `owner` of the wallet. Obtain a user ID
  by first [creating a user](/user-management/migrating-users-to-privy/create-or-import-a-user)
  before creating the wallet.

  Alternatively, [create a user and wallet at the same
  time](/user-management/migrating-users-to-privy/create-or-import-a-user) by passing `wallets:` to
  `users.create`.
</Info>

## 2. Signing a message

Next, sign a plaintext message with the wallet using the `rpc` method on the wallets service. Use
`personal_sign` for Ethereum and `signMessage` for Solana, and specify the wallet ID (not address)
from creation.

<Tabs>
  <Tab title="Ethereum">
    ```ruby theme={"system"}
    response = client.wallets.rpc(
      wallet_id,
      wallet_rpc_request_body: {
        method: "personal_sign",
        chain_type: "ethereum",
        params: {message: "Hello, Privy!", encoding: "utf-8"}
      }
    )

    # Signature is hex-encoded for Ethereum
    signature = response.data.signature
    ```
  </Tab>

  <Tab title="Solana">
    ```ruby theme={"system"}
    require "base64"

    message = "Hello, Privy!"
    # Solana requires the message to be base64 encoded
    base64_message = Base64.strict_encode64(message)

    response = client.wallets.rpc(
      wallet_id,
      wallet_rpc_request_body: {
        method: "signMessage",
        chain_type: "solana",
        params: {message: base64_message, encoding: "base64"}
      }
    )

    # Signature is base64-encoded for Solana
    signature = response.data.signature
    ```
  </Tab>
</Tabs>

<Tip>[Learn more](/wallets/using-wallets/ethereum/sign-a-message) about signing messages.</Tip>

## 3. Sending transactions

<Info>
  The wallet must have funds to send a transaction. Use a testnet
  [faucet](https://console.optimism.io/faucet) to test transacting on a testnet (e.g. Base Sepolia)
  or send funds to the wallet on the network of choice.
</Info>

To send a transaction from a wallet, call `wallets.rpc` with `eth_sendTransaction` for Ethereum or
`signAndSendTransaction` for Solana. The SDK populates missing network-related values, signs the
transaction, broadcasts it to the network, and returns the transaction hash.

In the request, specify the wallet `id` from wallet creation above, as well as the `caip2` chain ID
for the target network.

<Tabs>
  <Tab title="Ethereum">
    ```ruby theme={"system"}
    caip2 = "eip155:11155111" # Sepolia testnet

    response = client.wallets.rpc(
      wallet_id,
      wallet_rpc_request_body: {
        method: "eth_sendTransaction",
        chain_type: "ethereum",
        caip2: caip2,
        params: {
          transaction: {
            to: recipient_address,
            value: "0x1", # 1 wei
            chain_id: 11_155_111 # Sepolia testnet
          }
        }
      }
    )

    transaction_hash = response.data.hash
    ```
  </Tab>

  <Tab title="Solana">
    ```ruby theme={"system"}
    caip2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" # Solana Mainnet

    # A base64 encoded serialized transaction to sign
    transaction = "insert-base-64-encoded-serialized-transaction"

    response = client.wallets.rpc(
      wallet_id,
      wallet_rpc_request_body: {
        method: "signAndSendTransaction",
        chain_type: "solana",
        caip2: caip2,
        params: {transaction: transaction, encoding: "base64"}
      }
    )

    transaction_hash = response.data.hash
    ```
  </Tab>
</Tabs>

<Tip>
  [Learn more](/wallets/using-wallets/ethereum/send-a-transaction) about sending transactions.
</Tip>

<Tip>
  For more control, prepare and broadcast the transaction independently, and use raw signing methods
  (`eth_signTransaction` for [EVM](/wallets/using-wallets/ethereum/sign-a-transaction) and
  `signTransaction` for [Solana](/wallets/using-wallets/solana/sign-a-transaction)) to sign the
  transaction with a wallet.
</Tip>

## 4. Creating a user

To create a user for an application, use the `create` method on the users service. Pass in linked
accounts, custom metadata, and wallets to associate with the user.

```ruby theme={"system"}
user = client.users.create(
  user_create_params: {
    linked_accounts: [
      {type: :custom_auth, custom_user_id: "your-subject-id"},
      {type: :email, address: "user@example.com"}
    ]
  }
)

user_id = user.id
```

<Tip>
  [Learn more](/user-management/migrating-users-to-privy/create-or-import-a-user) about creating
  users, and see the [pregenerating wallets](/recipes/pregenerate-wallets) guide for linking wallets
  to users before they sign in.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Authorization keys" icon="key" href="/controls/authorization-keys/overview">
    Add an extra layer of security by signing requests with authorization keys.
  </Card>

  <Card title="Policies" icon="shield-check" href="/controls/policies/overview">
    Restrict what wallets can do with configurable policies.
  </Card>

  <Card title="Idempotency keys" icon="fingerprint" href="/api-reference/idempotency-keys">
    Prevent duplicate transactions with idempotency key support.
  </Card>

  <Card title="Quorum approvals" icon="users" href="/controls/quorum-approvals/overview">
    Require multiple parties to approve before sending a transaction.
  </Card>
</CardGroup>
