ox 0.8.4 → 0.8.6

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.
@@ -1,9 +1,11 @@
1
1
  import * as AbiParameters from '../core/AbiParameters.js'
2
2
  import type * as Address from '../core/Address.js'
3
+ import type * as Authorization from '../core/Authorization.js'
3
4
  import type * as Errors from '../core/Errors.js'
4
5
  import * as Hash from '../core/Hash.js'
5
6
  import * as Hex from '../core/Hex.js'
6
7
  import * as Signature from '../core/Signature.js'
8
+ import * as TypedData from '../core/TypedData.js'
7
9
  import type { Assign, Compute, OneOf } from '../core/internal/types.js'
8
10
  import type * as EntryPoint from './EntryPoint.js'
9
11
 
@@ -12,9 +14,13 @@ export type UserOperation<
12
14
  entryPointVersion extends EntryPoint.Version = EntryPoint.Version,
13
15
  signed extends boolean = boolean,
14
16
  bigintType = bigint,
17
+ numberType = number,
15
18
  > = OneOf<
16
19
  | (entryPointVersion extends '0.6' ? V06<signed, bigintType> : never)
17
20
  | (entryPointVersion extends '0.7' ? V07<signed, bigintType> : never)
21
+ | (entryPointVersion extends '0.8'
22
+ ? V08<signed, bigintType, numberType>
23
+ : never)
18
24
  >
19
25
 
20
26
  /**
@@ -50,6 +56,7 @@ export type Rpc<
50
56
  > = OneOf<
51
57
  | (entryPointVersion extends '0.6' ? V06<signed, Hex.Hex> : never)
52
58
  | (entryPointVersion extends '0.7' ? V07<signed, Hex.Hex> : never)
59
+ | (entryPointVersion extends '0.8' ? V08<signed, Hex.Hex, Hex.Hex> : never)
53
60
  >
54
61
 
55
62
  /** Transaction Info. */
