---
id: TIP-1114
title: Native Multisig Accounts
description: Native accounts with weighted owners, stable addresses, and bounded signature verification.
authors: Tanishk Goyal (@legion2002), Jake Moxey (@jxom)
status: Draft
related: TIP-0001, TIP-1020, TIP-1107, TIP-1108, TIP-1109, TIP-1110, TIP-1111
protocolVersion: T12
---

# TIP-1114: Native Multisig Accounts

## Abstract

A native multisig account has a set of owners. Each owner has a weight, and approvals must meet the account's threshold.

- The address stays the same when owners change.
- A 1-of-1 account uses the same rules with just one owner.

This TIP defines signatures, transaction authorization, and on-chain signature verification. The other components define:

- [TIP-1108](./tip-1108.md): storing the configuration hash.
- [TIP-1109](./tip-1109.md): changing owners.
- [TIP-1110](./tip-1110.md): deriving the address.

## Motivation

Users should not need to move funds or application permissions when replacing a key. Native authorization also lets the account pay its own transaction fees without a separate executor.

## Assumptions

Owners use Tempo's existing secp256k1, P256, or WebAuthn signatures. T12 assumes an attacker cannot discover a primitive private key for the derived account address.

## Threat Model

Invalid transactions can consume verification work without paying fees. Parsing and signature limits must apply before cryptography, including when only the final signature is invalid.

# Specification

## Configuration and limits

A configuration contains:

| Field | Type | Meaning |
| --- | --- | --- |
| `salt` | `bytes32` | A fixed value used in address derivation and configuration hashing. |
| `version` | `uint64` | Zero for the initial configuration. Updates use increasing positive versions. |
| `threshold` | `uint8` | The total approval weight needed to authorize an operation. |
| `owners` | List of `(address, uint8 weight)` | The keys that can approve and the weight of each approval. |

### Owners and approvals

- An account has 1–48 owners.
- Owner addresses must be nonzero, unique, and sorted in ascending order.
- Each owner has a nonzero weight.
- The sum of all owner weights must be at most 255.
- The threshold must be 1–255 and must be reachable with at most eight owners: the sum of the eight largest owner weights (or all owners if fewer than eight) must meet the threshold.
- A signature contains 1–8 primitive owner signatures.

The owner limit and signature limit are separate. An account can have more than eight owners, but an operation uses at most eight signatures.

Owners sign using primitive keys.

- Nested multisig and keychain owner signatures are rejected in both T12 and T13.
- Each primitive signature keeps its existing encoding and size limit.
- An encoded owner signature is at most 2,049 bytes.

### Configuration versions

- Version zero is the initial configuration used to derive the address.
- First use stores the version-zero hash. Positive versions represent later updates or migration.
- The initial configuration cannot list its derived account address as an owner.
- A positive-version configuration may explicitly list the account's primitive root as an owner. This supports migration with the existing key.

### Limit rationale

- **Limit:** At most 8 signatures per quorum. Weights and thresholds may exceed 8, provided the threshold is reachable with 8 or fewer signatures.
- **Direct transaction:** P256/WebAuthn verification costs 8,000 gas per signature: **8 × 8,000 = 64,000 gas**. A secp256k1 fee-payer signature adds 3,000, totaling **67,000 gas**.
- **Grant and use an access key:** The parent signs the grant and the delegate signs the transaction. Two full quorums plus a fee payer cost **16 × 8,000 + 3,000 = 131,000 gas**.

Invalid transactions pay no fees, so the signature cap bounds unpaid verification work. These costs exclude configuration processing, hashing, and WebAuthn-data charges.

Before approval, benchmark direct and combined grant-and-use transactions at these limits, including maximum owner counts, maximum-size WebAuthn signatures, an optional fee payer, and an invalid final signature. Compare each primitive signature type and the configurable 1-of-1 case with an ordinary account.

## Encoding and signing

```text
signature = 0x05 || rlp([account, config, signatures])
config    = [salt, version, threshold, owners]
owner     = [address, weight]
multisig_digest(inner_digest, account, version) = keccak256("tempo:multisig:signature" || inner_digest || account || uint64(version))
```

