ox 1.0.2 → 1.0.3

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 (53) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/tempo/KeyAuthorization.d.ts +22 -16
  3. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  4. package/dist/tempo/KeyAuthorization.js +31 -7
  5. package/dist/tempo/KeyAuthorization.js.map +1 -1
  6. package/dist/tempo/MultisigConfig.d.ts +8 -4
  7. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  8. package/dist/tempo/MultisigConfig.js +16 -4
  9. package/dist/tempo/MultisigConfig.js.map +1 -1
  10. package/dist/tempo/SignatureEnvelope.d.ts +31 -13
  11. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  12. package/dist/tempo/SignatureEnvelope.js +172 -55
  13. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  14. package/dist/zod/tempo/AuthorizationTempo.d.ts +70 -60
  15. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  16. package/dist/zod/tempo/KeyAuthorization.d.ts +0 -306
  17. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  18. package/dist/zod/tempo/KeyAuthorization.js +2 -2
  19. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  20. package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
  21. package/dist/zod/tempo/MultisigConfig.js +5 -2
  22. package/dist/zod/tempo/MultisigConfig.js.map +1 -1
  23. package/dist/zod/tempo/RpcSchemaTempo.d.ts +28 -132
  24. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  25. package/dist/zod/tempo/SignatureEnvelope.d.ts +79 -18
  26. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  27. package/dist/zod/tempo/SignatureEnvelope.js +39 -11
  28. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  29. package/dist/zod/tempo/Transaction.d.ts +84 -396
  30. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  31. package/dist/zod/tempo/TransactionRequest.d.ts +42 -234
  32. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  33. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +0 -108
  34. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  35. package/package.json +1 -1
  36. package/src/tempo/KeyAuthorization.test-d.ts +62 -0
  37. package/src/tempo/KeyAuthorization.test.ts +53 -0
  38. package/src/tempo/KeyAuthorization.ts +69 -27
  39. package/src/tempo/MultisigConfig.test.ts +34 -2
  40. package/src/tempo/MultisigConfig.ts +18 -4
  41. package/src/tempo/SignatureEnvelope.test-d.ts +97 -0
  42. package/src/tempo/SignatureEnvelope.test.ts +247 -36
  43. package/src/tempo/SignatureEnvelope.ts +271 -85
  44. package/src/tempo/e2e.test.ts +0 -416
  45. package/src/tempo/multisig.e2e.test.ts +582 -0
  46. package/src/version.ts +1 -1
  47. package/src/zod/tempo/KeyAuthorization.ts +2 -2
  48. package/src/zod/tempo/MultisigConfig.ts +13 -5
  49. package/src/zod/tempo/SignatureEnvelope.ts +81 -18
  50. package/src/zod/tempo/_test/KeyAuthorization.test.ts +25 -0
  51. package/src/zod/tempo/_test/MultisigConfig.test.ts +44 -0
  52. package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +9 -0
  53. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +142 -6
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable jsdoc-js/require-jsdoc, jsdoc-js/require-description, jsdoc-js/require-example */
2
2
  import * as core_SignatureEnvelope from '../../tempo/SignatureEnvelope.js'
3
3
  import * as core_Hex from '../../core/Hex.js'
4
+ import * as core_MultisigConfig from '../../tempo/MultisigConfig.js'
4
5
  import * as z_Address from '../Address.js'
5
6
  import * as z_Hex from '../Hex.js'
6
7
  import * as z_MultisigConfig from './MultisigConfig.js'
@@ -67,6 +68,9 @@ export const WebAuthnRpc = z.object({
67
68
  webauthnData: z_Hex.Hex,
68
69
  })
69
70
 
71
+ /** RPC primitive signature envelope schema. */
72
+ export const PrimitiveRpc = z.union([Secp256k1Rpc, P256Rpc, WebAuthnRpc])
73
+
70
74
  /** RPC keychain signature envelope schema. */
