ox 1.0.2 → 1.0.4

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 (64) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/tempo/EarnShares.d.ts +179 -0
  3. package/dist/tempo/EarnShares.d.ts.map +1 -0
  4. package/dist/tempo/EarnShares.js +160 -0
  5. package/dist/tempo/EarnShares.js.map +1 -0
  6. package/dist/tempo/KeyAuthorization.d.ts +22 -16
  7. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  8. package/dist/tempo/KeyAuthorization.js +31 -7
  9. package/dist/tempo/KeyAuthorization.js.map +1 -1
  10. package/dist/tempo/MultisigConfig.d.ts +8 -4
  11. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  12. package/dist/tempo/MultisigConfig.js +16 -4
  13. package/dist/tempo/MultisigConfig.js.map +1 -1
  14. package/dist/tempo/SignatureEnvelope.d.ts +31 -13
  15. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  16. package/dist/tempo/SignatureEnvelope.js +172 -55
  17. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  18. package/dist/tempo/index.d.ts +22 -0
  19. package/dist/tempo/index.d.ts.map +1 -1
  20. package/dist/tempo/index.js +22 -0
  21. package/dist/tempo/index.js.map +1 -1
  22. package/dist/zod/tempo/AuthorizationTempo.d.ts +70 -60
  23. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  24. package/dist/zod/tempo/KeyAuthorization.d.ts +0 -306
  25. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  26. package/dist/zod/tempo/KeyAuthorization.js +2 -2
  27. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  28. package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
  29. package/dist/zod/tempo/MultisigConfig.js +5 -2
  30. package/dist/zod/tempo/MultisigConfig.js.map +1 -1
  31. package/dist/zod/tempo/RpcSchemaTempo.d.ts +28 -132
  32. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  33. package/dist/zod/tempo/SignatureEnvelope.d.ts +79 -18
  34. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  35. package/dist/zod/tempo/SignatureEnvelope.js +39 -11
  36. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  37. package/dist/zod/tempo/Transaction.d.ts +84 -396
  38. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  39. package/dist/zod/tempo/TransactionRequest.d.ts +42 -234
  40. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  41. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +0 -108
  42. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  43. package/package.json +6 -1
  44. package/src/tempo/EarnShares.test.ts +120 -0
  45. package/src/tempo/EarnShares.ts +235 -0
  46. package/src/tempo/KeyAuthorization.test-d.ts +62 -0
  47. package/src/tempo/KeyAuthorization.test.ts +53 -0
  48. package/src/tempo/KeyAuthorization.ts +69 -27
  49. package/src/tempo/MultisigConfig.test.ts +34 -2
  50. package/src/tempo/MultisigConfig.ts +18 -4
  51. package/src/tempo/SignatureEnvelope.test-d.ts +97 -0
  52. package/src/tempo/SignatureEnvelope.test.ts +247 -36
  53. package/src/tempo/SignatureEnvelope.ts +271 -85
  54. package/src/tempo/e2e.test.ts +0 -416
  55. package/src/tempo/index.ts +22 -0
  56. package/src/tempo/multisig.e2e.test.ts +582 -0
  57. package/src/version.ts +1 -1
  58. package/src/zod/tempo/KeyAuthorization.ts +2 -2
  59. package/src/zod/tempo/MultisigConfig.ts +13 -5
  60. package/src/zod/tempo/SignatureEnvelope.ts +81 -18
  61. package/src/zod/tempo/_test/KeyAuthorization.test.ts +25 -0
  62. package/src/zod/tempo/_test/MultisigConfig.test.ts +44 -0
  63. package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +9 -0
  64. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +142 -6
@@ -2914,18 +2914,45 @@ describe('multisig', () => {
2914
2914
  expect(SignatureEnvelope.deserialize(serialized)).toEqual(envelope)
2915
2915
  })
2916
2916
 
