ox 1.0.0-beta.11 → 1.0.0-beta.13

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 (48) hide show
  1. package/dist/core/internal/rpcSchemas/eth.d.ts +16 -0
  2. package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
  3. package/dist/tempo/MultisigConfig.d.ts +37 -85
  4. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  5. package/dist/tempo/MultisigConfig.js +39 -83
  6. package/dist/tempo/MultisigConfig.js.map +1 -1
  7. package/dist/tempo/SignatureEnvelope.d.ts +32 -30
  8. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  9. package/dist/tempo/SignatureEnvelope.js +56 -51
  10. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  11. package/dist/tempo/index.d.ts +2 -2
  12. package/dist/tempo/index.js +2 -2
  13. package/dist/zod/RpcSchema.d.ts +1 -0
  14. package/dist/zod/RpcSchema.d.ts.map +1 -1
  15. package/dist/zod/internal/rpcSchemas/Eth.d.ts +1 -0
  16. package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
  17. package/dist/zod/internal/rpcSchemas/Eth.js +5 -0
  18. package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
  19. package/dist/zod/tempo/AuthorizationTempo.d.ts +0 -20
  20. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  21. package/dist/zod/tempo/KeyAuthorization.d.ts +0 -11
  22. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  23. package/dist/zod/tempo/RpcSchemaTempo.d.ts +0 -12
  24. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  25. package/dist/zod/tempo/SignatureEnvelope.d.ts +0 -6
  26. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  27. package/dist/zod/tempo/SignatureEnvelope.js +0 -2
  28. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  29. package/dist/zod/tempo/Transaction.d.ts +0 -24
  30. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  31. package/dist/zod/tempo/TransactionRequest.d.ts +0 -21
  32. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  33. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +0 -9
  34. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  35. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +0 -3
  36. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
  37. package/package.json +1 -1
  38. package/src/core/internal/rpcSchemas/eth.ts +16 -0
  39. package/src/tempo/MultisigConfig.test.ts +54 -37
  40. package/src/tempo/MultisigConfig.ts +59 -134
  41. package/src/tempo/SignatureEnvelope.test.ts +118 -17
  42. package/src/tempo/SignatureEnvelope.ts +77 -72
  43. package/src/tempo/e2e.test.ts +221 -1
  44. package/src/tempo/index.ts +2 -2
  45. package/src/version.ts +1 -1
  46. package/src/zod/internal/rpcSchemas/Eth.ts +6 -0
  47. package/src/zod/tempo/SignatureEnvelope.ts +0 -2
  48. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +0 -2