71
75
  export const KeychainRpc = z.object({
72
76
  keyId: z.optional(z_Address.Address),
@@ -81,13 +85,54 @@ export const KeychainRpc = z.object({
81
85
  })
82
86
 
83
87
  /** RPC native multisig signature envelope schema. */
84
- export const MultisigRpc = z.object({
85
- account: z_Address.Address,
86
- init: z.optional(z_MultisigConfig.Config),
87
- // Owner approvals are raw serialized signatures (node `Vec<Bytes>`).
88
- signatures: z.readonly(z.array(z_Hex.Hex)),
89
- type: z.literal('multisig'),
90
- })
88
+ export const MultisigRpc = z
89
+ .union([
90
+ z.strictObject({
91
+ account: z_Address.Address,
92
+ signatures: z.lazy(
93
+ (): z.ZodMiniType<
94
+ readonly core_SignatureEnvelope.SignatureEnvelopeRpc[],
95
+ readonly core_SignatureEnvelope.SignatureEnvelopeRpc[]
96
+ > =>
97
+ z.readonly(
98
+ z
99
+ .array(Rpc)
100
+ .check(
101
+ z.minLength(1),
102
+ z.maxLength(core_MultisigConfig.maxSignatures),
103
+ ),
104
+ ) as never,
105
+ ),
106
+ }),
107
+ z.strictObject({
108
+ init: z_MultisigConfig.Config,
109
+ signatures: z.lazy(
110
+ (): z.ZodMiniType<
111
+ readonly core_SignatureEnvelope.SignatureEnvelopeRpc[],
112
+ readonly core_SignatureEnvelope.SignatureEnvelopeRpc[]
113
+ > =>
114
+ z.readonly(
115
+ z
116
+ .array(Rpc)
117
+ .check(
118
+ z.minLength(1),
119
+ z.maxLength(core_MultisigConfig.maxSignatures),
120
+ ),
121
+ ) as never,
122
+ ),
123
+ }),
124
+ ])
125
+ // Keep invalid recursive approvals inside Zod's issue path.
126
+ .check(
127
+ z.refine((value) => {
128
+ try {
129
+ core_SignatureEnvelope.fromRpc(value)
130
+ return true
131
+ } catch {
132
+ return false
133
+ }
134
+ }, 'expected valid native multisig signature'),
135
+ )
91
136
 
92
137
  /** RPC signature envelope schema. */
93
138
  export const Rpc = z.union([
@@ -123,6 +168,9 @@ export const WebAuthn = z.object({
123
168
  type: z.literal('webAuthn'),
124
169
  })
125
170
 
171
+ /** Primitive signature envelope schema. */
172
+ export const Primitive = z.union([Secp256k1, P256, WebAuthn])
173
+
126
174
  /** Keychain signature envelope schema. */
127
175
  export const Keychain = z.object({
128
176
  // `inner` is recursive; type the getter concretely to break the cycle.
@@ -137,16 +185,31 @@ export const Keychain = z.object({
137
185
  })
138
186
 
139
187
  /** Native multisig signature envelope schema. */
140
- export const Multisig = z.object({
141
- account: z_Address.Address,
142
- init: z.optional(z_MultisigConfig.Config),
143
- // `signatures` is recursive; type the getter concretely to break the cycle.
144
- signatures: z.lazy(
145
- (): z.ZodMiniType<readonly core_SignatureEnvelope.SignatureEnvelope[]> =>
146
- z.readonly(z.array(Domain)) as never,
147
- ),
148
- type: z.literal('multisig'),
149
- })
188
+ export const Multisig = z
189
+ .object({
190
+ account: z_Address.Address,
191
+ init: z.optional(z_MultisigConfig.Config),
192
+ // `signatures` is recursive; type the getter concretely to break the cycle.
193
+ signatures: z.lazy(
194
+ (): z.ZodMiniType<readonly core_SignatureEnvelope.SignatureEnvelope[]> =>
195
+ z.readonly(
196
+ z
197
+ .array(Domain)
198
+ .check(
199
+ z.minLength(1),
200
+ z.maxLength(core_MultisigConfig.maxSignatures),
201
+ ),
202
+ ) as never,
203
+ ),
204
+ type: z.literal('multisig'),
205
+ })
206
+ // Keep invalid recursive approvals inside Zod's issue path.
207
+ .check(
208
+ z.refine(
209
+ (value) => core_SignatureEnvelope.validate(value),
210
+ 'expected valid native multisig signature',
211
+ ),
212
+ )
150
213
 
151
214
  /** Decoded signature envelope schema. */
152
215
  export const Domain = z.union([Secp256k1, P256, WebAuthn, Keychain, Multisig])
@@ -210,7 +273,7 @@ function fromRpc(
210
273
  }
211
274
  }
212
275
 
213
- if (value.type === 'multisig')
276
+ if ('signatures' in value && ('account' in value || 'init' in value))
214
277
  return core_SignatureEnvelope.fromRpc(
215
278
  value as core_SignatureEnvelope.MultisigRpc,
216
279
  )
@@ -140,4 +140,29 @@ describe('KeyAuthorization', () => {
140
140
  } as never).success,
141
141
  ).toBe(false)
142
142
  })
143
+
144
+ test('rejects multisig signatures', () => {
145
+ const multisigRpc = {
146
+ account: '0x1111111111111111111111111111111111111111',
147
+ signatures: [rpc.signature],
148
+ } as const
149
+ expect(
150
+ z.safeDecode(z_KeyAuthorization.KeyAuthorization, {
151
+ ...rpc,
152
+ signature: multisigRpc,
153
+ } as never).success,
154
+ ).toBe(false)
155
+
156
+ const authorization = core_KeyAuthorization.fromRpc(rpc)
157
+ expect(
158
+ z.safeEncode(z_KeyAuthorization.KeyAuthorization, {
159
+ ...authorization,
160
+ signature: {
161
+ account: multisigRpc.account,
162
+ signatures: [authorization.signature],
163
+ type: 'multisig',
164
+ },
165
+ } as never).success,
166
+ ).toBe(false)
167
+ })
143
168
  })
