ox 1.7.0 → 1.7.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 (126) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/core/Hash.js +1 -1
  3. package/dist/core/Hash.js.map +1 -1
  4. package/dist/core/Siwe.d.ts.map +1 -1
  5. package/dist/core/Siwe.js +45 -6
  6. package/dist/core/Siwe.js.map +1 -1
  7. package/dist/tempo/KeyAuthorization.d.ts +1 -1
  8. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  9. package/dist/tempo/MultisigConfig.d.ts +151 -83
  10. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  11. package/dist/tempo/MultisigConfig.js +164 -86
  12. package/dist/tempo/MultisigConfig.js.map +1 -1
  13. package/dist/tempo/MultisigOperation.d.ts +324 -0
  14. package/dist/tempo/MultisigOperation.d.ts.map +1 -0
  15. package/dist/tempo/MultisigOperation.js +809 -0
  16. package/dist/tempo/MultisigOperation.js.map +1 -0
  17. package/dist/tempo/MultisigWitness.d.ts +106 -0
  18. package/dist/tempo/MultisigWitness.d.ts.map +1 -0
  19. package/dist/tempo/MultisigWitness.js +118 -0
  20. package/dist/tempo/MultisigWitness.js.map +1 -0
  21. package/dist/tempo/RpcSchemaTempo.d.ts +35 -0
  22. package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
  23. package/dist/tempo/SignatureEnvelope.d.ts +60 -80
  24. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  25. package/dist/tempo/SignatureEnvelope.js +87 -146
  26. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  27. package/dist/tempo/TransactionRequest.d.ts +9 -17
  28. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  29. package/dist/tempo/TransactionRequest.js +7 -7
  30. package/dist/tempo/TransactionRequest.js.map +1 -1
  31. package/dist/tempo/TxEnvelopeTempo.d.ts +3 -2
  32. package/dist/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  33. package/dist/tempo/TxEnvelopeTempo.js +3 -2
  34. package/dist/tempo/TxEnvelopeTempo.js.map +1 -1
  35. package/dist/tempo/index.d.ts +22 -2
  36. package/dist/tempo/index.d.ts.map +1 -1
  37. package/dist/tempo/index.js +22 -2
  38. package/dist/tempo/index.js.map +1 -1
  39. package/dist/zod/tempo/AuthorizationTempo.d.ts +110 -110
  40. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  41. package/dist/zod/tempo/KeyAuthorization.d.ts +90 -83
  42. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  43. package/dist/zod/tempo/MultisigConfig.d.ts +31 -2
  44. package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
  45. package/dist/zod/tempo/MultisigConfig.js +26 -3
  46. package/dist/zod/tempo/MultisigConfig.js.map +1 -1
  47. package/dist/zod/tempo/MultisigOperation.d.ts +168 -0
  48. package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -0
  49. package/dist/zod/tempo/MultisigOperation.js +65 -0
  50. package/dist/zod/tempo/MultisigOperation.js.map +1 -0
  51. package/dist/zod/tempo/MultisigWitness.d.ts +152 -0
  52. package/dist/zod/tempo/MultisigWitness.d.ts.map +1 -0
  53. package/dist/zod/tempo/MultisigWitness.js +76 -0
  54. package/dist/zod/tempo/MultisigWitness.js.map +1 -0
  55. package/dist/zod/tempo/RpcSchemaTempo.d.ts +607 -102
  56. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  57. package/dist/zod/tempo/RpcSchemaTempo.js +38 -0
  58. package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
  59. package/dist/zod/tempo/SignatureEnvelope.d.ts +33 -33
  60. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  61. package/dist/zod/tempo/SignatureEnvelope.js +9 -16
  62. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  63. package/dist/zod/tempo/Transaction.d.ts +228 -222
  64. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  65. package/dist/zod/tempo/TransactionRequest.d.ts +379 -176
  66. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  67. package/dist/zod/tempo/TransactionRequest.js +11 -22
  68. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  69. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +60 -48
  70. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  71. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +15 -12
  72. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
  73. package/dist/zod/tempo/z.d.ts +2 -0
  74. package/dist/zod/tempo/z.d.ts.map +1 -1
  75. package/dist/zod/tempo/z.js +2 -0
  76. package/dist/zod/tempo/z.js.map +1 -1
  77. package/package.json +21 -1
  78. package/src/core/Hash.ts +1 -1
  79. package/src/core/Siwe.ts +45 -5
  80. package/src/core/_test/Hash.test.ts +15 -0
  81. package/src/core/_test/Siwe.test.ts +98 -0
  82. package/src/tempo/KeyAuthorization.test-d.ts +13 -1
  83. package/src/tempo/KeyAuthorization.test.ts +65 -0
  84. package/src/tempo/KeyAuthorization.ts +1 -1
  85. package/src/tempo/MultisigConfig.test-d.ts +52 -0
  86. package/src/tempo/MultisigConfig.test.ts +288 -227
  87. package/src/tempo/MultisigConfig.ts +275 -104
  88. package/src/tempo/MultisigOperation.test-d.ts +77 -0
  89. package/src/tempo/MultisigOperation.test.ts +1883 -0
  90. package/src/tempo/MultisigOperation.ts +1254 -0
  91. package/src/tempo/MultisigWitness.test-d.ts +42 -0
  92. package/src/tempo/MultisigWitness.test.ts +288 -0
  93. package/src/tempo/MultisigWitness.ts +210 -0
  94. package/src/tempo/RpcSchemaTempo.test-d.ts +56 -0
  95. package/src/tempo/RpcSchemaTempo.ts +37 -0
  96. package/src/tempo/SignatureEnvelope.test-d.ts +63 -46
  97. package/src/tempo/SignatureEnvelope.test.ts +523 -500
  98. package/src/tempo/SignatureEnvelope.ts +172 -242
  99. package/src/tempo/TransactionRequest.test-d.ts +11 -0
  100. package/src/tempo/TransactionRequest.test.ts +285 -33
  101. package/src/tempo/TransactionRequest.ts +15 -20
  102. package/src/tempo/TxEnvelopeTempo.test-d.ts +7 -0
  103. package/src/tempo/TxEnvelopeTempo.test.ts +25 -0
  104. package/src/tempo/TxEnvelopeTempo.ts +5 -3
  105. package/src/tempo/index.ts +22 -2
  106. package/src/tempo/multisig.e2e.test.ts +113 -86
  107. package/src/version.ts +1 -1
  108. package/src/zod/tempo/MultisigConfig.ts +29 -3
  109. package/src/zod/tempo/MultisigOperation.ts +69 -0
  110. package/src/zod/tempo/MultisigWitness.ts +101 -0
  111. package/src/zod/tempo/RpcSchemaTempo.ts +43 -0
  112. package/src/zod/tempo/SignatureEnvelope.ts +20 -38
  113. package/src/zod/tempo/TransactionRequest.ts +17 -26
  114. package/src/zod/tempo/_test/KeyAuthorization.test.ts +11 -0
  115. package/src/zod/tempo/_test/MultisigConfig.test-d.ts +13 -0
  116. package/src/zod/tempo/_test/MultisigConfig.test.ts +13 -2
  117. package/src/zod/tempo/_test/MultisigOperation.test-d.ts +27 -0
  118. package/src/zod/tempo/_test/MultisigOperation.test.ts +68 -0
  119. package/src/zod/tempo/_test/MultisigWitness.test-d.ts +13 -0
  120. package/src/zod/tempo/_test/MultisigWitness.test.ts +149 -0
  121. package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +31 -0
  122. package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +41 -0
  123. package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +1 -1
  124. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +25 -34
  125. package/src/zod/tempo/_test/TransactionRequest.test.ts +176 -8
  126. package/src/zod/tempo/z.ts +2 -0
