> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

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

### EIP-1193 Provider

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

### Openfort SDK

### 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;
```

## Transactions

Choose your preferred approach for handling wallet transactions:

### EIP-1193 Provider

### Native transactions with EIP-1193 provider

Get the provider and optionally configure a [gas sponsorship](https://www.openfort.io/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](https://www.openfort.io/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) |

### Openfort SDK

### 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](https://www.openfort.io/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.
:::
