1- import { publicClient , bundlerClient } from "./clients" ;
1+ import { publicClient , walletClient , bundlerClient } from "./clients" ;
22import {
33 hashAuthorization ,
44 recoverAuthorizationAddress ,
@@ -8,16 +8,26 @@ import { Command } from "commander";
88import { authority } from "./account" ;
99import { encodeFunctionData , parseAbi , parseSignature } from "viem" ;
1010import { sendTransaction } from "viem/actions" ;
11- import { anvil } from "viem/chains " ;
11+ import { network } from "./constants " ;
1212
1313import {
1414 guardianAddress ,
15+ guardianPrivateKey ,
1516 openfortSmartAccountImplementation ,
1617 openfortSmartAccountProxy ,
18+ recoveryPeriod ,
19+ securityPeriod ,
20+ securityWindow ,
21+ lockPeriod ,
1722} from "./constants" ;
1823import assert = require( "node:assert" ) ;
1924import { getAccount } from "./openfortSmartAccount" ;
20- import { entryPoint06Address } from "viem/account-abstraction" ;
25+ import {
26+ entryPoint06Address ,
27+ getUserOperationHash ,
28+ } from "viem/account-abstraction" ;
29+ import { privateKeyToAccount } from "viem/accounts" ;
30+ import { signTypedData } from "viem/accounts" ;
2131
2232const figlet = require ( "figlet" ) ;
2333const program = new Command ( ) ;
@@ -33,6 +43,7 @@ program
3343 . command ( "get-nonce" )
3444 . description ( "get authority account transaction count" )
3545 . action ( async ( ) => {
46+ console . log ( "authority address:" , authority . address ) ;
3647 const EOAnonce = await publicClient . getTransactionCount ( {
3748 address : authority . address ,
3849 } ) ;
@@ -57,7 +68,7 @@ program
5768
5869 const authorization = hashAuthorization ( {
5970 contractAddress : delegationDesignator ,
60- chainId : anvil . id ,
71+ chainId : network . id ,
6172 nonce : nonce + 1 ,
6273 } ) ;
6374 console . log ( "Authorization hash:" , authorization ) ;
@@ -90,7 +101,7 @@ program
90101
91102 const authorization : SignedAuthorization = {
92103 contractAddress : delegationDesignator ,
93- chainId : anvil . id ,
104+ chainId : network . id ,
94105 nonce : nonce + 1 ,
95106 ...parseSignature ( signature ) ,
96107 } ;
@@ -111,10 +122,10 @@ program
111122 args : [
112123 openfortSmartAccountImplementation ,
113124 entryPoint06Address ,
114- 172800n ,
115- 129600n ,
116- 43200n ,
117- 432000n ,
125+ recoveryPeriod ,
126+ securityPeriod ,
127+ securityWindow ,
128+ lockPeriod ,
118129 guardianAddress ,
119130 ] ,
120131 } ) ;
@@ -129,29 +140,177 @@ program
129140 } ) ;
130141
131142program
132- . command ( "test- send-batch" )
143+ . command ( "send-batch" )
133144 . description ( "send 4337 wei to alice and bob within the same UserOperation" )
134- . action ( async ( ) => {
145+ . option ( "-s, --signer <signer>" , "signer" )
146+ . action ( async ( { signer } ) => {
135147 console . log (
136- `Sending 4337 wei to alice & bob with EOA ${ authority . address } in one UserOperation!` ,
148+ `Sending 4337 wei to alice & bob from EOA ${ authority . address } in one UserOperation!` ,
137149 ) ;
138150 const alice = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" ;
139151 const bob = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" ;
140- const openfortSmartAccount = await getAccount ( authority ) ;
141- const userOp = await bundlerClient . sendUserOperation ( {
142- account : openfortSmartAccount ,
143- calls : [
144- {
145- to : alice ,
146- value : 4337n ,
147- } ,
148- {
149- to : bob ,
150- value : 4337n ,
152+ const openfortAccount = await getAccount ( authority ) ;
153+ // If session key is provided, we sign the UserOperation with the session key
154+ if ( signer ) {
155+ const signerAccount = privateKeyToAccount ( signer ) ;
156+ console . log ( "Signing UserOperation with signer" , signerAccount . address ) ;
157+ const unsignedUserOp = await bundlerClient . prepareUserOperation ( {
158+ account : openfortAccount ,
159+ calls : [
160+ {
161+ to : alice ,
162+ value : 4337n ,
163+ } ,
164+ {
165+ to : bob ,
166+ value : 4337n ,
167+ } ,
168+ ] ,
169+ } ) ;
170+ const userOpHash = await getUserOperationHash ( {
171+ chainId : network . id ,
172+ entryPointAddress : entryPoint06Address ,
173+ entryPointVersion : "0.6" ,
174+ userOperation : {
175+ ...( unsignedUserOp as any ) ,
176+ sender : openfortAccount . address ,
151177 } ,
178+ } ) ;
179+ const signature = await signerAccount . signMessage ( {
180+ message : { raw : userOpHash } ,
181+ } ) ;
182+ const hash = await bundlerClient . sendUserOperation ( {
183+ ...unsignedUserOp ,
184+ signature,
185+ } ) ;
186+ console . log ( "User operation hash:" , hash ) ;
187+ }
188+
189+ // If no session key is provided, we sign the UserOperation with authority EOA
190+ // owner of the smart accoubt
191+ else {
192+ console . log ( "Signing UserOperation with smart account owner (authority)" ) ;
193+ const userOp = await bundlerClient . sendUserOperation ( {
194+ account : openfortAccount ,
195+ calls : [
196+ {
197+ to : alice ,
198+ value : 4337n ,
199+ } ,
200+ {
201+ to : bob ,
202+ value : 4337n ,
203+ } ,
204+ ] ,
205+ } ) ;
206+ console . log ( "User operation hash:" , userOp ) ;
207+ }
208+ } ) ;
209+
210+ program
211+ . command ( "recover-account" )
212+ . description ( "recover a 7702-delegated EOA" )
213+ . requiredOption ( "-n, --new-owner <new-owner>" , "new owner account address" )
214+ . action ( async ( { newOwner } ) => {
215+ const guardian = privateKeyToAccount ( guardianPrivateKey ) ;
216+ const openfortAccount = await getAccount ( authority ) ;
217+
218+ // Start the recovery process
219+ const hash = await walletClient . sendTransaction ( {
220+ account : guardian ,
221+ to : openfortAccount . address ,
222+ data : encodeFunctionData ( {
223+ abi : parseAbi ( [ "function startRecovery(address)" ] ) ,
224+ args : [ newOwner ] ,
225+ } ) ,
226+ } ) ;
227+
228+ console . log ( "Recovery Started:" , hash ) ;
229+ // Get EIP712 domain
230+ const domainData = await publicClient . readContract ( {
231+ address : openfortAccount . address ,
232+ abi : parseAbi ( [
233+ "function eip712Domain() view returns (bytes1, string, string, uint256, address, bytes32, uint256[])" ,
234+ ] ) ,
235+ functionName : "eip712Domain" ,
236+ } ) ;
237+
238+ const [ , name , version , chainId , verifyingContract ] = domainData ;
239+
240+ console . log ( "name" , name ) ;
241+ console . log ( "version" , version ) ;
242+ console . log ( "chainId" , chainId ) ;
243+ console . log ( "verifyingContract" , verifyingContract ) ;
244+
245+ // Get recovery details
246+ const recoveryDetails = await publicClient . readContract ( {
247+ address : openfortAccount . address ,
248+ abi : parseAbi ( [
249+ "function recoveryDetails() view returns (address, uint256, uint256)" ,
250+ ] ) ,
251+ functionName : "recoveryDetails" ,
252+ } ) ;
253+
254+ const [ , executeAfter , guardiansRequired ] = recoveryDetails ;
255+
256+ console . log ( "executeAfter" , executeAfter ) ;
257+ console . log ( "guardiansRequired" , guardiansRequired ) ;
258+
259+ // Use the defined type for the domain object
260+ const domain : Record < string , any > = {
261+ name,
262+ version,
263+ chainId,
264+ verifyingContract,
265+ } ;
266+
267+ const types = {
268+ EIP712Domain : [
269+ { name : "name" , type : "string" } ,
270+ { name : "version" , type : "string" } ,
271+ { name : "chainId" , type : "uint256" } ,
272+ { name : "verifyingContract" , type : "address" } ,
273+ ] ,
274+ Recover : [
275+ { name : "recoveryAddress" , type : "address" } ,
276+ { name : "executeAfter" , type : "uint64" } ,
277+ { name : "guardiansRequired" , type : "uint32" } ,
152278 ] ,
279+ } ;
280+
281+ const message = {
282+ recoveryAddress : newOwner ,
283+ executeAfter,
284+ guardiansRequired,
285+ } ;
286+
287+ const signature = await signTypedData ( {
288+ privateKey : guardianPrivateKey ,
289+ domain,
290+ types,
291+ primaryType : "Recover" ,
292+ message,
293+ } ) ;
294+
295+ console . log ( "Signature:" , signature ) ;
296+ console . log ( `Mining ${ recoveryPeriod } blocks to pass the recovery period` ) ;
297+ // Mine blocks to pass the recovery period
298+ for ( let i = 0 ; i < Number ( recoveryPeriod ) ; i ++ ) {
299+ await publicClient . request ( {
300+ method : "evm_mine" as any ,
301+ params : undefined ,
302+ } ) ;
303+ }
304+
305+ const confirmRecoveryHash = await walletClient . sendTransaction ( {
306+ account : guardian ,
307+ to : openfortAccount . address ,
308+ data : encodeFunctionData ( {
309+ abi : parseAbi ( [ "function completeRecovery(bytes[])" ] ) ,
310+ args : [ [ signature ] ] ,
311+ } ) ,
153312 } ) ;
154- console . log ( "User operation hash :" , userOp ) ;
313+ console . log ( "Recovery Confirmed :" , confirmRecoveryHash ) ;
155314 } ) ;
156315
157316program . parse ( ) ;
0 commit comments