> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://viem-gdbqz16fk-wevm.vercel.app/api/mcp` to find what you need.

# Send Multisig Transactions

## Overview

Choose the flow based on how the account reaches quorum:

* **Coordinated approvals:** Use
  [`multisig.approveTransaction`](/tempo/actions/multisig.approveTransaction) when approvals come
  from separate wallets, devices, services, or processes. The shared store combines approvals and
  broadcasts the transaction after quorum.
* **Local quorum:** Use normal [`sendTransaction`](/docs/actions/wallet/sendTransaction) when the
  multisig account can meet quorum locally. This includes a 1-of-1 account, one owner whose weight
  meets the threshold, or an account that contains enough local owner signers.

## Recipes

### Configure a Coordinating Client

Enable multisig coordination on the Tempo client:

```ts twoslash
import { createClient } from 'viem/tempo'

// 1. Enable coordination with a process-local store.
export const client = createClient({
  experimental_multisig: true,
})
```

:::info
`experimental_multisig: true` creates a process-local [`Storage.memory`](/tempo/utilities/Storage.memory)
store. When owners use separate processes, configure every client with the same persistent store.
:::

Configure a shared store explicitly:

```ts twoslash
import { createClient, type Storage } from 'viem/tempo'

// 1. Provide a store shared by every coordinator.
declare const store: Storage.Storage

// 2. Enable coordination with the shared store.
export const client = createClient({
  experimental_multisig: { store },
})
```

When the store implements `compareAndSet`, concurrent approvals cannot overwrite each other.
Without it, coordination falls back to `getItem` and `setItem`, so an owner may need to retry after
concurrent submissions.

### Coordinated Approvals

The first owner passes the transaction fields and multisig identity. The action prepares the
request, signs that owner's approval, and returns the prepared `request` with the operation.

:::code-group
```ts twoslash [example.ts]
import { Account } from 'viem/tempo'
import { client } from './multisig.config'

// 1. Create the independent owners and multisig.
const owner_1 = Account.fromSecp256k1(
  '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
)
const owner_2 = Account.fromSecp256k1(
  '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'
)
const multisig = Account.fromMultisig({
  owners: [owner_1.address, owner_2.address],
  threshold: 2,
})

// 2. Store the first owner's approval and prepared request.
const pending = await client.multisig.approveTransaction({
  account: owner_1,
  calls: [
    { to: '0xcafebabecafebabecafebabecafebabecafebabe', value: 1n },
  ],
  multisig,
})
// @log: { status: 'pending', id: '0x...', signatures: 1, weight: 1, threshold: 2 }

// 3. Reuse the request for the second owner's approval.
const success = await client.multisig.approveTransaction({
  ...pending.request,
  account: owner_2,
})
// @log: { status: 'success', transactionHash: '0x...', signatures: 2, weight: 2, threshold: 2 }
```

```ts twoslash [multisig.config.ts] filename="multisig.config.ts"
// [!include ~/snippets/tempo/multisig.config.ts:setup]
```
:::

Reuse `pending.request` exactly. The request contains the resolved nonce, fees, gas, multisig
version, and every field covered by the approval. Changing any field requires new approvals.

The first transaction carries the initial multisig configuration and initializes the account.
Later requests resolve the current onchain configuration and version during preparation.

### Inspect an Operation

Use [`multisig.getOperation`](/tempo/actions/multisig.getOperation) to read current quorum state by
the deterministic operation ID:

```ts twoslash
import { client } from './multisig.config'

declare const id: `0x${string}`

// 1. Read the latest operation state.
const operation = await client.multisig.getOperation({ id })

// 2. Inspect the collected weight while it is pending.
if (operation?.status === 'pending') {
  console.log(operation.weight, operation.threshold)
  // @log: 1 2
}
```

Pending operations contain the unsigned transaction and collected approvals. Successful
operations contain `transactionHash` instead of the unsigned transaction.

### Choose Asynchronous or Synchronous Submission

Both approval actions return pending operations immediately below quorum. They differ only when an
approval reaches quorum:

* `approveTransaction` broadcasts through asynchronous transaction submission.
* [`approveTransactionSync`](/tempo/actions/multisig.approveTransactionSync) waits for synchronous
  submission before returning the successful operation.

### Local Quorum

When one trusted process holds enough owner accounts, put those accounts in the multisig and use
normal `sendTransaction`. The multisig account signs a complete quorum locally.

:::code-group
```ts twoslash [example.ts]
import { Account } from 'viem/tempo'
import { client } from './viem.config'

// 1. Create enough local owners to meet quorum.
const owner_1 = Account.fromSecp256k1(
  '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
)
const owner_2 = Account.fromSecp256k1(
  '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'
)
const multisig = Account.fromMultisig({
  owners: [owner_1, owner_2],
  threshold: 2,
})

// 2. Send with all owner approvals created locally.
const hash = await client.sendTransaction({
  account: multisig,
  to: '0xcafebabecafebabecafebabecafebabecafebabe',
  value: 1n,
})
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/tempo/viem.config.ts:setup]
```
:::

Normal `sendTransaction` also works when a prepared request already contains enough valid
`signatures`. It does not collect partial approvals in the multisig store.

## Best Practices

### Share One Store Across Coordinators

Every process that receives approvals for the same operation must use the same authoritative
store. Do not use `Storage.memory()` across processes or in a production service.

### Keep Owner Signers Separate

Use owner addresses when private keys live in separate trust boundaries. Put owner accounts in the
multisig only when one environment intentionally holds those signers.

## See More

<Cards>
  <Card icon="lucide:scale" title="Weighted Owners" description="Configure M-of-N approvals or assign different weights to owners." to="/tempo/guides/multisig/weighted-owners" />

  <Card icon="lucide:database" title="Storage" description="Provide a shared store for coordinated approvals." to="/tempo/utilities/Storage" />

  <Card icon="lucide:wallet-cards" title="Account.fromMultisig" description="Create a native multisig account from its initial configuration." to="/tempo/accounts/account.fromMultisig" />
</Cards>
