# Use smart wallets

To request signatures or transactions from a connected wallet, you can either:

* Use the wallet's EIP-1193 provider to send JSON-RPC requests to the wallet directly.
* Pass the wallet to a library like `viem`, `ethers`, or `wagmi`.
* For the embedded wallet specifically, create a `transactionIntent` from the **server** and `signMessage` it with the **embedded wallet (client)**.

:::tip[The request method]
At a high-level, the EIP-1193 provider implements a method called `request` that accepts an object with the following fields:

* **`method`**: the name of a JSON-RPC request to send to the wallet
* **`params`**: any parameters to include with the request
:::

## Signatures

Choose your preferred approach for handling wallet signatures:

<MultiOptionDisplay
  options={[
  { id: 'eip1193-signatures', label: 'EIP-1193 Provider' },
  { id: 'openfort-signatures', label: 'Openfort SDK' },
]}
/>

<span id="eip1193-signatures" className="hidden [&>*]:mb-6!">
  ### Sign messages with EIP-1193 provider

  The `request` method of the EIP-1193 provider can be used to request signatures. First, get the provider:

  :::code-group
  ```tsx [client.tsx]
  import openfort from "./openfortConfig"
  // This example assumes you have already checked that Openfort 'embeddedState' is
  // `ready` and the user is `authenticated`
  const provider = await openfort.embeddedWallet.getEthereumProvider();
  ```

  ```ts [openfortConfig.ts]
  import { Openfort } from '@openfort/openfort-js';

  const openfort = new Openfort({
    baseConfiguration: {
      publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
    },
    shieldConfiguration: {
      shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
    },
  });

  export default openfort;
  ```
  :::

  Then use the provider's `request` method with `eth_sign` or related methods:

  ```tsx
  const message = "Sign this message";
  const signature = await provider.request({
    method: 'personal_sign',
    params: [message, address]
  });
  ```

  ### Sign typed data with EIP-1193 provider

  The `request` method of the EIP-1193 provider can be used to sign typed data. First, get the provider:

  :::code-group
  ```tsx [client.tsx]
  import openfort from "./openfortConfig"
  // This example assumes you have already checked that Openfort 'embeddedState' is
  // `ready` and the user is `authenticated`
  const provider = await openfort.embeddedWallet.getEthereumProvider();
  ```

  ```ts [openfortConfig.ts]
  import { Openfort } from '@openfort/openfort-js';

  const openfort = new Openfort({
    baseConfiguration: {
      publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
    },
    shieldConfiguration: {
      shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
    },
  });

  export default openfort;
  ```
  :::

  Then use the provider's `request` method with `eth_signTypedData_v4`:

  ```tsx
  const address = "0x0000000000000000000000000000000000000000";

  const domain = {
    name: "Ether Mail",
    version: "1",
    chainId: 1,
    verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
  };

  const types = {
    EIP712Domain: [
      { name: "name", type: "string" },
      { name: "version", type: "string" },
      { name: "chainId", type: "uint256" },
      { name: "verifyingContract", type: "address" },
    ],
    Person: [
      { name: "name", type: "string" },
      { name: "wallet", type: "address" },
    ],
    Mail: [
      { name: "from", type: "Person" },
      { name: "to", type: "Person" },
      { name: "contents", type: "string" },
    ],
  };

  const message = {
    from: { name: "Cow", wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" },
    to: { name: "Bob", wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" },
    contents: "Hello, Bob!",
  };

  const signature = await provider.request({ method: "eth_signTypedData_v4",
    params: [address, JSON.stringify({ domain, types, primaryType: "Mail", message })],
  });
  ```

  ### Integration with web3 libraries

  If you've integrated Openfort with another library, you can also use that library's syntax for requesting signatures:

  | Library | Method |
  |---------|--------|
  | Ethers | [Use the signer's `signMessage` method](https://docs.ethers.org/v5/api/signer/#Signer-signMessage) |
  | Wagmi | [Use the `useSignMessage` hook](https://wagmi.sh/react/api/hooks/useSignMessage) |
  | Viem | [Use the `signMessage` action](https://viem.sh/account-abstraction/accounts/smart/signMessage) |

  For typed data:

  | Library | Method |
  |---------|--------|
  | Ethers | [Use the signer's `_signTypedData` method](https://docs.ethers.org/v5/api/signer/#Signer-signTypedData) |
  | Wagmi | [Use the `useSignTypedData` hook](https://wagmi.sh/react/api/hooks/useSignTypedData) |
  | Viem | [Use the `signTypedData` action](https://viem.sh/account-abstraction/accounts/smart/signTypedData) |
</span>

<span id="openfort-signatures" className="hidden [&>*]:mb-6!">
  ### Sign messages with Openfort SDK

  To request a signature from a user, use the `signMessage` method.

  When invoked, `signMessage` requests an EIP-191 [personal\_sign](https://docs.metamask.io/wallet/reference/personal_sign/) signature from the embedded wallet, and returns a Promise for the user's signature as a string.

  The method accepts either a `string` or `Uint8Array` as the message, and an optional `options` parameter:

  ```typescript
  signMessage(
    message: string | Uint8Array,
    options?: {
      hashMessage?: boolean    // Whether to hash the message before signing
      arrayifyMessage?: boolean // Whether to convert the message to a byte array
    }
  ): Promise<string>
  ```

  ```tsx [client.tsx]
  import openfort from "./openfortConfig"

  // This example assumes you have already checked that Openfort 'embeddedState' is
  // `ready` and the user is `authenticated`
  async function signMessageButton(message: string) {
    await openfort.embeddedWallet.signMessage(message);
  }

  // Sign raw bytes
  async function signRawBytes(data: Uint8Array) {
    await openfort.embeddedWallet.signMessage(data, { hashMessage: true });
  }
  ```

  ```ts [openfortConfig.ts]
  import { Openfort } from '@openfort/openfort-js';

  const openfort = new Openfort({
    baseConfiguration: {
      publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
    },
    shieldConfiguration: {
      shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
    },
  });

  export default openfort;
  ```

  ### Sign typed data with Openfort SDK

  To have a user sign an EIP-712 typed data signature, use the `signTypedData` method.

  When invoked, `signTypedData` requests an EIP-712 [eth\_signTypedData\_v4](https://docs.metamask.io/wallet/reference/eth_signtypeddata_v4/) signature from the embedded wallet, and returns a Promise for the user's signature as a string.

  ```tsx [client.tsx]
  import openfort from "./openfortConfig"

  // This example assumes you have already checked that Openfort 'embeddedState' is 
  // `ready` and the user is `authenticated`
  async function signTypedMessageButton(domain: any, types: any, message: any) {
    await openfort.embeddedWallet.signTypedData(domain, types, message);
  }
  ```

  ```ts [openfortConfig.ts]
  import { Openfort } from '@openfort/openfort-js';

  const openfort = new Openfort({
    baseConfiguration: {
      publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
    },
    shieldConfiguration: {
      shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
    },
  });

  export default openfort;
  ```
</span>

## Transactions

Choose your preferred approach for handling wallet transactions:

<MultiOptionDisplay
  options={[
  { id: 'eip1193-transactions', label: 'EIP-1193 Provider' },
  { id: 'openfort-transactions', label: 'Openfort SDK' },
]}
/>

<span id="eip1193-transactions" className="hidden [&>*]:mb-6!">
  ### Native transactions with EIP-1193 provider

  Get the provider and optionally configure a [gas sponsorship](/docs/configuration/gas-sponsorship) to make a sponsored transaction:

  :::code-group
  ```tsx [client.tsx]
  import openfort from "./openfortConfig"
  // This example assumes you have already checked that Openfort 'embeddedState' is 
  // `ready` and the user is `authenticated`
  const provider = await openfort.embeddedWallet.getEthereumProvider({
        feeSponsorship: 'pol_...',
      });
  ```

  ```ts [openfortConfig.ts]
  import { Openfort } from '@openfort/openfort-js';

  const openfort = new Openfort({
    baseConfiguration: {
      publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
    },
    shieldConfiguration: {
      shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
    },
  });

  export default openfort;
  ```
  :::

  :::note
  The `getEthereumProvider` method accepts the following options:

  ```typescript
  getEthereumProvider(options?: {
    feeSponsorship?: string                // gas sponsorship ID for sponsored transactions
    chains?: Record<number, string>       // Chain ID to RPC URL mapping for multi-chain support
    providerInfo?: {                      // EIP-6963 provider metadata
      icon: `data:image/${string}`
      name: string
      rdns: string
    }
    announceProvider?: boolean            // Announce via EIP-6963 (default: true)
  }): Promise<Provider>
  ```

  * **`feeSponsorship`**: specifies which [gas sponsorship](/docs/configuration/gas-sponsorship) to use for sponsoring transactions.
  * **`chains`**: provide RPC URLs for additional chains the provider should support.
  * **`providerInfo`**: customize the EIP-6963 provider metadata for wallet discovery.
  * **`announceProvider`**: set to `false` to prevent the provider from announcing itself via EIP-6963 (useful when using Wagmi or other libraries that handle provider discovery).
  :::

  Then, using the provider's `request` method, send a [`eth_sendTransaction`](https://docs.metamask.io/wallet/reference/eth_sendtransaction/) JSON-RPC to the wallet:

  ```tsx
  const transactionRequest = {
    to: '0xRecipientAddress',
    value: 100000,
  };
  const txHash = await provider.request({
    method: 'eth_sendTransaction',
    params: [transactionRequest],
  });
  ```

  :::info
  You do not need to specify `from` as we populate it from the user's connected wallet, and you can pass either a number, bigint, or a hexadecimal string into the value parameter.
  :::

  ### Smart contract interactions with EIP-1193 provider

  Calling a smart contract is a special case of sending a transaction. To call a smart contract, you should send a transaction with the following parameters:

  * **`to`**: the address of the smart contract to call
  * **`data`**: the encoded function call data
  * **`value`**: any value to send (for payable functions)

  To prepare the calldata for a smart contract interaction, use viem's [encodeFunctionData](https://viem.sh/docs/contract/encodeFunctionData#encodefunctiondata) method:

  ```tsx
  import { encodeFunctionData } from 'viem';

  const data = encodeFunctionData({
    abi: contractAbi,
    functionName: 'methodName',
    args: [arg1, arg2]
  });
  ```

  You can then send a transaction from the wallet as normal, and pass the calldata in the **`data`** field:

  ```tsx
  const transactionRequest = {
    to: '0xTheContractAddress',
    data: data,
    value: 100000, // Only necessary for payable methods
  };
  const txHash = await provider.request({
    method: 'eth_sendTransaction',
    params: [transactionRequest]
  });
  ```

  ### Batched transactions with EIP-1193 provider

  Smart wallets support sending a batch of transactions in a single, atomic submission to the network.

  To send batched transactions with a smart wallet, call the `wallet_sendCalls` method with a calls array of the transactions to batch together:

  ```tsx
  const txHash = await provider.request({
    method: 'wallet_sendCalls',
    params: [
      {
        calls: [
          // Approve transaction
          {
            to: USDC_ADDRESS,
            data: encodeFunctionData({
              abi: USDC_ABI,
              functionName: 'approve',
              args: ['insert-spender-address', BigInt(1e6)],
            }),
          },
          // Transfer transaction
          {
            to: USDC_ADDRESS,
            data: encodeFunctionData({
              abi: USDC_ABI,
              functionName: 'transfer',
              args: ['insert-recipient-address', BigInt(1e6)],
            }),
          },
        ],
      },
    ],
  });
  ```

  ### Integration with wallet libraries

  If you've integrated Openfort with another library, you can also use that library's syntax for sending transactions:

  | Library | Method |
  |---------|--------|
  | Ethers | Use the signer's [`sendTransaction` method.](https://docs.ethers.org/v5/api/signer/#Signer-sendTransaction) |
  | Wagmi | Use the [`useSendTransaction` hook.](https://wagmi.sh/react/api/hooks/useSendTransaction) |
  | Viem | [`sendTransaction` action](https://viem.sh/account-abstraction/actions/bundler/sendUserOperation) |

  For batched transactions:

  | Library | Method |
  |---------|--------|
  | Wagmi | Use the [`useWriteContracts` hook.](https://wagmi.sh/react/api/hooks/useWriteContracts#usewritecontracts) |
  | Viem | [`sendCalls` action](https://viem.sh/experimental/eip5792/sendCalls) |
</span>

<span id="openfort-transactions" className="hidden [&>*]:mb-6!">
  ### Transaction flow with Openfort SDK

  The Openfort SDK uses a two-step approach: create the transaction intent on the server, then sign it on the client.

  #### 1. Create transaction intent on server

  Create a request to your backend to create a transaction. In the body of the request:

  * Include the `account` that the transaction originates from
  * Include the `policy` (gas sponsorship ID) for gas sponsoring. If non-existent, the user needs to have gas tokens

  :::note
  The `policy` parameter specifies which [gas sponsorship](/docs/configuration/gas-sponsorship) to use for sponsoring the transaction. When set, the transaction will be sponsored according to the gas sponsorship's rules, so users don't need to hold native tokens for gas.
  :::

  ### Native token transfer

  Send the value of native tokens in the smallest denomination of the native currency (wei, 10^18) and as a `string`.

  :::tip
  The `to` parameter accepts:

  * any valid account `address` (for example, 0x680d2719F09B23F644c136Ab7336D42b6a76AdcC)
  * an account `id` (for example, acc\_...)
  :::

  :::code-group
  ```bash [REST]
  curl https://api.openfort.io/v1/transaction_intents \
    -H "Authorization: Bearer $YOUR_SECRET_KEY" \
    -d account="acc_..." \
    -d policy="pol_..." \
    -d chainId=80002 \
    -d "interactions[0][value]=1000" \
    -d "interactions[0][to]=acc_..."
  ```

  ```ts [Node SDK]
  import Openfort from '@openfort/openfort-node';
  const openfort = new Openfort(YOUR_SECRET_KEY);

  const accountId = "acc_...";
  const policyId = "pol_...";
  const chainId = 80002;

  const interaction_transfer = {
    value: "100",
    to: accountId
  };

  await openfort.transactionIntents.create({
      "account":accountId,
      "chainId":chainId,
      "interactions":[interaction_transfer],
      "policy":policyId
    });
  ```
  :::

  :::info
  The gas sponsorship used to sponsor a transaction that sends native tokens should be linked to a policy with the `account_functions` rule.
  :::

  For computing wei values, visit the [wei calculator](https://eth-converter.com/).

  ### Smart contract transactions

  :::code-group
  ```bash [REST]
  curl https://api.openfort.io/v1/transaction_intents \
    -H "Authorization: Bearer $YOUR_SECRET_KEY" \
    -d account="acc_..." \
    -d policy="pol_..." \
    -d chainId=80002 \
    -d "interactions[0][contract]"="con_..." \
    -d "interactions[0][functionName]"="mint" \
    -d "interactions[0][functionArgs][0]"="0x63B7...484f"
  ```

  ```ts [Node SDK]
  import Openfort from '@openfort/openfort-node';
  const openfort = new Openfort(YOUR_SECRET_KEY);

  const accountId = "acc_...";
  const contractId = "con_...";
  const policyId = "pol_...";
  const chainId = 80002;

  const interaction_mint = {
    contract: contractId,
    functionName: "mint",
    functionArgs: [accountId],
  };

  await openfort.transactionIntents.create({
      "account":accountId,
      "chainId":chainId,
      "interactions":[interaction_mint],
      "policy":policyId
    });
  ```
  :::

  ### Batch transactions

  Smart accounts support batching transactions, allowing multiple actions to be rolled into one. This feature significantly simplifies Web3 interactions for your users. For example, instead of executing `approve()` and then `transfer()`, your user can perform both in a single transaction.

  For security reasons, there is a limit of 9 interactions per transaction intent.

  :::tip
  To execute a batch transaction, you can send a transaction intent with **multiple interactions**, and each interaction will be executed in the order they are received.
  :::

  :::note
  Openfort systems check the contract's ABI to find a function signature based on the `functionName` that you provide and the number of `functionArgs`.

  If your contract has multiple functions with the same `functionName` and number of arguments, you can also include the `functionName` together with the argument types (for example, `mint(address)`).
  :::

  :::code-group
  ```bash [REST]
  curl https://api.openfort.io/v1/transaction_intents \
    -H "Authorization: Bearer $YOUR_SECRET_KEY" \
    -d account="acc_...", \
    -d policy="pol_..." \
    -d chainId=80002 \
    -d "interactions[0][contract]=con_..." \
    -d "interactions[0][functionName]=mint" \
    -d "interactions[0][functionArgs][0]=0x63B7...484f" \
    -d "interactions[1][contract]=con_..." \
    -d "interactions[1][functionName]=transfer" \
    -d "interactions[1][functionArgs][0]=0x32B7...213d"
  ```

  ```ts [Node SDK]
  import Openfort from '@openfort/openfort-node';
  const openfort = new Openfort(YOUR_SECRET_KEY);

  const accountId = "acc_...";
  const contractId = "con_...";
  const policyId = "pol_..."; 
  const chainId = 80002;

  const interaction_mint = {
    contract: contractId,
    functionName: "mint",
    functionArgs: [accountId],
  };
  const interaction_transfer = {
    contract: contractId,
    functionName: "transfer",
    functionArgs: [accountId],
  };

  await openfort.transactionIntents.create({
      "account":accountId,
      "chainId":chainId,
      "interactions":[interaction_mint, interaction_transfer],
      "policy":policyId
    });
  ```
  :::

  #### 2. Sign transaction on client

  Use the `nextAction` returned by the backend to sign the transaction with the embedded wallet.

  The transaction is automatically signed and broadcasted using the `sendSignatureTransactionIntentRequest` method:

  ```typescript
  sendSignatureTransactionIntentRequest(
    transactionIntentId: string,
    signableHash?: string | null,  // Hash to sign (from nextAction payload)
    signature?: string | null,     // Pre-computed signature (skip client signing)
    optimistic?: boolean           // Don't wait for tx confirmation (default: false)
  ): Promise<TransactionIntentResponse>
  ```

  ```js
  const handleCollectButtonClick = async () => {
    const collectResponse = await fetch(`https://your-backend.com/api/mint`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
    });
    const collectResponseJSON = await collectResponse.json();

    if (collectResponseJSON.data?.nextAction) {
      // This example assumes you have already checked that Openfort 'embeddedState' is
      // `ready` and the user is `authenticated`
      const response = await openfort.proxy.sendSignatureTransactionIntentRequest(
        collectResponseJSON.data.id,
        collectResponseJSON.data.nextAction.payload.signableHash
      );
      console.log("response", response);
    }
    console.log("success:", collectResponseJSON.data);
  };
  ```

  :::tip
  Set `optimistic` to `true` if you want the method to return immediately after submitting the transaction, without waiting for on-chain confirmation. This is useful for improving perceived speed in non-critical flows.
  :::
</span>
