ox 1.0.0-beta.0 → 1.0.0-beta.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 (66) hide show
  1. package/dist/core/Provider.d.ts +7 -7
  2. package/dist/core/Provider.d.ts.map +1 -1
  3. package/dist/core/RpcSchema.d.ts +50 -0
  4. package/dist/core/RpcSchema.d.ts.map +1 -1
  5. package/dist/core/RpcSchema.js.map +1 -1
  6. package/dist/core/RpcTransport.d.ts +2 -2
  7. package/dist/core/RpcTransport.d.ts.map +1 -1
  8. package/dist/core/RpcTransport.js.map +1 -1
  9. package/dist/core/internal/rpcTransport.d.ts +1 -1
  10. package/dist/core/internal/rpcTransport.d.ts.map +1 -1
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/tempo/MultisigConfig.d.ts +79 -16
  16. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  17. package/dist/tempo/MultisigConfig.js +69 -11
  18. package/dist/tempo/MultisigConfig.js.map +1 -1
  19. package/dist/tempo/SignatureEnvelope.d.ts +83 -19
  20. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  21. package/dist/tempo/SignatureEnvelope.js +99 -30
  22. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  23. package/dist/tempo/index.d.ts +2 -2
  24. package/dist/tempo/index.js +2 -2
  25. package/dist/zod/RpcSchema.d.ts +144 -25
  26. package/dist/zod/RpcSchema.d.ts.map +1 -1
  27. package/dist/zod/RpcSchema.js +85 -13
  28. package/dist/zod/RpcSchema.js.map +1 -1
  29. package/dist/zod/internal/rpcSchemas/Eth.d.ts +12 -12
  30. package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
  31. package/dist/zod/internal/rpcSchemas/Eth.js +14 -12
  32. package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
  33. package/dist/zod/tempo/AuthorizationTempo.d.ts +6 -6
  34. package/dist/zod/tempo/KeyAuthorization.d.ts +3 -3
  35. package/dist/zod/tempo/RpcSchemaTempo.d.ts +6 -6
  36. package/dist/zod/tempo/SignatureEnvelope.d.ts +3 -3
  37. package/dist/zod/tempo/SignatureEnvelope.js +1 -1
  38. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  39. package/dist/zod/tempo/Transaction.d.ts +6 -6
  40. package/dist/zod/tempo/TransactionRequest.d.ts +6 -6
  41. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +9 -9
  42. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +3 -3
  43. package/package.json +1 -1
  44. package/src/CHANGELOG.md +6 -0
  45. package/src/core/Provider.ts +7 -7
  46. package/src/core/RpcSchema.ts +60 -0
  47. package/src/core/RpcTransport.ts +7 -4
  48. package/src/core/_test/Provider.test-d.ts +21 -0
  49. package/src/core/_test/RpcSchema.test-d.ts +87 -1
  50. package/src/core/_test/RpcTransport.test-d.ts +43 -0
  51. package/src/core/internal/rpcTransport.ts +1 -1
  52. package/src/index.ts +2 -0
  53. package/src/tempo/MultisigConfig.test.ts +21 -12
  54. package/src/tempo/MultisigConfig.ts +103 -18
  55. package/src/tempo/SignatureEnvelope.test.ts +124 -26
  56. package/src/tempo/SignatureEnvelope.ts +150 -48
  57. package/src/tempo/e2e.test.ts +42 -47
  58. package/src/tempo/index.ts +2 -2
  59. package/src/version.ts +1 -1
  60. package/src/zod/RpcSchema.ts +190 -13
  61. package/src/zod/_test/RpcSchema.test-d.ts +53 -6
  62. package/src/zod/_test/RpcSchema.test.ts +138 -18
  63. package/src/zod/internal/rpcSchemas/Eth.ts +14 -12
  64. package/src/zod/tempo/SignatureEnvelope.ts +1 -1
  65. package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +1 -1
  66. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +23 -0
@@ -86,7 +86,7 @@ export type GetType<
86
86
  ? 'keychain'
87
87
  : envelope extends {
88
88
  account: Address.Address
89
- configId: `0x${string}`
89
+ genesisConfigId: `0x${string}`
90
90
  signatures: any
91
91
  }
92
92
  ? 'multisig'
