diff --git a/contracts/package.json b/contracts/package.json index 784a31c..de52da8 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -23,7 +23,9 @@ "test": "forge test", "test:lite": "FOUNDRY_PROFILE=lite forge test", "test:optimized": "pnpm run build:optimized && FOUNDRY_PROFILE=test-optimized forge test", - "add:module": "bun run ./test/add-module-to-account.js" + "create:account": "bun run ./test/create-smart-account.js", + "add:module": "bun run ./test/add-module-to-account.js", + "create:backup": "bun run ./test/create-backup.js" }, "dependencies": { "@rhinestone/modulekit": "^0.4.7" diff --git a/contracts/test/add-module-to-account.js b/contracts/test/add-module-to-account.js index 95f5c47..fb84aac 100644 --- a/contracts/test/add-module-to-account.js +++ b/contracts/test/add-module-to-account.js @@ -1,60 +1,11 @@ -import { - ENTRYPOINT_ADDRESS_V07, - createSmartAccountClient, -} from "permissionless"; -import { signerToSafeSmartAccount } from "permissionless/accounts"; -import { - createPimlicoBundlerClient, -} from "permissionless/clients/pimlico"; -import { createPublicClient, http, encodePacked } from "viem"; -import { sepolia } from "viem/chains"; -import {generatePrivateKey, privateKeyToAccount} from "viem/accounts"; -import {erc7579Actions} from "permissionless/actions/erc7579.js"; -import {pimlicoPaymasterActions} from "permissionless/actions/pimlico"; -import {abi} from "./abi"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { abi } from "./abi"; +import { createSmartWallet } from "./create-smart-account"; -const apiKey = "" +const beneficiaryPK = generatePrivateKey(); -const privateKey = generatePrivateKey() - -export const bundlerUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${apiKey}` - -const signer = privateKeyToAccount(privateKey); - - -const publicClient = createPublicClient({ - transport: http("https://rpc.ankr.com/eth_sepolia"), -}) - -const pimlicoBundlerClient = createPimlicoBundlerClient({ - transport: http(bundlerUrl), - entryPoint: ENTRYPOINT_ADDRESS_V07, -}).extend(pimlicoPaymasterActions(ENTRYPOINT_ADDRESS_V07)); - -const safeAccount = await signerToSafeSmartAccount(publicClient, { - signer, - safeVersion: "1.4.1", - entryPoint: ENTRYPOINT_ADDRESS_V07, - safe4337ModuleAddress: "0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2", - erc7579LaunchpadAddress: "0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE", -}) - -const smartAccountClient = createSmartAccountClient({ - account: safeAccount, - entryPoint: ENTRYPOINT_ADDRESS_V07, - chain: sepolia, - bundlerTransport: http(bundlerUrl), - middleware: { - gasPrice: async () => { - return (await pimlicoBundlerClient.getUserOperationGasPrice()).fast - }, - sponsorUserOperation: pimlicoBundlerClient.sponsorUserOperation - - }, -}).extend(erc7579Actions({ entryPoint: ENTRYPOINT_ADDRESS_V07 })) - -const beneficiary = privateKeyToAccount(privateKey); -const timeout = 100; //in seconds +const timeout = 123123123; +const beneficiary = privateKeyToAccount(beneficiaryPK); async function installModule({smartClient, beneficiaryAddress, timeout, moduleType, hook, account, bundlerClient, moduleAddress, publicClient}) { const isInitialized = (await publicClient.readContract({ @@ -66,9 +17,7 @@ async function installModule({smartClient, beneficiaryAddress, timeout, moduleTy const module = { module: moduleAddress, - initData: isInitialized - ? '0x' - : encodePacked(['address', 'uint48'], [beneficiaryAddress, timeout]), + initData: '0x', deInitData: '0x', additionalContext: '0x', type: moduleType, @@ -85,14 +34,20 @@ async function installModule({smartClient, beneficiaryAddress, timeout, moduleTy const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: opHash }) } +const { smartAccountClient, safeAccount, pimlicoBundlerClient, publicClient } = createSmartWallet({ + privateKey: "", + bundlerUrl: "" +}) + installModule({ smartClient: smartAccountClient, beneficiaryAddress: beneficiary.address, timeout, - moduleType: "validator", + moduleType: "validator", //todo: executor hook: '0x', account: safeAccount, bundlerClient: pimlicoBundlerClient, moduleAddress: "0x6A53E204b8A21dfD64516ff27484e6113640CB96", publicClient -}) \ No newline at end of file +}); + diff --git a/contracts/test/backup-abi.js b/contracts/test/backup-abi.js new file mode 100644 index 0000000..7caca8f --- /dev/null +++ b/contracts/test/backup-abi.js @@ -0,0 +1,26 @@ +export const generateBackupAbiWithArgs = ({name, unlockAt, beneficiaries}) => ({ + abi: [ + { + name: "updateBackup", + type: "function", + stateMutability: "nonpayable", + inputs: [ + { internalType: 'string', name: 'name', type: 'string' }, + { internalType: 'uint48', name: 'unlockAt', type: 'uint48' }, + { + internalType: 'struct Wingman.Beneficiary[]', + name: 'beneficiaries', + type: 'tuple[]', + components: [ + { internalType: 'address', name: 'account', type: 'address' }, + { internalType: 'uint8', name: 'percentage', type: 'uint8' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' } + ] + } + ], + outputs: [] + } + ], + functionName: "updateBackup", + args: [name, unlockAt, beneficiaries] +}) diff --git a/contracts/test/create-backup.js b/contracts/test/create-backup.js new file mode 100644 index 0000000..c28a452 --- /dev/null +++ b/contracts/test/create-backup.js @@ -0,0 +1,72 @@ +import {parseAccount, sendUserOperation} from "permissionless"; +import {generateBackupAbiWithArgs} from "./backup-abi"; +import {createSmartWallet} from "./create-smart-account"; + +async function updateBackup({ + smartClient, + accountAddress, + moduleAddress, + name, + unlockAt, + beneficiaries, + bundlerClient +}) { + const account = parseAccount(accountAddress); + + const updateBackupCallData = account.encodeFunctionData( + generateBackupAbiWithArgs({name, unlockAt, beneficiaries}) + ); + + const opHash = await smartClient.sendUserOperation({ + sender: account.address, + updateBackupCallData, + to: moduleAddress + }); + + const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: opHash }); + return receipt; +} + +async function main({ + name, + unlockAt, + beneficiaries +}) { + const { + smartAccountClient, + safeAccount, + pimlicoBundlerClient + } = createSmartWallet({ + privateKey: "", + bundlerUrl: "", + }); + + await updateBackup({ + smartClient: smartAccountClient, + accountAddress: safeAccount.address, + moduleAddress: "0xab614e4a5398bb2a2a0bf73f9c913ec7ff47d81f", + name, + unlockAt, + beneficiaries, + bundlerClient: pimlicoBundlerClient + }); + + console.log("Backup updated successfully!"); +} + +main({ + name: "name", + unlockAt: 1231231233, + beneficiaries: [ + { + account: "0xAddress1", + percentage: 50, + amount: BigInt(1000) + }, + { + account: "0xAddress2", + percentage: 50, + amount: BigInt(1000) + } + ] +}) diff --git a/contracts/test/create-smart-account.js b/contracts/test/create-smart-account.js new file mode 100644 index 0000000..f1600a3 --- /dev/null +++ b/contracts/test/create-smart-account.js @@ -0,0 +1,54 @@ +import { + ENTRYPOINT_ADDRESS_V07, + createSmartAccountClient, +} from "permissionless"; +import { signerToSafeSmartAccount } from "permissionless/accounts"; +import { createPimlicoBundlerClient } from "permissionless/clients/pimlico"; +import { createPublicClient, http } from "viem"; +import { sepolia } from "viem/chains"; +import { privateKeyToAccount } from "viem/accounts"; +import { erc7579Actions } from "permissionless/actions/erc7579.js"; +import { pimlicoPaymasterActions } from "permissionless/actions/pimlico"; + + +export async function createSmartWallet({privateKey, bundlerUrl}) { + const signer = privateKeyToAccount(privateKey); + + + const publicClient = createPublicClient({ + transport: http("https://rpc.ankr.com/eth_sepolia"), + }) + + const pimlicoBundlerClient = createPimlicoBundlerClient({ + transport: http(bundlerUrl), + entryPoint: ENTRYPOINT_ADDRESS_V07, + }).extend(pimlicoPaymasterActions(ENTRYPOINT_ADDRESS_V07)); + + const safeAccount = await signerToSafeSmartAccount(publicClient, { + signer, + safeVersion: "1.4.1", + entryPoint: ENTRYPOINT_ADDRESS_V07, + safe4337ModuleAddress: "0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2", + erc7579LaunchpadAddress: "0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE", + }) + + const smartAccountClient = createSmartAccountClient({ + account: safeAccount, + entryPoint: ENTRYPOINT_ADDRESS_V07, + chain: sepolia, + bundlerTransport: http(bundlerUrl), + middleware: { + gasPrice: async () => { + return (await pimlicoBundlerClient.getUserOperationGasPrice()).fast + }, + sponsorUserOperation: pimlicoBundlerClient.sponsorUserOperation + }, + }).extend(erc7579Actions({ entryPoint: ENTRYPOINT_ADDRESS_V07 })) + + return { smartAccountClient, safeAccount, pimlicoBundlerClient, publicClient } +} + +createSmartWallet({ + privateKey: "0x30320097c1d7009d6d970376c792fe157a5e989f057b8908345043393a56a8a5", + bundlerUrl: "https://api.pimlico.io/v2/sepolia/rpc?apikey=04287e53-84d2-413b-bbc6-450629d7e999", +})