viem 2.56.1 → 2.56.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/_cjs/actions/wallet/sendTransaction.js +9 -1
  3. package/_cjs/actions/wallet/sendTransaction.js.map +1 -1
  4. package/_cjs/actions/wallet/sendTransactionSync.js +9 -1
  5. package/_cjs/actions/wallet/sendTransactionSync.js.map +1 -1
  6. package/_cjs/errors/version.js +1 -1
  7. package/_cjs/tempo/Abis.js +11 -23
  8. package/_cjs/tempo/Abis.js.map +1 -1
  9. package/_cjs/tempo/Account.js +66 -61
  10. package/_cjs/tempo/Account.js.map +1 -1
  11. package/_cjs/tempo/Client.js +10 -2
  12. package/_cjs/tempo/Client.js.map +1 -1
  13. package/_cjs/tempo/Decorator.js +2 -1
  14. package/_cjs/tempo/Decorator.js.map +1 -1
  15. package/_cjs/tempo/Formatters.js +29 -9
  16. package/_cjs/tempo/Formatters.js.map +1 -1
  17. package/_cjs/tempo/Multisig.js +990 -0
  18. package/_cjs/tempo/Multisig.js.map +1 -0
  19. package/_cjs/tempo/Selectors.js +2 -3
  20. package/_cjs/tempo/Selectors.js.map +1 -1
  21. package/_cjs/tempo/{Storage.js → Store.js} +34 -9
  22. package/_cjs/tempo/Store.js.map +1 -0
  23. package/_cjs/tempo/Transaction.js +15 -30
  24. package/_cjs/tempo/Transaction.js.map +1 -1
  25. package/_cjs/tempo/Transport.js +51 -5
  26. package/_cjs/tempo/Transport.js.map +1 -1
  27. package/_cjs/tempo/actions/accessKey.js +102 -8
  28. package/_cjs/tempo/actions/accessKey.js.map +1 -1
  29. package/_cjs/tempo/actions/amm.js +6 -0
  30. package/_cjs/tempo/actions/amm.js.map +1 -1
  31. package/_cjs/tempo/actions/channel.js +12 -0
  32. package/_cjs/tempo/actions/channel.js.map +1 -1
  33. package/_cjs/tempo/actions/dex.js +10 -0
  34. package/_cjs/tempo/actions/dex.js.map +1 -1
  35. package/_cjs/tempo/actions/earn/deployment.js +13 -1
  36. package/_cjs/tempo/actions/earn/deployment.js.map +1 -1
  37. package/_cjs/tempo/actions/earn.js +17 -0
  38. package/_cjs/tempo/actions/earn.js.map +1 -1
  39. package/_cjs/tempo/actions/fee.js +4 -0
  40. package/_cjs/tempo/actions/fee.js.map +1 -1
  41. package/_cjs/tempo/actions/multisig.js +86 -28
  42. package/_cjs/tempo/actions/multisig.js.map +1 -1
  43. package/_cjs/tempo/actions/policy.js +8 -0
  44. package/_cjs/tempo/actions/policy.js.map +1 -1
  45. package/_cjs/tempo/actions/receivePolicy.js +6 -0
  46. package/_cjs/tempo/actions/receivePolicy.js.map +1 -1
  47. package/_cjs/tempo/actions/reward.js +4 -0
  48. package/_cjs/tempo/actions/reward.js.map +1 -1
  49. package/_cjs/tempo/actions/token.js +32 -0
  50. package/_cjs/tempo/actions/token.js.map +1 -1
  51. package/_cjs/tempo/actions/virtualAddress.js +2 -0
  52. package/_cjs/tempo/actions/virtualAddress.js.map +1 -1
  53. package/_cjs/tempo/actions/zone.js +7 -5
  54. package/_cjs/tempo/actions/zone.js.map +1 -1
  55. package/_cjs/tempo/chainConfig.js +180 -69
  56. package/_cjs/tempo/chainConfig.js.map +1 -1
  57. package/_cjs/tempo/index.js +5 -2
  58. package/_cjs/tempo/index.js.map +1 -1
  59. package/_cjs/tempo/internal/utils.js +2 -1
  60. package/_cjs/tempo/internal/utils.js.map +1 -1
  61. package/_cjs/tempo/multisig/Config.js +80 -0
  62. package/_cjs/tempo/multisig/Config.js.map +1 -0
  63. package/_cjs/tempo/multisig/Operation.js +163 -0
  64. package/_cjs/tempo/multisig/Operation.js.map +1 -0
  65. package/_cjs/utils/encoding/fromHex.js +1 -1
  66. package/_cjs/utils/encoding/fromHex.js.map +1 -1
  67. package/_cjs/utils/hash/isHash.js +1 -2
  68. package/_cjs/utils/hash/isHash.js.map +1 -1
  69. package/_esm/actions/wallet/sendTransaction.js +9 -1
  70. package/_esm/actions/wallet/sendTransaction.js.map +1 -1
  71. package/_esm/actions/wallet/sendTransactionSync.js +9 -1
  72. package/_esm/actions/wallet/sendTransactionSync.js.map +1 -1
  73. package/_esm/errors/version.js +1 -1
  74. package/_esm/tempo/Abis.js +11 -23
  75. package/_esm/tempo/Abis.js.map +1 -1
  76. package/_esm/tempo/Account.js +66 -61
  77. package/_esm/tempo/Account.js.map +1 -1
  78. package/_esm/tempo/Client.js +22 -3
  79. package/_esm/tempo/Client.js.map +1 -1
  80. package/_esm/tempo/Decorator.js +2 -1
  81. package/_esm/tempo/Decorator.js.map +1 -1
  82. package/_esm/tempo/Formatters.js +32 -11
  83. package/_esm/tempo/Formatters.js.map +1 -1
  84. package/_esm/tempo/Multisig.js +1020 -0
  85. package/_esm/tempo/Multisig.js.map +1 -0
  86. package/_esm/tempo/Selectors.js +2 -3
  87. package/_esm/tempo/Selectors.js.map +1 -1
  88. package/_esm/tempo/{Storage.js → Store.js} +37 -26
  89. package/_esm/tempo/Store.js.map +1 -0
  90. package/_esm/tempo/Transaction.js +16 -31
  91. package/_esm/tempo/Transaction.js.map +1 -1
  92. package/_esm/tempo/Transport.js +71 -6
  93. package/_esm/tempo/Transport.js.map +1 -1
  94. package/_esm/tempo/actions/accessKey.js +104 -20
  95. package/_esm/tempo/actions/accessKey.js.map +1 -1
  96. package/_esm/tempo/actions/amm.js +6 -0
  97. package/_esm/tempo/actions/amm.js.map +1 -1
  98. package/_esm/tempo/actions/channel.js +12 -0
  99. package/_esm/tempo/actions/channel.js.map +1 -1
  100. package/_esm/tempo/actions/dex.js +10 -0
  101. package/_esm/tempo/actions/dex.js.map +1 -1
  102. package/_esm/tempo/actions/earn/deployment.js +13 -1
  103. package/_esm/tempo/actions/earn/deployment.js.map +1 -1
  104. package/_esm/tempo/actions/earn.js +17 -0
  105. package/_esm/tempo/actions/earn.js.map +1 -1
  106. package/_esm/tempo/actions/fee.js +4 -0
  107. package/_esm/tempo/actions/fee.js.map +1 -1
  108. package/_esm/tempo/actions/multisig.js +129 -63
  109. package/_esm/tempo/actions/multisig.js.map +1 -1
  110. package/_esm/tempo/actions/policy.js +8 -0
  111. package/_esm/tempo/actions/policy.js.map +1 -1
  112. package/_esm/tempo/actions/receivePolicy.js +6 -0
  113. package/_esm/tempo/actions/receivePolicy.js.map +1 -1
  114. package/_esm/tempo/actions/reward.js +4 -0
  115. package/_esm/tempo/actions/reward.js.map +1 -1
  116. package/_esm/tempo/actions/token.js +32 -0
  117. package/_esm/tempo/actions/token.js.map +1 -1
  118. package/_esm/tempo/actions/virtualAddress.js +2 -0
  119. package/_esm/tempo/actions/virtualAddress.js.map +1 -1
  120. package/_esm/tempo/actions/zone.js +8 -6
  121. package/_esm/tempo/actions/zone.js.map +1 -1
  122. package/_esm/tempo/chainConfig.js +188 -86
  123. package/_esm/tempo/chainConfig.js.map +1 -1
  124. package/_esm/tempo/index.js +5 -3
  125. package/_esm/tempo/index.js.map +1 -1
  126. package/_esm/tempo/internal/utils.js +2 -1
  127. package/_esm/tempo/internal/utils.js.map +1 -1
  128. package/_esm/tempo/multisig/Config.js +84 -0
  129. package/_esm/tempo/multisig/Config.js.map +1 -0
  130. package/_esm/tempo/multisig/Operation.js +170 -0
  131. package/_esm/tempo/multisig/Operation.js.map +1 -0
  132. package/_esm/utils/encoding/fromHex.js +1 -1
  133. package/_esm/utils/encoding/fromHex.js.map +1 -1
  134. package/_esm/utils/hash/isHash.js +1 -2
  135. package/_esm/utils/hash/isHash.js.map +1 -1
  136. package/_types/actions/wallet/sendTransaction.d.ts.map +1 -1
  137. package/_types/actions/wallet/sendTransactionSync.d.ts.map +1 -1
  138. package/_types/chains/definitions/tempo.d.ts +72 -92
  139. package/_types/chains/definitions/tempo.d.ts.map +1 -1
  140. package/_types/chains/definitions/tempoDevnet.d.ts +72 -92
  141. package/_types/chains/definitions/tempoDevnet.d.ts.map +1 -1
  142. package/_types/chains/definitions/tempoLocalnet.d.ts +72 -92
  143. package/_types/chains/definitions/tempoLocalnet.d.ts.map +1 -1
  144. package/_types/chains/definitions/tempoModerato.d.ts +72 -92
  145. package/_types/chains/definitions/tempoModerato.d.ts.map +1 -1
  146. package/_types/errors/version.d.ts +1 -1
  147. package/_types/tempo/Abis.d.ts +54 -108
  148. package/_types/tempo/Abis.d.ts.map +1 -1
  149. package/_types/tempo/Account.d.ts +47 -20
  150. package/_types/tempo/Account.d.ts.map +1 -1
  151. package/_types/tempo/Client.d.ts +23 -1
  152. package/_types/tempo/Client.d.ts.map +1 -1
  153. package/_types/tempo/Decorator.d.ts +30 -5
  154. package/_types/tempo/Decorator.d.ts.map +1 -1
  155. package/_types/tempo/Formatters.d.ts.map +1 -1
  156. package/_types/tempo/Multisig.d.ts +32 -0
  157. package/_types/tempo/Multisig.d.ts.map +1 -0
  158. package/_types/tempo/Selectors.d.ts +2 -3
  159. package/_types/tempo/Selectors.d.ts.map +1 -1
  160. package/_types/tempo/Store.d.ts +62 -0
  161. package/_types/tempo/Store.d.ts.map +1 -0
  162. package/_types/tempo/Transaction.d.ts +12 -31
  163. package/_types/tempo/Transaction.d.ts.map +1 -1
  164. package/_types/tempo/Transport.d.ts +37 -5
  165. package/_types/tempo/Transport.d.ts.map +1 -1
  166. package/_types/tempo/Zone.d.ts +468 -598
  167. package/_types/tempo/Zone.d.ts.map +1 -1
  168. package/_types/tempo/actions/accessKey.d.ts +38 -8
  169. package/_types/tempo/actions/accessKey.d.ts.map +1 -1
  170. package/_types/tempo/actions/amm.d.ts.map +1 -1
  171. package/_types/tempo/actions/channel.d.ts.map +1 -1
  172. package/_types/tempo/actions/dex.d.ts.map +1 -1
  173. package/_types/tempo/actions/earn/deployment.d.ts +3 -3
  174. package/_types/tempo/actions/earn/deployment.d.ts.map +1 -1
  175. package/_types/tempo/actions/earn.d.ts.map +1 -1
  176. package/_types/tempo/actions/fee.d.ts.map +1 -1
  177. package/_types/tempo/actions/multisig.d.ts +139 -136
  178. package/_types/tempo/actions/multisig.d.ts.map +1 -1
  179. package/_types/tempo/actions/policy.d.ts.map +1 -1
  180. package/_types/tempo/actions/receivePolicy.d.ts.map +1 -1
  181. package/_types/tempo/actions/reward.d.ts.map +1 -1
  182. package/_types/tempo/actions/token.d.ts.map +1 -1
  183. package/_types/tempo/actions/virtualAddress.d.ts.map +1 -1
  184. package/_types/tempo/actions/zone.d.ts +4 -4
  185. package/_types/tempo/actions/zone.d.ts.map +1 -1
  186. package/_types/tempo/chainConfig.d.ts +38 -48
  187. package/_types/tempo/chainConfig.d.ts.map +1 -1
  188. package/_types/tempo/index.d.ts +5 -5
  189. package/_types/tempo/index.d.ts.map +1 -1
  190. package/_types/tempo/internal/types.d.ts +1 -1
  191. package/_types/tempo/internal/types.d.ts.map +1 -1
  192. package/_types/tempo/internal/utils.d.ts +1 -0
  193. package/_types/tempo/internal/utils.d.ts.map +1 -1
  194. package/_types/tempo/multisig/Config.d.ts +42 -0
  195. package/_types/tempo/multisig/Config.d.ts.map +1 -0
  196. package/_types/tempo/multisig/Operation.d.ts +32 -0
  197. package/_types/tempo/multisig/Operation.d.ts.map +1 -0
  198. package/_types/types/chain.d.ts +10 -0
  199. package/_types/types/chain.d.ts.map +1 -1
  200. package/_types/utils/hash/isHash.d.ts +1 -2
  201. package/_types/utils/hash/isHash.d.ts.map +1 -1
  202. package/actions/wallet/sendTransaction.ts +9 -1
  203. package/actions/wallet/sendTransactionSync.ts +9 -1
  204. package/errors/version.ts +1 -1
  205. package/package.json +2 -2
  206. package/tempo/Abis.ts +11 -23
  207. package/tempo/Account.ts +136 -105
  208. package/tempo/Client.ts +46 -3
  209. package/tempo/Decorator.ts +40 -8
  210. package/tempo/Formatters.ts +39 -21
  211. package/tempo/Multisig.ts +1349 -0
  212. package/tempo/Selectors.ts +2 -3
  213. package/tempo/Store.ts +170 -0
  214. package/tempo/Transaction.ts +39 -64
  215. package/tempo/Transport.ts +125 -10
  216. package/tempo/actions/accessKey.ts +210 -23
  217. package/tempo/actions/amm.ts +7 -0
  218. package/tempo/actions/channel.ts +12 -0
  219. package/tempo/actions/dex.ts +10 -0
  220. package/tempo/actions/earn/deployment.ts +16 -3
  221. package/tempo/actions/earn.ts +19 -0
  222. package/tempo/actions/fee.ts +4 -0
  223. package/tempo/actions/multisig.ts +192 -94
  224. package/tempo/actions/policy.ts +8 -0
  225. package/tempo/actions/receivePolicy.ts +6 -0
  226. package/tempo/actions/reward.ts +5 -0
  227. package/tempo/actions/token.ts +33 -0
  228. package/tempo/actions/virtualAddress.ts +2 -0
  229. package/tempo/actions/zone.ts +10 -8
  230. package/tempo/chainConfig.ts +259 -100
  231. package/tempo/index.ts +5 -4
  232. package/tempo/internal/types.ts +3 -2
  233. package/tempo/internal/utils.ts +2 -0
  234. package/tempo/multisig/Config.ts +131 -0
  235. package/tempo/multisig/Operation.ts +211 -0
  236. package/types/chain.ts +14 -0
  237. package/utils/encoding/fromHex.ts +1 -1
  238. package/utils/hash/isHash.ts +2 -3
  239. package/_cjs/tempo/Storage.js.map +0 -1
  240. package/_cjs/tempo/internal/multisig.js +0 -56
  241. package/_cjs/tempo/internal/multisig.js.map +0 -1
  242. package/_esm/tempo/Storage.js.map +0 -1
  243. package/_esm/tempo/internal/multisig.js +0 -56
  244. package/_esm/tempo/internal/multisig.js.map +0 -1
  245. package/_types/tempo/Storage.d.ts +0 -42
  246. package/_types/tempo/Storage.d.ts.map +0 -1
  247. package/_types/tempo/internal/multisig.d.ts +0 -13
  248. package/_types/tempo/internal/multisig.d.ts.map +0 -1
  249. package/tempo/Storage.ts +0 -115
  250. package/tempo/internal/multisig.ts +0 -72