@@ -173,7 +173,7 @@ export type Multisig<numberType = number> = {
173
173
  /** Native multisig account address. */
174
174
  account: Address.Address
175
175
  /** Permanent config ID derived from the initial multisig config. */
176
- configId: Hex.Hex
176
+ genesisConfigId: Hex.Hex
177
177
  /** Primitive owner approvals over the multisig owner approval digest. */
178
178
  signatures: readonly SignatureEnvelope<numberType>[]
179
179
  /**
@@ -187,6 +187,11 @@ export type Multisig<numberType = number> = {
187
187
  export type MultisigRpc = {
188
188
  type: 'multisig'
189
189
  account: Address.Address
190
+ /**
191
+ * The permanent multisig config ID (TIP-1061 wire field `config_id`).
192
+ * Maps to `genesisConfigId` on the typed
193
+ * {@link ox#SignatureEnvelope.Multisig} envelope.
194
+ */
190
195
  configId: Hex.Hex
191
196
  /**
192
197
  * Encoded primitive owner approvals (raw serialized signatures), matching the
@@ -340,7 +345,7 @@ export function assert(envelope: PartialBy<SignatureEnvelope, 'type'>): void {
340
345
  const multisig = envelope as Multisig
341
346
  const missing: string[] = []
342
347
  if (!multisig.account) missing.push('account')
343
- if (!multisig.configId) missing.push('configId')
348
+ if (!multisig.genesisConfigId) missing.push('genesisConfigId')
344
349
  if (!Array.isArray(multisig.signatures)) missing.push('signatures')
345
350
  if (missing.length > 0)
346
351
  throw new MissingPropertiesError({ envelope, missing, type: 'multisig' })
@@ -625,10 +630,10 @@ export function deserialize(value: Serialized): SignatureEnvelope {
625
630
  }
626
631
 
627
632
  if (typeId === serializedMultisigType) {
628
- // Wire format: `0x05 || rlp([account, configId, signatures, init])`. `init`
633
+ // Wire format: `0x05 || rlp([account, genesisConfigId, signatures, init])`. `init`
629
634
  // is optional: absent when the element is missing or the `0x80` placeholder
630
635
  // (decoded as the empty string `0x`), otherwise the bootstrap config list.
631
- const [account, configId, signatures, init] = Rlp.toHex(data) as [
636
+ const [account, genesisConfigId, signatures, init] = Rlp.toHex(data) as [
632
637
  Hex.Hex,
633
638
  Hex.Hex,
634
639
  readonly Hex.Hex[],
@@ -637,7 +642,7 @@ export function deserialize(value: Serialized): SignatureEnvelope {
637
642
  return {
638
643
  type: 'multisig',
639
644
  account,
640
- configId,
645
+ genesisConfigId,
641
646
  signatures: signatures.map((signature) => deserialize(signature)),
642
647
  ...(init && init !== '0x'
643
648
  ? {
@@ -778,6 +783,46 @@ export function deserialize(value: Serialized): SignatureEnvelope {
778
783
  * })
779
784
  * ```
780
785
  *
786
+ * @example
787
+ * ### Multisig (from genesis config)
788
+ *
789
+ * Pass `genesisConfig` to derive `account` and `genesisConfigId` automatically.
790
+ * Set `init: true` to opt into bootstrap (uses `genesisConfig` as the
791
+ * bootstrap `init`); omit `init` for subsequent (non-bootstrap) transactions.
792
+ *
793
+ * ```ts twoslash
794
+ * import { Secp256k1 } from 'ox'
795
+ * import { MultisigConfig, SignatureEnvelope } from 'ox/tempo'
796
+ *
797
+ * const genesisConfig = MultisigConfig.from({
798
+ * threshold: 1,
799
+ * owners: [
800
+ * {
801
+ * owner: '0x1111111111111111111111111111111111111111',
802
+ * weight: 1
803
+ * }
804
+ * ]
805
+ * })
806
+ *
807
+ * const privateKey = Secp256k1.randomPrivateKey()
808
+ * const signature = SignatureEnvelope.from(
809
+ * Secp256k1.sign({ payload: '0xdeadbeef', privateKey })
810
+ * )
811
+ *
812
+ * // Bootstrap transaction
813
+ * const bootstrap = SignatureEnvelope.from({
814
+ * genesisConfig,
815
+ * signatures: [signature],
816
+ * init: true
817
+ * })
818
+ *
819
+ * // Subsequent (non-bootstrap) transactions
820
+ * const subsequent = SignatureEnvelope.from({
821
+ * genesisConfig,
822
+ * signatures: [signature]
823
+ * })
824
+ * ```
825
+ *
781
826
  * @param value - The value to coerce (either a hex string or signature envelope).
782
827
  * @returns The signature envelope.
783
828
  */
@@ -799,20 +844,42 @@ export function from<const value extends from.Value>(
799
844
  const type = getType(value)
800
845
 
801
846
  if (type === 'multisig') {
802
- const multisig = value as Multisig
847
+ const multisig = value as Multisig & {
848
+ genesisConfig?: MultisigConfig.Config | undefined
849
+ init?: MultisigConfig.Config | boolean | undefined
850
+ }
851
+ const { genesisConfig, init, ...rest } = multisig
852
+ // Derive `account`/`genesisConfigId` from `genesisConfig` when not provided
853
+ // explicitly.
854
+ const account = (() => {
855
+ if (rest.account) return rest.account
856
+ if (genesisConfig) return MultisigConfig.getAddress(genesisConfig)
857
+ return rest.account
858
+ })()
859
+ const genesisConfigId = (() => {
860
+ if (rest.genesisConfigId) return rest.genesisConfigId
861
+ if (genesisConfig) return MultisigConfig.toId(genesisConfig)
862
+ return rest.genesisConfigId
863
+ })()
864
+ // `init: true` opts into bootstrap using the supplied `genesisConfig`.
865
+ // Otherwise, `init` is treated as the explicit bootstrap config (or
866
+ // omitted).
867
+ const initSource = init === true ? genesisConfig : init || undefined
803
868
  return {
804
- ...multisig,
805
- signatures: multisig.signatures.map((signature) => from(signature)),
869
+ ...rest,
870
+ account,
871
+ genesisConfigId,
872
+ signatures: rest.signatures.map((signature) => from(signature)),
806
873
  // Normalize the bootstrap config (sorts owners, defaults the salt) so the
807
874
  // in-memory envelope matches what `deserialize` reconstructs.
808
- ...(multisig.init ? { init: MultisigConfig.from(multisig.init) } : {}),
875
+ ...(initSource ? { init: MultisigConfig.from(initSource) } : {}),
809
876
  type,
810
877
  } as never
811
878
  }
812
879
 
813
880
  return {
814
881
  ...value,
815
- ...(type === 'p256' ? { prehash: value.prehash } : {}),
882
+ ...(type === 'p256' ? { prehash: (value as P256).prehash } : {}),
816
883
  ...(type === 'keychain'
817
884
  ? {
818
885
  ...(!(
@@ -852,10 +919,24 @@ export declare namespace from {
852
919
  payload?: Hex.Hex | Bytes.Bytes | undefined
853
920
  }
854
921
 
922
+ /**
923
+ * Multisig envelope input variant where `account` and `genesisConfigId` are derived
924
+ * from the supplied `genesisConfig`. Pass `init: true` to opt into bootstrap
925
+ * (uses `genesisConfig` as the bootstrap `init`); omit `init` for subsequent
926
+ * (non-bootstrap) transactions.
927
+ */
928
+ type MultisigFromGenesisConfig = {
929
+ type?: 'multisig' | undefined
930
+ genesisConfig: MultisigConfig.Config
931
+ signatures: readonly SignatureEnvelope[]
932
+ init?: MultisigConfig.Config | boolean | undefined
933
+ }
934
+
855
935
  type Value =
856
936
  | UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
857
937
  | Secp256k1Flat
858
938
  | Serialized
939
+ | MultisigFromGenesisConfig
859
940
 
860
941
  type ReturnValue<value extends Value> = Compute<
861
942
  OneOf<
@@ -863,16 +944,18 @@ export declare namespace from {
863
944
  ? SignatureEnvelope
864
945
  : value extends Secp256k1Flat
865
946
  ? Secp256k1
866
- : IsNarrowable<value, SignatureEnvelope> extends true
867
- ? SignatureEnvelope
868
- : Assign<
869
- value,
870
- {
871
- readonly type: GetType<value>
872
- } & (GetType<value> extends 'keychain'
873
- ? { keyId?: Address.Address | undefined }
874
- : {})
875
- >
947
+ : value extends MultisigFromGenesisConfig
948
+ ? Multisig
949
+ : IsNarrowable<value, SignatureEnvelope> extends true
950
+ ? SignatureEnvelope
951
+ : Assign<
952
+ value,
953
+ {
954
+ readonly type: GetType<value>
955
+ } & (GetType<value> extends 'keychain'
956
+ ? { keyId?: Address.Address | undefined }
957
+ : {})
958
+ >
876
959
  >
877
960
  >
878
961
  }
@@ -988,7 +1071,9 @@ export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
988
1071
  return {
989
1072
  type: 'multisig',
990
1073
  account: multisig.account,
991
- configId: multisig.configId,
1074
+ // Map RPC wire field `configId` (TIP-1061 spec name) to the typed
1075
+ // envelope's `genesisConfigId`.
1076
+ genesisConfigId: multisig.configId,
992
1077
  // Owner approvals are raw serialized signatures (node `Vec<Bytes>`).
993
1078
  signatures: multisig.signatures.map((signature) =>
994
1079
  deserialize(signature),
@@ -1079,8 +1164,8 @@ export function getType<
1079
1164
 
1080
1165
  // Detect Multisig signature
1081
1166
  if (
1082
- 'account' in envelope &&
1083
- 'configId' in envelope &&
1167
+ (('account' in envelope && 'genesisConfigId' in envelope) ||
1168
+ 'genesisConfig' in envelope) &&
1084
1169
  'signatures' in envelope
1085
1170
  )
1086
1171
  return 'multisig' as never
@@ -1184,7 +1269,7 @@ export function serialize(
1184
1269
 
1185
1270
  if (type === 'multisig') {
1186
1271
  const multisig = envelope as Multisig
1187
- // Format: `0x05 || rlp([account, configId, signatures, init])`, where each
1272
+ // Format: `0x05 || rlp([account, genesisConfigId, signatures, init])`, where each
1188
1273
  // owner approval is an encoded primitive signature. `init` is the bootstrap
1189
1274
  // config (an RLP list) when present, otherwise the canonical empty-string
1190
1275
  // placeholder (`0x` → RLP `0x80`).
@@ -1192,7 +1277,7 @@ export function serialize(
1192
1277
  serializedMultisigType,
1193
1278
  Rlp.fromHex([
1194
1279
  multisig.account,
1195
- multisig.configId,
1280
+ multisig.genesisConfigId,
1196
1281
  multisig.signatures.map((signature) => serialize(signature)),
1197
1282
  multisig.init ? MultisigConfig.toTuple(multisig.init) : '0x',
1198
1283
  ]),
@@ -1224,6 +1309,9 @@ export declare namespace serialize {
1224
1309
  * recovered owner address. Works for any owner key type (secp256k1, p256,
1225
1310
  * webAuthn, keychain).
1226
1311
  *
1312
+ * Config updates never change `account`/`genesisConfigId`, so the genesis
1313
+ * config is the correct input even for post-update transactions.
1314
+ *
1227
1315
  * @example
1228
1316
  * ```ts twoslash
1229
1317
  * import { Secp256k1 } from 'ox'
@@ -1233,7 +1321,7 @@ export declare namespace serialize {
1233
1321
  * TxEnvelopeTempo
1234
1322
  * } from 'ox/tempo'
1235
1323
  *
1236
- * const config = MultisigConfig.from({
1324
+ * const genesisConfig = MultisigConfig.from({
1237
1325
  * threshold: 2,
1238
1326
  * owners: [
1239
1327
  * {
@@ -1246,21 +1334,18 @@ export declare namespace serialize {
1246
1334
  * }
1247
1335
  * ]
1248
1336
  * })
1249
- * const configId = MultisigConfig.toId(config)
1250
- * const account = MultisigConfig.getAddress({ configId })
1251
1337
  *
1252
1338
  * const tx = TxEnvelopeTempo.from({ chainId: 1, calls: [] })
1253
1339
  * const payload = TxEnvelopeTempo.getSignPayload(tx)
1254
- * const digest = MultisigConfig.getSignPayload({
1255
- * payload,
1256
- * account,
1257
- * configId
1258
- * })
1259
1340
  *
1260
1341
  * const privateKeys = [
1261
1342
  * Secp256k1.randomPrivateKey(),
1262
1343
  * Secp256k1.randomPrivateKey()
1263
1344
  * ]
1345
+ * const digest = MultisigConfig.getSignPayload({
1346
+ * payload,
1347
+ * genesisConfig
1348
+ * })
1264
1349
  * const signatures = privateKeys.map((privateKey) =>
1265
1350
  * SignatureEnvelope.from(
1266
1351
  * Secp256k1.sign({ payload: digest, privateKey })
@@ -1269,8 +1354,7 @@ export declare namespace serialize {
1269
1354
  *
1270
1355
  * const ordered = SignatureEnvelope.sortMultisigApprovals({
1271
1356
  * // [!code focus]
1272
- * account, // [!code focus]
1273
- * configId, // [!code focus]
1357
+ * genesisConfig, // [!code focus]
1274
1358
  * payload, // [!code focus]
1275
1359
  * signatures // [!code focus]
1276
1360
  * }) // [!code focus]
@@ -1282,12 +1366,17 @@ export declare namespace serialize {
1282
1366
  export function sortMultisigApprovals(
1283
1367
  value: sortMultisigApprovals.Value,
1284
1368
  ): readonly SignatureEnvelope[] {
1285
- const { account, configId, payload, signatures } = value
1286
- const digest = MultisigConfig.getSignPayload({
1287
- account,
1288
- configId,
1289
- payload,
1290
- })
1369
+ const { payload, signatures } = value
1370
+ const digest = MultisigConfig.getSignPayload(
1371
+ 'genesisConfig' in value && value.genesisConfig
1372
+ ? { payload, genesisConfig: value.genesisConfig }
1373
+ : {
1374
+ payload,
1375
+ account: (value as { account: Address.Address }).account,
1376
+ genesisConfigId: (value as { genesisConfigId: Hex.Hex })
1377
+ .genesisConfigId,
1378
+ },
1379
+ )
1291
1380
  // Recover each signer once (decorate–sort–undecorate) rather than inside the
1292
1381
  // comparator.
1293
1382
  return signatures
@@ -1301,15 +1390,26 @@ export function sortMultisigApprovals(
1301
1390
 
1302
1391
  export declare namespace sortMultisigApprovals {
1303
1392
  type Value = {
1304
- /** The native multisig account address. */
1305
- account: Address.Address
1306
- /** The permanent config ID. */
1307
- configId: Hex.Hex
1308
1393
  /** The inner transaction sign payload (`tx.signature_hash()`). */
1309
1394
  payload: Hex.Hex | Bytes.Bytes
1310
1395
  /** The primitive owner approvals to order. */
1311
1396
  signatures: readonly SignatureEnvelope[]
1312
- }
1397
+ } & OneOf<
1398
+ | {
1399
+ /** The native multisig account address. */
1400
+ account: Address.Address
1401
+ /** The permanent config ID. */
1402
+ genesisConfigId: Hex.Hex
1403
+ }
1404
+ | {
1405
+ /**
1406
+ * The initial multisig config (the bootstrap config that derived the
1407
+ * permanent `account` and `genesisConfigId`). Used to derive both values
1408
+ * automatically.
1409
+ */
1410
+ genesisConfig: MultisigConfig.Config
1411
+ }
1412
+ >
1313
1413
 
1314
1414
  type ErrorType =
1315
1415
  | MultisigConfig.getSignPayload.ErrorType
@@ -1393,7 +1493,9 @@ export function toRpc(envelope: SignatureEnvelope): SignatureEnvelopeRpc {
1393
1493
  return {
1394
1494
  type: 'multisig',
1395
1495
  account: multisig.account,
1396
- configId: multisig.configId,
1496
+ // Map the typed envelope's `genesisConfigId` to the RPC wire field
1497
+ // `configId` (TIP-1061 spec name).
1498
+ configId: multisig.genesisConfigId,
1397
1499
  // Owner approvals are raw serialized signatures (node `Vec<Bytes>`).
1398
1500
  signatures: multisig.signatures.map((signature) => serialize(signature)),
1399
1501
  ...(multisig.init ? { init: multisig.init } : {}),