@@ -0,0 +1,44 @@
1
+ import { describe, expect, test } from 'vp/test'
2
+ import * as z_MultisigConfig from '../MultisigConfig.js'
3
+ import * as z from 'zod/mini'
4
+
5
+ const config = {
6
+ owners: [
7
+ {
8
+ owner: '0x1111111111111111111111111111111111111111',
9
+ weight: 1,
10
+ },
11
+ ],
12
+ threshold: 1,
13
+ } as const
14
+
15
+ describe('Config', () => {
16
+ test('accepts valid native multisig configurations', () => {
17
+ expect(z.safeDecode(z_MultisigConfig.Config, config).success).toBe(true)
18
+ })
19
+
20
+ test('rejects configurations outside protocol limits', () => {
21
+ expect(
22
+ z.safeDecode(z_MultisigConfig.Config, {
23
+ ...config,
24
+ owners: [{ ...config.owners[0], weight: 9 }],
25
+ threshold: 9,
26
+ }).success,
27
+ ).toMatchInlineSnapshot(`false`)
28
+ })
29
+
30
+ test('rejects non-canonical owner order', () => {
31
+ expect(
32
+ z.safeDecode(z_MultisigConfig.Config, {
33
+ ...config,
34
+ owners: [
35
+ {
36
+ owner: '0x2222222222222222222222222222222222222222',
37
+ weight: 1,
38
+ },
39
+ config.owners[0],
40
+ ],
41
+ }).success,
42
+ ).toMatchInlineSnapshot(`false`)
43
+ })
44
+ })
@@ -12,6 +12,15 @@ test('SignatureEnvelope decodes RPC into a signature envelope', () => {
12
12
  >()
13
13
  })
14
14
 
