1+ import { bytesToHex , randomBytes } from '@noble/hashes/utils' ;
12import {
23 OWNABLE_VALIDATOR_ADDRESS ,
3- Session ,
4+ type Policy ,
5+ type Session ,
46 encodeSmartSessionSignature ,
57 encodeValidationData ,
68 encodeValidatorNonce ,
7- getAccount ,
8- getEnableSessionDetails ,
99 getOwnableValidator ,
10- getOwnableValidatorMockSignature ,
1110 getSmartSessionsValidator ,
1211 getSudoPolicy ,
1312} from '@rhinestone/module-sdk' ;
14- import { createSmartAccountClient } from 'permissionless' ;
15- import { toSafeSmartAccount } from 'permissionless/accounts' ;
13+ import { type SmartAccountClient , createSmartAccountClient , encodeInstallModule } from 'permissionless' ;
14+ import { type ToSafeSmartAccountParameters , toSafeSmartAccount } from 'permissionless/accounts' ;
1615import { erc7579Actions } from 'permissionless/actions/erc7579' ;
16+ import type { Erc7579Actions } from 'permissionless/actions/erc7579' ;
1717import { createPimlicoClient } from 'permissionless/clients/pimlico' ;
18- import { http , type WalletClient , createPublicClient } from 'viem' ;
19- import { entryPoint07Address } from 'viem/account-abstraction' ;
18+ import { http , Address , type Chain , Hex , type WalletClient , createPublicClient , encodeFunctionData } from 'viem' ;
19+ import { type SmartAccount , entryPoint07Address } from 'viem/account-abstraction' ;
20+ import { safeModuleManagerAbi } from './abis.js' ;
2021
2122const DEFAULT_RPC_URL = 'https://rpc-geo-genesis-h0q2s21xx8.t.conduit.xyz' ;
2223/**
2324 * We provide a fallback API key for gas sponsorship for the duration of the
2425 * Geo Genesis early access period. This API key is gas-limited.
2526 */
2627const DEFAULT_API_KEY = 'pim_KqHm63txxhbCYjdDaWaHqH' ;
27- const BUNDLER_TRANSPORT_URL_BASE = 'https://api.pimlico.io/v2/80451/rpc?apikey= ' ;
28+ const BUNDLER_TRANSPORT_URL_BASE = 'https://api.pimlico.io/v2/' ;
2829
2930const SAFE_7579_MODULE_ADDRESS = '0x7579EE8307284F293B1927136486880611F20002' ;
31+ const SAFE_4337_MODULE_ADDRESS = '0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226' ;
3032const ERC7579_LAUNCHPAD_ADDRESS = '0x7579011aB74c46090561ea277Ba79D510c6C00ff' ;
3133
32- const GEOGENESIS = {
34+ const SPACE_FACTORY_ADDRESS = '0x0000000000000000000000000000000000000000' ; // TODO: add address
35+ const SPACE_FACTORY_CREATE_SPACE_SELECTOR = '0x00000000' ; // TODO: add selector
36+
37+ // TODO: add details for testnet too
38+ export const GEOGENESIS = {
3339 id : Number ( '80451' ) ,
3440 name : 'Geo Genesis' ,
3541 nativeCurrency : {
36- name : 'Ethereum ' ,
37- symbol : 'ETH ' ,
42+ name : 'Graph Token ' ,
43+ symbol : 'GRT ' ,
3844 decimals : 18 ,
3945 } ,
4046 rpcUrls : {
@@ -47,15 +53,19 @@ const GEOGENESIS = {
4753 } ,
4854} ;
4955
56+ export type LegacySmartAccountClient = SmartAccountClient ;
57+ export type HypergraphSmartAccountClient = SmartAccountClient & Erc7579Actions < SmartAccount > ;
58+
5059export const getLegacySmartAccountWalletClient = async (
5160 walletClient : WalletClient ,
61+ chain : Chain = GEOGENESIS ,
5262 rpcUrl : string = DEFAULT_RPC_URL ,
5363 apiKey : string = DEFAULT_API_KEY ,
54- ) => {
64+ ) : Promise < LegacySmartAccountClient > => {
5565 const transport = http ( rpcUrl ) ;
5666 const publicClient = createPublicClient ( {
5767 transport,
58- chain : GEOGENESIS ,
68+ chain,
5969 } ) ;
6070
6171 const safeAccount = await toSafeSmartAccount ( {
@@ -69,18 +79,18 @@ export const getLegacySmartAccountWalletClient = async (
6979 version : '1.4.1' ,
7080 } ) ;
7181
72- const bundlerTransport = http ( `${ BUNDLER_TRANSPORT_URL_BASE } ${ apiKey } ` ) ;
82+ const bundlerTransport = http ( `${ BUNDLER_TRANSPORT_URL_BASE } ${ chain . id } /rpc?apikey= ${ apiKey } ` ) ;
7383 const paymasterClient = createPimlicoClient ( {
7484 transport : bundlerTransport ,
75- chain : GEOGENESIS ,
85+ chain,
7686 entryPoint : {
7787 address : entryPoint07Address ,
7888 version : '0.7' ,
7989 } ,
8090 } ) ;
8191
8292 const smartAccount = createSmartAccountClient ( {
83- chain : GEOGENESIS ,
93+ chain,
8494 account : safeAccount ,
8595 paymaster : paymasterClient ,
8696 bundlerTransport,
@@ -95,58 +105,67 @@ export const getLegacySmartAccountWalletClient = async (
95105
96106export const get7579SmartAccountWalletClient = async (
97107 walletClient : WalletClient ,
98- address : string | undefined ,
108+ address : `0x${string } ` | undefined ,
109+ chain : Chain = GEOGENESIS ,
99110 rpcUrl : string = DEFAULT_RPC_URL ,
100111 apiKey : string = DEFAULT_API_KEY ,
101- ) => {
112+ ) : Promise < HypergraphSmartAccountClient > => {
102113 if ( ! walletClient . account ) {
103114 throw new Error ( 'Wallet client must be an account' ) ;
104115 }
105116
106117 const transport = http ( rpcUrl ) ;
107118 const publicClient = createPublicClient ( {
108119 transport,
109- chain : GEOGENESIS ,
120+ chain,
110121 } ) ;
111122
112123 const ownableValidator = getOwnableValidator ( {
113124 owners : [ walletClient . account . address ] ,
114125 threshold : 1 ,
115126 } ) ;
127+ const smartSessionsValidator = getSmartSessionsValidator ( { } ) ;
116128
117- const safeAccount = await toSafeSmartAccount ( {
129+ const safeAccountParams : ToSafeSmartAccountParameters < '0.7' , `0x${ string } ` > = {
118130 client : publicClient ,
119- address,
120131 owners : [ walletClient ] ,
121- version : '1.4.1' ,
132+ version : '1.4.1' as const ,
122133 entryPoint : {
123134 address : entryPoint07Address ,
124- version : '0.7' ,
135+ version : '0.7' as const ,
125136 } ,
126- safe4337ModuleAddress : SAFE_7579_MODULE_ADDRESS ,
127- erc7579LaunchpadAddress : ERC7579_LAUNCHPAD_ADDRESS ,
137+ safe4337ModuleAddress : SAFE_7579_MODULE_ADDRESS as `0x${ string } ` ,
138+ erc7579LaunchpadAddress : ERC7579_LAUNCHPAD_ADDRESS as `0x${ string } ` ,
128139 attesters : [ ] ,
129140 attestersThreshold : 0 ,
130141 validators : [
131142 {
132143 address : ownableValidator . address ,
133144 context : ownableValidator . initData ,
134145 } ,
146+ {
147+ address : smartSessionsValidator . address ,
148+ context : smartSessionsValidator . initData ,
149+ } ,
135150 ] ,
136- } ) ;
151+ } ;
152+ if ( address ) {
153+ safeAccountParams . address = address ;
154+ }
155+ const safeAccount = await toSafeSmartAccount ( safeAccountParams ) ;
137156
138- const bundlerTransport = http ( `${ BUNDLER_TRANSPORT_URL_BASE } ${ apiKey } ` ) ;
157+ const bundlerTransport = http ( `${ BUNDLER_TRANSPORT_URL_BASE } ${ chain . id } /rpc?apikey= ${ apiKey } ` ) ;
139158 const paymasterClient = createPimlicoClient ( {
140159 transport : bundlerTransport ,
141- chain : GEOGENESIS ,
160+ chain,
142161 entryPoint : {
143162 address : entryPoint07Address ,
144163 version : '0.7' ,
145164 } ,
146165 } ) ;
147166
148167 const smartAccount = createSmartAccountClient ( {
149- chain : GEOGENESIS ,
168+ chain,
150169 account : safeAccount ,
151170 paymaster : paymasterClient ,
152171 bundlerTransport,
@@ -159,14 +178,133 @@ export const get7579SmartAccountWalletClient = async (
159178 return smartAccount ;
160179} ;
161180
162- const updateLegacySmartAccount = async ( smartAccount : GeoSmartAccount ) => {
163- // TODO We need to enable the 7579 module and disable the 4337 module
181+ export const isSmartAccountDeployed = async (
182+ smartAccount : LegacySmartAccountClient | HypergraphSmartAccountClient ,
183+ ) : Promise < boolean > => {
184+ return smartAccount . account . isDeployed ( ) ;
164185} ;
165186
166- const createSmartSession = async ( smartAccount : GeoSmartAccount ) => {
167- // TODO
168- // Check if the smart account has the smart session module enabled
169- // Enable it if needed
170- // Create a smart session
171- // Execute the userOp
187+ export const updateLegacySmartAccount = async ( smartAccount : LegacySmartAccountClient ) => {
188+ const ownableValidator = getOwnableValidator ( {
189+ owners : [ smartAccount . account . address ] ,
190+ threshold : 1 ,
191+ } ) ;
192+ const smartSessionsValidator = getSmartSessionsValidator ( { } ) ;
193+ const installOwnableValidatorTx = encodeInstallModule ( {
194+ account : smartAccount . account ,
195+ modules : [
196+ {
197+ type : ownableValidator . type ,
198+ address : ownableValidator . address ,
199+ context : ownableValidator . initData ,
200+ } ,
201+ {
202+ type : smartSessionsValidator . type ,
203+ address : smartSessionsValidator . address ,
204+ context : smartSessionsValidator . initData ,
205+ } ,
206+ ] ,
207+ } ) ;
208+
209+ const calls = [
210+ {
211+ to : smartAccount . account . address ,
212+ data : encodeFunctionData ( {
213+ abi : safeModuleManagerAbi ,
214+ functionName : 'enableModule' ,
215+ args : [ SAFE_7579_MODULE_ADDRESS as `0x${string } `] ,
216+ } ) ,
217+ value : BigInt ( 0 ) ,
218+ } ,
219+ {
220+ to : smartAccount . account . address ,
221+ data : encodeFunctionData ( {
222+ abi : safeModuleManagerAbi ,
223+ functionName : 'setFallbackHandler' ,
224+ args : [ SAFE_7579_MODULE_ADDRESS as `0x${string } `] ,
225+ } ) ,
226+ value : BigInt ( 0 ) ,
227+ } ,
228+ {
229+ to : smartAccount . account . address ,
230+ data : encodeFunctionData ( {
231+ abi : safeModuleManagerAbi ,
232+ functionName : 'disableModule' ,
233+ args : [ SAFE_4337_MODULE_ADDRESS as `0x${string } `] ,
234+ } ) ,
235+ value : BigInt ( 0 ) ,
236+ } ,
237+ {
238+ to : installOwnableValidatorTx [ 0 ] . to ,
239+ data : installOwnableValidatorTx [ 0 ] . data ,
240+ value : installOwnableValidatorTx [ 0 ] . value ,
241+ } ,
242+ {
243+ to : installOwnableValidatorTx [ 1 ] . to ,
244+ data : installOwnableValidatorTx [ 1 ] . data ,
245+ value : installOwnableValidatorTx [ 1 ] . value ,
246+ } ,
247+ ] ;
248+
249+ const tx = await smartAccount . sendTransaction ( {
250+ calls,
251+ } ) ;
252+ const receipt = await smartAccount . waitForTransactionReceipt ( {
253+ hash : tx . hash ,
254+ } ) ;
255+ if ( receipt . status !== 'success' ) {
256+ throw new Error ( 'Transaction to update legacy smart account failed' ) ;
257+ }
258+ return receipt ;
259+ } ;
260+
261+ type Action = {
262+ actionTarget : Address ;
263+ actionTargetSelector : Hex ;
264+ actionPolicies : { policy : Address ; address : Address ; initData : Hex } [ ] ;
265+ } ;
266+
267+ export const createSmartSession = async (
268+ smartAccount : HypergraphSmartAccountClient ,
269+ sessionKeyAddress : `0x${string } `,
270+ {
271+ allowCreateSpace = false ,
272+ spaceAddresses = [ ] ,
273+ } : {
274+ allowCreateSpace ?: boolean ;
275+ spaceAddresses ?: `0x${string } `[ ] ;
276+ } = { } ,
277+ ) => {
278+ const actions : Action [ ] = [ ] ;
279+ for ( const spaceAddress of spaceAddresses ) {
280+ actions . push ( {
281+ actionTarget : spaceAddress ,
282+ actionTargetSelector : '0x00000000' as Hex ,
283+ actionPolicies : [ getSudoPolicy ( ) ] ,
284+ } ) ;
285+ }
286+ if ( allowCreateSpace ) {
287+ actions . push ( {
288+ actionTarget : SPACE_FACTORY_ADDRESS ,
289+ actionTargetSelector : SPACE_FACTORY_CREATE_SPACE_SELECTOR as Hex ,
290+ actionPolicies : [ getSudoPolicy ( ) ] ,
291+ } ) ;
292+ }
293+
294+ const session : Session = {
295+ sessionValidator : OWNABLE_VALIDATOR_ADDRESS ,
296+ sessionValidatorInitData : encodeValidationData ( {
297+ threshold : 1 ,
298+ owners : [ sessionKeyAddress ] ,
299+ } ) ,
300+ salt : bytesToHex ( randomBytes ( 32 ) ) ,
301+ userOpPolicies : [ getSudoPolicy ( ) ] ,
302+ erc7739Policies : {
303+ allowedERC7739Content : [ ] ,
304+ erc1271Policies : [ ] ,
305+ } ,
306+ actions,
307+ chainId : BigInt ( smartAccount . chain . id ) ,
308+ permitERC4337Paymaster : true ,
309+ } ;
172310} ;
0 commit comments