@@ -8,6 +8,7 @@ import type {
8
8
  IsNarrowable,
9
9
  OneOf,
10
10
  PartialBy,
11
+ Undefined,
11
12
  UnionPartialBy,
12
13
  } from '../core/internal/types.js'
13
14
  import * as Json from '../core/Json.js'
@@ -27,6 +28,15 @@ const serializedKeychainType = '0x03'
27
28
  const serializedKeychainV2Type = '0x04'
28
29
  const serializedMultisigType = '0x05'
29
30
 
31
+ type NonMultisig<numberType = number> = OneOf<
32
+ | Secp256k1<numberType>
33
+ | P256<numberType>
34
+ | WebAuthn<numberType>
35
+ | Keychain<numberType>
36
+ >
37
+
38
+ type NonMultisigRpc = OneOf<Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc>
39
+
30
40
  /** Serialized magic identifier for Tempo signature envelopes. */
31
41
  export const magicBytes =
32
42
  '0x7777777777777777777777777777777777777777777777777777777777777777' // 32 "T"s
@@ -84,15 +94,7 @@ export type GetType<
84
94
  userAddress: Address.Address
85
95
  }
86
96
  ? 'keychain'
87
- : envelope extends
88
- | {
89
- account: Address.Address
90
- signatures: any
91
- }
92
- | {
93
- init: MultisigConfig.Config
94
- signatures: any
95
- }
97
+ : envelope extends { config: unknown; signatures: any }
96
98
  ? 'multisig'
