Skip to content

Commit dfa589b

Browse files
committed
still wip
1 parent 894c8b9 commit dfa589b

File tree

4 files changed

+231
-45
lines changed

4 files changed

+231
-45
lines changed

packages/hypergraph/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@xstate/store": "^3.5.1",
4040
"bs58check": "^4.0.0",
4141
"effect": "^3.16.3",
42+
"permissionless": "^0.2.47",
4243
"siwe": "^3.0.0",
4344
"uuid": "^11.1.0",
4445
"viem": "^2.30.6"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Simplified ABI for the Safe Module Manager with the functions we need
2+
export const safeModuleManagerAbi = [
3+
{
4+
inputs: [
5+
{
6+
internalType: 'address',
7+
name: 'module',
8+
type: 'address',
9+
},
10+
],
11+
name: 'enableModule',
12+
outputs: [],
13+
stateMutability: 'nonpayable',
14+
type: 'function',
15+
},
16+
{
17+
inputs: [
18+
{
19+
internalType: 'address',
20+
name: 'module',
21+
type: 'address',
22+
},
23+
],
24+
name: 'disableModule',
25+
outputs: [],
26+
stateMutability: 'nonpayable',
27+
type: 'function',
28+
},
29+
];
Lines changed: 176 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
1+
import { bytesToHex, randomBytes } from '@noble/hashes/utils';
12
import {
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';
1615
import { erc7579Actions } from 'permissionless/actions/erc7579';
16+
import type { Erc7579Actions } from 'permissionless/actions/erc7579';
1717
import { 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

2122
const 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
*/
2627
const 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

2930
const SAFE_7579_MODULE_ADDRESS = '0x7579EE8307284F293B1927136486880611F20002';
31+
const SAFE_4337_MODULE_ADDRESS = '0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226';
3032
const 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+
5059
export 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

96106
export 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

Comments
 (0)