15
+ test('MultisigRpc preserves the native multisig RPC union', () => {
16
+ expectTypeOf<core_SignatureEnvelope.MultisigRpc>().toExtend<
17
+ z.input<typeof z_SignatureEnvelope.MultisigRpc>
18
+ >()
19
+ expectTypeOf<
20
+ z.output<typeof z_SignatureEnvelope.MultisigRpc>
21
+ >().toExtend<core_SignatureEnvelope.MultisigRpc>()
22
+ })
23
+
15
24
  test('Type preserves the key type union', () => {
16
25
  expectTypeOf<
17
26
  z.output<typeof z_SignatureEnvelope.Type>
@@ -21,10 +21,21 @@ const p256 = {
21
21
 
22
22
  const multisig = {
23
23
  account: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
24
- signatures: [
25
- core_SignatureEnvelope.serialize(core_SignatureEnvelope.fromRpc(secp256k1)),
26
- ],
27
- type: 'multisig',
24
+ signatures: [secp256k1],
25
+ } as const
26
+
27
+ const multisigBootstrap = {
28
+ init: {
29
+ owners: [
30
+ {
31
+ owner: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
32
+ weight: 1,
33
+ },
34
+ ],
35
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
36
+ threshold: 1,
37
+ },
38
+ signatures: [secp256k1],
28
39
  } as const
29
40
 
30
41
  describe('SignatureEnvelope', () => {
@@ -51,12 +62,33 @@ describe('SignatureEnvelope', () => {
51
62
  )
52
63
  })
53
64
 
54
- test('decodes a multisig envelope', () => {
65
+ test('decodes an initialized multisig envelope', () => {
55
66
  expect(z.decode(z_SignatureEnvelope.SignatureEnvelope, multisig)).toEqual(
56
67
  core_SignatureEnvelope.fromRpc(multisig),
57
68
  )
58
69
  })
59
70
 
71
+ test('decodes a bootstrap multisig envelope', () => {
72
+ expect(
73
+ z.decode(z_SignatureEnvelope.SignatureEnvelope, multisigBootstrap),
74
+ ).toEqual(core_SignatureEnvelope.fromRpc(multisigBootstrap))
75
+ })
76
+
77
+ test('decodes recursive multisig approvals', () => {
78
+ const nested = {
79
+ ...multisig,
80
+ signatures: [
81
+ {
82
+ account: '0x1111111111111111111111111111111111111111',
83
+ signatures: [secp256k1],
84
+ },
85
+ ],
86
+ } as const
87
+ expect(z.decode(z_SignatureEnvelope.SignatureEnvelope, nested)).toEqual(
88
+ core_SignatureEnvelope.fromRpc(nested),
89
+ )
90
+ })
91
+
60
92
  test('round-trips secp256k1 via encode', () => {
61
93
  const decoded = z.decode(z_SignatureEnvelope.SignatureEnvelope, secp256k1)
62
94
  expect(z.encode(z_SignatureEnvelope.SignatureEnvelope, decoded)).toEqual(
@@ -64,13 +96,117 @@ describe('SignatureEnvelope', () => {
64
96
  )
65
97
  })
66
98
 
67
- test('round-trips multisig via encode', () => {
99
+ test('round-trips initialized multisig via encode', () => {
68
100
  const decoded = z.decode(z_SignatureEnvelope.SignatureEnvelope, multisig)
69
101
  expect(z.encode(z_SignatureEnvelope.SignatureEnvelope, decoded)).toEqual(
70
102
  core_SignatureEnvelope.toRpc(decoded),
71
103
  )
72
104
  })
73
105
 
106
+ test('round-trips bootstrap multisig via encode', () => {
107
+ const decoded = z.decode(
108
+ z_SignatureEnvelope.SignatureEnvelope,
109
+ multisigBootstrap,
110
+ )
111
+ expect(z.encode(z_SignatureEnvelope.SignatureEnvelope, decoded)).toEqual(
112
+ multisigBootstrap,
113
+ )
114
+ })
115
+
116
+ test('rejects invalid recursive multisig domains before encode', () => {
117
+ const invalid = {
118
+ account: multisig.account,
119
+ signatures: [
120
+ {
121
+ inner: core_SignatureEnvelope.fromRpc(secp256k1),
122
+ type: 'keychain',
123
+ userAddress: multisig.account,
124
+ },
125
+ ],
126
+ type: 'multisig',
127
+ } as const
128
+
129
+ expect(
130
+ z.safeEncode(z_SignatureEnvelope.SignatureEnvelope, invalid as never)
131
+ .success,
132
+ ).toMatchInlineSnapshot(`false`)
133
+ })
134
+
135
+ test('rejects legacy multisig approval encoding', () => {
136
+ expect(
137
+ z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {
138
+ ...multisig,
139
+ signatures: [
140
+ core_SignatureEnvelope.serialize(
141
+ core_SignatureEnvelope.fromRpc(secp256k1),
142
+ ),
143
+ ],
144
+ } as never).success,
145
+ ).toMatchInlineSnapshot(`false`)
146
+ })
147
+
148
+ test('rejects multisig approval counts outside protocol limits', () => {
149
+ expect(
150
+ z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {
151
+ ...multisig,
152
+ signatures: [],
153
+ }).success,
154
+ ).toMatchInlineSnapshot(`false`)
155
+ expect(
156
+ z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {
157
+ ...multisig,
158
+ signatures: Array.from({ length: 9 }, () => secp256k1),
159
+ }).success,
160
+ ).toMatchInlineSnapshot(`false`)
161
+ })
162
+
163
+ test('rejects invalid recursive multisig approvals', () => {
164
+ const keychain = {
165
+ signature: secp256k1,
166
+ type: 'keychain',
167
+ userAddress: multisig.account,
168
+ } as const
169
+ const depth2 = {
170
+ account: '0x1111111111111111111111111111111111111111',
171
+ signatures: [secp256k1],
172
+ } as const
173
+ const depth3 = {
174
+ account: '0x2222222222222222222222222222222222222222',
175
+ signatures: [depth2],
176
+ } as const
177
+
178
+ expect(
179
+ z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {
180
+ ...multisig,
181
+ signatures: [keychain],
182
+ }).success,
183
+ ).toMatchInlineSnapshot(`false`)
184
+ expect(
185
+ z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {
186
+ ...multisig,
187
+ signatures: [depth3],
188
+ }).success,
189
+ ).toMatchInlineSnapshot(`false`)
190
+ })
191
+
192
+ test('rejects mixed multisig initialization fields', () => {
193
+ expect(
194
+ z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {
195
+ ...multisigBootstrap,
196
+ account: multisig.account,
197
+ } as never).success,
198
+ ).toMatchInlineSnapshot(`false`)
199
+ })
200
+
201
+ test('rejects tagged multisig RPC envelopes', () => {
202
+ expect(
203
+ z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {
204
+ ...multisig,
205
+ type: 'multisig',
206
+ } as never).success,
207
+ ).toMatchInlineSnapshot(`false`)
208
+ })
209
+
74
210
  test('rejects an invalid envelope', () => {
75
211
  expect(
76
212
  z.safeDecode(z_SignatureEnvelope.SignatureEnvelope, {