97
99
  : never
98
100
 
@@ -119,20 +121,14 @@ export type GetType<
119
121
  *
120
122
  * [Signature Types Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
121
123
  */
122
- export type SignatureEnvelope<numberType = number> = OneOf<
123
- | Secp256k1<numberType>
124
- | P256<numberType>
125
- | WebAuthn<numberType>
126
- | Keychain<numberType>
127
- | Multisig<numberType>
128
- >
124
+ export type SignatureEnvelope<numberType = number> =
125
+ | (NonMultisig<numberType> & Undefined<Omit<Multisig<numberType>, 'type'>>)
126
+ | MultisigOneOf<numberType>
129
127
 
130
- /**
131
- * RPC-formatted signature envelope.
132
- */
133
- export type SignatureEnvelopeRpc = OneOf<
134
- Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc | MultisigRpc
135
- >
128
+ /** RPC-formatted signature envelope. */
129
+ export type SignatureEnvelopeRpc =
130
+ | (NonMultisigRpc & Undefined<Omit<MultisigRpc, 'type'>>)
131
+ | MultisigRpcOneOf
136
132
 
137
133
  /** Primitive signature envelope accepted by protocol sidecars. */
138
134
  export type Primitive<numberType = number> = OneOf<
@@ -182,42 +178,38 @@ export type KeychainRpc = {
182
178
  * [TIP-1061](https://tips.sh/1061)
183
179
  */
184
180
  export type Multisig<numberType = number> = {
185
- type: 'multisig'
186
181
  /** Native multisig account address. */
187
182
  account: Address.Address
183
+ /** Complete applicable multisig configuration witness. */
184
+ config: MultisigConfig.Config<bigint, numberType>
188
185
  /**
189
186
  * Owner approvals over the multisig owner approval digest. Each approval is
190
187
  * either a primitive signature or a nested multisig signature (keychain
191
188
  * approvals are invalid).
192
189
  */
193
190
  signatures: readonly SignatureEnvelope<numberType>[]
194
- /**
195
- * Initial native multisig config for bootstrapping this account. Present only on
196
- * the first (bootstrap) transaction from the derived account; absent on every
197
- * subsequent transaction.
198
- */
199
- init?: MultisigConfig.Config<numberType> | undefined
191
+ type: 'multisig'
200
192
  }
201
193
 
194
+ interface MultisigOneOf<numberType = number>
195
+ extends
196
+ Multisig<numberType>,
197
+ Undefined<Omit<NonMultisig<numberType>, 'type'>> {}
198
+
202
199
  /** RPC-formatted native multisig signature. */
203
- export type MultisigRpc = OneOf<
204
- | {
205
- /** Existing native multisig account. */
206
- account: Address.Address
207
- /** Structured owner approvals. */
208
- signatures: readonly SignatureEnvelopeRpc[]
209
- /** Multisig RPC signatures are untagged. */
210
- type?: undefined
211
- }
212
- | {
213
- /** Initial config for bootstrapping a native multisig account. */
214
- init: MultisigConfig.Config
215
- /** Structured owner approvals. */
216
- signatures: readonly SignatureEnvelopeRpc[]
217
- /** Multisig RPC signatures are untagged. */
218
- type?: undefined
219
- }
220
- >
200
+ export type MultisigRpc = {
201
+ /** Native multisig account address. */
202
+ account: Address.Address
203
+ /** Complete applicable multisig configuration witness. */
204
+ config: MultisigConfig.Rpc
205
+ /** Structured owner approvals. */
206
+ signatures: readonly SignatureEnvelopeRpc[]
207
+ /** Multisig RPC signatures are untagged. */
208
+ type?: undefined
209
+ }
210
+
211
+ interface MultisigRpcOneOf
212
+ extends MultisigRpc, Undefined<Omit<NonMultisigRpc, 'type'>> {}
221
213
 
222
214
  export type P256<numberType = number> = {
223
215
  prehash: boolean
@@ -380,6 +372,7 @@ export declare namespace assert {
380
372
  function assertMultisig(envelope: Multisig, depth: number): void {
381
373
  const missing: string[] = []
382
374
  if (!envelope.account) missing.push('account')
375
+ if (!envelope.config) missing.push('config')
383
376
  if (!Array.isArray(envelope.signatures)) missing.push('signatures')
384
377
  if (missing.length > 0)
385
378
  throw new MissingPropertiesError({
@@ -399,6 +392,25 @@ function assertMultisig(envelope: Multisig, depth: number): void {
399
392
  throw new InvalidMultisigApprovalError({
400
393
  reason: 'multisig account cannot be zero',
401
394
  })
395
+ MultisigConfig.assert(envelope.config)
396
+ if (
397
+ envelope.config.owners.some((owner) =>
398
+ Address.isEqual(owner.owner, envelope.account),
399
+ )
400
+ )
401
+ throw new InvalidMultisigApprovalError({
402
+ reason: 'multisig account cannot be an owner',
403
+ })
404
+ if (
405
+ envelope.config.version === 0n &&
406
+ !Address.isEqual(
407
+ MultisigConfig.getAddress(envelope.config),
408
+ envelope.account,
409
+ )
410
+ )
411
+ throw new InvalidMultisigApprovalError({
412
+ reason: 'initial multisig config does not derive account',
413
+ })
402
414
  if (envelope.signatures.length === 0)
403
415
  throw new InvalidMultisigApprovalError({
404
416
  reason: 'multisig signatures cannot be empty',
@@ -408,33 +420,14 @@ function assertMultisig(envelope: Multisig, depth: number): void {
408
420
  reason: `multisig signatures exceed ${MultisigConfig.maxSignatures}`,
409
421
  })
410
422
 
411
- if (envelope.init) {
412
- MultisigConfig.assert(envelope.init)
413
- if (
414
- !Address.isEqual(
415
- MultisigConfig.getAddress(envelope.init),
416
- envelope.account,
417
- )
418
- )
419
- throw new InvalidMultisigApprovalError({
420
- reason: 'multisig init does not derive account',
421
- })
422
- }
423
-
424
423
  for (const inner of envelope.signatures) {
425
424
  const type = getType(inner)
426
425
  if (type === 'keychain')
427
426
  throw new InvalidMultisigApprovalError({
428
427
  reason: 'keychain owner approvals are not allowed',
429
428
  })
430
- if (type === 'multisig') {
431
- const multisig = inner as Multisig
432
- if (multisig.init)
433
- throw new InvalidMultisigApprovalError({
434
- reason: 'nested multisig owner approvals cannot carry `init`',
435
- })
436
- assertMultisig(multisig, depth + 1)
437
- } else {
429
+ if (type === 'multisig') assertMultisig(inner as Multisig, depth + 1)
430
+ else {
438
431
  assert(inner)
439
432
  if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
440
433
  throw new InvalidMultisigApprovalError({
@@ -724,16 +717,48 @@ function deserialize_(
724
717
  serialized,
725
718
  })
726
719
 
727
- // The first field distinguishes the static wire shapes: a bootstrap init
728
- // config is an RLP list, while an initialized account is a 20-byte string.
729
720
  const decoded = Rlp.toHex(data)
730
- if (!Array.isArray(decoded) || decoded.length !== 2)
721
+ if (!Array.isArray(decoded) || decoded.length !== 3)
731
722
  throw new InvalidSerializedError({
732
- reason: 'invalid multisig wire shape: expected exactly two fields',
723
+ reason: 'invalid multisig wire shape: expected exactly three fields',
733
724
  serialized,
734
725
  })
735
726
 
736
- const [address, signatures] = decoded
727
+ const [account, config, signatures] = decoded
728
+ if (!Address.validate(account))
729
+ throw new InvalidSerializedError({
730
+ reason: 'invalid multisig account',
731
+ serialized,
732
+ })
733
+ if (!Array.isArray(config))
734
+ throw new InvalidSerializedError({
735
+ reason: 'invalid multisig config',
736
+ serialized,
737
+ })
738
+ const [salt, version, threshold, owners] = config
739
+ if (
740
+ config.length !== 4 ||
741
+ Array.isArray(salt) ||
742
+ Hex.size(salt) !== 32 ||
743
+ Array.isArray(version) ||
744
+ Hex.size(version) > 8 ||
745
+ (version !== '0x' && Hex.slice(version, 0, 1) === '0x00') ||
746
+ Array.isArray(threshold) ||
747
+ Hex.size(threshold) > 1 ||
748
+ !Array.isArray(owners) ||
749
+ owners.some(
750
+ (owner) =>
751
+ !Array.isArray(owner) ||
752
+ owner.length !== 2 ||
753
+ owner.some(Array.isArray) ||
754
+ !Address.validate(owner[0]) ||
755
+ Hex.size(owner[1] as Hex.Hex) > 1,
756
+ )
757
+ )
758
+ throw new InvalidSerializedError({
759
+ reason: 'invalid multisig config',
760
+ serialized,
761
+ })
737
762
  if (!Array.isArray(signatures) || signatures.some(Array.isArray))
738
763
  throw new InvalidSerializedError({
739
764
  reason: 'invalid multisig signatures list',
@@ -761,47 +786,15 @@ function deserialize_(
761
786
  serialized,
762
787
  })
763
788
 
764
- if (!Array.isArray(address) && !Address.validate(address))
765
- throw new InvalidSerializedError({
766
- reason: 'invalid multisig account',
767
- serialized,
768
- })
769
- if (Array.isArray(address)) {
770
- const [salt, threshold, owners] = address
771
- if (
772
- address.length !== 3 ||
773
- Array.isArray(salt) ||
774
- Hex.size(salt) !== 32 ||
775
- Array.isArray(threshold) ||
776
- Hex.size(threshold) > 1 ||
777
- !Array.isArray(owners) ||
778
- owners.some(
779
- (owner) =>
780
- !Array.isArray(owner) ||
781
- owner.length !== 2 ||
782
- owner.some(Array.isArray) ||
783
- Hex.size(owner[1] as Hex.Hex) > 1,
784
- )
785
- )
786
- throw new InvalidSerializedError({
787
- reason: 'invalid multisig init config',
788
- serialized,
789
- })
790
- }
791
-
792
- const init = Array.isArray(address)
793
- ? MultisigConfig.fromTuple(address as unknown as MultisigConfig.Tuple)
794
- : undefined
795
- const account = init
796
- ? MultisigConfig.getAddress(init)
797
- : (address as Address.Address)
798
789
  const envelope = {
799
- type: 'multisig',
800
- account,
790
+ account: account as Address.Address,
791
+ config: MultisigConfig.fromTuple(
792
+ config as unknown as MultisigConfig.Tuple,
793
+ ),
801
794
  signatures: signatures.map((signature) =>
802
795
  deserialize_(signature as Hex.Hex, depth),
803
796
  ),
804
- ...(init ? { init } : {}),
797
+ type: 'multisig',
805
798
  } satisfies Multisig
806
799
  assertMultisig(envelope, depth)
807
800
  return envelope
@@ -939,24 +932,20 @@ function deserialize_(
939
932
  * ```
940
933
  *
941
934
  * @example
942
- * ### Multisig (from initial config)
943
- *
944
- * Pass `initialConfig` to derive `account` automatically. Set `init: true` to
945
- * opt into bootstrap (uses `initialConfig` as the bootstrap `init`); omit
946
- * `init` for subsequent (non-bootstrap) transactions.
935
+ * ### Multisig
947
936
  *
948
937
  * ```ts twoslash
949
938
  * import { Secp256k1 } from 'ox'
950
939
  * import { MultisigConfig, SignatureEnvelope } from 'ox/tempo'
951
940
  *
952
- * const initialConfig = MultisigConfig.from({
953
- * threshold: 1,
941
+ * const config = MultisigConfig.from({
954
942
  * owners: [
955
943
  * {
956
944
  * owner: '0x1111111111111111111111111111111111111111',
957
945
  * weight: 1
958
946
  * }
959
- * ]
947
+ * ],
948
+ * threshold: 1
960
949
  * })
961
950
  *
962
951
  * const privateKey = Secp256k1.randomPrivateKey()
@@ -964,16 +953,8 @@ function deserialize_(
964
953
  * Secp256k1.sign({ payload: '0xdeadbeef', privateKey })
965
954
  * )
966
955
  *
967
- * // Bootstrap transaction
968
- * const bootstrap = SignatureEnvelope.from({
969
- * initialConfig,
970
- * signatures: [signature],
971
- * init: true
972
- * })
973
- *
974
- * // Subsequent (non-bootstrap) transactions
975
- * const subsequent = SignatureEnvelope.from({
976
- * initialConfig,
956
+ * const multisig = SignatureEnvelope.from({
957
+ * config,
977
958
  * signatures: [signature]
978
959
  * })
979
960
  * ```
@@ -999,30 +980,17 @@ export function from<const value extends from.Value>(
999
980
  const type = getType(value)
1000
981
 
1001
982
  if (type === 'multisig') {
1002
- const multisig = value as Multisig & {
1003
- initialConfig?: MultisigConfig.Config | undefined
1004
- init?: MultisigConfig.Config | boolean | undefined
1005
- }
1006
- const { initialConfig, init, ...rest } = multisig
1007
- // Derive `account` from `initialConfig` when not provided explicitly.
1008
- const account = (() => {
1009
- if (rest.account) return rest.account
1010
- if (initialConfig) return MultisigConfig.getAddress(initialConfig)
1011
- return rest.account
1012
- })()
1013
- // `init: true` opts into bootstrap using the supplied `initialConfig`.
1014
- // Otherwise, `init` is treated as the explicit bootstrap config (or
1015
- // omitted).
1016
- const initSource = init === true ? initialConfig : init || undefined
1017
- return {
1018
- ...rest,
983
+ const multisig = value as from.MultisigFromConfig
984
+ const config = MultisigConfig.from(multisig.config)
985
+ const account = multisig.account ?? MultisigConfig.getAddress(config)
986
+ const envelope = {
1019
987
  account,
1020
- signatures: rest.signatures.map((signature) => from(signature)),
1021
- // Normalize the bootstrap config (sorts owners, defaults the salt) so the
1022
- // in-memory envelope matches what `deserialize` reconstructs.
1023
- ...(initSource ? { init: MultisigConfig.from(initSource) } : {}),
988
+ config,
989
+ signatures: multisig.signatures.map((signature) => from(signature)),
1024
990
  type,
1025
- } as never
991
+ } satisfies Multisig
992
+ assert(envelope)
993
+ return envelope as never
1026
994
  }
1027
995
 
1028
996
  return {
@@ -1067,24 +1035,32 @@ export declare namespace from {
1067
1035
  payload?: Hex.Hex | Bytes.Bytes | undefined
1068
1036
  }
1069
1037
 
1070
- /**
1071
- * Multisig envelope input variant where `account` is derived from the
1072
- * supplied `initialConfig`. Pass `init: true` to opt into bootstrap (uses
1073
- * `initialConfig` as the bootstrap `init`); omit `init` for subsequent
1074
- * (non-bootstrap) transactions.
1075
- */
1076
- type MultisigFromInitialConfig = {
1077
- type?: 'multisig' | undefined
1078
- initialConfig: MultisigConfig.Config
1079
- signatures: readonly SignatureEnvelope[]
1080
- init?: MultisigConfig.Config | boolean | undefined
1081
- }
1038
+ /** Multisig signature input with an optional derived initial account. */
1039
+ type MultisigFromConfig =
1040
+ | {
1041
+ /** Initial native multisig account, derived from config when omitted. */
1042
+ account?: Address.Address | undefined
1043
+ /** Initial version-0 multisig configuration witness. */
1044
+ config: MultisigConfig.Input<0 | 0n>
1045
+ /** Primitive or nested owner approvals. */
1046
+ signatures: readonly SignatureEnvelope[]
1047
+ type?: 'multisig' | undefined
1048
+ }
1049
+ | {
1050
+ /** Native multisig account. */
1051
+ account: Address.Address
1052
+ /** Complete applicable multisig configuration witness. */
1053
+ config: MultisigConfig.Input
1054
+ /** Primitive or nested owner approvals. */
1055
+ signatures: readonly SignatureEnvelope[]
1056
+ type?: 'multisig' | undefined
1057
+ }
1082
1058
 
1083
1059
  type Value =
1084
1060
  | UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
1085
1061
  | Secp256k1Flat
1086
1062
  | Serialized
1087
- | MultisigFromInitialConfig
1063
+ | MultisigFromConfig
1088
1064
 
1089
1065
  type ReturnValue<value extends Value> = Compute<
1090
1066
  OneOf<
@@ -1092,7 +1068,7 @@ export declare namespace from {
1092
1068
  ? SignatureEnvelope
1093
1069
  : value extends Secp256k1Flat
1094
1070
  ? Secp256k1
1095
- : value extends MultisigFromInitialConfig
1071
+ : value extends MultisigFromConfig
1096
1072
  ? Multisig
1097
1073
  : IsNarrowable<value, SignatureEnvelope> extends true
1098
1074
  ? SignatureEnvelope
@@ -1211,24 +1187,14 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
1211
1187
 
1212
1188
  if (
1213
1189
  (envelope as { type?: string | undefined }).type === 'multisig' ||
1214
- ('signatures' in envelope && ('account' in envelope || 'init' in envelope))
1190
+ ('config' in envelope && 'signatures' in envelope)
1215
1191
  ) {
1216
1192
  const multisig = envelope as MultisigRpc
1217
- const hasAccount = typeof multisig.account !== 'undefined'
1218
- const hasInit = typeof multisig.init !== 'undefined'
1219
- if (hasAccount === hasInit)
1220
- throw new InvalidMultisigApprovalError({
1221
- reason: 'RPC multisig must contain exactly one of `account` or `init`',
1222
- })
1223
- const init = hasInit
1224
- ? MultisigConfig.from(multisig.init as MultisigConfig.Config)
1225
- : undefined
1226
- const account = init ? MultisigConfig.getAddress(init) : multisig.account
1227
1193
  const result = {
1228
- type: 'multisig',
1229
- account: account as Address.Address,
1194
+ account: multisig.account,
1195
+ config: MultisigConfig.fromRpc(multisig.config),
1230
1196
  signatures: multisig.signatures.map((signature) => fromRpc(signature)),
1231
- ...(init ? { init } : {}),
1197
+ type: 'multisig',
1232
1198
  } satisfies Multisig
1233
1199
  assert(result)
1234
1200
  return result
@@ -1242,7 +1208,7 @@ export declare namespace fromRpc {
1242
1208
  | assert.ErrorType
1243
1209
  | CoercionError
1244
1210
  | InvalidSerializedError
1245
- | MultisigConfig.getAddress.ErrorType
1211
+ | MultisigConfig.fromRpc.ErrorType
1246
1212
  | Signature.fromRpc.ErrorType
1247
1213
  | Errors.GlobalErrorType
1248
1214
  }
@@ -1317,12 +1283,7 @@ export function getType<
1317
1283
  return 'keychain' as never
1318
1284
 
1319
1285
  // Detect Multisig signature
1320
- if (
1321
- ('account' in envelope ||
1322
- 'initialConfig' in envelope ||
1323
- 'init' in envelope) &&
1324
- 'signatures' in envelope
1325
- )
1286
+ if ('config' in envelope && 'signatures' in envelope)
1326
1287
  return 'multisig' as never
1327
1288
 
1328
1289
  throw new CoercionError({
@@ -1339,7 +1300,7 @@ export function getType<
1339
1300
  * - WebAuthn: `0x02` + webauthnData (variable) + r (32) + s (32) + pubKeyX (32) + pubKeyY (32)
1340
1301
  * - Keychain V1: `0x03` + userAddress (20) + inner signature (recursive)
1341
1302
  * - Keychain V2: `0x04` + userAddress (20) + inner signature (recursive)
1342
- * - Multisig: `0x05` + RLP `[account | init, signatures]`
1303
+ * - Multisig: `0x05` + RLP `[account, config, signatures]`
1343
1304
  *
1344
1305
  * [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
1345
1306
  *
@@ -1426,14 +1387,11 @@ export function serialize(
1426
1387
  if (type === 'multisig') {
1427
1388
  const multisig = envelope as Multisig
1428
1389
  assert(multisig)
1429
- // The first field is either the initialized account or the bootstrap init
1430
- // config. Each owner approval is an encoded signature.
1431
1390
  return Hex.concat(
1432
1391
  serializedMultisigType,
1433
1392
  Rlp.fromHex([
1434
- multisig.init
1435
- ? MultisigConfig.toTuple(multisig.init)
1436
- : multisig.account,
1393
+ multisig.account,
1394
+ MultisigConfig.toTuple(multisig.config),
1437
1395
  multisig.signatures.map((signature) => serialize(signature)),
1438
1396
  ]),
1439
1397
  options.magic ? magicBytes : '0x',
@@ -1474,9 +1432,6 @@ export declare namespace serialize {
1474
1432
  * recovered owner address. Works for any owner key type (secp256k1, p256,
1475
1433
  * webAuthn).
1476
1434
  *
1477
- * Config updates never change `account`, so the initial config is the correct
1478
- * input even for post-update transactions.
1479
- *
1480
1435
  * @example
1481
1436
  * ```ts twoslash
1482
1437
  * import { Secp256k1 } from 'ox'
@@ -1486,8 +1441,7 @@ export declare namespace serialize {
1486
1441
  * TxEnvelopeTempo
1487
1442
  * } from 'ox/tempo'
1488
1443
  *
1489
- * const initialConfig = MultisigConfig.from({
1490
- * threshold: 2,
1444
+ * const config = MultisigConfig.from({
1491
1445
  * owners: [
1492
1446
  * {
1493
1447
  * owner: '0x1111111111111111111111111111111111111111',
@@ -1497,8 +1451,10 @@ export declare namespace serialize {
1497
1451
  * owner: '0x2222222222222222222222222222222222222222',
1498
1452
  * weight: 1
1499
1453
  * }
1500
- * ]
1454
+ * ],
1455
+ * threshold: 2
1501
1456
  * })
1457
+ * const account = MultisigConfig.getAddress(config)
1502
1458
  *
1503
1459
  * const tx = TxEnvelopeTempo.from({ chainId: 1, calls: [] })
1504
1460
  * const payload = TxEnvelopeTempo.getSignPayload(tx)
@@ -1508,8 +1464,9 @@ export declare namespace serialize {
1508
1464
  * Secp256k1.randomPrivateKey()
1509
1465
  * ]
1510
1466
  * const digest = MultisigConfig.getSignPayload({
1511
- * payload,
1512
- * initialConfig
1467
+ * account,
1468
+ * config,
1469
+ * payload
1513
1470
  * })
1514
1471
  * const signatures = privateKeys.map((privateKey) =>
1515
1472
  * SignatureEnvelope.from(
@@ -1519,7 +1476,8 @@ export declare namespace serialize {
1519
1476
  *
1520
1477
  * const ordered = SignatureEnvelope.sortMultisigApprovals({
1521
1478
  * // [!code focus]
1522
- * initialConfig, // [!code focus]
1479
+ * account, // [!code focus]
1480
+ * config, // [!code focus]
1523
1481
  * payload, // [!code focus]
1524
1482
  * signatures // [!code focus]
1525
1483
  * }) // [!code focus]
@@ -1531,16 +1489,12 @@ export declare namespace serialize {
1531
1489
  export function sortMultisigApprovals(
1532
1490
  value: sortMultisigApprovals.Value,
1533
1491
  ): readonly SignatureEnvelope[] {
1534
- const { payload, signatures, version = 0n } = value
1535
- const digest = MultisigConfig.getSignPayload(
1536
- 'initialConfig' in value && value.initialConfig
1537
- ? { payload, initialConfig: value.initialConfig, version }
1538
- : {
1539
- payload,
1540
- account: (value as { account: Address.Address }).account,
1541
- version,
1542
- },
1543
- )
1492
+ const { account, config, payload, signatures } = value
1493
+ const digest = MultisigConfig.getSignPayload({
1494
+ account,
1495
+ config: MultisigConfig.from(config),
1496
+ payload,
1497
+ })
1544
1498
  // Recover each signer once (decorate–sort–undecorate) rather than inside the
1545
1499
  // comparator.
1546
1500
  return signatures
@@ -1554,25 +1508,15 @@ export function sortMultisigApprovals(
1554
1508
 
1555
1509
  export declare namespace sortMultisigApprovals {
1556
1510
  type Value = {
1511
+ /** The native multisig account address. */
1512
+ account: Address.Address
1513
+ /** Complete applicable multisig configuration witness. */
1514
+ config: MultisigConfig.Input
1557
1515
  /** The inner transaction sign payload (`tx.signature_hash()`). */
1558
1516
  payload: Hex.Hex | Bytes.Bytes
1559
1517
  /** The owner approvals to order. */
1560
1518
  signatures: readonly SignatureEnvelope[]
1561
- /** Current multisig config version. Defaults to `0n`. */
1562
- version?: bigint | undefined
1563
- } & OneOf<
1564
- | {
1565
- /** The native multisig account address. */
1566
- account: Address.Address
1567
- }
1568
- | {
1569
- /**
1570
- * The initial multisig config (the bootstrap config that derived the
1571
- * permanent `account`). Used to derive the account automatically.
1572
- */
1573
- initialConfig: MultisigConfig.Config
1574
- }
1575
- >
1519
+ }
1576
1520
 
1577
1521
  type ErrorType =
1578
1522
  | MultisigConfig.getSignPayload.ErrorType
@@ -1656,25 +1600,10 @@ export function toRpc<const envelope extends toRpc.Input>(
1656
1600
  if (type === 'multisig') {
1657
1601
  const multisig = envelope as Multisig
1658
1602
  assert(multisig)
1659
- const signatures = multisig.signatures.map((signature) => toRpc(signature))
1660
- if (multisig.init) {
1661
- const init = {
1662
- ...multisig.init,
1663
- salt: multisig.init.salt ?? MultisigConfig.zeroSalt,
1664
- threshold: Number(multisig.init.threshold),
1665
- owners: multisig.init.owners.map((owner) => ({
1666
- ...owner,
1667
- weight: Number(owner.weight),
1668
- })),
1669
- }
1670
- return {
1671
- init,
1672
- signatures,
1673
- } as never
1674
- }
1675
1603
  return {
1676
1604
  account: multisig.account,
1677
- signatures,
1605
+ config: MultisigConfig.toRpc(multisig.config),
1606
+ signatures: multisig.signatures.map((signature) => toRpc(signature)),
1678
1607
  } as never
1679
1608
  }
1680
1609
 
@@ -1702,6 +1631,7 @@ export declare namespace toRpc {
1702
1631
  type ErrorType =
1703
1632
  | assert.ErrorType
1704
1633
  | CoercionError
1634
+ | MultisigConfig.toRpc.ErrorType
1705
1635
  | Signature.toRpc.ErrorType
1706
1636
  | Errors.GlobalErrorType
1707
1637
  }