@@ -74,6 +74,14 @@ export const GEOGENESIS = {
7474 } ,
7575} ;
7676
77+ type Action = {
78+ actionTarget : Address ;
79+ actionTargetSelector : Hex ;
80+ actionPolicies : { policy : Address ; address : Address ; initData : Hex } [ ] ;
81+ } ;
82+
83+ // Gets the legacy Geo smart account wallet client. If the smart account returned
84+ // by this function is deployed, it means it might need to be updated to have the 7579 module installed
7785export const getLegacySmartAccountWalletClient = async (
7886 walletClient : WalletClient ,
7987 chain : Chain = GEOGENESIS ,
@@ -204,6 +212,24 @@ export const isSmartAccountDeployed = async (smartAccountClient: SmartAccountCli
204212 return smartAccountClient . account . isDeployed ( ) ;
205213} ;
206214
215+ export const legacySmartAccountNeedsUpdate = async (
216+ smartAccountClient : SmartAccountClient ,
217+ chain : Chain ,
218+ rpcUrl : string ,
219+ ) : Promise < boolean > => {
220+ if ( ! smartAccountClient . account ) {
221+ throw new Error ( 'Invalid smart account' ) ;
222+ }
223+ // We assume the smart account is deployed, so we just need to check if it has the 7579 module and smart sesions validator installed
224+ // TODO: call the isModuleInstalled function from the Safe7579 ABI on the
225+ // smart account, checking if the smart sessions validator is installed. This would fail
226+ // if the smart account doesn't have the 7579 module installed.
227+ return false ;
228+ } ;
229+
230+ // Legacy Geo smart accounts (i.e. the ones that don't have the 7579 module installed)
231+ // need to be updated to have the 7579 module installed
232+ // with the ownable and smart sessions validators.
207233export const updateLegacySmartAccount = async ( smartAccountClient : SmartAccountClient ) => {
208234 if ( ! smartAccountClient . account ?. address ) {
209235 throw new Error ( 'Invalid smart account' ) ;
@@ -281,12 +307,11 @@ export const updateLegacySmartAccount = async (smartAccountClient: SmartAccountC
281307 return receipt ;
282308} ;
283309
284- type Action = {
285- actionTarget : Address ;
286- actionTargetSelector : Hex ;
287- actionPolicies : { policy : Address ; address : Address ; initData : Hex } [ ] ;
288- } ;
289-
310+ // This is the function that the Connect app uses to create a smart session and
311+ // enable it on the smart account.
312+ // It will prompt the user to sign the message to enable the session, and then
313+ // execute the transaction to enable the session.
314+ // It will return the permissionId.
290315export const createSmartSession = async (
291316 walletClient : WalletClient ,
292317 smartAccountClient : SmartAccountClient ,
@@ -457,3 +482,70 @@ export const createSmartSession = async (
457482 }
458483 return getPermissionId ( { session } ) ;
459484} ;
485+
486+ // This is the function that we use on the end user app to create a smart session client that can send transactions to the smart account.
487+ // The session must have previously been created by the createSmartSession function.
488+ export const getSmartSessionClient = async (
489+ smartAccountClient : SmartAccountClient ,
490+ sessionPrivateKey : `0x${string } `,
491+ permissionId : Hex ,
492+ chain : Chain ,
493+ rpcUrl : string ,
494+ ) => {
495+ if ( ! smartAccountClient . account ) {
496+ throw new Error ( 'Invalid smart account' ) ;
497+ }
498+ const sessionDetails = {
499+ mode : SmartSessionMode . USE ,
500+ permissionId,
501+ signature : getOwnableValidatorMockSignature ( {
502+ threshold : 1 ,
503+ } ) ,
504+ } ;
505+ const smartSessions = getSmartSessionsValidator ( { } ) ;
506+ const publicClient = createPublicClient ( {
507+ transport : http ( rpcUrl ) ,
508+ chain,
509+ } ) ;
510+ const sessionKeyAccount = privateKeyToAccount ( sessionPrivateKey ) ;
511+ return {
512+ sendTransaction : async < const calls extends readonly unknown [ ] > ( { calls } : { calls : calls } ) => {
513+ if ( ! smartAccountClient . account ) {
514+ throw new Error ( 'Invalid smart account' ) ;
515+ }
516+ const account = getAccount ( {
517+ address : smartAccountClient . account . address ,
518+ type : 'safe' ,
519+ } ) ;
520+ const nonce = await getAccountNonce ( publicClient , {
521+ address : smartAccountClient . account . address ,
522+ entryPointAddress : entryPoint07Address ,
523+ key : encodeValidatorNonce ( {
524+ account,
525+ validator : smartSessions ,
526+ } ) ,
527+ } ) ;
528+ const userOperation = await smartAccountClient . prepareUserOperation ( {
529+ account : smartAccountClient . account ,
530+ calls,
531+ nonce,
532+ signature : encodeSmartSessionSignature ( sessionDetails ) ,
533+ } ) ;
534+
535+ const userOpHashToSign = getUserOperationHash ( {
536+ chainId : chain . id ,
537+ entryPointAddress : entryPoint07Address ,
538+ entryPointVersion : '0.7' ,
539+ userOperation,
540+ } ) ;
541+
542+ sessionDetails . signature = await sessionKeyAccount . signMessage ( {
543+ message : { raw : userOpHashToSign } ,
544+ } ) ;
545+
546+ userOperation . signature = encodeSmartSessionSignature ( sessionDetails ) ;
547+
548+ return smartAccountClient . sendUserOperation ( userOperation as UserOperation ) ;
549+ } ,
550+ } ;
551+ } ;
0 commit comments