@@ -135,6 +142,53 @@ export type V07<signed extends boolean = boolean, bigintType = bigint> = {
135
142
  /** RPC User Operation on EntryPoint 0.7 */
136
143
  export type RpcV07<signed extends boolean = true> = V07<signed, Hex.Hex>
137
144
 
145
+ /** Type for User Operation on EntryPoint 0.8 */
146
+ export type V08<
147
+ signed extends boolean = boolean,
148
+ bigintType = bigint,
149
+ numberType = number,
150
+ > = {
151
+ /** Authorization data. */
152
+ authorization?: Authorization.Signed<bigintType, numberType> | undefined
153
+ /** The data to pass to the `sender` during the main execution call. */
154
+ callData: Hex.Hex
155
+ /** The amount of gas to allocate the main execution call */
156
+ callGasLimit: bigintType
157
+ /** Account factory. Only for new accounts. */
158
+ factory?: Address.Address | undefined
159
+ /** Data for account factory. */
160
+ factoryData?: Hex.Hex | undefined
161
+ /** Maximum fee per gas. */
162
+ maxFeePerGas: bigintType
163
+ /** Maximum priority fee per gas. */
164
+ maxPriorityFeePerGas: bigintType
165
+ /** Anti-replay parameter. */
166
+ nonce: bigintType
167
+ /** Address of paymaster contract. */
168
+ paymaster?: Address.Address | undefined
169
+ /** Data for paymaster. */
170
+ paymasterData?: Hex.Hex | undefined
171
+ /** The amount of gas to allocate for the paymaster post-operation code. */
172
+ paymasterPostOpGasLimit?: bigintType | undefined
173
+ /** The amount of gas to allocate for the paymaster validation code. */
174
+ paymasterVerificationGasLimit?: bigintType | undefined
175
+ /** Extra gas to pay the Bundler. */
176
+ preVerificationGas: bigintType
177
+ /** The account making the operation. */
178
+ sender: Address.Address
179
+ /** Data passed into the account to verify authorization. */
180
+ signature?: Hex.Hex | undefined
181
+ /** The amount of gas to allocate for the verification step. */
182
+ verificationGasLimit: bigintType
183
+ } & (signed extends true ? { signature: Hex.Hex } : {})
184
+
185
+ /** RPC User Operation on EntryPoint 0.8 */
186
+ export type RpcV08<signed extends boolean = true> = V08<
187
+ signed,
188
+ Hex.Hex,
189
+ Hex.Hex
190
+ >
191
+
138
192
  /**
139
193
  * Instantiates a {@link ox#UserOperation.UserOperation} from a provided input.
140
194
  *
@@ -156,6 +210,27 @@ export type RpcV07<signed extends boolean = true> = V07<signed, Hex.Hex>
156
210
  * ```
157
211
  *
158
212
  * @example
213
+ * ### From Packed User Operation
214
+ *
215
+ * ```ts twoslash
216
+ * import { UserOperation } from 'ox/erc4337'
217
+ *
218
+ * const packed: UserOperation.Packed = {
219
+ * accountGasLimits: '0x...',
220
+ * callData: '0xdeadbeef',
221
+ * initCode: '0x',
222
+ * gasFees: '0x...',
223
+ * nonce: 69n,
224
+ * paymasterAndData: '0x',
225
+ * preVerificationGas: 100_000n,
226
+ * sender: '0x9f1fdab6458c5fc642fa0f4c5af7473c46837357',
227
+ * signature: '0x',
228
+ * }
229
+ *
230
+ * const userOperation = UserOperation.from(packed)
231
+ * ```
232
+ *
233
+ * @example
159
234
  * ### Attaching Signatures
160
235
  *
161
236
  * ```ts twoslash
@@ -184,22 +259,29 @@ export type RpcV07<signed extends boolean = true> = V07<signed, Hex.Hex>
184
259
  * const userOperation_signed = UserOperation.from(userOperation, { signature }) // [!code focus]
185
260
  * ```
186
261
  *
187
- * @param userOperation - The user operation to instantiate.
262
+ * @param userOperation - The user operation to instantiate (structured or packed format).
188
263
  * @returns User Operation.
189
264
  */
190
265
  export function from<
191
- const userOperation extends UserOperation,
266
+ const userOperation extends UserOperation | Packed,
192
267
  const signature extends Hex.Hex | undefined = undefined,
193
268
  >(
194
- userOperation: userOperation | UserOperation,
269
+ userOperation: userOperation | UserOperation | Packed,
195
270
  options: from.Options<signature> = {},
196
271
  ): from.ReturnType<userOperation, signature> {
197
272
  const signature = (() => {
198
- if (!options.signature) return undefined
199
273
  if (typeof options.signature === 'string') return options.signature
200
- return Signature.toHex(options.signature)
274
+ if (typeof options.signature === 'object')
275
+ return Signature.toHex(options.signature)
276
+ if (userOperation.signature) return userOperation.signature
277
+ return undefined
201
278
  })()
202
- return { ...userOperation, signature } as never
279
+
280
+ const packed =
281
+ 'accountGasLimits' in userOperation && 'gasFees' in userOperation
282
+
283
+ const userOp = packed ? fromPacked(userOperation) : userOperation
284
+ return { ...userOp, signature } as never
203
285
  }
204
286
 
205
287
  export declare namespace from {
@@ -210,7 +292,7 @@ export declare namespace from {
210
292
  }
211
293
 
212
294
  export type ReturnType<
213
- userOperation extends UserOperation = UserOperation,
295
+ userOperation extends UserOperation | Packed = UserOperation | Packed,
214
296
  signature extends Signature.Signature | Hex.Hex | undefined = undefined,
215
297
  > = Compute<
216
298
  Assign<
@@ -370,6 +452,14 @@ export function hash<
370
452
  verificationGasLimit,
371
453
  } = userOperation as UserOperation
372
454
 
455
+ if (entryPointVersion === '0.8') {
456
+ const typedData = toTypedData(userOperation as UserOperation<'0.8', true>, {
457
+ chainId,
458
+ entryPointAddress,
459
+ })
460
+ return TypedData.getSignPayload(typedData)
461
+ }
462
+
373
463
  const packedUserOp = (() => {
374
464
  if (entryPointVersion === '0.6') {
375
465
  return AbiParameters.encode(
@@ -479,6 +569,52 @@ export declare namespace hash {
479
569
  | Errors.GlobalErrorType
480
570
  }
481
571
 
572
+ /**
573
+ * Converts a {@link ox#UserOperation.UserOperation} to `initCode`.
574
+ *
575
+ * @example
576
+ * ```ts twoslash
577
+ * import { Value } from 'ox'
578
+ * import { UserOperation } from 'ox/erc4337'
579
+ *
580
+ * const initCode = UserOperation.toInitCode({
581
+ * authorization: {
582
+ * address: '0x9f1fdab6458c5fc642fa0f4c5af7473c46837357',
583
+ * chainId: 1,
584
+ * nonce: 69n,
585
+ * yParity: 0,
586
+ * r: 1n,
587
+ * s: 2n,
588
+ * },
589
+ * callData: '0xdeadbeef',
590
+ * callGasLimit: 300_000n,
591
+ * factory: '0x7702',
592
+ * factoryData: '0xdeadbeef',
593
+ * maxFeePerGas: Value.fromGwei('20'),
594
+ * maxPriorityFeePerGas: Value.fromGwei('2'),
595
+ * nonce: 69n,
596
+ * preVerificationGas: 100_000n,
597
+ * sender: '0x9f1fdab6458c5fc642fa0f4c5af7473c46837357',
598
+ * })
599
+ * ```
600
+ *
601
+ * @param userOperation - The user operation to convert.
602
+ * @returns The init code.
603
+ */
604
+ export function toInitCode(userOperation: Partial<UserOperation>): Hex.Hex {
605
+ const { authorization, factory, factoryData } = userOperation
606
+ if (
607
+ factory === '0x7702' ||
608
+ factory === '0x7702000000000000000000000000000000000000'
609
+ ) {
610
+ if (!authorization) return '0x7702000000000000000000000000000000000000'
611
+ const delegation = authorization.address
612
+ return Hex.concat(delegation, factoryData ?? '0x')
613
+ }
614
+ if (!factory) return '0x'
615
+ return Hex.concat(factory, factoryData ?? '0x')
616
+ }
617
+
482
618
  /**
483
619
  * Transforms a User Operation into "packed" format.
484
620
  *
@@ -503,12 +639,12 @@ export declare namespace hash {
503
639
  * @param userOperation - The user operation to transform.
504
640
  * @returns The packed user operation.
505
641
  */
506
- export function toPacked(userOperation: UserOperation<'0.7', true>): Packed {
642
+ export function toPacked(
643
+ userOperation: UserOperation<'0.7' | '0.8', true>,
644
+ ): Packed {
507
645
  const {
508
646
  callGasLimit,
509
647
  callData,
510
- factory,
511
- factoryData,
512
648
  maxPriorityFeePerGas,
513
649
  maxFeePerGas,
514
650
  nonce,
@@ -525,8 +661,7 @@ export function toPacked(userOperation: UserOperation<'0.7', true>): Packed {
525
661
  Hex.padLeft(Hex.fromNumber(verificationGasLimit || 0n), 16),
526
662
  Hex.padLeft(Hex.fromNumber(callGasLimit || 0n), 16),
527
663
  )
528
- const initCode =
529
- factory && factoryData ? Hex.concat(factory, factoryData) : '0x'
664
+ const initCode = toInitCode(userOperation)
530
665
  const gasFees = Hex.concat(
531
666
  Hex.padLeft(Hex.fromNumber(maxPriorityFeePerGas || 0n), 16),
532
667
  Hex.padLeft(Hex.fromNumber(maxFeePerGas || 0n), 16),
@@ -558,6 +693,119 @@ export declare namespace toPacked {
558
693
  export type ErrorType = Errors.GlobalErrorType
559
694
  }
560
695
 
696
+ /**
697
+ * Transforms a "packed" User Operation into a structured {@link ox#UserOperation.UserOperation}.
698
+ *
699
+ * @example
700
+ * ```ts twoslash
701
+ * import { UserOperation } from 'ox/erc4337'
702
+ *
703
+ * const packed: UserOperation.Packed = {
704
+ * accountGasLimits: '0x...',
705
+ * callData: '0xdeadbeef',
706
+ * initCode: '0x...',
707
+ * gasFees: '0x...',
708
+ * nonce: 69n,
709
+ * paymasterAndData: '0x',
710
+ * preVerificationGas: 100_000n,
711
+ * sender: '0x9f1fdab6458c5fc642fa0f4c5af7473c46837357',
712
+ * signature: '0x...',
713
+ * }
714
+ *
715
+ * const userOperation = UserOperation.fromPacked(packed)
716
+ * ```
717
+ *
718
+ * @param packed - The packed user operation to transform.
719
+ * @returns The structured user operation.
720
+ */
721
+ export function fromPacked(packed: Packed): UserOperation<'0.7' | '0.8', true> {
722
+ const {
723
+ accountGasLimits,
724
+ callData,
725
+ initCode,
726
+ gasFees,
727
+ nonce,
728
+ paymasterAndData,
729
+ preVerificationGas,
730
+ sender,
731
+ signature,
732
+ } = packed
733
+
734
+ const verificationGasLimit = BigInt(Hex.slice(accountGasLimits, 0, 16))
735
+ const callGasLimit = BigInt(Hex.slice(accountGasLimits, 16, 32))
736
+
737
+ const { factory, factoryData } = (() => {
738
+ if (initCode === '0x') return { factory: undefined, factoryData: undefined }
739
+
740
+ const factory = Hex.slice(initCode, 0, 20)
741
+ const factoryData =
742
+ Hex.size(initCode) > 20 ? Hex.slice(initCode, 20) : undefined
743
+
744
+ return { factory, factoryData }
745
+ })()
746
+
747
+ const maxPriorityFeePerGas = BigInt(Hex.slice(gasFees, 0, 16))
748
+ const maxFeePerGas = BigInt(Hex.slice(gasFees, 16, 32))
749
+
750
+ const {
751
+ paymaster,
752
+ paymasterVerificationGasLimit,
753
+ paymasterPostOpGasLimit,
754
+ paymasterData,
755
+ } = (() => {
756
+ if (paymasterAndData === '0x')
757
+ return {
758
+ paymaster: undefined,
759
+ paymasterVerificationGasLimit: undefined,
760
+ paymasterPostOpGasLimit: undefined,
761
+ paymasterData: undefined,
762
+ }
763
+
764
+ const paymaster = Hex.slice(paymasterAndData, 0, 20)
765
+ const paymasterVerificationGasLimit = BigInt(
766
+ Hex.slice(paymasterAndData, 20, 36),
767
+ )
768
+ const paymasterPostOpGasLimit = BigInt(Hex.slice(paymasterAndData, 36, 52))
769
+ const paymasterData =
770
+ Hex.size(paymasterAndData) > 52
771
+ ? Hex.slice(paymasterAndData, 52)
772
+ : undefined
773
+
774
+ return {
775
+ paymaster,
776
+ paymasterVerificationGasLimit,
777
+ paymasterPostOpGasLimit,
778
+ paymasterData,
779
+ }
780
+ })()
781
+
782
+ return {
783
+ callData,
784
+ callGasLimit,
785
+ ...(factory && { factory }),
786
+ ...(factoryData && { factoryData }),
787
+ maxFeePerGas,
788
+ maxPriorityFeePerGas,
789
+ nonce,
790
+ ...(paymaster && { paymaster }),
791
+ ...(paymasterData && { paymasterData }),
792
+ ...(typeof paymasterPostOpGasLimit === 'bigint' && {
793
+ paymasterPostOpGasLimit,
794
+ }),
795
+ ...(typeof paymasterVerificationGasLimit === 'bigint' && {
796
+ paymasterVerificationGasLimit,
797
+ }),
798
+ preVerificationGas,
799
+ sender,
800
+ signature,
801
+ verificationGasLimit,
802
+ }
803
+ }
804
+
805
+ export declare namespace fromPacked {
806
+ export type ErrorType = Hex.slice.ErrorType | Errors.GlobalErrorType
807
+ }
808
+
561
809
  /**
562
810
  * Converts a {@link ox#UserOperation.UserOperation} to a {@link ox#UserOperation.Rpc}.
563
811
  *
@@ -615,3 +863,81 @@ export function toRpc(userOperation: UserOperation): Rpc {
615
863
  export declare namespace toRpc {
616
864
  export type ErrorType = Hex.fromNumber.ErrorType | Errors.GlobalErrorType
617
865
  }
866
+
867
+ /**
868
+ * Converts a signed {@link ox#UserOperation.UserOperation} to a {@link ox#TypedData.Definition}.
869
+ *
870
+ * @example
871
+ * ```ts twoslash
872
+ * import { Value } from 'ox'
873
+ * import { UserOperation } from 'ox/erc4337'
874
+ *
875
+ * const typedData = UserOperation.toTypedData({
876
+ * authorization: {
877
+ * chainId: 1,
878
+ * address: '0x9f1fdab6458c5fc642fa0f4c5af7473c46837357',
879
+ * nonce: 69n,
880
+ * yParity: 0,
881
+ * r: 1n,
882
+ * s: 2n,
883
+ * },
884
+ * callData: '0xdeadbeef',
885
+ * callGasLimit: 300_000n,
886
+ * maxFeePerGas: Value.fromGwei('20'),
887
+ * maxPriorityFeePerGas: Value.fromGwei('2'),
888
+ * nonce: 69n,
889
+ * preVerificationGas: 100_000n,
890
+ * sender: '0x9f1fdab6458c5fc642fa0f4c5af7473c46837357',
891
+ * signature: '0x...',
892
+ * verificationGasLimit: 100_000n,
893
+ * }, {
894
+ * chainId: 1,
895
+ * entryPointAddress: '0x1234567890123456789012345678901234567890',
896
+ * })
897
+ * ```
898
+ *
899
+ * @param userOperation - The user operation to convert.
900
+ * @returns A Typed Data definition.
901
+ */
902
+ export function toTypedData(
903
+ userOperation: UserOperation<'0.8', true>,
904
+ options: toTypedData.Options,
905
+ ): TypedData.Definition<typeof toTypedData.types, 'PackedUserOperation'> {
906
+ const { chainId, entryPointAddress } = options
907
+
908
+ const packedUserOp = toPacked(userOperation)
909
+
910
+ return {
911
+ domain: {
912
+ name: 'ERC4337',
913
+ version: '1',
914
+ chainId,
915
+ verifyingContract: entryPointAddress,
916
+ },
917
+ message: packedUserOp,
918
+ primaryType: 'PackedUserOperation',
919
+ types: toTypedData.types,
920
+ }
921
+ }
922
+
923
+ export namespace toTypedData {
924
+ export type Options = {
925
+ chainId: number
926
+ entryPointAddress: Address.Address
927
+ }
928
+
929
+ export type ErrorType = Errors.GlobalErrorType
930
+
931
+ export const types = {
932
+ PackedUserOperation: [
933
+ { type: 'address', name: 'sender' },
934
+ { type: 'uint256', name: 'nonce' },
935
+ { type: 'bytes', name: 'initCode' },
936
+ { type: 'bytes', name: 'callData' },
937
+ { type: 'bytes32', name: 'accountGasLimits' },
938
+ { type: 'uint256', name: 'preVerificationGas' },
939
+ { type: 'bytes32', name: 'gasFees' },
940
+ { type: 'bytes', name: 'paymasterAndData' },
941
+ ],
942
+ } as const
943
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ox",
3
3
  "description": "Ethereum Standard Library",
4
- "version": "0.8.4",
4
+ "version": "0.8.6",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
package/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** @internal */
2
- export const version = '0.8.4'
2
+ export const version = '0.8.6'