ox 1.0.0-beta.13 → 1.0.0-beta.14

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.
@@ -57,6 +57,16 @@ const CallToRpc = z.object({
57
57
  value: z.optional(uintBigintNumberish()),
58
58
  })
59
59
 
60
+ const Capabilities = z.record(z.string(), z.unknown())
61
+
62
+ const MultisigInit = z.object({
63
+ salt: z_Hex.Hex,
64
+ threshold: z.number(),
65
+ owners: z.readonly(
66
+ z.array(z.object({ owner: z_Address.Address, weight: z.number() })),
67
+ ),
68
+ })
69
+
60
70
  /** RPC tempo transaction request schema. */
61
71
  export const Rpc = z.object({
62
72
  accessList: z.optional(z_AccessList.AccessList),
@@ -64,6 +74,7 @@ export const Rpc = z.object({
64
74
  blobVersionedHashes: z.optional(z.readonly(z.array(z_Hex.Hex))),
65
75
  blobs: z.optional(z.readonly(z.array(z_Hex.Hex))),
66
76
  calls: z.optional(z.readonly(z.array(CallRpc))),
77
+ capabilities: z.optional(Capabilities),
67
78
  chainId: z.optional(z_Hex.Hex),
68
79
  data: z.optional(z_Hex.Hex),
69
80
  feePayer: z.optional(z.boolean()),
@@ -75,10 +86,13 @@ export const Rpc = z.object({
75
86
  input: z.optional(z_Hex.Hex),
76
87
  keyAuthorization: z.optional(z_KeyAuthorization.Rpc),
77
88
  keyData: z.optional(z_Hex.Hex),
89
+ keyId: z.optional(z_Address.Address),
78
90
  keyType: z.optional(KeyType),
79
91
  maxFeePerBlobGas: z.optional(z_Hex.Hex),
80
92
  maxFeePerGas: z.optional(z_Hex.Hex),
81
93
  maxPriorityFeePerGas: z.optional(z_Hex.Hex),
94
+ multisigInit: z.optional(MultisigInit),
95
+ multisigSignatureCount: z.optional(z.number()),
82
96
  nonce: z.optional(z_Hex.Hex),
83
97
  nonceKey: z.optional(z_Hex.Hex),
84
98
  r: z.optional(z_Hex.Hex),
@@ -114,6 +128,7 @@ export const Domain = z.object({
114
128
  blobVersionedHashes: z.optional(z.readonly(z.array(z_Hex.Hex))),
115
129
  blobs: z.optional(z.readonly(z.array(z_Hex.Hex))),
116
130
  calls: z.optional(z.readonly(z.array(Call))),
131
+ capabilities: z.optional(Capabilities),
117
132
  chainId: z.optional(z.number()),
118
133
  data: z.optional(z_Hex.Hex),
119
134
  feePayer: z.optional(z.boolean()),
@@ -125,10 +140,13 @@ export const Domain = z.object({
125
140
  input: z.optional(z_Hex.Hex),
126
141
  keyAuthorization: z.optional(z_KeyAuthorization.Domain),
127
142
  keyData: z.optional(z_Hex.Hex),
143
+ keyId: z.optional(z_Address.Address),
128
144
  keyType: z.optional(KeyType),
129
145
  maxFeePerBlobGas: z.optional(z.bigint()),
130
146
  maxFeePerGas: z.optional(z.bigint()),
131
147
  maxPriorityFeePerGas: z.optional(z.bigint()),
148
+ multisigInit: z.optional(MultisigInit),
149
+ multisigSignatureCount: z.optional(z.number()),
132
150
  nonce: z.optional(z.bigint()),
133
151
  nonceKey: z.optional(z.union([z.bigint(), z.literal('random')])),
134
152
  r: z.optional(z_Hex.Hex),
@@ -150,6 +168,7 @@ export const DomainToRpc = z.object({
150
168
  blobVersionedHashes: z.optional(z.readonly(z.array(z_Hex.Hex))),
151
169
  blobs: z.optional(z.readonly(z.array(z_Hex.Hex))),
152
170
  calls: z.optional(z.readonly(z.array(CallToRpc))),
171
+ capabilities: z.optional(Capabilities),
153
172
  chainId: z.optional(uintNumberNumberish()),
154
173
  data: z.optional(z_Hex.Hex),
155
174
  feePayer: z.optional(z.boolean()),
@@ -161,10 +180,13 @@ export const DomainToRpc = z.object({
161
180
  input: z.optional(z_Hex.Hex),
162
181
  keyAuthorization: z.optional(z_KeyAuthorization.DomainToRpc),
163
182
  keyData: z.optional(z_Hex.Hex),
183
+ keyId: z.optional(z_Address.Address),
164
184
  keyType: z.optional(KeyType),
165
185
  maxFeePerBlobGas: z.optional(uintBigintNumberish()),
166
186
  maxFeePerGas: z.optional(uintBigintNumberish()),
167
187
  maxPriorityFeePerGas: z.optional(uintBigintNumberish()),
188
+ multisigInit: z.optional(MultisigInit),
189
+ multisigSignatureCount: z.optional(z.number()),
168
190
  nonce: z.optional(uintBigintNumberish()),
169
191
  nonceKey: z.optional(z.union([uintBigintNumberish(), z.literal('random')])),
170
192
  r: z.optional(z_Hex.Hex),
@@ -232,6 +254,17 @@ function fromRpc(
232
254
  z_KeyAuthorization.KeyAuthorization,
233
255
  request.keyAuthorization as never,
234
256
  ) as never
257
+ if (typeof request.capabilities !== 'undefined')
258
+ request_.capabilities = request.capabilities
259
+ if (typeof request.feePayer !== 'undefined')
260
+ request_.feePayer = request.feePayer
261
+ if (typeof request.keyData !== 'undefined') request_.keyData = request.keyData
262
+ if (typeof request.keyId !== 'undefined') request_.keyId = request.keyId
263
+ if (typeof request.keyType !== 'undefined') request_.keyType = request.keyType
264
+ if (typeof request.multisigInit !== 'undefined')
265
+ request_.multisigInit = request.multisigInit
266
+ if (typeof request.multisigSignatureCount !== 'undefined')
267
+ request_.multisigSignatureCount = request.multisigSignatureCount
235
268
  if (typeof request.validBefore !== 'undefined')
236
269
  request_.validBefore = core_Hex.toNumber(request.validBefore)
237
270
  if (typeof request.validAfter !== 'undefined')
@@ -251,6 +284,22 @@ function toRpc(
251
284
  authorizationList: undefined,
252
285
  } as never) as core_TransactionRequest.Rpc
253
286
 
287
+ const tempo =
288
+ typeof request.calls !== 'undefined' ||
289
+ typeof request.capabilities !== 'undefined' ||
290
+ typeof request.feePayer !== 'undefined' ||
291
+ typeof request.feeToken !== 'undefined' ||
292
+ typeof request.keyAuthorization !== 'undefined' ||
293
+ typeof request.keyData !== 'undefined' ||
294
+ typeof request.keyId !== 'undefined' ||
295
+ typeof request.keyType !== 'undefined' ||
296
+ typeof request.multisigInit !== 'undefined' ||
297
+ typeof request.multisigSignatureCount !== 'undefined' ||
298
+ typeof request.nonceKey !== 'undefined' ||
299
+ typeof request.validBefore !== 'undefined' ||
300
+ typeof request.validAfter !== 'undefined' ||
301
+ request.type === 'tempo'
302
+
254
303
  if (request.authorizationList)
255
304
  request_rpc.authorizationList = z.encode(
256
305
  z_AuthorizationTempo.ListSignedToRpc,
@@ -269,15 +318,22 @@ function toRpc(
269
318
  value: call.value ? encodeNumberish(call.value) : '0x',
270
319
  data: call.data ?? '0x',
271
320
  }))
272
- else if (request.to || request.data || request.value)
321
+ else if (request.to || request.data || request.value || tempo)
273
322
  request_rpc.calls = [
274
323
  {
275
- to: request.to ?? undefined,
324
+ to:
325
+ request.to ||
326
+ (tempo && (!request.data || request.data === '0x')
327
+ ? '0x0000000000000000000000000000000000000000'
328
+ : undefined),
276
329
  value: request.value ? encodeNumberish(request.value) : '0x',
277
330
  data: request.data ?? '0x',
278
331
  },
279
332
  ]
280
- if (typeof request.feeToken !== 'undefined')
333
+ if (
334
+ typeof request.feeToken !== 'undefined' &&
335
+ !(request.feePayer === true && !request.feePayerSignature)
336
+ )
281
337
  request_rpc.feeToken =
282
338
  typeof request.feeToken === 'bigint'
283
339
  ? z.encode(z_TokenId.address, request.feeToken)
@@ -287,6 +343,19 @@ function toRpc(
287
343
  z_KeyAuthorization.KeyAuthorizationToRpc,
288
344
  request.keyAuthorization as never,
289
345
  ) as never
346
+ if (typeof request.capabilities !== 'undefined')
347
+ request_rpc.capabilities = request.capabilities
348
+ if (typeof request.feePayer !== 'undefined')
349
+ request_rpc.feePayer = request.feePayer
350
+ if (typeof request.keyData !== 'undefined')
351
+ request_rpc.keyData = shimKeyData(request.keyData)
352
+ if (typeof request.keyId !== 'undefined') request_rpc.keyId = request.keyId
353
+ if (typeof request.keyType !== 'undefined')
354
+ request_rpc.keyType = request.keyType
355
+ if (typeof request.multisigInit !== 'undefined')
356
+ request_rpc.multisigInit = request.multisigInit
357
+ if (typeof request.multisigSignatureCount !== 'undefined')
358
+ request_rpc.multisigSignatureCount = request.multisigSignatureCount
290
359
  if (typeof request.validBefore !== 'undefined')
291
360
  request_rpc.validBefore = encodeNumberish(request.validBefore)
292
361
  if (typeof request.validAfter !== 'undefined')
@@ -300,16 +369,7 @@ function toRpc(
300
369
  })()
301
370
  if (nonceKey) request_rpc.nonceKey = nonceKey
302
371
 
303
- if (
304
- typeof request.calls !== 'undefined' ||
305
- typeof request.feePayer !== 'undefined' ||
306
- typeof request.feeToken !== 'undefined' ||
307
- typeof request.keyAuthorization !== 'undefined' ||
308
- typeof request.nonceKey !== 'undefined' ||
309
- typeof request.validBefore !== 'undefined' ||
310
- typeof request.validAfter !== 'undefined' ||
311
- request.type === 'tempo'
312
- ) {
372
+ if (tempo) {
313
373
  request_rpc.type = toRpcType.tempo
314
374
  delete request_rpc.data
315
375
  delete request_rpc.input
@@ -320,6 +380,16 @@ function toRpc(
320
380
  return request_rpc
321
381
  }
322
382
 
383
+ /**
384
+ * Shims key data longer than 4 bytes into a 2-byte big-endian length hint
385
+ * (the node's gas estimator only accepts 1, 2, or 4-byte key data).
386
+ */
387
+ function shimKeyData(data: core_Hex.Hex): core_Hex.Hex {
388
+ const byteLength = core_Hex.size(data)
389
+ if (byteLength <= 4) return data
390
+ return core_Hex.fromNumber(byteLength, { size: 2 })
391
+ }
392
+
323
393
  /** Decodes an RPC signature into a recovered signature. */
324
394
  function signatureFromRpc(signature: {
325
395
  r: core_Hex.Hex
@@ -44,6 +44,50 @@ describe('TransactionRequest', () => {
44
44
  )
45
45
  })
46
46
 
47
+ test('gas-model hints and capabilities round-trip', () => {
48
+ const hints = {
49
+ capabilities: { balanceDiffs: true },
50
+ keyData: '0x0578',
51
+ keyId: '0xcccccccccccccccccccccccccccccccccccccccc',
52
+ keyType: 'webAuthn',
53
+ multisigInit: {
54
+ salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
55
+ threshold: 2,
56
+ owners: [
57
+ {
58
+ owner: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
59
+ weight: 1,
60
+ },
61
+ {
62
+ owner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
63
+ weight: 1,
64
+ },
65
+ ],
66
+ },
67
+ multisigSignatureCount: 2,
68
+ } as const
69
+
70
+ const decoded = z.decode(z_TransactionRequest.TransactionRequest, {
71
+ ...rpc,
72
+ ...hints,
73
+ })
74
+ expect(decoded).toMatchObject(hints)
75
+
76
+ const encoded = z.encode(z_TransactionRequest.TransactionRequest, decoded)
77
+ expect(encoded).toMatchObject(hints)
78
+ expect(encoded).toEqual(core_TransactionRequest.toRpc(decoded))
79
+ })
80
+
81
+ test('feeToken is withheld until the fee payer signs (TIP-76)', () => {
82
+ const decoded = z.decode(z_TransactionRequest.TransactionRequest, rpc)
83
+ const pending = z.encode(z_TransactionRequest.TransactionRequest, {
84
+ ...decoded,
85
+ feePayer: true,
86
+ })
87
+ expect(pending.feeToken).toBeUndefined()
88
+ expect(pending.feePayer).toBe(true)
89
+ })
90
+
47
91
  test('rejects an invalid request', () => {
48
92
  expect(
49
93
  z.safeDecode(z_TransactionRequest.TransactionRequest, {