- `signatures` is a list of byte strings containing primitive Tempo signatures.
- Encode integers in the digest as fixed-width big-endian values.
- Encode the domain as raw ASCII and the account address as 20 bytes.
- For a direct transaction, `inner_digest` is the existing transaction signing hash.
- [TIP-1111](./tip-1111.md) defines the T12 delegation signing contexts.

Before verifying signatures:

- Require canonical RLP.
- Reject trailing fields, invalid lengths, and limit violations.
- Decode an exactly 65-byte signature as secp256k1, regardless of its first byte.
- Reject signature type `0x05` before T12.

## Authorization

### Check the account and configuration

Reject accounts with EVM code or EIP-7702 delegation. Read the configuration hash at the transaction's position in the block.

If the stored hash is zero:

- Require configuration version zero.
- Derive the address using TIP-1110 and require it to equal `account`.
- Compute the TIP-1108 configuration hash and reject a zero result.

If the stored hash is nonzero:

- Accept version zero or a positive version.
- Require the TIP-1108 configuration hash to match the stored commitment exactly.

### Check the owner approvals

- Verify each owner signature over `multisig_digest`.
- Require recovered owner addresses in strictly ascending order.
- Require each signer to belong to the supplied configuration.
- Add the signers' weights.
- The final signature must reach the threshold.
- Reject insufficient approval weight or any signature after the threshold is reached.

### Initial use

An account can use its initial configuration even if it already has a balance, storage, or a nonzero transaction nonce.

After all transaction validation and authorization checks pass and fee collection succeeds, register each configurable account whose commitment is still zero:

- Write its version-zero configuration hash before executing any transaction calls, outside the execution rollback checkpoint.
- Precompiles and the TIP-1109 getter see the nonzero hash during those calls.
- Keep the initial registration even if a subcall or the entire transaction execution reverts or halts. The registration has been authorized and its write cost paid through intrinsic gas. Normal fee and nonce rules still apply.
- A later owner update may replace the initial hash in the same transaction. That update follows ordinary execution rollback rules: if execution rolls it back, restore the initial hash rather than zero. It must never clear the hash.

After an included transaction, every configurable account that authorized it has a nonzero commitment, regardless of execution success. This includes configurable parents signing key authorizations and configurable delegates signing access-key transactions.

Receiving funds or deriving an address does not register the account. Simulation applies registration only to its temporary state.

A transaction rejected as invalid does not register any account. An account that has not yet authorized an included transaction may still have a zero hash.

State-independent recovery may check the signature's shape and return `account`. Consensus must still perform all account, configuration, and approval checks above.

### Failure behavior

For a valid, included transaction whose execution reverts or halts:

| Change | Result |
|---|---|
| Initial multisig registration | Persists |
| Owner/threshold update | Rolls back, including its event |

Initial registration happens before user calls, so it survives their failure. Owner updates execute as user calls and roll back with the batch. Access keys already use this distinction: an inline `key_authorization` persists, but a grant made through a precompile call rolls back.

**Example:** A first transaction registers configuration C0, updates it to C1, then a later call reverts the batch. C0 remains registered; C1 and its event are discarded.

A failed batch rolls back all user-call changes, including earlier successful calls. A transaction rejected as invalid commits none of these changes.

## Transaction behavior

### Sender and fees

- The account is `tx.from`, `tx.origin`, and the top-level `msg.sender`.
- One owner quorum authorizes the complete call batch.
- Existing nonce, fee-token, sponsorship, and transaction signing rules apply.
- A separate fee payer uses the existing secp256k1 sponsorship signature.
- Sponsoring fees gives no authority over the account.

### Supported signature contexts

From T12, multisig signatures are supported in:

- Outer transaction signatures.
- Access-key envelopes under TIP-1111.
- Signed key authorizations under TIP-1111.

A transaction may grant access and use it immediately. TIP-1111 defines the combined verification limit.

Reject multisig signatures in subblocks and authorization lists. Stateless TIP-1020 `recover` and `verify` do not support bare multisig signatures.

Primitive authorization-list entries keep their existing behavior. Keychain-signed entries keep their existing skip behavior.

No new transaction fields are added. Non-multisig transactions keep their existing encoding.