2917
- test('deserialize: rejects the empty `init` placeholder (`0x80`)', () => {
2918
- const withPlaceholder = Hex.concat(
2917
+ test('deserialize: rejects the legacy three-field wire shape', () => {
2918
+ const init = MultisigConfig.toTuple({
2919
+ threshold: 1,
2920
+ owners: [
2921
+ { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
2922
+ ],
2923
+ })
2924
+ const legacy = Hex.concat(
2919
2925
  '0x05',
2920
- Rlp.fromHex([account, [SignatureEnvelope.serialize(innerP256)], '0x']),
2926
+ Rlp.fromHex([account, [SignatureEnvelope.serialize(innerP256)], init]),
2921
2927
  )
2922
- expect(() => SignatureEnvelope.deserialize(withPlaceholder)).toThrowError(
2928
+ expect(() => SignatureEnvelope.deserialize(legacy)).toThrowError(
2929
+ SignatureEnvelope.InvalidSerializedError,
2930
+ )
2931
+ })
2932
+
2933
+ test('deserialize: rejects an invalid initialized account', () => {
2934
+ const malformed = Hex.concat(
2935
+ '0x05',
2936
+ Rlp.fromHex(['0x11', [SignatureEnvelope.serialize(innerP256)]]),
2937
+ )
2938
+ expect(() => SignatureEnvelope.deserialize(malformed)).toThrowError(
2923
2939
  SignatureEnvelope.InvalidSerializedError,
2924
2940
  )
2925
2941
  })
2926
2942
 
2927
2943
  test('getType', () => {
2928
2944
  expect(SignatureEnvelope.getType(envelope)).toBe('multisig')
2945
+ expect(
2946
+ SignatureEnvelope.getType({
2947
+ init: {
2948
+ threshold: 1,
2949
+ owners: [
2950
+ { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
2951
+ ],
2952
+ },
2953
+ signatures: [innerP256],
2954
+ } as never),
2955
+ ).toBe('multisig')
2929
2956
  })
2930
2957
 
2931
2958
  test('extractAddress returns the multisig account', () => {
@@ -2938,9 +2965,19 @@ describe('multisig', () => {
2938
2965
  })
2939
2966
 
2940
2967
  test('toRpc/fromRpc round-trip', () => {
2941
- const rpc = SignatureEnvelope.toRpc(envelope)
2942
- expect(rpc.type).toBe('multisig')
2968
+ const rpc = SignatureEnvelope.toRpc(
2969
+ envelope,
2970
+ ) as SignatureEnvelope.MultisigRpc
2971
+ expect(rpc).toMatchObject({
2972
+ account,
2973
+ signatures: [{ type: 'secp256k1' }, { type: 'p256' }],
2974
+ })
2975
+ expect(rpc.init).toBeUndefined()
2976
+ expect('type' in rpc).toBe(false)
2943
2977
  expect(SignatureEnvelope.fromRpc(rpc)).toEqual(envelope)
2978
+ expect(
2979
+ SignatureEnvelope.fromRpc({ ...rpc, type: 'multisig' } as never),
2980
+ ).toEqual(envelope)
2944
2981
  })
2945
2982
 
2946
2983
  test('assert: missing properties', () => {
@@ -2949,6 +2986,63 @@ describe('multisig', () => {
2949
2986
  ).toThrowError()
2950
2987
  })
2951
2988
 
2989
+ test('assert: rejects empty owner approvals', () => {
2990
+ expect(() =>
2991
+ SignatureEnvelope.assert({
2992
+ type: 'multisig',
2993
+ account,
2994
+ signatures: [],
2995
+ }),
2996
+ ).toThrowErrorMatchingInlineSnapshot(
2997
+ `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures cannot be empty.]`,
2998
+ )
2999
+ })
3000
+
3001
+ test('assert: accepts at most eight owner approvals', () => {
3002
+ expect(() =>
3003
+ SignatureEnvelope.assert({
3004
+ type: 'multisig',
3005
+ account,
3006
+ signatures: Array.from(
3007
+ { length: MultisigConfig.maxSignatures },
3008
+ () => innerP256,
3009
+ ),
3010
+ }),
3011
+ ).not.toThrow()
3012
+
3013
+ expect(() =>
3014
+ SignatureEnvelope.assert({
3015
+ type: 'multisig',
3016
+ account,
3017
+ signatures: Array.from(
3018
+ { length: MultisigConfig.maxSignatures + 1 },
3019
+ () => innerP256,
3020
+ ),
3021
+ }),
3022
+ ).toThrowErrorMatchingInlineSnapshot(
3023
+ `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures exceed 8.]`,
3024
+ )
3025
+ })
3026
+
3027
+ test('assert: rejects oversized owner approvals', () => {
3028
+ const oversized = {
3029
+ ...signature_webauthn,
3030
+ metadata: {
3031
+ ...signature_webauthn.metadata,
3032
+ clientDataJSON: JSON.stringify({ value: 'x'.repeat(2048) }),
3033
+ },
3034
+ }
3035
+ expect(() =>
3036
+ SignatureEnvelope.assert({
3037
+ type: 'multisig',
3038
+ account,
3039
+ signatures: [oversized],
3040
+ }),
3041
+ ).toThrowErrorMatchingInlineSnapshot(
3042
+ `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig owner signature exceeds 2049 bytes.]`,
3043
+ )
3044
+ })
3045
+
2952
3046
  test('assert: rejects keychain owner approvals', () => {
2953
3047
  expect(() =>
2954
3048
  SignatureEnvelope.assert({
@@ -2980,42 +3074,44 @@ describe('multisig', () => {
2980
3074
  })
2981
3075
 
2982
3076
  test('assert: accepts the maximum nesting depth', () => {
2983
- const depth3 = SignatureEnvelope.from({
3077
+ const depth2 = SignatureEnvelope.from({
2984
3078
  type: 'multisig',
2985
3079
  account,
2986
- signatures: [
2987
- SignatureEnvelope.from({
2988
- type: 'multisig',
2989
- account: nestedAccount,
2990
- signatures: [nested],
2991
- }),
2992
- ],
3080
+ signatures: [nested],
2993
3081
  })
2994
- expect(() => SignatureEnvelope.assert(depth3)).not.toThrowError()
3082
+ expect(() => SignatureEnvelope.assert(depth2)).not.toThrowError()
2995
3083
  })
2996
3084
 
2997
3085
  test('assert: rejects nesting deeper than the maximum', () => {
2998
- const depth4 = SignatureEnvelope.from({
3086
+ const depth3 = SignatureEnvelope.from({
2999
3087
  type: 'multisig',
3000
3088
  account,
3001
3089
  signatures: [
3002
3090
  SignatureEnvelope.from({
3003
3091
  type: 'multisig',
3004
3092
  account: nestedAccount,
3005
- signatures: [
3006
- SignatureEnvelope.from({
3007
- type: 'multisig',
3008
- account: nestedAccount,
3009
- signatures: [nested],
3010
- }),
3011
- ],
3093
+ signatures: [nested],
3012
3094
  }),
3013
3095
  ],
3014
3096
  })
3015
3097
  expect(() =>
3016
- SignatureEnvelope.assert(depth4),
3098
+ SignatureEnvelope.assert(depth3),
3017
3099
  ).toThrowErrorMatchingInlineSnapshot(
3018
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig nesting depth exceeds 3.]`,
3100
+ `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig nesting depth exceeds 2.]`,
3101
+ )
3102
+ })
3103
+
3104
+ test('deserialize: rejects nesting deeper than the maximum during decode', () => {
3105
+ const primitive = SignatureEnvelope.serialize(innerP256)
3106
+ const depth1 = Hex.concat(
3107
+ '0x05',
3108
+ Rlp.fromHex([nestedAccount, [primitive]]),
3109
+ )
3110
+ const depth2 = Hex.concat('0x05', Rlp.fromHex([nestedAccount, [depth1]]))
3111
+ const depth3 = Hex.concat('0x05', Rlp.fromHex([account, [depth2]]))
3112
+
3113
+ expect(() => SignatureEnvelope.deserialize(depth3)).toThrowError(
3114
+ SignatureEnvelope.InvalidSerializedError,
3019
3115
  )
3020
3116
  })
3021
3117
 
@@ -3051,10 +3147,11 @@ describe('multisig', () => {
3051
3147
  },
3052
3148
  ],
3053
3149
  }
3150
+ const bootstrapAccount = MultisigConfig.getAddress(init)
3054
3151
 
3055
3152
  const bootstrapEnvelope = SignatureEnvelope.from({
3056
3153
  type: 'multisig',
3057
- account,
3154
+ account: bootstrapAccount,
3058
3155
  signatures: [SignatureEnvelope.from(signature_secp256k1), innerP256],
3059
3156
  init,
3060
3157
  })
@@ -3066,12 +3163,68 @@ describe('multisig', () => {
3066
3163
  )
3067
3164
  })
3068
3165
 
3166
+ test('deserialize: validates bootstrap init semantics', () => {
3167
+ const invalid = Hex.concat(
3168
+ '0x05',
3169
+ Rlp.fromHex([
3170
+ [MultisigConfig.zeroSalt, '0x01', []],
3171
+ [SignatureEnvelope.serialize(innerP256)],
3172
+ ]),
3173
+ )
3174
+ expect(() => SignatureEnvelope.deserialize(invalid)).toThrowError(
3175
+ MultisigConfig.InvalidConfigError,
3176
+ )
3177
+ })
3178
+
3179
+ test('deserialize: rejects noncanonical bootstrap field widths', () => {
3180
+ const signatures = [SignatureEnvelope.serialize(innerP256)]
3181
+ const shortSalt = Hex.concat(
3182
+ '0x05',
3183
+ Rlp.fromHex([
3184
+ [`0x${'00'.repeat(31)}`, '0x01', [[init.owners[0]!.owner, '0x01']]],
3185
+ signatures,
3186
+ ]),
3187
+ )
3188
+ const wideThreshold = Hex.concat(
3189
+ '0x05',
3190
+ Rlp.fromHex([
3191
+ [init.salt, '0x0001', [[init.owners[0]!.owner, '0x01']]],
3192
+ signatures,
3193
+ ]),
3194
+ )
3195
+ const wideWeight = Hex.concat(
3196
+ '0x05',
3197
+ Rlp.fromHex([
3198
+ [init.salt, '0x01', [[init.owners[0]!.owner, '0x0001']]],
3199
+ signatures,
3200
+ ]),
3201
+ )
3202
+
3203
+ for (const serialized of [shortSalt, wideThreshold, wideWeight])
3204
+ expect(() => SignatureEnvelope.deserialize(serialized)).toThrowError(
3205
+ SignatureEnvelope.InvalidSerializedError,
3206
+ )
3207
+ })
3208
+
3209
+ test('serialize: wire shape is `0x05 || rlp([init, signatures])`', () => {
3210
+ const serialized = SignatureEnvelope.serialize(bootstrapEnvelope)
3211
+ const [address] = Rlp.toHex(Hex.slice(serialized, 1)) as readonly [
3212
+ MultisigConfig.Tuple,
3213
+ readonly Hex.Hex[],
3214
+ ]
3215
+ expect(MultisigConfig.fromTuple(address)).toEqual(init)
3216
+ })
3217
+
3069
3218
  test('serialize/deserialize round-trip preserves non-zero salt', () => {
3219
+ const saltedInit = {
3220
+ ...init,
3221
+ salt: `0x${'42'.repeat(32)}` as const,
3222
+ }
3070
3223
  const salted = SignatureEnvelope.from({
3071
3224
  type: 'multisig',
3072
- account,
3225
+ account: MultisigConfig.getAddress(saltedInit),
3073
3226
  signatures: [SignatureEnvelope.from(signature_secp256k1), innerP256],
3074
- init: { ...init, salt: `0x${'42'.repeat(32)}` },
3227
+ init: saltedInit,
3075
3228
  })
3076
3229
  const serialized = SignatureEnvelope.serialize(salted)
3077
3230
  const deserialized = SignatureEnvelope.deserialize(
@@ -3094,27 +3247,72 @@ describe('multisig', () => {
3094
3247
  })
3095
3248
 
3096
3249
  test('toRpc/fromRpc round-trip with init', () => {
3097
- const rpc = SignatureEnvelope.toRpc(bootstrapEnvelope)
3250
+ const rpc = SignatureEnvelope.toRpc(
3251
+ bootstrapEnvelope,
3252
+ ) as SignatureEnvelope.MultisigRpc
3098
3253
  expect(rpc.init).toEqual(init)
3254
+ expect(rpc.account).toBeUndefined()
3255
+ expect('type' in rpc).toBe(false)
3099
3256
  expect(SignatureEnvelope.fromRpc(rpc)).toEqual(bootstrapEnvelope)
3100
3257
  })
3101
3258
 
3102
- test('toRpc encodes owner approvals as serialized hex (node `Vec<Bytes>`)', () => {
3259
+ test('toRpc normalizes numberish init fields and the default salt', () => {
3260
+ const rpc = SignatureEnvelope.toRpc({
3261
+ ...bootstrapEnvelope,
3262
+ init: {
3263
+ threshold: '0x1',
3264
+ owners: init.owners.map((owner) => ({
3265
+ ...owner,
3266
+ weight: '0x1' as const,
3267
+ })),
3268
+ },
3269
+ } as SignatureEnvelope.Multisig<
3270
+ number | Hex.Hex
3271
+ >) as SignatureEnvelope.MultisigRpc
3272
+ expect(rpc.init).toEqual({
3273
+ salt: MultisigConfig.zeroSalt,
3274
+ threshold: 1,
3275
+ owners: init.owners,
3276
+ })
3277
+ })
3278
+
3279
+ test('toRpc encodes owner approvals as structured RPC envelopes', () => {
3103
3280
  const multisig = bootstrapEnvelope as SignatureEnvelope.Multisig
3104
3281
  const rpc = SignatureEnvelope.toRpc(
3105
3282
  multisig,
3106
3283
  ) as SignatureEnvelope.MultisigRpc
3107
3284
  expect(rpc.signatures).toEqual(
3108
- multisig.signatures.map((s) => SignatureEnvelope.serialize(s)),
3285
+ multisig.signatures.map((signature) =>
3286
+ SignatureEnvelope.toRpc(signature),
3287
+ ),
3109
3288
  )
3110
3289
  })
3111
3290
 
3112
3291
  test('fromRpc detects multisig by shape (no `type` field)', () => {
3113
3292
  const rpc = SignatureEnvelope.toRpc(bootstrapEnvelope)
3114
- // The node omits the `type` discriminant; detection is shape-based.
3115
- const { type: _type, ...untyped } = rpc
3116
- expect(SignatureEnvelope.fromRpc(untyped as never)).toEqual(
3293
+ expect(SignatureEnvelope.fromRpc(rpc)).toEqual(bootstrapEnvelope)
3294
+ })
3295
+
3296
+ test('fromRpc accepts the opposite static property as undefined', () => {
3297
+ const rpc = SignatureEnvelope.toRpc(
3298
+ bootstrapEnvelope,
3299
+ ) as SignatureEnvelope.MultisigRpc
3300
+ expect(
3301
+ SignatureEnvelope.fromRpc({ ...rpc, account: undefined } as never),
3302
+ ).toEqual(bootstrapEnvelope)
3303
+ })
3304
+
3305
+ test('fromRpc rejects the legacy combined account and init shape', () => {
3306
+ const rpc = SignatureEnvelope.toRpc(
3117
3307
  bootstrapEnvelope,
3308
+ ) as SignatureEnvelope.MultisigRpc
3309
+ expect(() =>
3310
+ SignatureEnvelope.fromRpc({
3311
+ ...rpc,
3312
+ account: bootstrapAccount,
3313
+ } as never),
3314
+ ).toThrowErrorMatchingInlineSnapshot(
3315
+ `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: RPC multisig must contain exactly one of \`account\` or \`init\`.]`,
3118
3316
  )
3119
3317
  })
3120
3318
 
@@ -3122,11 +3320,24 @@ describe('multisig', () => {
3122
3320
  expect(() =>
3123
3321
  SignatureEnvelope.assert({
3124
3322
  type: 'multisig',
3125
- account,
3126
- signatures: [],
3323
+ account: bootstrapAccount,
3324
+ signatures: [innerP256],
3127
3325
  init: { threshold: 1, owners: [] },
3128
3326
  } as never),
3129
3327
  ).toThrowError()
3130
3328
  })
3329
+
3330
+ test('assert: init must derive the supplied account', () => {
3331
+ expect(() =>
3332
+ SignatureEnvelope.assert({
3333
+ type: 'multisig',
3334
+ account,
3335
+ signatures: [innerP256],
3336
+ init,
3337
+ }),
3338
+ ).toThrowErrorMatchingInlineSnapshot(
3339
+ `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig init does not derive account.]`,
3340
+ )
3341
+ })
3131
3342
  })
3132
3343
  })