@@ -3028,7 +3028,7 @@ describe.skip('behavior: multisig (TIP-1061)', () => {
3028
3028
 
3029
3029
  const genesisConfig = MultisigConfig.from({
3030
3030
  // A fresh random salt yields a distinct account each run, exercising the
3031
- // salt-inclusive config-ID derivation against the node.
3031
+ // salt-inclusive account derivation against the node.
3032
3032
  salt: Hex.random(32),
3033
3033
  threshold,
3034
3034
  owners: ownerKeys.map((key) => ({
@@ -3204,4 +3204,224 @@ describe.skip('behavior: multisig (TIP-1061)', () => {
3204
3204
  }),
3205
3205
  ).rejects.toThrow()
3206
3206
  })
3207
+
3208
+ test('behavior: keychain access key (authorize via AccountKeychain + spend)', async () => {
3209
+ const { account, genesisConfig, ownerKeys } = setup({
3210
+ count: 2,
3211
+ threshold: 2,
3212
+ })
3213
+
3214
+ await fundAddress(client, { address: account })
3215
+
3216
+ // Fresh secp256k1 access key for the multisig account.
3217
+ const access = (() => {
3218
+ const privateKey = Secp256k1.randomPrivateKey()
3219
+ const address = Address.fromPublicKey(
3220
+ Secp256k1.getPublicKey({ privateKey }),
3221
+ )
3222
+ return { address, privateKey } as const
3223
+ })()
3224
+
3225
+ // Bootstrap the multisig (nonce 0).
3226
+ const bootstrap = TxEnvelopeTempo.from({
3227
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
3228
+ chainId,
3229
+ feeToken: '0x20c0000000000000000000000000000000000001',
3230
+ nonce: 0n,
3231
+ gas: 2_000_000n,
3232
+ maxFeePerGas: Value.fromGwei('20'),
3233
+ maxPriorityFeePerGas: Value.fromGwei('10'),
3234
+ })
3235
+
3236
+ const bootstrap_signed = TxEnvelopeTempo.serialize(bootstrap, {
3237
+ signature: SignatureEnvelope.from({
3238
+ genesisConfig,
3239
+ init: true,
3240
+ signatures: approve({
3241
+ genesisConfig,
3242
+ payload: TxEnvelopeTempo.getSignPayload(bootstrap),
3243
+ signers: ownerKeys,
3244
+ }),
3245
+ }),
3246
+ })
3247
+
3248
+ const bootstrap_receipt = (await client
3249
+ .request({
3250
+ method: 'eth_sendRawTransactionSync',
3251
+ params: [bootstrap_signed],
3252
+ })
3253
+ .then((tx) => TransactionReceipt.fromRpc(tx as any)))!
3254
+ expect(bootstrap_receipt.status).toBe('success')
3255
+
3256
+ // Authorize the access key (nonce 1): multisig transactions cannot carry
3257
+ // a TIP-1049 `keyAuthorization`, so the multisig account calls the
3258
+ // AccountKeychain precompile's `authorizeKey` directly.
3259
+ const authorizeKey = AbiFunction.from(
3260
+ 'function authorizeKey(address keyId, uint8 signatureType, (uint64 expiry, bool enforceLimits, (address token, uint256 amount, uint64 period)[] limits, bool allowAnyCalls, (address target, (bytes4 selector, address[] recipients)[] selectorRules)[] allowedCalls) config)',
3261
+ )
3262
+
3263
+ const authorize = TxEnvelopeTempo.from({
3264
+ calls: [
3265
+ {
3266
+ to: '0xaaaaaaaa00000000000000000000000000000000',
3267
+ data: AbiFunction.encodeData(authorizeKey, [
3268
+ access.address,
3269
+ 0, // secp256k1
3270
+ {
3271
+ expiry: 18446744073709551615n, // u64 max (never expires)
3272
+ enforceLimits: false,
3273
+ limits: [],
3274
+ allowAnyCalls: true,
3275
+ allowedCalls: [],
3276
+ },
3277
+ ]),
3278
+ },
3279
+ ],
3280
+ chainId,
3281
+ feeToken: '0x20c0000000000000000000000000000000000001',
3282
+ nonce: 1n,
3283
+ gas: 2_000_000n,
3284
+ maxFeePerGas: Value.fromGwei('20'),
3285
+ maxPriorityFeePerGas: Value.fromGwei('10'),
3286
+ })
3287
+
3288
+ const authorize_signed = TxEnvelopeTempo.serialize(authorize, {
3289
+ signature: SignatureEnvelope.from({
3290
+ genesisConfig,
3291
+ signatures: approve({
3292
+ genesisConfig,
3293
+ payload: TxEnvelopeTempo.getSignPayload(authorize),
3294
+ signers: ownerKeys,
3295
+ }),
3296
+ }),
3297
+ })
3298
+
3299
+ const authorize_receipt = (await client
3300
+ .request({
3301
+ method: 'eth_sendRawTransactionSync',
3302
+ params: [authorize_signed],
3303
+ })
3304
+ .then((tx) => TransactionReceipt.fromRpc(tx as any)))!
3305
+ expect(authorize_receipt.status).toBe('success')
3306
+ expect(authorize_receipt.from).toBe(account)
3307
+
3308
+ // Spend with the access key (nonce 2): outer keychain signature with the
3309
+ // multisig account as `userAddress` — no owner approvals involved.
3310
+ const nonce = await getTransactionCount(client, {
3311
+ address: account,
3312
+ blockTag: 'pending',
3313
+ })
3314
+
3315
+ const spend = TxEnvelopeTempo.from({
3316
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
3317
+ chainId,
3318
+ feeToken: '0x20c0000000000000000000000000000000000001',
3319
+ nonce: BigInt(nonce),
3320
+ gas: 2_000_000n,
3321
+ maxFeePerGas: Value.fromGwei('20'),
3322
+ maxPriorityFeePerGas: Value.fromGwei('10'),
3323
+ })
3324
+
3325
+ const spend_signature = Secp256k1.sign({
3326
+ payload: TxEnvelopeTempo.getSignPayload(spend, { from: account }),
3327
+ privateKey: access.privateKey,
3328
+ })
3329
+
3330
+ const spend_signed = TxEnvelopeTempo.serialize(spend, {
3331
+ signature: SignatureEnvelope.from({
3332
+ userAddress: account,
3333
+ inner: SignatureEnvelope.from(spend_signature),
3334
+ type: 'keychain',
3335
+ }),
3336
+ })
3337
+
3338
+ const spend_receipt = (await client
3339
+ .request({
3340
+ method: 'eth_sendRawTransactionSync',
3341
+ params: [spend_signed],
3342
+ })
3343
+ .then((tx) => TransactionReceipt.fromRpc(tx as any)))!
3344
+ expect(spend_receipt.status).toBe('success')
3345
+ expect(spend_receipt.from).toBe(account)
3346
+
3347
+ {
3348
+ const response = await client
3349
+ .request({
3350
+ method: 'eth_getTransactionByHash',
3351
+ params: [spend_receipt.transactionHash],
3352
+ })
3353
+ .then((tx) => Transaction.fromRpc(tx as any))
3354
+ if (!response) throw new Error()
3355
+ expect(response.from).toBe(account)
3356
+ expect(response.signature?.type).toBe('keychain')
3357
+ expect(
3358
+ (response.signature as SignatureEnvelope.Keychain | undefined)
3359
+ ?.userAddress,
3360
+ ).toBe(account)
3361
+ }
3362
+ })
3363
+
3364
+ test('behavior: rejects `keyAuthorization` on multisig transactions', async () => {
3365
+ const { account, genesisConfig, ownerKeys } = setup({
3366
+ count: 1,
3367
+ threshold: 1,
3368
+ })
3369
+
3370
+ await fundAddress(client, { address: account })
3371
+
3372
+ const access = (() => {
3373
+ const privateKey = Secp256k1.randomPrivateKey()
3374
+ const address = Address.fromPublicKey(
3375
+ Secp256k1.getPublicKey({ privateKey }),
3376
+ )
3377
+ return { address, privateKey } as const
3378
+ })()
3379
+
3380
+ // TIP-1049 key authorizations are only valid on primitive/keychain
3381
+ // transactions; multisig accounts must call the AccountKeychain
3382
+ // precompile instead.
3383
+ const keyAuth = KeyAuthorization.from({
3384
+ address: access.address,
3385
+ chainId: BigInt(chainId),
3386
+ type: 'secp256k1',
3387
+ })
3388
+ const keyAuth_signed = KeyAuthorization.from(keyAuth, {
3389
+ signature: SignatureEnvelope.from(
3390
+ Secp256k1.sign({
3391
+ payload: KeyAuthorization.getSignPayload(keyAuth),
3392
+ privateKey: ownerKeys[0]!.privateKey,
3393
+ }),
3394
+ ),
3395
+ })
3396
+
3397
+ const bootstrap = TxEnvelopeTempo.from({
3398
+ calls: [{ to: '0x0000000000000000000000000000000000000000' }],
3399
+ chainId,
3400
+ feeToken: '0x20c0000000000000000000000000000000000001',
3401
+ keyAuthorization: keyAuth_signed,
3402
+ nonce: 0n,
3403
+ gas: 2_000_000n,
3404
+ maxFeePerGas: Value.fromGwei('20'),
3405
+ maxPriorityFeePerGas: Value.fromGwei('10'),
3406
+ })
3407
+
3408
+ const serialized_signed = TxEnvelopeTempo.serialize(bootstrap, {
3409
+ signature: SignatureEnvelope.from({
3410
+ genesisConfig,
3411
+ init: true,
3412
+ signatures: approve({
3413
+ genesisConfig,
3414
+ payload: TxEnvelopeTempo.getSignPayload(bootstrap),
3415
+ signers: ownerKeys,
3416
+ }),
3417
+ }),
3418
+ })
3419
+
3420
+ await expect(
3421
+ client.request({
3422
+ method: 'eth_sendRawTransactionSync',
3423
+ params: [serialized_signed],
3424
+ }),
3425
+ ).rejects.toThrow()
3426
+ })
3207
3427
  })
@@ -122,8 +122,8 @@ export * as KeyAuthorization from './KeyAuthorization.js'
122
122
  /**
123
123
  * Native multisig account utilities (TIP-1061).
124
124
  *
125
- * Derives stable multisig account addresses and permanent config IDs from a weighted
126
- * owner configuration, and computes the owner approval digest that owners sign.
125
+ * Derives stable multisig account addresses from a weighted owner
126
+ * configuration, and computes the owner approval digest that owners sign.
127
127
  *
128
128
  * [TIP-1061](https://tips.sh/1061)
129
129
  *
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** @internal */
2
- export const version = '1.0.0-beta.11'
2
+ export const version = '1.0.0-beta.13'
@@ -181,6 +181,12 @@ export const eth_getProof = from({
181
181
  returns: z_AccountProof.AccountProof,
182
182
  })
183
183
 
184
+ export const eth_getRawTransactionByHash = from({
185
+ method: 'eth_getRawTransactionByHash',
186
+ params: z.tuple([z_Hex.Hex]),
187
+ returns: z.nullable(z_Hex.Hex),
188
+ })
189
+
184
190
  export const eth_getStorageAt = from({
185
191
  method: 'eth_getStorageAt',
186
192
  params: z.tuple([z_Address.Address, z_Hex.Hex, BlockNumberOrTagOrIdentifier]),
@@ -83,7 +83,6 @@ export const KeychainRpc = z.object({
83
83
  /** RPC native multisig signature envelope schema. */
84
84
  export const MultisigRpc = z.object({
85
85
  account: z_Address.Address,
86
- configId: z_Hex.Hex,
87
86
  init: z.optional(z_MultisigConfig.Config),
88
87
  // Owner approvals are raw serialized signatures (node `Vec<Bytes>`).
89
88
  signatures: z.readonly(z.array(z_Hex.Hex)),
@@ -140,7 +139,6 @@ export const Keychain = z.object({
140
139
  /** Native multisig signature envelope schema. */
141
140
  export const Multisig = z.object({
142
141
  account: z_Address.Address,
143
- genesisConfigId: z_Hex.Hex,
144
142
  init: z.optional(z_MultisigConfig.Config),
145
143
  // `signatures` is recursive; type the getter concretely to break the cycle.
146
144
  signatures: z.lazy(
@@ -21,8 +21,6 @@ const p256 = {
21
21
 
22
22
  const multisig = {
23
23
  account: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
24
- configId:
25
- '0x0000000000000000000000000000000000000000000000000000000000000000',
26
24
  signatures: [
27
25
  core_SignatureEnvelope.serialize(core_SignatureEnvelope.fromRpc(secp256k1)),
28
26
  ],