T13 adds the root-authority checks defined in [TIP-1112](./tip-1112.md).

## On-chain signature verification

From T12, [TIP-1020](./tip-1020.md)'s `SignatureVerifier` precompile adds:

```solidity
function verifyMultisig(
    address account,
    bytes32 hash,
    bytes calldata signature
) external view returns (bool);
```

- Accept the `0x05` signature format, with owners signing `multisig_digest(hash, account, config.version)`.
- Apply the authorization rules above against state at call time. For an unregistered account, verify against the supplied initial configuration and require it to derive the account's address. Verification does not register the account.
- Return `false` for an embedded-account or stored-commitment mismatch, address-derivation failure, or an account with code or EIP-7702 delegation. Otherwise, return `true` after successful verification.
- Revert with `InvalidFormat()` for encoding/configuration errors and `InvalidSignature()` for owner-approval errors.
- Charge standard precompile input and account-access costs plus `V(signature, hash)`, without the transaction's 3,000-gas subtraction or registration write charge.

## Gas and validation cost

Use `K(n) = 30 + 6 * ceil(n / 32)` for hashing `n` bytes. Use the active calldata schedule for `calldata_gas`.

Multisig verification costs:

```text
V(signature, inner_digest) =
    calldata_gas(rlp(account) || rlp(config))
  + configuration_proof_hash_cost
  + K(84)
  + sum(primitive_signature_cost)
```

The configuration proof cost depends on whether the account is registered:

- Zero stored hash: `K(56 + 21 * owner_count) + K(85)` for address derivation, plus `K(63 + 21 * owner_count)` for the initial commitment.
- Nonzero stored hash: `K(63 + 21 * owner_count)` to check the commitment, including version zero.

Add TIP-1108's zero-to-nonzero write cost to intrinsic gas for each account being registered. Charge the write once per account, even if it appears in more than one authorization role.

If an account being registered is empty (zero nonce, zero balance, no code), registration creates it. Also charge TIP-1000's new-account cost for that account, including its state-gas portion. Skip this charge only when the account is the transaction sender and the transaction already pays TIP-1000's `nonce == 0` charge.

Primitive signature costs are:

| Signature | Gas |
| --- | --- |
| secp256k1 | 3,000 |
| P256 | 8,000 |
| WebAuthn | 8,000 plus the existing WebAuthn-data calldata charge |

Add `V - 3,000` to ordinary intrinsic gas. The subtraction removes the base signature cost already included in ordinary gas, exactly once.

- Direct authorization adds no separate precompile storage read or keychain processing buffer.
- Compute intrinsic gas and check fee affordability before verifying owner signatures.
- Gas charging does not replace the limits on unpaid validation work.

# Tooling

- SDKs build witnesses with the full configuration and signatures in owner-address order.
- Recover configurations from initial signatures and update events.
- Pools revalidate affected transactions after commitment changes or reorgs.
- RPC simulation uses the same stateful authorization rules and reports the state used.

# Observability

Initial registration emits no new event. An included transaction carries the initial configuration in its authorization witness and stores its hash, even if execution fails. Later changes are recorded by TIP-1109.

# Invariants

- Signatures cannot be replayed across accounts or configuration versions.
- Every included transaction authorized by a configurable account leaves that account with a nonzero hash, regardless of execution success.
- Test registration with an existing balance, storage, or nonce, persistence after a reverted or halted first execution, and no registration for invalid transactions.
- Test that an owner update followed by execution failure restores the initial hash, not zero.
- Test registration persistence for configurable parents and delegates in access-key flows when execution fails.
- Test that registering an empty account charges TIP-1000's new-account cost exactly once, for senders under every nonce type and for delegates.
- Test hash visibility during the first call, reverted subcalls, and an owner update in the first transaction.
- Test the final-signature threshold rule and worst-case verification limits.
- Test thresholds above eight that are reachable within eight signatures, and reject configurations that require more than eight owners during creation, updates, and migration.
- Test rotation followed by another transaction in the same block.
- Test `verifyMultisig` before and after rotation, unregistered accounts without state writes, `STATICCALL`, each false/revert case, and insufficient gas.
