@@ -21,14 +21,20 @@ import {
21
21
getContract ,
22
22
concatHex ,
23
23
maxUint152 ,
24
- hexToBigInt ,
25
24
hexToNumber ,
26
25
decodeFunctionData ,
26
+ createCursor ,
27
+ toFunctionSelector ,
28
+ hexToBytes ,
29
+ decodeAbiParameters ,
30
+ AbiConstructorNotFoundError ,
31
+ type DecodeFunctionDataParameters ,
27
32
} from "viem" ;
28
33
import { modularAccountAbi } from "../../abis/modularAccountAbi.js" ;
29
34
import { serializeModuleEntity } from "../../actions/common/utils.js" ;
30
35
import { nativeSMASigner } from "../nativeSMASigner.js" ;
31
36
import { singleSignerMessageSigner } from "../../modules/single-signer-validation/signer.js" ;
37
+ import { formatAbiItem } from "viem/utils" ;
32
38
33
39
export const executeUserOpSelector : Hex = "0x8DD7712F" ;
34
40
@@ -55,17 +61,10 @@ export type ValidationDataParams =
55
61
| {
56
62
validationModuleAddress : Address ;
57
63
entityId ?: never ;
58
- deferredActionDigest ?: never ;
59
64
}
60
65
| {
61
66
validationModuleAddress ?: never ;
62
67
entityId : number ;
63
- deferredActionDigest ?: never ;
64
- }
65
- | {
66
- deferredActionDigest : Hex ;
67
- validationModuleAddress ?: never ;
68
- entityId ?: never ;
69
68
} ;
70
69
71
70
export type ModularAccountV2 <
@@ -95,8 +94,16 @@ export type CreateMAV2BaseParams<
95
94
signer : TSigner ;
96
95
signerEntity ?: SignerEntity ;
97
96
accountAddress : Address ;
98
- deferredActionDigest ?: Hex ;
99
- } ;
97
+ } & (
98
+ | {
99
+ deferredActionDigest : Hex ;
100
+ nonce : bigint ;
101
+ }
102
+ | {
103
+ deferredActionDigest ?: never ;
104
+ nonce ?: never ;
105
+ }
106
+ ) ;
100
107
101
108
export type CreateMAV2BaseReturnType <
102
109
TSigner extends SmartAccountSigner = SmartAccountSigner
@@ -120,6 +127,7 @@ export async function createMAv2Base<
120
127
} = { } ,
121
128
accountAddress,
122
129
deferredActionDigest,
130
+ nonce,
123
131
...remainingToSmartContractAccountParams
124
132
} = config ;
125
133
@@ -140,61 +148,54 @@ export async function createMAv2Base<
140
148
141
149
let deferredAction :
142
150
| undefined
143
- | { nonce : bigint ; data : Hex ; hasAssociatedExecHooks : boolean } ;
151
+ | { data : Hex ; hasAssociatedExecHooks : boolean } ;
144
152
145
153
// deferred action format:
146
- // 32 bytes nonce | 4 bytes len | 21 bytes valLocator | 8 bytes deadline |
147
- // variable bytes calldata | 4 bytes sig length | variable bytes sig
154
+ // 4 bytes len | 21 bytes valLocator | 6 bytes deadline | variable bytes calldata |
155
+ // 4 bytes sig length | variable bytes sig
148
156
if ( deferredActionDigest ) {
149
- // we always infer entityId and isGlobalValidation from the deferred action case
150
- // this number here is in the range of [0, 7]
151
- if ( Number ( deferredActionDigest [ 116 ] ) >= 4 ) {
152
- // TODO: implement > 4 case which is direct validation
153
- throw new Error ( "Direct call validation not supported yet" ) ;
154
- } else {
155
- // 1st bit is isGlobalValidation, 2nd bit is isDeferredAction
156
- signerEntity . isGlobalValidation =
157
- Number ( deferredActionDigest [ 116 ] ) % 2 === 0 ? false : true ;
158
- signerEntity . entityId = hexToNumber (
159
- `0x${ deferredActionDigest . slice ( 98 , 116 ) } `
160
- ) ;
161
- }
162
-
163
157
// Set these values if the deferred action has not been consumed. We check this with the EP
164
- const deferredActionNonce = hexToBigInt (
165
- `0x${ deferredActionDigest . slice ( 2 , 66 ) } `
166
- ) ;
167
- const nextNonceForDeferredActionNonceKey : bigint =
158
+ const nextNonceForDeferredActionNonce : bigint =
168
159
( await entryPointContract . read . getNonce ( [
169
160
accountAddress ,
170
- deferredActionNonce >> 64n ,
161
+ nonce >> 64n ,
171
162
] ) ) as bigint ;
172
- if ( deferredActionNonce === nextNonceForDeferredActionNonceKey ) {
173
- // parse deferred action to get
163
+
164
+ if ( nonce === nextNonceForDeferredActionNonce ) {
165
+ // parse deferred action to get length. -21 for val locator, -6 for timestamp
166
+
174
167
const callBytesLength =
175
- hexToNumber ( `0x${ deferredActionDigest . slice ( 66 , 74 ) } ` ) - 29 ;
168
+ hexToNumber ( `0x${ deferredActionDigest . slice ( 2 , 10 ) } ` ) - 27 ;
176
169
if ( callBytesLength < 4 ) {
177
170
throw new Error ( "Invalid deferred action calldata length" ) ;
178
171
}
179
- const deferredActionCall = decodeFunctionData ( {
180
- abi : modularAccountAbi ,
181
- data : `0x${ deferredActionDigest . slice ( 132 , 132 + callBytesLength * 2 ) } ` ,
182
- } ) ;
183
-
184
- const hooks =
185
- deferredActionCall . functionName !== "installValidation"
186
- ? [ ]
187
- : deferredActionCall . args [ 3 ] ;
188
-
189
- deferredAction = {
190
- nonce : deferredActionNonce ,
191
- data : `0x${ deferredActionDigest . slice ( 66 ) } ` ,
192
- // get the 25th byte of each hook, execution hooks have the 1st bit empty and val have it set.
193
- // for a string, this is 2 (0x) + 2 * 25 = 52
194
- // we can just get the single character since we are just checking a single bit
195
- hasAssociatedExecHooks : hooks . map ( ( h ) => Number ( h [ 52 ] ) % 2 ) . includes ( 0 ) ,
196
- } ;
197
- } else if ( nextNonceForDeferredActionNonceKey < deferredActionNonce ) {
172
+ try {
173
+ const bytes2 : Hex = `0x${ deferredActionDigest . slice (
174
+ 64 ,
175
+ 64 + callBytesLength * 2
176
+ ) } `;
177
+
178
+ const { functionName, args } = decodeFunctionData ( {
179
+ abi : modularAccountAbi ,
180
+ data : bytes2 as `0x${string } `,
181
+ } ) ;
182
+
183
+ if ( functionName === "installValidation" ) {
184
+ deferredAction = {
185
+ data : `0x${ deferredActionDigest . slice ( 2 ) } ` ,
186
+ // get the 25th byte of each hook, execution hooks have the 1st bit empty and val have it set.
187
+ // for a string, this is 2 (0x) + 2 * 25 = 52
188
+ // we can just get the single character since we are just checking a single bit
189
+ hasAssociatedExecHooks : args [ 3 ]
190
+ . map ( ( h ) => Number ( h [ 52 ] ) % 2 )
191
+ . includes ( 0 ) ,
192
+ } ;
193
+ }
194
+ } catch {
195
+ // if the decode fails, it could be because the function is an non-native selector that has been installed
196
+ // in these cases, we don't do anything.
197
+ }
198
+ } else if ( nextNonceForDeferredActionNonce < nonce ) {
198
199
throw new Error ( "Deferred action nonce invalid" ) ;
199
200
}
200
201
}
@@ -231,8 +232,8 @@ export async function createMAv2Base<
231
232
! ! ( await client . getCode ( { address : accountAddress } ) ) ;
232
233
233
234
const getNonce = async ( nonceKey : bigint = 0n ) : Promise < bigint > => {
234
- if ( deferredAction ) {
235
- return deferredAction . nonce ;
235
+ if ( deferredActionDigest ) {
236
+ return nonce ;
236
237
}
237
238
238
239
if ( nonceKey > maxUint152 ) {
0 commit comments