@@ -66,7 +66,7 @@ import {
66
66
  pickWriteSyncParameters,
67
67
  } from '../internal/utils.js'
68
68
  import * as WithdrawalSenderTag from '../internal/WithdrawalSenderTag.js'
69
- import * as Storage from '../Storage.js'
69
+ import * as Store from '../Store.js'
70
70
  import type { TransactionReceipt } from '../Transaction.js'
71
71
 
72
72
  const defaultWithdrawalGas = 10_000_000n
@@ -1707,6 +1707,8 @@ export async function requestWithdrawalSync<
1707
1707
  gas: parameters.gas ?? defaultWithdrawalGas,
1708
1708
  throwOnReceiptRevert,
1709
1709
  } as never)
1710
+ if ((receipt as TransactionReceipt).status === 'pending')
1711
+ return { receipt } as never
1710
1712
  const [event] = parseEventLogs({
1711
1713
  abi: Abis.zoneOutbox,
1712
1714
  logs: receipt.logs,
@@ -1950,7 +1952,7 @@ export namespace requestVerifiableWithdrawalSync {
1950
1952
  * ```
1951
1953
  *
1952
1954
  * @param client - Zone wallet client.
1953
- * @param parameters - Options including optional storage override.
1955
+ * @param parameters - Options including optional store override.
1954
1956
  * @returns The authentication object and serialized token.
1955
1957
  */
1956
1958
  export async function signAuthorizationToken<
@@ -1968,7 +1970,7 @@ export async function signAuthorizationToken<
1968
1970
  account = client.account,
1969
1971
  issuedAt = Math.floor(Date.now() / 1000),
1970
1972
  expiresAt = issuedAt + 86_400,
1971
- storage = Storage.defaultStorage(),
1973
+ store = Store.defaultStore(),
1972
1974
  } = parameters
1973
1975
 
1974
1976
  const chain = parameters.chain ?? client.chain
@@ -1980,7 +1982,7 @@ export async function signAuthorizationToken<
1980
1982
  if (!account_ || !account_.sign)
1981
1983
  throw new Error('`account` with `sign` is required.')
1982
1984
 
1983
- const storageKey = `auth:${account_.address.toLowerCase()}:${chain.id}`
1985
+ const storeKey = `auth:${account_.address.toLowerCase()}:${chain.id}`
1984
1986
 
1985
1987
  const authentication = ZoneRpcAuthentication.from({
1986
1988
  chainId: chain.id,
@@ -1996,8 +1998,8 @@ export async function signAuthorizationToken<
1996
1998
  signature,
1997
1999
  })
1998
2000
 
1999
- await storage.setItem(storageKey, token)
2000
- await storage.setItem(`auth:token:${chain.id}`, token)
2001
+ await store.setItem(storeKey, token)
2002
+ await store.setItem(`auth:token:${chain.id}`, token)
2001
2003
 
2002
2004
  return { authentication, token }
2003
2005
  }
@@ -2016,8 +2018,8 @@ export namespace signAuthorizationToken {
2016
2018
  expiresAt?: number | undefined
2017
2019
  /** Token issue time as a unix timestamp (seconds). @default `Date.now() / 1000`. */
2018
2020
  issuedAt?: number | undefined
2019
- /** Storage to persist the token. @default sessionStorage (web) or memory (server). */
2020
- storage?: Storage.Storage | undefined
2021
+ /** Store used to persist the token. @default sessionStorage (web) or memory (server). */
2022
+ store?: Store.Store | undefined
2021
2023
  /** Zone ID to scope the token to (`0` for unscoped). @default derived from `chain.id`. */
2022
2024
  zoneId?: number | undefined
2023
2025
  }
@@ -1,7 +1,14 @@
1
1
  import type { Address } from 'abitype'
2
2
  import * as Hex from 'ox/Hex'
3
- import { MultisigConfig, SignatureEnvelope, type TokenId } from 'ox/tempo'
3
+ import {
4
+ MultisigConfig,
5
+ MultisigOperation,
6
+ type MultisigSimulation,
7
+ SignatureEnvelope,
8
+ type TokenId,
9
+ } from 'ox/tempo'
4
10
  import { getCode } from '../actions/public/getCode.js'
11
+ import { getTransaction } from '../actions/public/getTransaction.js'
5
12
  import { verifyHash } from '../actions/public/verifyHash.js'
6
13
  import { maxUint256 } from '../constants/number.js'
7
14
  import type { Chain, ChainConfig as viem_ChainConfig } from '../types/chain.js'
@@ -15,25 +22,17 @@ import { keccak256 } from '../utils/hash/keccak256.js'
15
22
  import type { SerializeTransactionFn } from '../utils/transaction/serializeTransaction.js'
16
23
  import type { Account, MultisigAccount } from './Account.js'
17
24
  import { getMetadata } from './actions/accessKey.js'
18
- import * as multisig from './actions/multisig.js'
25
+ import { getConfig } from './actions/multisig.js'
19
26
  import * as Formatters from './Formatters.js'
20
27
  import type { Hardfork } from './Hardfork.js'
21
28
  import * as Concurrent from './internal/concurrent.js'
22
- import {
23
- createMultisigStateResolver,
24
- getMultisigOwnerStates,
25
- } from './internal/multisig.js'
26
29
  import * as Transaction from './Transaction.js'
27
30
 
28
31
  const maxExpirySecs = 25
29
32
 
30
- /** Returns random past seconds to distinguish otherwise-identical expiring transactions. */
31
- function randomValidAfter(): number {
32
- const now = BigInt(Math.floor(Date.now() / 1_000))
33
- const latest = now - 60n
34
- if (latest <= 0n) return 0
35
- return Number(BigInt(Hex.random(8)) % latest)
36
- }
33
+ // TODO: casting to satisfy viem – viem v3 to have more flexible serializer type.
34
+ const serializeTransaction = ((transaction, signature) =>
35
+ Transaction.serialize(transaction, signature)) as SerializeTransactionFn
37
36
 
38
37
  export const chainConfig = {
39
38
  blockTime: 1_000,
@@ -65,13 +64,75 @@ export const chainConfig = {
65
64
  | undefined
66
65
  feePayerSignature?: Transaction.TransactionSerializableTempo['feePayerSignature']
67
66
  from?: Address | undefined
67
+ hash?: Hex.Hex | undefined
68
68
  keyData?: Hex.Hex | undefined
69
69
  keyType?: 'p256' | 'secp256k1' | 'webAuthn' | undefined
70
- multisig?: Address | MultisigConfig.Config | undefined
71
- multisigOwnerStates?: Transaction.TransactionRequestTempo['multisigOwnerStates']
70
+ multisigSimulation?: MultisigSimulation.Spec | undefined
71
+ owner?: Account | MultisigAccount | Address | undefined
72
72
  signatures?: readonly unknown[] | undefined
73
73
  }
74
74
 
75
+ if (request.hash) {
76
+ if (
77
+ !request.account ||
78
+ typeof request.account === 'string' ||
79
+ request.account.source !== 'multisig'
80
+ )
81
+ throw new Error(
82
+ 'A local multisig account is required to approve a stored multisig transaction.',
83
+ )
84
+ if (!request.owner || typeof request.owner === 'string')
85
+ throw new Error(
86
+ 'A local owner account is required to approve a stored multisig transaction.',
87
+ )
88
+ if (
89
+ request.owner.source !== 'root' &&
90
+ request.owner.source !== 'multisig'
91
+ )
92
+ throw new Error(
93
+ 'A Tempo owner account is required to approve a stored multisig transaction.',
94
+ )
95
+ const transaction = await getAction(
96
+ client,
97
+ getTransaction,
98
+ 'getTransaction',
99
+ )({ hash: request.hash })
100
+ const operation =
101
+ 'multisig' in transaction
102
+ ? (transaction.multisig as
103
+ | MultisigOperation.TransactionOperation
104
+ | undefined)
105
+ : undefined
106
+ if (!operation)
107
+ throw new Error('Expected a multisig operation transaction.')
108
+ if (!isAddressEqual(operation.account, request.account.address))
109
+ throw new Error(
110
+ 'Multisig operation account does not match the requested account.',
111
+ )
112
+ const storedTransaction = Transaction.deserialize(
113
+ operation.transaction as Transaction.TransactionSerializedTempo,
114
+ )
115
+ const hash = MultisigOperation.getHash({
116
+ account: operation.account,
117
+ config: operation.config,
118
+ transaction: operation.transaction,
119
+ type: 'transaction',
120
+ })
121
+ if (hash.toLowerCase() !== request.hash.toLowerCase())
122
+ throw new Error('Multisig operation hash does not match transaction.')
123
+ return {
124
+ ...storedTransaction,
125
+ account: request.account,
126
+ from: operation.account,
127
+ multisigSimulation: getMultisigSimulation({
128
+ account: operation.account,
129
+ config: operation.config,
130
+ local: request.account,
131
+ }),
132
+ owner: request.owner,
133
+ } as unknown as typeof r
134
+ }
135
+
75
136
  // FIXME: node estimates gas with secp256k1 dummy sig + null feePayerSignature.
76
137
  // Actual tx has larger keychain/webAuthn sigs + real fee payer sig, costing more intrinsic gas.
77
138
  if (phase === 'afterFillParameters') {
@@ -90,90 +151,65 @@ export const chainConfig = {
90
151
  return request as unknown as typeof r
91
152
  }
92
153
 
93
- // Native multisig (TIP-1061). The transaction sender is the stable
94
- // multisig address, not a signing account. Initial configs support
95
- // bootstrap; initialized accounts can be reconstructed from their
96
- // address and resolved through the multisig precompile.
97
- //
98
- // The config is taken from an explicit `multisig` field, or inferred from
99
- // a multisig account (so callers can just pass `account` to
100
- // `prepareTransactionRequest` without also passing `multisig`).
101
- const multisigIdentity =
102
- request.multisig ??
103
- (request.account?.source === 'multisig'
104
- ? ((request.account as MultisigAccount).config ??
105
- request.account.address)
106
- : undefined)
107
- if (multisigIdentity) {
108
- const initialConfig =
109
- typeof multisigIdentity === 'string'
110
- ? undefined
111
- : MultisigConfig.from(multisigIdentity)
112
- const multisigAddress =
113
- typeof multisigIdentity === 'string'
114
- ? multisigIdentity
115
- : MultisigConfig.getAddress(initialConfig!)
116
- request.multisig = initialConfig ?? multisigIdentity
117
- request.from = multisigAddress
118
- // Key types are not part of the config, so conservatively model every
119
- // approval as a maximum-size WebAuthn signature.
120
- if (typeof request.keyType === 'undefined') {
121
- request.keyType = 'webAuthn'
122
- if (typeof request.keyData === 'undefined') request.keyData = '0x0578'
123
- }
124
- const getState = createMultisigStateResolver((account) =>
125
- getAction(client, multisig.getConfig, 'getConfig')({ account }),
154
+ const multisigIdentity = (() => {
155
+ if (request.account?.source === 'multisig')
156
+ return {
157
+ account: request.account.address,
158
+ config: (request.account as MultisigAccount).config,
159
+ local: request.account as MultisigAccount,
160
+ }
161
+ return undefined
162
+ })()
163
+ if (request.owner && !multisigIdentity)
164
+ throw new Error(
165
+ 'A multisig account is required when an owner is provided.',
126
166
  )
127
- const ownerStates =
128
- request.account?.source === 'multisig'
129
- ? getMultisigOwnerStates(
130
- request.account as MultisigAccount,
131
- getState,
132
- )
133
- : undefined
134
- const state = ownerStates
135
- ? (await ownerStates)[0]!
136
- : await getState(multisigAddress)
137
- if (!initialConfig && !state.initialized)
167
+ if (request.owner && typeof request.owner === 'string')
168
+ throw new Error(
169
+ 'A local owner account is required to approve a multisig transaction.',
170
+ )
171
+ if (
172
+ request.owner &&
173
+ request.owner.source !== 'root' &&
174
+ request.owner.source !== 'multisig'
175
+ )
176
+ throw new Error(
177
+ 'A Tempo owner account is required to approve a multisig transaction.',
178
+ )
179
+ const coordinatedMultisig =
180
+ !!multisigIdentity &&
181
+ !!request.owner &&
182
+ (client.transport as { multisig?: boolean }).multisig === true
183
+ if (multisigIdentity) {
184
+ const { account, local } = multisigIdentity
185
+ if (!account)
138
186
  throw new Error(
139
- 'Cannot prepare an uninitialized multisig account from an address. Provide its initial config instead.',
187
+ 'A multisig account address is required with a current config.',
140
188
  )
141
- if (typeof request.multisigVersion === 'undefined')
142
- request.multisigVersion = state.version
143
- const authorizationSignature = request.keyAuthorization?.signature
144
- if (
145
- authorizationSignature?.type === 'multisig' &&
146
- typeof authorizationSignature.init !== 'undefined'
147
- )
148
- request.keyAuthorization = {
149
- ...request.keyAuthorization!,
150
- signature: SignatureEnvelope.from({
151
- initialConfig: authorizationSignature.init,
152
- signatures: authorizationSignature.signatures,
153
- }),
154
- }
155
- const keyAuthorizationSignature = request.keyAuthorization?.signature
156
- const keyAuthorizationInitializes =
157
- keyAuthorizationSignature?.type === 'multisig' &&
158
- typeof keyAuthorizationSignature.init !== 'undefined'
159
- if (initialConfig && !keyAuthorizationInitializes)
160
- request.multisigInit = {
161
- salt: initialConfig.salt ?? MultisigConfig.zeroSalt,
162
- threshold: Number(initialConfig.threshold),
163
- owners: initialConfig.owners.map((owner) => ({
164
- owner: owner.owner,
165
- weight: Number(owner.weight),
166
- })),
167
- }
168
- if (ownerStates) {
169
- const states = await ownerStates
170
- if (states.length > 0) request.multisigOwnerStates = states
171
- }
172
- // A non-multisig `account` (e.g. the client's default) isn't the sender,
173
- // so drop it: core then fills nonce/gas/fees for the multisig sender via
174
- // `request.from`. A multisig account *is* the sender, so keep it so the
175
- // prepared request can be sent without re-passing `account`.
176
- if (request.account?.source !== 'multisig') delete request.account
189
+ const config = await (async () => {
190
+ if (multisigIdentity.config) return multisigIdentity.config
191
+ if (!coordinatedMultisig) return undefined
192
+ const cachedConfig = await getAction(
193
+ client,
194
+ getConfig,
195
+ 'getConfig',
196
+ )({ address: account })
197
+ if (!cachedConfig)
198
+ throw new Error(
199
+ `No current multisig config is cached for account ${account}. Provide the current config.`,
200
+ )
201
+ return cachedConfig
202
+ })()
203
+ if (!config)
204
+ throw new Error(
205
+ 'A multisig config is required to prepare a transaction.',
206
+ )
207
+ request.from = account
208
+ request.multisigSimulation = getMultisigSimulation({
209
+ account,
210
+ config,
211
+ local,
212
+ })
177
213
  }
178
214
 
179
215
  // Register concurrency before account preparation performs storage or
@@ -181,6 +217,7 @@ export const chainConfig = {
181
217
  const useExpiringNonce = await (async () => {
182
218
  if (request.nonceKey === 'expiring' || request.nonceKey === maxUint256)
183
219
  return true
220
+ if (typeof request.nonceKey !== 'undefined') return false
184
221
  if (multisigIdentity) return false
185
222
  if (request.feePayer && typeof request.nonceKey === 'undefined')
186
223
  return true
@@ -195,7 +232,12 @@ export const chainConfig = {
195
232
  return false
196
233
  })()
197
234
 
198
- if (useExpiringNonce) {
235
+ if (coordinatedMultisig && typeof request.nonceKey === 'undefined') {
236
+ // A random nonce lane lets a stored approval ceremony remain valid
237
+ // indefinitely without blocking other pending operations.
238
+ request.nonceKey = Hex.toBigInt(Hex.random(31)) + 1n
239
+ request.nonce = 0
240
+ } else if (useExpiringNonce) {
199
241
  request.nonceKey = maxUint256
200
242
  request.nonce = 0
201
243
  if (typeof request.validAfter === 'undefined')
@@ -254,9 +296,27 @@ export const chainConfig = {
254
296
  { runAt: ['beforeFillTransaction', 'afterFillParameters'] },
255
297
  ],
256
298
  serializers: {
257
- // TODO: casting to satisfy viem – viem v3 to have more flexible serializer type.
258
- transaction: ((transaction, signature) =>
259
- Transaction.serialize(transaction, signature)) as SerializeTransactionFn,
299
+ transaction: serializeTransaction,
300
+ async transactionEnvelope({ serializedTransaction, transaction }) {
301
+ const request = transaction as Transaction.TransactionSerializableTempo
302
+ if (!request.multisigSimulation) return serializedTransaction
303
+ try {
304
+ SignatureEnvelope.deserialize(serializedTransaction)
305
+ } catch {
306
+ const transaction = Transaction.deserialize(
307
+ serializedTransaction as Transaction.TransactionSerializedTempo,
308
+ )
309
+ if (transaction.signature?.type === 'multisig')
310
+ return serializedTransaction
311
+ throw new Error(
312
+ 'A Tempo owner account is required to approve a multisig transaction.',
313
+ )
314
+ }
315
+ return await serializeTransaction({
316
+ ...request,
317
+ signatures: [...(request.signatures ?? []), serializedTransaction],
318
+ } as never)
319
+ },
260
320
  },
261
321
  async verifyHash(client, parameters) {
262
322
  const { address, hash, signature, mode } = parameters
@@ -346,3 +406,102 @@ export const chainConfig = {
346
406
  } as const satisfies viem_ChainConfig & { blockTime: number }
347
407
 
348
408
  export type ChainConfig = typeof chainConfig
409
+
410
+ /** Builds a bounded multisig spec for gas simulation. */
411
+ function getMultisigSimulation(options: {
412
+ account: Address
413
+ config: MultisigConfig.Config
414
+ local?: MultisigAccount | undefined
415
+ }): MultisigSimulation.Spec {
416
+ const { account, config, local } = options
417
+ return {
418
+ account,
419
+ approvals: selectOwners(config).map((owner) => {
420
+ const localOwner = local?.owners.find((account) =>
421
+ isAddressEqual(account.address, owner.owner),
422
+ )
423
+ if (localOwner?.source === 'multisig') {
424
+ const nested = localOwner as MultisigAccount
425
+ if (!nested.config)
426
+ throw new Error(
427
+ 'A nested multisig config is required for gas estimation.',
428
+ )
429
+ return {
430
+ type: 'multisig',
431
+ spec: {
432
+ account: nested.address,
433
+ approvals: selectOwners(nested.config).map((owner) => {
434
+ const nestedOwner = nested.owners.find((account) =>
435
+ isAddressEqual(account.address, owner.owner),
436
+ )
437
+ if (nestedOwner?.source === 'multisig')
438
+ throw new Error(
439
+ 'Multisig simulation nesting exceeds depth two.',
440
+ )
441
+ return {
442
+ ...getSimulationKey(nestedOwner),
443
+ owner: owner.owner,
444
+ }
445
+ }),
446
+ config: nested.config,
447
+ },
448
+ }
449
+ }
450
+ return {
451
+ ...getSimulationKey(localOwner),
452
+ owner: owner.owner,
453
+ type: 'primitive',
454
+ }
455
+ }),
456
+ config,
457
+ }
458
+ }
459
+
460
+ /** Returns signature metadata for conservative gas estimation. */
461
+ function getSimulationKey(
462
+ account: unknown,
463
+ ):
464
+ | { keyData: Hex.Hex; keyType: 'webAuthn' }
465
+ | { keyType: 'p256' | 'secp256k1' } {
466
+ const keyType = (() => {
467
+ if (
468
+ account &&
469
+ typeof account === 'object' &&
470
+ 'keyType' in account &&
471
+ typeof account.keyType === 'string'
472
+ )
473
+ return account.keyType
474
+ return undefined
475
+ })()
476
+ if (!keyType || keyType === 'webAuthn')
477
+ return { keyData: '0x0578' as const, keyType: 'webAuthn' as const }
478
+ if (keyType === 'p256' || keyType === 'secp256k1') return { keyType }
479
+ return { keyData: '0x0578' as const, keyType: 'webAuthn' as const }
480
+ }
481
+
482
+ /** Returns random past seconds to distinguish otherwise-identical expiring transactions. */
483
+ function randomValidAfter(): number {
484
+ const now = BigInt(Math.floor(Date.now() / 1_000))
485
+ const latest = now - 60n
486
+ if (latest <= 0n) return 0
487
+ return Number(BigInt(Hex.random(8)) % latest)
488
+ }
489
+
490
+ /** Selects a deterministic minimum-cardinality owner quorum. */
491
+ function selectOwners(config: MultisigConfig.Config) {
492
+ const owners = [...config.owners].sort(
493
+ (a, b) =>
494
+ Number(b.weight) - Number(a.weight) ||
495
+ a.owner.toLowerCase().localeCompare(b.owner.toLowerCase()),
496
+ )
497
+ const selected: typeof owners = []
498
+ let weight = 0
499
+ for (const owner of owners.slice(0, MultisigConfig.maxSignatures)) {
500
+ if (weight >= Number(config.threshold)) break
501
+ selected.push(owner)
502
+ weight += Number(owner.weight)
503
+ }
504
+ return selected.sort((a, b) =>
505
+ a.owner.toLowerCase().localeCompare(b.owner.toLowerCase()),
506
+ )
507
+ }
package/tempo/index.ts CHANGED
@@ -15,6 +15,7 @@ export {
15
15
  Channel,
16
16
  EarnShares,
17
17
  MultisigConfig,
18
+ MultisigOperation,
18
19
  Period,
19
20
  ReceivePolicyReceipt,
20
21
  TempoAddress,
@@ -23,7 +24,6 @@ export {
23
24
  VirtualAddress,
24
25
  VirtualMaster,
25
26
  } from 'ox/tempo'
26
- // TODO: remove
27
27
  export type {
28
28
  /** @deprecated */
29
29
  Owner as z_MultisigConfigOwner,
@@ -72,17 +72,17 @@ export * from './errors.js'
72
72
  export * as Formatters from './Formatters.js'
73
73
  export * as Hardfork from './Hardfork.js'
74
74
  export * as KeyAuthorizationManager from './KeyAuthorizationManager.js'
75
+ /** @experimental */
76
+ export * as Multisig from './Multisig.js'
75
77
  export * as P256 from './P256.js'
76
78
  /** @experimental */
77
79
  export * as Scopes from './Scopes.js'
78
80
  /** @experimental */
79
81
  export * as Selectors from './Selectors.js'
80
- export * as Storage from './Storage.js'
82
+ export * as Store from './Store.js'
81
83
  export * as TokenIds from './TokenIds.js'
82
84
  // Export types required for inference.
83
85
  export type {
84
- /** @deprecated */
85
- MultisigOwnerState as z_MultisigOwnerState,
86
86
  /** @deprecated */
87
87
  Transaction as z_Transaction,
88
88
  /** @deprecated */
@@ -117,6 +117,7 @@ export {
117
117
  http,
118
118
  walletNamespaceCompat,
119
119
  withFeePayer,
120
+ withMultisig,
120
121
  withRelay,
121
122
  } from './Transport.js'
122
123
  export * as WebAuthnP256 from './WebAuthnP256.js'
@@ -70,10 +70,11 @@ export type WriteParameters<
70
70
  > &
71
71
  UnionPick<
72
72
  TransactionRequestTempo,
73
- | 'keyAuthorization'
74
- | 'feeToken'
75
73
  | 'feePayer'
74
+ | 'feeToken'
75
+ | 'keyAuthorization'
76
76
  | 'nonceKey'
77
+ | 'owner'
77
78
  | 'validAfter'
78
79
  | 'validBefore'
79
80
  >
@@ -197,6 +197,7 @@ export function pickWriteParameters(parameters: Record<string, unknown>) {
197
197
  maxPriorityFeePerGas,
198
198
  nonce,
199
199
  nonceKey,
200
+ owner,
200
201
  validAfter,
201
202
  validBefore,
202
203
  } = parameters
@@ -211,6 +212,7 @@ export function pickWriteParameters(parameters: Record<string, unknown>) {
211
212
  maxPriorityFeePerGas,
212
213
  nonce,
213
214
  nonceKey,
215
+ owner,
214
216
  validAfter,
215
217
  validBefore,
216
218
  }