diff --git a/.gitmodules b/.gitmodules index b62534a..13cdda8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,4 @@ [submodule "snap-account-abstraction-keyring"] path = snap-account-abstraction-keyring url = https://github.com/bobanetwork/snap-account-abstraction-keyring.git + branch = wsdt/nonce-gas \ No newline at end of file diff --git a/contracts/package.json b/contracts/package.json index b3f8a5c..0718bed 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -5,7 +5,8 @@ "scripts": { "build": "forge build", "test": "forge test && jest", - "deploy": "ts-node script/deploy-stack.ts" + "deploy": "ts-node script/deploy-stack.ts", + "deploy-local": "ts-node script/deploy-local.ts" }, "license": "MIT", "devDependencies": { diff --git a/contracts/script/deploy-local.ts b/contracts/script/deploy-local.ts index 4ca34b0..2252d06 100644 --- a/contracts/script/deploy-local.ts +++ b/contracts/script/deploy-local.ts @@ -4,8 +4,9 @@ import * as path from "path"; import { Readable } from "stream"; import * as dotenv from "dotenv"; import { ethers } from "ethers"; -import {DEFAULT_SNAP_VERSION, getLocalIpAddress} from "./utils"; +import {DEFAULT_SNAP_VERSION, getLocalIpAddress, isPortInUse} from "./utils"; import {execPromise} from './utils' +import {SimpleAccountFactory__factory} from "../typechain-types"; dotenv.config(); @@ -40,6 +41,7 @@ const ha1Privkey = const updateEnvVariable = (key: string, value: string, envPath: string) => { + console.log(`Updating ${key} = ${value}`) let envFile = fs.readFileSync(envPath, "utf8"); const regex = new RegExp(`^${key}=.*`, "m"); if (regex.test(envFile)) { @@ -105,22 +107,33 @@ const deleteIgnitionDeployments = () => { // TODO: fix .env file loading. Currently .env needs to be in /script directory async function main() { try { - await execPromise( - "pnpm install", - [], - path.resolve(__dirname, "../../boba") - ); - await execPromise( - "make devnet-hardhat-up", - [], - path.resolve(__dirname, "../../boba") - ); - + if (!isPortInUse(8545) && !isPortInUse(9545)) { + await execPromise( + "pnpm install", + [], + path.resolve(__dirname, "../../boba") + ); + + await execPromise( + "make devnet-hardhat-up", + [], + path.resolve(__dirname, "../../boba") + ); + } else { + console.log("Boba Chain already running, skipping") + } const fundL2Vars = { ...process.env, PRIVATE_KEY: deployKey, }; - await execPromise("node fundL2.js", undefined, undefined, fundL2Vars); + + await execPromise( + "node fundL2.js", + undefined, + path.resolve(__dirname, "../script/"), + fundL2Vars + ); + console.log("Funding L2 done..."); const baseDeployVars = { @@ -132,13 +145,20 @@ async function main() { BACKEND_URL: process.env.BACKEND_URL, }; - await execPromise( - "forge script --json --broadcast --silent --rpc-url=http://127.0.0.1:9545 deploy.s.sol", - undefined, - undefined, - baseDeployVars - ); + console.log('vars are: ', baseDeployVars) + try { + await execPromise( + "forge script --json --broadcast --rpc-url http://127.0.0.1:9545 deploy.s.sol", + undefined, + path.resolve(__dirname, "../script/"), + baseDeployVars + ); + }catch (e) { + console.log('error is: ', e); + } + const contracts = parseLocalDeployAddresses(); + const entrypoint = contracts?.find((c) => c.contractName === "EntryPoint")?.address const envVars = { HC_HELPER_ADDR: contracts?.find((c) => c.contractName === "HCHelper") ?.address, @@ -146,8 +166,7 @@ async function main() { ?.address, HC_SYS_OWNER: ha0Owner, HC_SYS_PRIVKEY: ha0Privkey, - ENTRY_POINTS: contracts?.find((c) => c.contractName === "EntryPoint") - ?.address, + ENTRY_POINTS: entrypoint, BUILDER_PRIVKEY: builderPrivkey, NODE_HTTP: `http://${getLocalIpAddress()}:9545`, CHAIN_ID: "901", @@ -160,7 +179,6 @@ async function main() { { ...process.env, ...envVars } ); - // deleteIgnitionDeployments(); console.log( "HA ADDRESS: ", @@ -180,11 +198,11 @@ async function main() { ); const tokenPriceMatch = ignitionOutput.match( - /TokenPrice#TokenPrice - (0x[a-fA-F0-9]{40})/ + /TokenPrice#TokenPrice - (0x[a-fA-F0-9]{40})/ ); if (!tokenPriceMatch) { throw new Error( - "Failed to extract TokenPrice address from Ignition output" + "Failed to extract TokenPrice address from Ignition output" ); } const tokenPriceAddress = tokenPriceMatch[1]; @@ -193,14 +211,14 @@ async function main() { // Frontend env vars const frontendEnvPath = path.resolve(__dirname, "../../frontend/.env-local"); updateEnvVariable( - "VITE_SMART_CONTRACT", - tokenPriceAddress, - frontendEnvPath + "VITE_SMART_CONTRACT", + tokenPriceAddress, + frontendEnvPath ); updateEnvVariable( - "VITE_RPC_PROVIDER", - "http://localhost:9545", - frontendEnvPath + "VITE_RPC_PROVIDER", + "http://localhost:9545", + frontendEnvPath ); updateEnvVariable( "VITE_SNAP_ORIGIN", @@ -218,21 +236,26 @@ async function main() { // Backend env vars const backendEnvPath = path.resolve(__dirname, "../../backend/.env"); updateEnvVariable( - "OC_HYBRID_ACCOUNT", - contracts?.find((c) => c.contractName === "HybridAccount")?.address ?? "", - backendEnvPath + "OC_HYBRID_ACCOUNT", + contracts?.find((c) => c.contractName === "HybridAccount")?.address ?? "", + backendEnvPath ); + + if (!entrypoint) { + throw Error("Entrypoint not defined!") + } + updateEnvVariable( - "ENTRY_POINTS", - contracts?.find((c) => c.contractName === "EntryPoint")?.address ?? "", - backendEnvPath + "ENTRY_POINTS", + entrypoint, + backendEnvPath ); updateEnvVariable("CHAIN_ID", "901", backendEnvPath); updateEnvVariable("OC_PRIVKEY", deployKey, backendEnvPath); updateEnvVariable( - "HC_HELPER_ADDR", - contracts?.find((c) => c.contractName === "HCHelper")?.address ?? "", - backendEnvPath + "HC_HELPER_ADDR", + contracts?.find((c) => c.contractName === "HCHelper")?.address ?? "", + backendEnvPath ); console.log("Backend ENV vars set..."); @@ -241,55 +264,76 @@ async function main() { const ENTRYPOINT = contracts?.find((c) => c.contractName === "EntryPoint")?.address ?? "" const contractsEnvPath = path.resolve(__dirname, "../.env"); updateEnvVariable( - "HYBRID_ACCOUNT", - contracts?.find((c) => c.contractName === "HybridAccount")?.address ?? "", - contractsEnvPath + "HYBRID_ACCOUNT", + contracts?.find((c) => c.contractName === "HybridAccount")?.address ?? "", + contractsEnvPath ); updateEnvVariable( - "ENTRY_POINT", - ENTRYPOINT, - contractsEnvPath + "ENTRY_POINT", + ENTRYPOINT, + contractsEnvPath ); updateEnvVariable( - "TOKEN_PRICE_CONTRACT", - tokenPriceAddress, - contractsEnvPath + "TOKEN_PRICE_CONTRACT", + tokenPriceAddress, + contractsEnvPath ); updateEnvVariable( - "HC_HELPER_ADDR", - contracts?.find((c) => c.contractName === "HCHelper")?.address ?? "", - contractsEnvPath + "HC_HELPER_ADDR", + contracts?.find((c) => c.contractName === "HCHelper")?.address ?? "", + contractsEnvPath ); updateEnvVariable("PRIVATE_KEY", deployKey, contractsEnvPath); updateEnvVariable( - "CLIENT_ADDR", - contracts?.find((c) => c.contractName === "SimpleAccount")?.address ?? "", - contractsEnvPath + "CLIENT_ADDR", + contracts?.find((c) => c.contractName === "SimpleAccount")?.address ?? "", + contractsEnvPath ); - console.log("Contracts ENV vars set..."); + /** @DEV Snap */ + const snapEnv = '../snap-account-abstraction-keyring/packages/snap/.env-local' + const l2provider = new ethers.JsonRpcProvider('http://localhost:9545'); + const SimpleAccountFactory = new SimpleAccountFactory__factory(new ethers.Wallet(deployKey, l2provider)); + const simpleAccountFactoryAddress = await SimpleAccountFactory.deploy(entrypoint!); + console.log('SimpleAccountFactory: ', simpleAccountFactoryAddress.target.toString()); - const snapEnv = '../../snap-account-abstraction-keyring/packages/snap/.env-local' - updateEnvVariable("LOCAL_ENTRYPOINT", ENTRYPOINT, snapEnv); - updateEnvVariable("LOCAL_SIMPLE_ACCOUNT_FACTORY", contracts?.find((c) => c.contractName === "SimpleAccountFactory")?.address ?? "", snapEnv) - updateEnvVariable("LOCAL_BOBAPAYMASTER", contracts?.find((c) => c.contractName === "TokenPaymaster")?.address ?? "", snapEnv) - updateEnvVariable("VERIFYING_PAYMASTER_ADDRESS", contracts?.find((c) => c.contractName === "VerifyingPaymaster")?.address ?? "", snapEnv) + const bobaVerifyingPaymaster = contracts?.find((c) => c.contractName === "VerifyingPaymaster")?.address; + console.log('Deployed SimpleAccountFactory', simpleAccountFactoryAddress.target); + let content = fs.readFileSync('../snap-account-abstraction-keyring/packages/snap/src/constants/aa-config.ts', 'utf8'); + content = content.replace(/entryPoint: '0x0'/, `entryPoint: '${ENTRYPOINT}'`); + content = content.replace(/simpleAccountFactory: '0x0'/, `simpleAccountFactory: '${simpleAccountFactoryAddress.target}'`); + content = content.replace(/bobaPaymaster: '0x0'/, `bobaPaymaster: '0x0}'`); - await execPromise( - "docker compose up -d", - [], - path.resolve(__dirname, "../../backend") + // Update the AA-config + fs.writeFileSync('../snap-account-abstraction-keyring/packages/snap/src/constants/aa-config.ts', content, 'utf8'); + + // Update LOCAL_ENTRYPOINT + updateEnvVariable( + "LOCAL_ENTRYPOINT", + ENTRYPOINT, + snapEnv ); - console.log("Backend container started..."); + // Update Account Factory + updateEnvVariable( + "LOCAL_SIMPLE_ACCOUNT_FACTORY", + simpleAccountFactoryAddress.target.toString(), + snapEnv + ); - await execPromise( - "docker compose up -d", - [], - path.resolve(__dirname, "../../frontend") + // Update Account Factory + updateEnvVariable( + "VERIFYING_PAYMASTER_ADDRESS", + bobaVerifyingPaymaster?.toString() ?? '0x0', + snapEnv ); - console.log("Frontend container started..."); + /** @DEV bootstrap frontend, backend and snap */ + await execPromise( + "docker-compose -f docker-compose.local.yml up", + [], + path.resolve(__dirname, "../../") + ); } catch (error) { console.error(error); } diff --git a/contracts/script/fundL2.js b/contracts/script/fundL2.js index a8cc1be..f324eb1 100644 --- a/contracts/script/fundL2.js +++ b/contracts/script/fundL2.js @@ -1,17 +1,33 @@ -const { ethers } = require('ethers'); +const {ethers} = require('ethers'); require("dotenv").config(); -const RPC_URL = 'http://localhost:8545'; +const RPC_URL_L1 = 'http://localhost:8545'; +const RPC_URL_L2 = 'http://localhost:9545'; const PRIVATE_KEY = process.env.PRIVATE_KEY; const L1StandardBridge = '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9'; -const provider = new ethers.JsonRpcProvider(RPC_URL); -const wallet = new ethers.Wallet(PRIVATE_KEY, provider); + +const l1provider = new ethers.JsonRpcProvider(RPC_URL_L1); +const l2provider = new ethers.JsonRpcProvider(RPC_URL_L2); + +const walletL2 = new ethers.Wallet(PRIVATE_KEY, l2provider); +const walletL1 = new ethers.Wallet(PRIVATE_KEY, l1provider); + async function main() { - const tx = { - to: L1StandardBridge, - value: ethers.parseEther('100.0') - }; - const response = await wallet.sendTransaction(tx); - await response.wait(); + if ((await l2provider.getBalance(walletL2)) > 1) { + console.log("Deployer already has funds on L2, continue"); + } else { + console.log('Funding L2...') + try { + const tx = { + to: L1StandardBridge, + value: ethers.parseEther('100') + }; + const response = await walletL1.sendTransaction(tx); + await response.wait(); + } catch (e) { + console.error("Error: ", e); + } + } } + main().catch(console.error); \ No newline at end of file diff --git a/contracts/script/utils.ts b/contracts/script/utils.ts index e8294b8..38eba5b 100644 --- a/contracts/script/utils.ts +++ b/contracts/script/utils.ts @@ -1,5 +1,5 @@ import * as os from "os"; -import { ProcessEnvOptions, spawn } from "child_process"; +import {execSync, ProcessEnvOptions, spawn} from "child_process"; import { Readable } from "stream"; import * as fs from "fs"; import * as dotenv from "dotenv"; @@ -38,6 +38,15 @@ export const updateEnvVariable = (key: string, value: string, customEnvPath?: st dotenv.config(); }; +export const isPortInUse = (port: number) => { + try { + execSync(`nc -z localhost ${port}`); + return true; + } catch (error) { + return false; + } +} + export const execPromise = ( command: string, inputs: string[] = [], @@ -79,7 +88,8 @@ export const execPromise = ( child.on("close", (code) => { if (code !== 0) { - reject(new Error(`Command failed with exit code ${code}`)); + console.log(stdout) + reject(new Error(`${command} Command failed with exit code ${code}`)); } else { resolve(stdout); }