Skip to content
Merged
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
2fbd761
feat(tempo): support multisig mailbox approvals
jxom Aug 19, 2026
3a70d72
Update pending-tempo-multisig-receipts.md
jxom Aug 19, 2026
a419dec
refactor(tempo): configure transaction envelopes
jxom Aug 19, 2026
a320d6a
fix(tempo): preserve portable chain declarations
jxom Aug 19, 2026
e2cc4b7
docs: update agent guidelines
jxom Aug 20, 2026
8afcd79
feat(tempo): add multisig operation store
jxom Aug 20, 2026
a485f97
feat(tempo): add multisig approval actions
jxom Aug 20, 2026
f547f1f
feat(tempo): refine multisig approval flow
jxom Aug 20, 2026
7f04b9d
feat(tempo): prepare multisig approval requests
jxom Aug 20, 2026
9417015
feat(tempo): add experimental multisig client option
jxom Aug 20, 2026
dc6aca7
feat(tempo): coordinate multisig approvals with storage
jxom Aug 20, 2026
c9fd81a
refactor(tempo): namespace multisig rpc methods
jxom Aug 20, 2026
fe04735
fix(tempo): serialize stored multisig operation
jxom Aug 20, 2026
6bdd0ad
refactor(tempo): use transaction actions for multisig approvals
jxom Aug 20, 2026
b629125
docs(tempo): refine multisig transaction examples
jxom Aug 20, 2026
ea53c8d
Delete .changeset/calm-mailboxes-coordinate.md
jxom Aug 20, 2026
7d073d5
chore: update ox to 0.14.36
jxom Aug 20, 2026
631dfed
fix multisig approval preparation and tests
jxom Aug 20, 2026
a937eab
update tempo transaction snapshots
jxom Aug 20, 2026
62a09c9
fix(tempo): harden multisig transaction coordination
jxom Aug 21, 2026
4c2fc3c
refactor(tempo): simplify multisig transport and receipt types
jxom Aug 21, 2026
c297edf
chore: format multisig transport and update size limits
jxom Aug 21, 2026
0481f87
docs(tempo): simplify multisig export annotation
jxom Aug 21, 2026
a581fa0
refactor(tempo): inline multisig polling helpers
jxom Aug 21, 2026
4565fc0
Merge remote-tracking branch 'origin/main' into jxom/multisig-store-o…
jxom Aug 21, 2026
e315905
Merge remote-tracking branch 'origin/main' into jxom/multisig-store-o…
jxom Aug 21, 2026
17140d7
fix(test): reset sepolia fork in cross-chain test
jxom Aug 21, 2026
f568fae
feat(tempo): add multisig operation RPCs
jxom Aug 24, 2026
9a6fa51
refactor(tempo): use ox multisig operation helpers
jxom Aug 24, 2026
6c30e37
refactor(tempo): export multisig operations
jxom Aug 24, 2026
2a41cbb
feat(tempo): add multisig transport
jxom Aug 24, 2026
876a1e8
Merge remote-tracking branch 'origin/main' into jxom/multisig-store-o…
jxom Aug 24, 2026
aff1bb0
docs(tempo): document multisig operations
jxom Aug 24, 2026
a6cb3c5
test(tempo): separate multisig owner approvals
jxom Aug 24, 2026
533c718
fix(tempo): harden multisig coordination
jxom Aug 24, 2026
015c98f
refactor(tempo): rename storage utilities to store
jxom Aug 24, 2026
aa72fa5
refactor(tempo): scope multisig submission derivation
jxom Aug 24, 2026
2e459f8
fix(tempo): correct multisig transaction coordination
jxom Aug 24, 2026
ebe79a7
refactor(tempo): internalize operation serialization
jxom Aug 24, 2026
5397932
chore: update chain bundle limits
jxom Aug 24, 2026
5ede31e
fix(tempo): harden multisig coordination
jxom Aug 25, 2026
c299e0a
fix(tempo): settle multisig submissions
jxom Aug 25, 2026
31c2ebb
fix(tempo): validate multisig submissions
jxom Aug 25, 2026
9d00058
feat(tempo): support multisig config witnesses
jxom Aug 26, 2026
a46a11d
refactor(tempo): flatten multisig config inputs
jxom Aug 26, 2026
12e4cf3
refactor(tempo): update multisig action interfaces
jxom Aug 26, 2026
07cb272
Merge remote-tracking branch 'origin/main' into jxom/multisig-store-o…
jxom Aug 26, 2026
8cf30c2
fix(tempo): update multisig operations for ox 0.14.41
jxom Aug 27, 2026
08344ae
ci: update Tempo multisig image
jxom Aug 27, 2026
315dbcb
feat(tempo): cache multisig configs
jxom Aug 27, 2026
5bb99ae
feat(tempo): coordinate multisig key authorizations
jxom Aug 27, 2026
9e249ca
test(tempo): expand multisig authorization coverage
jxom Aug 27, 2026
5eeae1b
fix(tempo): stabilize multisig authorization coordination
jxom Aug 27, 2026
cbb006f
chore: update Tempo chain size limits
jxom Aug 27, 2026
a98427c
Delete .changeset/tender-mails-coordinate.md
jxom Aug 27, 2026
cf62bf7
refactor(tempo): use account and owner for multisig approvals
jxom Aug 28, 2026
e172491
test(tempo): update multisig error snapshot
jxom Aug 28, 2026
fadfa58
fix: use secure randomness in vector generators
jxom Aug 28, 2026
dc78be2
test(tempo): update multisig owner error snapshot
jxom Aug 28, 2026
1626e1c
test(tempo): update access key error snapshot
jxom Aug 28, 2026
e42ac3f
test(tempo): update submission error snapshot
jxom Aug 28, 2026
01c9a8e
Merge branch 'main' into jxom/multisig-store
jxom Aug 30, 2026
d1a23e4
refactor(tempo): rename multisig address inference sentinel
jxom Aug 30, 2026
6005360
feat(tempo): coordinate multisig through relays
jxom Aug 30, 2026
054193c
feat(tempo): support t12 multisig rpc
jxom Aug 31, 2026
ac00d5d
fix(tempo): correct multisig coordination
jxom Aug 31, 2026
3b61969
chore(tempo): simplify multisig references
jxom Aug 31, 2026
7b65ac8
Merge remote-tracking branch 'origin/main' into jxom/multisig-store-o…
jxom Aug 31, 2026
a250139
fix(tempo): propagate relay lookup failures
jxom Aug 31, 2026
339187f
fix(tempo): preserve account overrides
jxom Aug 31, 2026
5691d0c
fix(tempo): align multisig account derivation
jxom Aug 31, 2026
e7cd17b
ci: update Tempo multisig image
jxom Aug 31, 2026
a22eeba
test(tempo): update multisig owner snapshot
jxom Aug 31, 2026
202a332
test: update multisig access key snapshot
jxom Aug 31, 2026
e2f8c9e
test: update multisig submission snapshot
jxom Sep 1, 2026
9f1fda6
Merge branch 'main' into jxom/multisig-store
jxom Sep 1, 2026
7292473
fix(tempo): harden multisig coordination
jxom Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ jobs:
CI: true
VITE_TEMPO_ENV: localnet
VITE_TEMPO_MULTISIG: 'true'
VITE_TEMPO_TAG: sha-7825976
VITE_TEMPO_TAG: sha-5190cf5

test-tempo:
name: Test Local Tempo (${{ matrix.hardfork }}, ${{ matrix.shard }}/${{ matrix.total-shards }})
Expand Down
675 changes: 327 additions & 348 deletions AGENTS.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"fast-check": "^4.9.0",
"knip": "^5.64.0",
"micro-eth-signer": "^0.14.0",
"ox": "0.14.34",
"ox": "0.14.43",
"permissionless": "^0.2.57",
"prool": "~0.2.14",
"publint": "^0.2.12",
Expand Down Expand Up @@ -149,7 +149,7 @@
{
"name": "const viem = require('viem') (cjs)",
"path": "./src/_cjs/index.js",
"limit": "106 kB"
"limit": "112 kB"
},
{
"name": "import { createClient, http } from 'viem'",
Expand Down Expand Up @@ -196,7 +196,7 @@
{
"name": "import * from 'viem/chains'",
"path": "./src/_esm/chains/index.js",
"limit": "113 kB",
"limit": "119 kB",
"import": "*"
},
{
Expand All @@ -208,7 +208,7 @@
{
"name": "import { tempoTestnet } from 'viem/chains'",
"path": "./src/_esm/chains/index.js",
"limit": "70 kB",
"limit": "76 kB",
"import": "{ tempoTestnet }"
},
{
Expand Down
39 changes: 19 additions & 20 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ auditConfig:
- GHSA-jmr9-qjv8-65gv

overrides:
ox: 0.14.34
ox: 0.14.43
typescript: ^5.9.3
'@opentelemetry/api@^1.8.0': ~1.7.0
permissionless>viem: workspace:*
Expand Down
15 changes: 5 additions & 10 deletions scripts/generateTempoAbis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,18 @@ function tempoAdapter(): SourceAdapter {
after: 'IAddressRegistry',
items: [
'function deriveAccount(bytes32 salt, uint8 threshold, (address owner, uint8 weight)[] owners) pure returns (address account)',
'function isMultisigAccount(address account) view returns (bool)',
'function getConfig(address account) view returns ((uint64 version, uint8 threshold, (address owner, uint8 weight)[] owners))',
'function updateConfig(uint8 threshold, (address owner, uint8 weight)[] owners)',
'event MultisigInitialized(address indexed account)',
'event MultisigConfigUpdated(address indexed account, uint8 threshold, (address owner, uint8 weight)[] owners)',
'error NotMultisigAccount()',
'function getConfigCommitment(address account) view returns (bytes32 commitment)',
'function updateConfig((bytes32 salt, uint64 version, uint8 threshold, (address owner, uint8 weight)[] owners) current, uint8 threshold, (address owner, uint8 weight)[] owners)',
'event MultisigConfigUpdated(address indexed account, bytes32 salt, uint64 version, uint8 threshold, (address owner, uint8 weight)[] owners)',
'error InvalidAccount()',
'error InvalidConfig()',
'error InvalidThreshold()',
'error InvalidOwner()',
'error InvalidMultisigOwner()',
'error InvalidWeight()',
'error TooManyOwners()',
'error DuplicateOwner()',
'error InvalidOwnerOrder()',
'error AccountAlreadyInitialized()',
'error UnauthorizedCaller()',
'error SameTransactionUpdateNotAllowed()',
'error UnauthorizedMultisigCaller()',
],
},
}
Expand Down
86 changes: 65 additions & 21 deletions site/pages/tempo/accounts/account.fromMultisig.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
description: Instantiates a native multisig account from its initial config or initialized address.
description: Instantiates a native multisig account from an initial or current config.
---

# `Account.fromMultisig`

Instantiates an Account for a [native multisig](https://tips.sh/1061). Pass an initial config to
derive and bootstrap an account, or pass the address of an initialized account to reconstruct it.
Instantiates an Account for a [native multisig](https://tips.sh/1061). Set `address` to `infer`
to derive the stable account address. After the config changes, replace `infer` with the stable
address and pass the current config fields.

:::warning
**Experimental.** Native multisig support is experimental and may change in a future release.
Expand Down Expand Up @@ -37,6 +38,7 @@ const owner_2 = Account.fromSecp256k1(
)

const account = Account.fromMultisig({
address: 'infer',
owners: [owner_1, owner_2],
threshold: 2,
})
Expand All @@ -45,12 +47,34 @@ console.log('Address:', account.address)
```

`Account.fromMultisig` normalizes the config with `MultisigConfig.from`, including canonical
owner ordering. You can also pass a pre-built `MultisigConfig.Config`.
owner ordering. The initial config always uses version `0`.

### Initialized Account
### Current Config

After bootstrap, reconstruct the account from its stable address. Request preparation reads the
current owners, threshold, and version from the multisig precompile.
After a config update, reconstruct the account from its stable address and current config.
The address does not change when the config changes.

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

const account = Account.fromMultisig({
address: '0x0000000000000000000000000000000000000001',
owners: [
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
'0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
],
salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
threshold: 2,
version: 1,
})
```

Tempo stores the config commitment, not the config fields. Retain each current config or
recover it from the account's `MultisigConfigUpdated` events.

### Address Only

Use an address-only account when only the stable account identity is needed.

```ts twoslash
import { Account } from 'viem/tempo'
Expand All @@ -60,12 +84,13 @@ const account = Account.fromMultisig(
)
```

The address form cannot bootstrap an uninitialized account or aggregate local owner signatures.
Collect approvals independently and pass them to the prepared request's `signatures` field.
An address-only account contains no config or local owner keys. A coordinated client can load the
current config from its store. Otherwise, provide the config through `{ address, ...config }`
before sending.

:::tip
You can continue passing the original initial config after the account is initialized. The
initial config and initialized address identify the same stable account, even after owner rotation.
Keep using `address: 'infer'` while the account remains at version `0`. After a config update,
use the stable address with the new config.
:::

### External Owners
Expand All @@ -76,6 +101,7 @@ Pass addresses when owner approvals are collected outside the current process.
import { Account } from 'viem/tempo'

const account = Account.fromMultisig({
address: 'infer',
owners: [
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
'0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
Expand All @@ -96,6 +122,7 @@ const owner = Account.fromSecp256k1(
)

const account = Account.fromMultisig({
address: 'infer',
owners: [
{ owner, weight: 2 },
{ owner: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', weight: 1 },
Expand All @@ -110,7 +137,7 @@ const account = Account.fromMultisig({
type ReturnType = MultisigAccount

type MultisigAccount = LocalAccount<'multisig'> & {
/** Normalized initial configuration, when supplied. */
/** Normalized config, when supplied. */
config: MultisigConfig.Config | undefined
/** @internal Local owner accounts retained for signing. */
owners: readonly LocalAccount[]
Expand All @@ -125,27 +152,44 @@ supported.

### value

- **Type:** `Address | Account.fromMultisig.Config`
- **Type:** `Account.fromMultisig.Parameters`

An initial config, a stable address with its current config, or an address without a retained
config.

An initialized multisig address or the initial config used to derive and bootstrap an account.
#### value.address

- **Type:** `'infer' | Address`

Set this field to `infer` to derive an account from a version-zero config. For a current config,
pass the account's stable address. Pass an `Address` as `value` when no config is needed.

#### value.owners

- **Type:** `readonly (Address | LocalAccount | { owner: Address | LocalAccount; weight: number })[]`

The initial owners. Passing a local account makes that account available for local aggregation.
The config owners. Passing a local account makes that account available for local aggregation.
Passing an address keeps that owner's approval external.

#### value.threshold (optional)
#### value.salt

- **Type:** `Hex`
- **Default:** `0x0000000000000000000000000000000000000000000000000000000000000000`

The 32-byte config salt. This field is optional for an initial config and required for a current
config.

#### value.threshold

- **Type:** `number`
- **Default:** `1`

The total owner weight required to authorize a transaction.
The total owner weight required to authorize an operation. This field is optional for an initial
config and required for a current config.

#### value.salt (optional)
#### value.version

- **Type:** `Hex`
- **Default:** `0x0000000000000000000000000000000000000000000000000000000000000000`
- **Type:** `bigint | number`

A 32-byte salt used to derive a distinct address for the same owner set and threshold.
The nonzero current config version. For an initial config, omit this field or pass `0` when
spreading a normalized `MultisigConfig.Config` value.
Loading
Loading