From 28f05459e51813551db228bb398bef1236a5fbb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 29 Nov 2024 14:33:58 +0100 Subject: [PATCH 1/4] Install and configure Prettier --- safe-eip7702-backend/.prettierrc.json | 6 + safe-eip7702-backend/package-lock.json | 213 +++- safe-eip7702-backend/package.json | 4 +- safe-eip7702-contracts/.prettierrc.json | 6 + safe-eip7702-contracts/package-lock.json | 1370 ++++++++++++++++++++-- safe-eip7702-contracts/package.json | 6 +- safe-eip7702-ui/.prettierrc.json | 6 + safe-eip7702-ui/package-lock.json | 714 +++++------ safe-eip7702-ui/package.json | 4 +- 9 files changed, 1845 insertions(+), 484 deletions(-) create mode 100644 safe-eip7702-backend/.prettierrc.json create mode 100644 safe-eip7702-contracts/.prettierrc.json create mode 100644 safe-eip7702-ui/.prettierrc.json diff --git a/safe-eip7702-backend/.prettierrc.json b/safe-eip7702-backend/.prettierrc.json new file mode 100644 index 0000000..855a65a --- /dev/null +++ b/safe-eip7702-backend/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true +} \ No newline at end of file diff --git a/safe-eip7702-backend/package-lock.json b/safe-eip7702-backend/package-lock.json index f7f5118..bfa0c23 100644 --- a/safe-eip7702-backend/package-lock.json +++ b/safe-eip7702-backend/package-lock.json @@ -20,8 +20,10 @@ "dotenv": "^16.4.5", "express": "^4.21.0", "nodemon": "^3.1.7", + "prettier": "^3.4.1", "ts-node": "^10.9.2", - "typescript": "^5.6.2" + "typescript": "^5.6.2", + "yargs": "^17.7.2" } }, "node_modules/@adraffy/ens-normalize": { @@ -321,6 +323,32 @@ "node": ">=0.4.0" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -462,6 +490,41 @@ "fsevents": "~2.3.2" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -595,6 +658,13 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -625,6 +695,16 @@ "node": ">= 0.4" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -753,6 +833,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -923,6 +1013,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1171,6 +1271,22 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prettier": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1241,6 +1357,16 @@ "node": ">=8.10.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1395,6 +1521,34 @@ "node": ">= 0.8" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1596,6 +1750,24 @@ "@noble/hashes": "^1.4.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", @@ -1616,6 +1788,45 @@ } } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/safe-eip7702-backend/package.json b/safe-eip7702-backend/package.json index d138d95..2af07a8 100644 --- a/safe-eip7702-backend/package.json +++ b/safe-eip7702-backend/package.json @@ -7,7 +7,8 @@ "start": "ts-node src/index.ts", "build": "tsc", "serve": "node dist/index.js", - "dev": "nodemon src/index.ts" + "dev": "nodemon src/index.ts", + "fmt": "prettier src/{*,**/*}.ts -w" }, "keywords": [], "author": "", @@ -21,6 +22,7 @@ "dotenv": "^16.4.5", "express": "^4.21.0", "nodemon": "^3.1.7", + "prettier": "^3.4.1", "ts-node": "^10.9.2", "typescript": "^5.6.2", "yargs": "^17.7.2" diff --git a/safe-eip7702-contracts/.prettierrc.json b/safe-eip7702-contracts/.prettierrc.json new file mode 100644 index 0000000..855a65a --- /dev/null +++ b/safe-eip7702-contracts/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true +} \ No newline at end of file diff --git a/safe-eip7702-contracts/package-lock.json b/safe-eip7702-contracts/package-lock.json index 38d984a..804bd97 100644 --- a/safe-eip7702-contracts/package-lock.json +++ b/safe-eip7702-contracts/package-lock.json @@ -8,17 +8,44 @@ "name": "@safe-global/safe-fallback-handler", "version": "0.0.1", "license": "LGPL-3.0", + "dependencies": { + "@account-abstraction/contracts": "^0.7.0", + "@safe-global/safe-smart-account": "github:safe-global/safe-smart-account#feature/eip-7702" + }, "devDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.6", "@nomicfoundation/hardhat-toolbox": "^5.0.0", + "@types/chai": "4", + "@types/mocha": "^10.0.9", + "@types/node": "^22.7.7", + "@types/yargs": "^17.0.33", + "chai": "4", + "dotenv": "^16.4.5", "eslint": "^9.11.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-prettier": "^5.2.1", "ethers": "^6.13.2", "hardhat": "^2.22.12", - "prettier": "^3.3.3", + "hardhat-deploy": "^0.12.4", + "hardhat-deploy-ethers": "^0.4.2", + "prettier": "^3.4.1", "prettier-plugin-solidity": "^1.4.1", - "solhint": "^5.0.3" + "rimraf": "^6.0.1", + "solhint": "^5.0.3", + "ts-node": "^10.9.2", + "tsx": "^4.19.1", + "typescript": "^5.6.3" + } + }, + "node_modules/@account-abstraction/contracts": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@account-abstraction/contracts/-/contracts-0.7.0.tgz", + "integrity": "sha512-Bt/66ilu3u8I9+vFZ9fTd+cWs55fdb9J5YKfrhsrFafH1drkzwuCSL/xEot1GGyXXNJLQuXbMRztQPyelNbY1A==", + "license": "MIT", + "dependencies": { + "@openzeppelin/contracts": "^5.0.0", + "@uniswap/v3-periphery": "^1.4.3" } }, "node_modules/@adraffy/ens-normalize": { @@ -140,7 +167,6 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -148,6 +174,414 @@ "node": ">=12" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -515,7 +949,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/bytes": "^5.7.0", "@ethersproject/properties": "^5.7.0" @@ -595,7 +1028,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/abi": "^5.7.0", "@ethersproject/abstract-provider": "^5.7.0", @@ -651,7 +1083,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/basex": "^5.7.0", @@ -682,7 +1113,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/address": "^5.7.0", @@ -703,8 +1133,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@ethersproject/keccak256": { "version": "5.7.0", @@ -776,7 +1205,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/bytes": "^5.7.0", "@ethersproject/sha2": "^5.7.0" @@ -816,7 +1244,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0", @@ -845,7 +1272,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "dev": true, - "peer": true, "engines": { "node": ">=8.3.0" }, @@ -877,7 +1303,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/bytes": "^5.7.0", "@ethersproject/logger": "^5.7.0" @@ -918,7 +1343,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/bytes": "^5.7.0", "@ethersproject/logger": "^5.7.0", @@ -964,7 +1388,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/bignumber": "^5.7.0", "@ethersproject/bytes": "^5.7.0", @@ -1037,7 +1460,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/bignumber": "^5.7.0", "@ethersproject/constants": "^5.7.0", @@ -1059,7 +1481,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0", @@ -1116,7 +1537,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "peer": true, "dependencies": { "@ethersproject/bytes": "^5.7.0", "@ethersproject/hash": "^5.7.0", @@ -1134,30 +1554,133 @@ "node": ">=14" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=12.22" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, "engines": { - "node": ">=18.18" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1165,7 +1688,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "peer": true, "engines": { "node": ">=6.0.0" } @@ -1174,15 +1696,13 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1475,7 +1995,6 @@ "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.8.tgz", "integrity": "sha512-zhOZ4hdRORls31DTOqg+GmEZM0ujly8GGIuRY7t7szEk2zW/arY1qDug/py8AEktT00v5K+b6RvbVog+va51IA==", "dev": true, - "peer": true, "dependencies": { "debug": "^4.1.1", "lodash.isequal": "^4.5.0" @@ -1807,6 +2326,12 @@ "node": ">= 12" } }, + "node_modules/@openzeppelin/contracts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.1.0.tgz", + "integrity": "sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==", + "license": "MIT" + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -1860,6 +2385,11 @@ "node": ">=12" } }, + "node_modules/@safe-global/safe-smart-account": { + "version": "1.4.1-build.0", + "resolved": "git+ssh://git@github.com/safe-global/safe-smart-account.git#b4ca9edd180b96c541c8127abefb3f54d098ebe0", + "license": "LGPL-3.0" + }, "node_modules/@scure/base": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", @@ -2113,29 +2643,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@typechain/ethers-v6": { "version": "0.5.1", @@ -2198,8 +2724,7 @@ "version": "4.3.20", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/chai-as-promised": { "version": "7.1.8", @@ -2274,19 +2799,20 @@ "peer": true }, "node_modules/@types/mocha": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.8.tgz", - "integrity": "sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw==", + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", "dev": true, - "peer": true + "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.2.tgz", - "integrity": "sha512-866lXSrpGpgyHBZUa2m9YNWqHDjjM0aBTJlNtYaGEw4rqY/dcD7deRVTbBBAJelfA7oaGDbNftXF/TL/A6RgoA==", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, "node_modules/@types/pbkdf2": { @@ -2309,8 +2835,7 @@ "version": "6.9.16", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/secp256k1": { "version": "4.0.6", @@ -2321,6 +2846,72 @@ "@types/node": "*" } }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@uniswap/lib": { + "version": "4.0.1-alpha", + "resolved": "https://registry.npmjs.org/@uniswap/lib/-/lib-4.0.1-alpha.tgz", + "integrity": "sha512-f6UIliwBbRsgVLxIaBANF6w09tYqc6Y/qXdsrbEmXHyFA7ILiKrIwRFXe1yOg8M3cksgVsO9N7yuL2DdCGQKBA==", + "license": "GPL-3.0-or-later", + "engines": { + "node": ">=10" + } + }, + "node_modules/@uniswap/v2-core": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@uniswap/v2-core/-/v2-core-1.0.1.tgz", + "integrity": "sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q==", + "license": "GPL-3.0-or-later", + "engines": { + "node": ">=10" + } + }, + "node_modules/@uniswap/v3-core": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@uniswap/v3-core/-/v3-core-1.0.1.tgz", + "integrity": "sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ==", + "license": "BUSL-1.1", + "engines": { + "node": ">=10" + } + }, + "node_modules/@uniswap/v3-periphery": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@uniswap/v3-periphery/-/v3-periphery-1.4.4.tgz", + "integrity": "sha512-S4+m+wh8HbWSO3DKk4LwUCPZJTpCugIsHrWR86m/OrUyvSqGDTXKFfc2sMuGXCZrD1ZqO3rhQsKgdWg3Hbb2Kw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@openzeppelin/contracts": "3.4.2-solc-0.7", + "@uniswap/lib": "^4.0.1-alpha", + "@uniswap/v2-core": "^1.0.1", + "@uniswap/v3-core": "^1.0.0", + "base64-sol": "1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@uniswap/v3-periphery/node_modules/@openzeppelin/contracts": { + "version": "3.4.2-solc-0.7", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-3.4.2-solc-0.7.tgz", + "integrity": "sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA==", + "license": "MIT" + }, "node_modules/abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -2354,7 +2945,6 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, - "peer": true, "dependencies": { "acorn": "^8.11.0" }, @@ -2519,8 +3109,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/argparse": { "version": "2.0.1", @@ -2570,7 +3159,6 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, - "peer": true, "engines": { "node": "*" } @@ -2601,8 +3189,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "peer": true + "dev": true }, "node_modules/at-least-node": { "version": "1.0.0", @@ -2641,12 +3228,17 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/base64-sol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/base64-sol/-/base64-sol-1.0.1.tgz", + "integrity": "sha512-ld3cCNMeXt4uJXmLZBHFGMvVpK9KsLVEhPpFRXnvSVAqABKbuNZg/+dsq3NuM+wxFLb/UrVkz7m1ciWmkMfTbg==", + "license": "MIT" + }, "node_modules/bech32": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -2827,7 +3419,6 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, - "peer": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -2888,7 +3479,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, - "peer": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -2946,7 +3536,6 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, - "peer": true, "dependencies": { "get-func-name": "^2.0.2" }, @@ -3114,7 +3703,6 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -3405,8 +3993,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -3515,7 +4102,6 @@ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, - "peer": true, "dependencies": { "type-detect": "^4.0.0" }, @@ -3552,7 +4138,6 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, - "peer": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3570,7 +4155,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "peer": true, "engines": { "node": ">=0.4.0" } @@ -3619,6 +4203,26 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -3646,6 +4250,13 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "dev": true, + "license": "MIT" + }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -3682,7 +4293,6 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, - "peer": true, "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -3695,11 +4305,50 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "peer": true, "engines": { "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4597,6 +5246,16 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/fmix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fmix/-/fmix-0.1.0.tgz", + "integrity": "sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "imul": "^1.0.0" + } + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -4617,12 +5276,28 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, - "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4652,7 +5327,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -4694,7 +5368,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4713,7 +5386,6 @@ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, - "peer": true, "engines": { "node": "*" } @@ -4723,7 +5395,6 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, - "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -4760,6 +5431,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/ghost-testrpc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", @@ -4950,7 +5634,6 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, - "peer": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -5079,13 +5762,155 @@ "ts-node": "*", "typescript": "*" }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - }, - "typescript": { - "optional": true - } + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-deploy": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/hardhat-deploy/-/hardhat-deploy-0.12.4.tgz", + "integrity": "sha512-bYO8DIyeGxZWlhnMoCBon9HNZb6ji0jQn7ngP1t5UmGhC8rQYhji7B73qETMOFhzt5ECZPr+U52duj3nubsqdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/contracts": "^5.7.0", + "@ethersproject/providers": "^5.7.2", + "@ethersproject/solidity": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wallet": "^5.7.0", + "@types/qs": "^6.9.7", + "axios": "^0.21.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.2", + "debug": "^4.3.2", + "enquirer": "^2.3.6", + "ethers": "^5.7.0", + "form-data": "^4.0.0", + "fs-extra": "^10.0.0", + "match-all": "^1.2.6", + "murmur-128": "^0.2.1", + "qs": "^6.9.4", + "zksync-ethers": "^5.0.0" + } + }, + "node_modules/hardhat-deploy-ethers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/hardhat-deploy-ethers/-/hardhat-deploy-ethers-0.4.2.tgz", + "integrity": "sha512-AskNH/XRYYYqPT94MvO5s1yMi+/QvoNjS4oU5VcVqfDU99kgpGETl+uIYHIrSXtH5sy7J6gyVjpRMf4x0tjLSQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.2", + "hardhat": "^2.16.0", + "hardhat-deploy": "^0.12.0" + } + }, + "node_modules/hardhat-deploy/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/hardhat-deploy/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/hardhat-deploy/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, + "node_modules/hardhat-deploy/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, "node_modules/hardhat-gas-reporter": { @@ -5298,7 +6123,6 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, - "peer": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -5311,7 +6135,6 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, - "peer": true, "engines": { "node": ">= 0.4" }, @@ -5324,7 +6147,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "peer": true, "engines": { "node": ">= 0.4" }, @@ -5361,7 +6183,6 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, - "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -5540,6 +6361,16 @@ "node": ">=4" } }, + "node_modules/imul": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/imul/-/imul-1.0.1.tgz", + "integrity": "sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -5710,6 +6541,22 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -5792,7 +6639,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -5926,8 +6772,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5962,7 +6807,6 @@ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, - "peer": true, "dependencies": { "get-func-name": "^2.0.1" } @@ -5985,12 +6829,21 @@ "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", "dev": true }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/markdown-table": { "version": "1.1.3", @@ -5999,6 +6852,13 @@ "dev": true, "peer": true }, + "node_modules/match-all": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/match-all/-/match-all-1.2.6.tgz", + "integrity": "sha512-0EESkXiTkWzrQQntBu2uzKvLu6vVkUGz40nGPbSZuegcfE5UuSzNjLaIu76zJWuaT/2I3Z/8M06OlUOZLGwLlQ==", + "dev": true, + "license": "MIT" + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -6055,7 +6915,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "peer": true, "engines": { "node": ">= 0.6" } @@ -6065,7 +6924,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -6118,6 +6976,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -6343,6 +7211,18 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/murmur-128": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/murmur-128/-/murmur-128-0.2.1.tgz", + "integrity": "sha512-WseEgiRkI6aMFBbj8Cg9yBj/y+OdipwVC7zUo3W2W1JAJITwouUOtpqsmGSg67EQmwwSyod7hsVsWY5LsrfQVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "encode-utf8": "^1.0.2", + "fmix": "^0.1.0", + "imul": "^1.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6484,7 +7364,6 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, - "peer": true, "engines": { "node": ">= 0.4" }, @@ -6616,6 +7495,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/package-json/node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -6698,6 +7584,23 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6712,7 +7615,6 @@ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, - "peer": true, "engines": { "node": "*" } @@ -6781,10 +7683,11 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -6899,7 +7802,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, - "peer": true, "dependencies": { "side-channel": "^1.0.6" }, @@ -7152,6 +8054,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/responselike": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", @@ -7177,6 +8089,76 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -7404,7 +8386,6 @@ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "peer": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7500,7 +8481,6 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, - "peer": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -7514,6 +8494,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -7993,6 +8986,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -8005,6 +9014,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -8266,7 +9289,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -8310,7 +9332,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "peer": true, "engines": { "node": ">=0.3.1" } @@ -8327,6 +9348,26 @@ "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", "dev": true }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -8357,7 +9398,6 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "peer": true, "engines": { "node": ">=4" } @@ -8493,11 +9533,11 @@ "peer": true }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, - "peer": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8543,17 +9583,17 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "peer": true, "engines": { "node": ">= 10.0.0" } @@ -8602,8 +9642,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/web3-utils": { "version": "1.10.4", @@ -8752,6 +9791,25 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8835,7 +9893,6 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "peer": true, "engines": { "node": ">=6" } @@ -8851,6 +9908,71 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zksync-ethers": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/zksync-ethers/-/zksync-ethers-5.10.0.tgz", + "integrity": "sha512-OAjTGAHF9wbdkRGkj7XZuF/a1Sk/FVbwH4pmLjAKlR7mJ7sQtQhBhrPU2dCc67xLaNvEESPfwil19ES5wooYFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ethers": "~5.7.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "ethers": "~5.7.0" + } + }, + "node_modules/zksync-ethers/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } } } } diff --git a/safe-eip7702-contracts/package.json b/safe-eip7702-contracts/package.json index 4b15fd3..950e9cd 100644 --- a/safe-eip7702-contracts/package.json +++ b/safe-eip7702-contracts/package.json @@ -18,8 +18,8 @@ "lint:ts:fix": "eslint 'src/**/*.ts' 'test/**/*.ts' --fix", "lint:ts:prettier": "prettier 'src/**/*.ts' 'test/**/*.ts' --list-different", "fmt": "npm run fmt:sol && npm run fmt:ts", - "fmt:sol": "prettier 'contracts/**/*.sol' -w", - "fmt:ts": "prettier 'src/**/*.ts' 'test/**/*.ts' -w" + "fmt:sol": "prettier contracts/{*,**/*}.sol -w --config .prettierrc", + "fmt:ts": "prettier {test,src}/{*,**/*}.ts -w --config .prettierrc.json" }, "devDependencies": { "@nomicfoundation/hardhat-ethers": "^3.0.6", @@ -38,7 +38,7 @@ "hardhat": "^2.22.12", "hardhat-deploy": "^0.12.4", "hardhat-deploy-ethers": "^0.4.2", - "prettier": "^3.3.3", + "prettier": "^3.4.1", "prettier-plugin-solidity": "^1.4.1", "rimraf": "^6.0.1", "solhint": "^5.0.3", diff --git a/safe-eip7702-ui/.prettierrc.json b/safe-eip7702-ui/.prettierrc.json new file mode 100644 index 0000000..855a65a --- /dev/null +++ b/safe-eip7702-ui/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "none", + "tabWidth": 2, + "semi": false, + "singleQuote": true +} \ No newline at end of file diff --git a/safe-eip7702-ui/package-lock.json b/safe-eip7702-ui/package-lock.json index 4e39a70..56f544d 100644 --- a/safe-eip7702-ui/package-lock.json +++ b/safe-eip7702-ui/package-lock.json @@ -10,24 +10,26 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@mui/icons-material": "^6.1.3", - "@mui/material": "^6.1.3", + "@mui/icons-material": "^6.1.5", + "@mui/material": "^6.1.5", "@tanstack/react-query": "5.45.1", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "react-router-dom": "^6.26.2", - "viem": "latest", + "permissionless": "^0.2.15", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.27.0", + "viem": "^2.21.34", "wagmi": "latest" }, "devDependencies": { - "@biomejs/biome": "^1.8.0", - "@types/react": "^18.3.1", - "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react": "^4.2.1", + "@biomejs/biome": "^1.9.4", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", "@wagmi/cli": "latest", "buffer": "^6.0.3", - "typescript": "^5.4.5", - "vite": "^5.2.11" + "prettier": "^3.4.1", + "typescript": "^5.6.3", + "vite": "^5.4.10" }, "peerDependencies": { "react": "^18.0.0", @@ -37,13 +39,15 @@ "node_modules/@adraffy/ens-normalize": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz", - "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==" + "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==", + "license": "MIT" }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -53,11 +57,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -65,30 +71,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", - "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", - "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -104,11 +112,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -118,13 +128,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -134,27 +145,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -164,85 +176,64 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", - "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -252,12 +243,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.7.tgz", - "integrity": "sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -267,12 +259,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.7.tgz", - "integrity": "sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -282,9 +275,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", - "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -293,28 +287,30 @@ } }, "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -323,24 +319,25 @@ } }, "node_modules/@babel/types": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", - "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@biomejs/biome": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.3.tgz", - "integrity": "sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", + "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", "dev": true, "hasInstallScript": true, + "license": "MIT OR Apache-2.0", "bin": { "biome": "bin/biome" }, @@ -352,24 +349,25 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.3", - "@biomejs/cli-darwin-x64": "1.9.3", - "@biomejs/cli-linux-arm64": "1.9.3", - "@biomejs/cli-linux-arm64-musl": "1.9.3", - "@biomejs/cli-linux-x64": "1.9.3", - "@biomejs/cli-linux-x64-musl": "1.9.3", - "@biomejs/cli-win32-arm64": "1.9.3", - "@biomejs/cli-win32-x64": "1.9.3" + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.3.tgz", - "integrity": "sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", + "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" @@ -379,13 +377,14 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.3.tgz", - "integrity": "sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", + "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" @@ -395,13 +394,14 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.3.tgz", - "integrity": "sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", + "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -411,13 +411,14 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.3.tgz", - "integrity": "sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", + "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -427,13 +428,14 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.3.tgz", - "integrity": "sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -443,13 +445,14 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.3.tgz", - "integrity": "sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -459,13 +462,14 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.3.tgz", - "integrity": "sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", + "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" @@ -475,13 +479,14 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.3.tgz", - "integrity": "sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", + "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" @@ -546,13 +551,14 @@ } }, "node_modules/@emotion/cache": { - "version": "11.13.1", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", - "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.5.tgz", + "integrity": "sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==", + "license": "MIT", "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.0", + "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } @@ -599,14 +605,15 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz", - "integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.1", + "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, @@ -651,9 +658,10 @@ } }, "node_modules/@emotion/utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz", - "integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" }, "node_modules/@emotion/weak-memoize": { "version": "0.4.0", @@ -1605,20 +1613,22 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.3.tgz", - "integrity": "sha512-ajMUgdfhTb++rwqj134Cq9f4SRN8oXUqMRnY72YBnXiXai3olJLLqETheRlq3MM8wCKrbq7g6j7iWL1VvP44VQ==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.9.tgz", + "integrity": "sha512-TWqj7b1w5cmSz4H/uf+y2AHxAH4ldPR7D2bz0XVyn60GCAo/zRbRPx7cF8gTs/i7CiYeHzV6dtat0VpMwOtolw==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.3.tgz", - "integrity": "sha512-QBQCCIMSAv6IkArTg4Hg8q2sJRhHOci8oPAlkHWFlt2ghBdy3EqyLbIELLE/bhpqhX+E/ZkPYGIUQCd5/L0owA==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.9.tgz", + "integrity": "sha512-AzlhIT51rdjkZ/EcUV2dbhNkNSUHIqCnNoUxodpiTw8buyAUBd+qnxg5OBSuPpun/ZEdSSB8Q7Uyh6zqjiMsEQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.25.6" + "@babel/runtime": "^7.26.0" }, "engines": { "node": ">=14.0.0" @@ -1628,7 +1638,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.1.3", + "@mui/material": "^6.1.9", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1639,15 +1649,16 @@ } }, "node_modules/@mui/material": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.3.tgz", - "integrity": "sha512-loV5MBoMKLrK80JeWINmQ1A4eWoLv51O2dBPLJ260IAhupkB3Wol8lEQTEvvR2vO3o6xRHuXe1WaQEP6N3riqg==", - "dependencies": { - "@babel/runtime": "^7.25.6", - "@mui/core-downloads-tracker": "^6.1.3", - "@mui/system": "^6.1.3", - "@mui/types": "^7.2.18", - "@mui/utils": "^6.1.3", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.9.tgz", + "integrity": "sha512-NwqIN0bdsgzSbZd5JFcC+2ez0XW/XNs8uiV2PDHrqQ4qf/FEasFJG1z6g8JbCN0YlTrHZekVb17X0Fv0qcYJfQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.1.9", + "@mui/system": "^6.1.9", + "@mui/types": "^7.2.19", + "@mui/utils": "^6.1.9", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", @@ -1666,7 +1677,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.1.3", + "@mui/material-pigment-css": "^6.1.9", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1695,12 +1706,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.3.tgz", - "integrity": "sha512-XK5OYCM0x7gxWb/WBEySstBmn+dE3YKX7U7jeBRLm6vHU5fGUd7GiJWRirpivHjOK9mRH6E1MPIVd+ze5vguKQ==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.9.tgz", + "integrity": "sha512-7aum/O1RquBYhfwL/7egDyl9GqJgPM6hoJDFFBbhF6Sgv9yI9v4w3ArKUkuVvR0CtVj4NXRVMKEioh1bjUzvuA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.25.6", - "@mui/utils": "^6.1.3", + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.1.9", "prop-types": "^15.8.1" }, "engines": { @@ -1721,13 +1733,14 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.3.tgz", - "integrity": "sha512-i4yh9m+eMZE3cNERpDhVr6Wn73Yz6C7MH0eE2zZvw8d7EFkIJlCQNZd1xxGZqarD2DDq2qWHcjIOucWGhxACtA==", - "dependencies": { - "@babel/runtime": "^7.25.6", - "@emotion/cache": "^11.13.1", - "@emotion/serialize": "^1.3.2", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.9.tgz", + "integrity": "sha512-xynSLlJRxHLzSfQaiDjkaTx8LiFb9ByVa7aOdwFnTxGWFMY1F+mkXwAUY4jDDE+MAxkWxlzzQE0wOohnsxhdQg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1754,15 +1767,16 @@ } }, "node_modules/@mui/system": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.3.tgz", - "integrity": "sha512-ILaD9UsLTBLjMcep3OumJMXh1PYr7aqnkHm/L47bH46+YmSL1zWAX6tWG8swEQROzW2GvYluEMp5FreoxOOC6w==", - "dependencies": { - "@babel/runtime": "^7.25.6", - "@mui/private-theming": "^6.1.3", - "@mui/styled-engine": "^6.1.3", - "@mui/types": "^7.2.18", - "@mui/utils": "^6.1.3", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.9.tgz", + "integrity": "sha512-8x+RucnNp21gfFYsklCaZf0COXbv3+v0lrVuXONxvPEkESi2rwLlOi8UPJfcz6LxZOAX3v3oQ7qw18vnpgueRg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.1.9", + "@mui/styled-engine": "^6.1.9", + "@mui/types": "^7.2.19", + "@mui/utils": "^6.1.9", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1796,14 +1810,16 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/@mui/types": { - "version": "7.2.18", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.18.tgz", - "integrity": "sha512-uvK9dWeyCJl/3ocVnTOS6nlji/Knj8/tVqVX03UVTpdmTJYu/s4jtDd9Kvv0nRGE0CUSNW1UYAci7PYypjealg==", + "version": "7.2.19", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", + "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==", + "license": "MIT", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1814,12 +1830,13 @@ } }, "node_modules/@mui/utils": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.3.tgz", - "integrity": "sha512-4JBpLkjprlKjN10DGb1aiy/ii9TKbQ601uSHtAmYFAS879QZgAD7vRnv/YBE4iBbc7NXzFgbQMCOFrupXWekIA==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.9.tgz", + "integrity": "sha512-N7uzBp7p2or+xanXn3aH2OTINC6F/Ru/U8h6amhRZEev8bJhKN86rIDIoxZZ902tj+09LXtH83iLxFMjMHyqNA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.25.6", - "@mui/types": "^7.2.18", + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.19", "@types/prop-types": "^15.7.13", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -1846,6 +1863,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -2175,9 +2193,10 @@ } }, "node_modules/@remix-run/router": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", - "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", + "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -2708,19 +2727,21 @@ "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { - "version": "18.3.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", - "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } @@ -2752,14 +2773,15 @@ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz", - "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, @@ -2767,7 +2789,7 @@ "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "node_modules/@wagmi/cli": { @@ -3234,17 +3256,6 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -3385,9 +3396,9 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -3403,11 +3414,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -3510,9 +3522,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001667", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", - "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", "dev": true, "funding": [ { @@ -3527,7 +3539,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/cbw-sdk": { "name": "@coinbase/wallet-sdk", @@ -3546,19 +3559,6 @@ "sha.js": "^2.4.11" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/change-case": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", @@ -3699,19 +3699,6 @@ "node": ">=6" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, "node_modules/confbox": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", @@ -3729,7 +3716,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cookie-es": { "version": "1.2.2", @@ -3973,10 +3961,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.32", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz", - "integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==", - "dev": true + "version": "1.5.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz", + "integrity": "sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==", + "dev": true, + "license": "ISC" }, "node_modules/elliptic": { "version": "6.5.7", @@ -4128,14 +4117,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eth-block-tracker": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-7.1.0.tgz", @@ -4458,6 +4439,7 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -4557,14 +4539,6 @@ "unenv": "^1.10.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -5071,6 +5045,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -5244,6 +5219,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -5443,7 +5419,8 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -5616,6 +5593,35 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/ox": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.1.2.tgz", + "integrity": "sha512-ak/8K0Rtphg9vnRJlbOdaX9R7cmxD2MiSthjWGaQdMk3D7hrAlDoM+6Lxn7hN52Za3vrXfZ7enfke/5WjolDww==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -5717,6 +5723,15 @@ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==" }, + "node_modules/permissionless": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/permissionless/-/permissionless-0.2.20.tgz", + "integrity": "sha512-41BDkEVgfNIa1xpQ+U0ca44JH602EvaGIPTI2cEnSjo0OGaJONJ+vd4DX+TIG4RaXLObD0/UWPXrQrKkj7oseg==", + "license": "MIT", + "peerDependencies": { + "viem": "^2.21.22" + } + }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -5849,10 +5864,11 @@ } }, "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -5993,7 +6009,8 @@ "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/react-native-webview": { "version": "11.26.1", @@ -6026,11 +6043,12 @@ } }, "node_modules/react-router": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", - "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", + "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.19.2" + "@remix-run/router": "1.21.0" }, "engines": { "node": ">=14.0.0" @@ -6040,12 +6058,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", - "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", + "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.19.2", - "react-router": "6.26.2" + "@remix-run/router": "1.21.0", + "react-router": "6.28.0" }, "engines": { "node": ">=14.0.0" @@ -6442,6 +6461,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -6688,17 +6708,6 @@ "node": ">=14.0.0" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -6729,14 +6738,6 @@ "real-require": "^0.1.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6759,10 +6760,11 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6925,6 +6927,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" @@ -7011,23 +7014,24 @@ } }, "node_modules/viem": { - "version": "2.21.19", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.21.19.tgz", - "integrity": "sha512-FdlkN+UI1IU5sYOmzvygkxsUNjDRD5YHht3gZFu2X9xFv6Z3h9pXq9ycrYQ3F17lNfb41O2Ot4/aqbUkwOv9dA==", + "version": "2.21.52", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.21.52.tgz", + "integrity": "sha512-O53JhgVhp9CB0T3afy79O5ixNr8XXRGYQtlGYerzttnP56Oh2NBx4+Otz8IFd9N5DVPywf6tmYxHP1RVA99xJA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/wevm" } ], + "license": "MIT", "dependencies": { - "@adraffy/ens-normalize": "1.11.0", "@noble/curves": "1.6.0", "@noble/hashes": "1.5.0", "@scure/bip32": "1.5.0", "@scure/bip39": "1.4.0", "abitype": "1.0.6", "isows": "1.0.6", + "ox": "0.1.2", "webauthn-p256": "0.0.10", "ws": "8.18.0" }, @@ -7041,10 +7045,11 @@ } }, "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -7721,7 +7726,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "1.10.2", diff --git a/safe-eip7702-ui/package.json b/safe-eip7702-ui/package.json index 71c7596..355bf14 100644 --- a/safe-eip7702-ui/package.json +++ b/safe-eip7702-ui/package.json @@ -7,7 +7,8 @@ "dev": "vite", "build": "tsc && vite build", "lint": "biome check .", - "preview": "vite preview" + "preview": "vite preview", + "fmt": "prettier src/{*,**/*}.{ts,tsx} -w" }, "dependencies": { "@emotion/react": "^11.13.3", @@ -29,6 +30,7 @@ "@vitejs/plugin-react": "^4.3.3", "@wagmi/cli": "latest", "buffer": "^6.0.3", + "prettier": "^3.4.1", "typescript": "^5.6.3", "vite": "^5.4.10" }, From 486e01166ad842ef87c0b687397423f593d3a6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 29 Nov 2024 14:35:40 +0100 Subject: [PATCH 2/4] Format code in backend folder --- safe-eip7702-backend/src/config/addresses.ts | 81 +++++----- safe-eip7702-backend/src/index.ts | 156 +++++++++++-------- safe-eip7702-backend/src/multisend.ts | 30 ++-- safe-eip7702-backend/src/utils.ts | 57 +++---- safe-eip7702-backend/src/wallet.ts | 74 +++++---- 5 files changed, 221 insertions(+), 177 deletions(-) diff --git a/safe-eip7702-backend/src/config/addresses.ts b/safe-eip7702-backend/src/config/addresses.ts index 690fcd8..608c4d9 100644 --- a/safe-eip7702-backend/src/config/addresses.ts +++ b/safe-eip7702-backend/src/config/addresses.ts @@ -1,39 +1,46 @@ - -const {NETWORK_ID, PROXY_FACTORY, SAFE_SINGLETON, FALLBACK_HANDLER, MODULE_SETUP, MULTI_SEND, MULTI_SEND_CALL_ONLY} = process.env; +const { + NETWORK_ID, + PROXY_FACTORY, + SAFE_SINGLETON, + FALLBACK_HANDLER, + MODULE_SETUP, + MULTI_SEND, + MULTI_SEND_CALL_ONLY +} = process.env export const safeEIP7702Addresses: any = { - 7042905162: { - rpc: process.env.RPC_URL_PECTRA, - name: "pectra-devnet", - explorer: "https://explorer.pectra-devnet-4.ethpandaops.io", - proxyFactory: "0xE60EcE6588DCcFb7373538034963B4D20a280DB0", - safeSingleton: "0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB", - fallbackHandler: "0x4fFeBe9E5af056a73555223E9319Ae94D43461C0", - moduleSetup: "0x2204DcA7d254897ae6d815D2189032db87F50Bba", - multiSend: "0xd58De9D288831482346fA36e6bdc16925d9cFC85", - multiSendCallOnly: "0x4873593fC8e788eFc06287327749fdDe08C0146b", - testnet: true, - }, - [NETWORK_ID || "" ]: { - rpc: process.env.RPC_URL_CUSTOM, - name: process.env.NETWORK_NAME, - proxyFactory: PROXY_FACTORY, - safeSingleton: SAFE_SINGLETON, - fallbackHandler: FALLBACK_HANDLER, - moduleSetup: MODULE_SETUP, - multiSend: MULTI_SEND, - multiSendCallOnly: MULTI_SEND_CALL_ONLY, - testnet: true, - }, - 911867: { - rpc: process.env.RPC_URL_ITHACA, - name: "ithaca", - proxyFactory: "0xE60EcE6588DCcFb7373538034963B4D20a280DB0", - safeSingleton: "0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB", - fallbackHandler: "0x4fFeBe9E5af056a73555223E9319Ae94D43461C0", - moduleSetup: "0x2204DcA7d254897ae6d815D2189032db87F50Bba", - multiSend: "0xd58De9D288831482346fA36e6bdc16925d9cFC85", - multiSendCallOnly: "0x4873593fC8e788eFc06287327749fdDe08C0146b", - testnet: true - }, - }; + 7042905162: { + rpc: process.env.RPC_URL_PECTRA, + name: 'pectra-devnet', + explorer: 'https://explorer.pectra-devnet-4.ethpandaops.io', + proxyFactory: '0xE60EcE6588DCcFb7373538034963B4D20a280DB0', + safeSingleton: '0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB', + fallbackHandler: '0x4fFeBe9E5af056a73555223E9319Ae94D43461C0', + moduleSetup: '0x2204DcA7d254897ae6d815D2189032db87F50Bba', + multiSend: '0xd58De9D288831482346fA36e6bdc16925d9cFC85', + multiSendCallOnly: '0x4873593fC8e788eFc06287327749fdDe08C0146b', + testnet: true + }, + [NETWORK_ID || '']: { + rpc: process.env.RPC_URL_CUSTOM, + name: process.env.NETWORK_NAME, + proxyFactory: PROXY_FACTORY, + safeSingleton: SAFE_SINGLETON, + fallbackHandler: FALLBACK_HANDLER, + moduleSetup: MODULE_SETUP, + multiSend: MULTI_SEND, + multiSendCallOnly: MULTI_SEND_CALL_ONLY, + testnet: true + }, + 911867: { + rpc: process.env.RPC_URL_ITHACA, + name: 'ithaca', + proxyFactory: '0xE60EcE6588DCcFb7373538034963B4D20a280DB0', + safeSingleton: '0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB', + fallbackHandler: '0x4fFeBe9E5af056a73555223E9319Ae94D43461C0', + moduleSetup: '0x2204DcA7d254897ae6d815D2189032db87F50Bba', + multiSend: '0xd58De9D288831482346fA36e6bdc16925d9cFC85', + multiSendCallOnly: '0x4873593fC8e788eFc06287327749fdDe08C0146b', + testnet: true + } +} diff --git a/safe-eip7702-backend/src/index.ts b/safe-eip7702-backend/src/index.ts index a22435e..fc30079 100644 --- a/safe-eip7702-backend/src/index.ts +++ b/safe-eip7702-backend/src/index.ts @@ -1,104 +1,120 @@ -import express, { Request, Response } from "express"; -import dotenv from "dotenv"; -import cors from "cors"; -dotenv.config(); - -import { safeEIP7702Addresses } from "./config/addresses"; -import { getAccount, getChain, getPublicClient, getWalletClient } from "./wallet"; -import { toHex, zeroHash, encodeFunctionData, zeroAddress } from "viem"; -import { MultiSendABI } from "./utils"; -import { encodeMultiSend, MetaTransaction } from "./multisend"; -import SafeEIP7702ProxyFactoryArtifact from "./artifacts/SafeEIP7702ProxyFactory.json"; - -const TX_GAS = process.env.TX_GAS || 1_000_000; +import express, { Request, Response } from 'express' +import dotenv from 'dotenv' +import cors from 'cors' +dotenv.config() + +import { safeEIP7702Addresses } from './config/addresses' +import { + getAccount, + getChain, + getPublicClient, + getWalletClient +} from './wallet' +import { toHex, zeroHash, encodeFunctionData, zeroAddress } from 'viem' +import { MultiSendABI } from './utils' +import { encodeMultiSend, MetaTransaction } from './multisend' +import SafeEIP7702ProxyFactoryArtifact from './artifacts/SafeEIP7702ProxyFactory.json' + +const TX_GAS = process.env.TX_GAS || 1_000_000 declare global { interface BigInt { - toJSON(): Number; + toJSON(): Number } } BigInt.prototype.toJSON = function () { - return Number(this); -}; + return Number(this) +} -const app = express(); -app.use(express.json()); -app.use(cors()); +const app = express() +app.use(express.json()) +app.use(cors()) -app.get("/", (req: Request, res: Response) => { +app.get('/', (req: Request, res: Response) => { res.send({ - Description: "Relayer for Safe EIP-7702", - supportedChains: Object.keys(safeEIP7702Addresses), - }); -}); + Description: 'Relayer for Safe EIP-7702', + supportedChains: Object.keys(safeEIP7702Addresses) + }) +}) -app.post("/", async (req: Request, res: Response) => { - const { initData, authorizationList, from } = req.body; - const chainId = authorizationList[0].chainId as number; +app.post('/', async (req: Request, res: Response) => { + const { initData, authorizationList, from } = req.body + const chainId = authorizationList[0].chainId as number - const proxyAddress = authorizationList[0].contractAddress as `0x${string}`; - const addresses = safeEIP7702Addresses[chainId]; + const proxyAddress = authorizationList[0].contractAddress as `0x${string}` + const addresses = safeEIP7702Addresses[chainId] // Check if chain is supported if (!addresses) { - res.status(400).json({ error: `Chain not supported. No proxy factory found for chainId: ${chainId}` }); - return; + res + .status(400) + .json({ + error: `Chain not supported. No proxy factory found for chainId: ${chainId}` + }) + return } - let transactions: MetaTransaction[] = []; + let transactions: MetaTransaction[] = [] if (initData) { - const publicClient = getPublicClient(chainId); + const publicClient = getPublicClient(chainId) const proxyCalldata = encodeFunctionData({ abi: SafeEIP7702ProxyFactoryArtifact.abi, - functionName: "createProxyWithNonce", - args: [addresses.safeSingleton, initData, BigInt(0)], - }); + functionName: 'createProxyWithNonce', + args: [addresses.safeSingleton, initData, BigInt(0)] + }) // Check if proxy is already deployed if (await publicClient.getCode({ address: proxyAddress })) { - console.log(`Proxy already deployed [${proxyAddress}]`); + console.log(`Proxy already deployed [${proxyAddress}]`) } else { - console.log(`Adding transaction to deploy proxy [${proxyAddress}]`); + console.log(`Adding transaction to deploy proxy [${proxyAddress}]`) // Transaction to deploy proxy with initData transactions.push({ to: addresses.proxyFactory as `0x${string}`, // to: proxy factory address value: BigInt(0), // value: 0 data: proxyCalldata as `0x${string}`, // data: initData - operation: 0, - }); + operation: 0 + }) } } - const publicClient = getPublicClient(chainId); + const publicClient = getPublicClient(chainId) // Check if EOA is already initialized - const slotZero = await publicClient.getStorageAt({ address: from, slot: toHex(0) }); + const slotZero = await publicClient.getStorageAt({ + address: from, + slot: toHex(0) + }) if (slotZero === zeroHash) { // Transaction to initialize EOA transactions.push({ to: from, // to: EOA address value: BigInt(0), // value: 0 data: initData, // data: Init data - operation: 0, - }); - console.log(`Added transaction to initialize EOA [${from}]`); + operation: 0 + }) + console.log(`Added transaction to initialize EOA [${from}]`) } else { - console.log(`EOA [${from}] already initialized. Slot 0: [${slotZero}]`); + console.log(`EOA [${from}] already initialized. Slot 0: [${slotZero}]`) } try { - let txHash; + let txHash if (transactions.length > 0) { // Encode all transactions into a single byte string for multiSend - const encodedTransactions = encodeMultiSend(transactions); + const encodedTransactions = encodeMultiSend(transactions) + + const data = encodeFunctionData({ + abi: MultiSendABI, + functionName: 'multiSend', + args: [encodedTransactions] + }) - const data = encodeFunctionData({ abi: MultiSendABI, functionName: "multiSend", args: [encodedTransactions] }); + const walletClient = await getWalletClient(chainId) - const walletClient = await getWalletClient(chainId); - const request = await walletClient.prepareTransactionRequest({ chain: getChain(chainId), account: getAccount(chainId), @@ -106,38 +122,42 @@ app.post("/", async (req: Request, res: Response) => { data: data, // MultiSend call value: BigInt(0), // Value sent with the transaction authorizationList, - gas: BigInt(TX_GAS), + gas: BigInt(TX_GAS) }) - console.log(`Sending transaction on chain [${chainId}] for EOA [${from}]`, request); + console.log( + `Sending transaction on chain [${chainId}] for EOA [${from}]`, + request + ) const serializedTransaction = await walletClient.signTransaction(request) // Send the multiSend transaction - txHash = await walletClient.sendRawTransaction({ serializedTransaction }); - + txHash = await walletClient.sendRawTransaction({ serializedTransaction }) } else { - const walletClient = await getWalletClient(chainId); - console.log(`Sending only authorization transaction on chain [${chainId}] for EOA [${from}]`); + const walletClient = await getWalletClient(chainId) + console.log( + `Sending only authorization transaction on chain [${chainId}] for EOA [${from}]` + ) txHash = await walletClient.sendTransaction({ account: getAccount(chainId), chain: getChain(chainId), to: zeroAddress, - data: "0x", + data: '0x', value: BigInt(0), authorizationList, - gas: BigInt(TX_GAS), - }); + gas: BigInt(TX_GAS) + }) } - console.log(`Transaction hash: [${txHash}]`); - res.status(201).json({ txHash: txHash }); + console.log(`Transaction hash: [${txHash}]`) + res.status(201).json({ txHash: txHash }) } catch (error) { - console.error("Failed to relay authorization", error); - res.status(500).json({ error: error }); + console.error('Failed to relay authorization', error) + res.status(500).json({ error: error }) } -}); +}) -const port = process.env.PORT || 3000; +const port = process.env.PORT || 3000 app.listen(port, () => { - console.log(`Server running at http://localhost:${port}`); -}); + console.log(`Server running at http://localhost:${port}`) +}) diff --git a/safe-eip7702-backend/src/multisend.ts b/safe-eip7702-backend/src/multisend.ts index c520a4c..3c8748c 100644 --- a/safe-eip7702-backend/src/multisend.ts +++ b/safe-eip7702-backend/src/multisend.ts @@ -1,22 +1,22 @@ -import { Address, encodePacked, Hex, toBytes } from "viem"; - +import { Address, encodePacked, Hex, toBytes } from 'viem' export interface MetaTransaction { - to: Address; - value: bigint; - data: Hex; - operation: number; + to: Address + value: bigint + data: Hex + operation: number } const encodeMetaTransaction = (tx: MetaTransaction): string => { - const data = toBytes(tx.data); - const encoded = encodePacked( - ["uint8", "address", "uint256", "uint256", "bytes"], - [tx.operation, tx.to, tx.value, BigInt(data.length), tx.data], - ); - return encoded.slice(2); -}; + const data = toBytes(tx.data) + const encoded = encodePacked( + ['uint8', 'address', 'uint256', 'uint256', 'bytes'], + [tx.operation, tx.to, tx.value, BigInt(data.length), tx.data] + ) + return encoded.slice(2) +} export const encodeMultiSend = (txs: MetaTransaction[]): `0x${string}` => { - return "0x" + txs.map((tx) => encodeMetaTransaction(tx)).join("") as `0x${string}`; -}; + return ('0x' + + txs.map((tx) => encodeMetaTransaction(tx)).join('')) as `0x${string}` +} diff --git a/safe-eip7702-backend/src/utils.ts b/safe-eip7702-backend/src/utils.ts index 2989086..0021f68 100644 --- a/safe-eip7702-backend/src/utils.ts +++ b/safe-eip7702-backend/src/utils.ts @@ -1,57 +1,60 @@ -import { parseAbi, toBytes, encodePacked } from "viem"; +import { parseAbi, toBytes, encodePacked } from 'viem' export type SafeStorage = { - owners: string[]; - threshold: number; - nonce: number; - fallbackHandler: string; - modules: string[]; - guard: string; -}; + owners: string[] + threshold: number + nonce: number + fallbackHandler: string + modules: string[] + guard: string +} export const getSafeStorage = (): SafeStorage => { return { owners: [], threshold: 0, nonce: 0, - fallbackHandler: "", + fallbackHandler: '', modules: [], - guard: "", - }; -}; + guard: '' + } +} -export const MultiSendABI = parseAbi(["function multiSend(bytes transactions) public payable"]); +export const MultiSendABI = parseAbi([ + 'function multiSend(bytes transactions) public payable' +]) // Assuming MetaTransaction interface looks something like this: export interface MetaTransaction { - to: `0x${string}`; // address - value: bigint; // uint256 - data: `0x${string}`; // bytes + to: `0x${string}` // address + value: bigint // uint256 + data: `0x${string}` // bytes } // Function to encode a single meta-transaction const encodeMetaTransaction = (tx: MetaTransaction): string => { // Convert data into bytes format - const data = toBytes(tx.data); + const data = toBytes(tx.data) // Encode using encodePacked (similar to ethers' solidityPacked) const encoded = encodePacked( - ["uint8", "address", "uint256", "uint256", "bytes"], // Solidity types + ['uint8', 'address', 'uint256', 'uint256', 'bytes'], // Solidity types [0, tx.to, tx.value, BigInt(data.length), tx.data] // Values to encode - ); + ) - return encoded.slice(2); // Remove '0x' from the encoded result -}; + return encoded.slice(2) // Remove '0x' from the encoded result +} // Function to encode multiple transactions for multiSend export const encodeMultiSend = (txs: MetaTransaction[]): `0x${string}` => { - return ("0x" + txs.map((tx) => encodeMetaTransaction(tx)).join("")) as `0x${string}`; -}; + return ('0x' + + txs.map((tx) => encodeMetaTransaction(tx)).join('')) as `0x${string}` +} export const getDefaultEmptyTransaction = (): MetaTransaction => { return { - to: ("0x" + "00".repeat(20)) as `0x${string}`, // to: empty address + to: ('0x' + '00'.repeat(20)) as `0x${string}`, // to: empty address value: BigInt(0), // value: 0 - data: "0x" as `0x${string}`, // data: empty - }; -}; + data: '0x' as `0x${string}` // data: empty + } +} diff --git a/safe-eip7702-backend/src/wallet.ts b/safe-eip7702-backend/src/wallet.ts index c8477a1..b096e04 100644 --- a/safe-eip7702-backend/src/wallet.ts +++ b/safe-eip7702-backend/src/wallet.ts @@ -6,66 +6,80 @@ import { defineChain, http, PublicClient, - WalletClient, -} from "viem"; -import { privateKeyToAccount } from "viem/accounts"; -import { eip7702Actions } from "viem/experimental"; -import { safeEIP7702Addresses } from "./config/addresses"; + WalletClient +} from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { eip7702Actions } from 'viem/experimental' +import { safeEIP7702Addresses } from './config/addresses' -const chains: any = {}; +const chains: any = {} Object.keys(safeEIP7702Addresses).map((key: string) => { - - const account = privateKeyToAccount(process.env.RELAYER_PRIVATE_KEY as `0x${string}`); + const account = privateKeyToAccount( + process.env.RELAYER_PRIVATE_KEY as `0x${string}` + ) const chain = defineChain({ id: parseInt(key), name: safeEIP7702Addresses[key].name, nativeCurrency: { - name: "Ethereum", - symbol: "ETH", - decimals: 18, + name: 'Ethereum', + symbol: 'ETH', + decimals: 18 }, rpcUrls: { default: { http: [safeEIP7702Addresses[key].rpc as string], - webSocket: undefined, - }, + webSocket: undefined + } }, - testnet: safeEIP7702Addresses[key].testnet, - }); + testnet: safeEIP7702Addresses[key].testnet + }) const walletClient = createWalletClient({ account, chain: chain, - transport: http(safeEIP7702Addresses[key].rpc), - }).extend(eip7702Actions()); + transport: http(safeEIP7702Addresses[key].rpc) + }).extend(eip7702Actions()) const publicClient = createPublicClient({ chain: chain, - transport: http(safeEIP7702Addresses[key].rpc), - }); + transport: http(safeEIP7702Addresses[key].rpc) + }) - chains[parseInt(key)] = { chain: chain, walletClient: walletClient, publicClient: publicClient, account: account } + chains[parseInt(key)] = { + chain: chain, + walletClient: walletClient, + publicClient: publicClient, + account: account + } }) -console.log("chains", chains) -export const getWalletClient = async (chainId: number): Promise => { +console.log('chains', chains) +export const getWalletClient = async ( + chainId: number +): Promise => { if (chains[chainId]) return chains[chainId].walletClient - throw new Error(`Unsupported chainId: [${chainId}]. No wallet client available.`); -}; + throw new Error( + `Unsupported chainId: [${chainId}]. No wallet client available.` + ) +} export const getPublicClient = (chainId: number): PublicClient => { if (chains[chainId]) return chains[chainId].publicClient - throw new Error(`Unsupported chainId: [${chainId}]. No public client available.`); -}; + throw new Error( + `Unsupported chainId: [${chainId}]. No public client available.` + ) +} export const getAccount = (chainId: number): Account => { if (chains[chainId]) return chains[chainId].account - throw new Error(`Unsupported chainId: [${chainId}]. No account available.`); -}; + throw new Error(`Unsupported chainId: [${chainId}]. No account available.`) +} export const getChain = (chainId: number): Chain => { if (chains[chainId]) return chains[chainId].chain - throw new Error(`Unsupported chainId: [${chainId}]. No chain information available.`); -}; + throw new Error( + `Unsupported chainId: [${chainId}]. No chain information available.` + ) +} From 548c10caf229ace261bb7ccd2e298db489383cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 29 Nov 2024 14:38:03 +0100 Subject: [PATCH 3/4] Format code in contracts folder --- .../contracts/experimental/SafeLite.sol | 6 +- safe-eip7702-contracts/src/deploy/deploy.ts | 236 ++--- safe-eip7702-contracts/src/eip7702/helper.ts | 151 ++-- safe-eip7702-contracts/src/eip7702/storage.ts | 46 +- .../src/scripts/1_set_code.ts | 254 +++--- .../src/scripts/2_execute.ts | 82 +- .../src/scripts/3_batch_execute.ts | 150 ++-- safe-eip7702-contracts/src/utils/encodeRLP.ts | 141 ++- safe-eip7702-contracts/src/utils/safe.ts | 189 ++-- safe-eip7702-contracts/src/utils/safeLite.ts | 100 ++- safe-eip7702-contracts/src/utils/setup.ts | 92 +- .../src/utils/storageReader.ts | 80 +- safe-eip7702-contracts/test/EIP7702.spec.ts | 824 +++++++++++------- safe-eip7702-contracts/test/SafeLite.spec.ts | 501 +++++++---- 14 files changed, 1740 insertions(+), 1112 deletions(-) diff --git a/safe-eip7702-contracts/contracts/experimental/SafeLite.sol b/safe-eip7702-contracts/contracts/experimental/SafeLite.sol index c852e5d..fc1a557 100644 --- a/safe-eip7702-contracts/contracts/experimental/SafeLite.sol +++ b/safe-eip7702-contracts/contracts/experimental/SafeLite.sol @@ -81,11 +81,7 @@ contract SafeLite { assembly ("memory-safe") { let length := mload(transactions) let i := 0x20 - for { - - } lt(i, length) { - - } { + for {} lt(i, length) {} { let operation := shr(0xf8, mload(add(transactions, i))) let to := shr(0x60, mload(add(transactions, add(i, 0x01)))) let value := mload(add(transactions, add(i, 0x15))) diff --git a/safe-eip7702-contracts/src/deploy/deploy.ts b/safe-eip7702-contracts/src/deploy/deploy.ts index 5b6d508..1452562 100644 --- a/safe-eip7702-contracts/src/deploy/deploy.ts +++ b/safe-eip7702-contracts/src/deploy/deploy.ts @@ -1,116 +1,120 @@ -import MultiSend from "@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSend.sol/MultiSend.json"; -import MultiSendCallOnly from "@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSendCallOnly.sol/MultiSendCallOnly.json"; -import SafeProxyFactory from "@safe-global/safe-smart-account/build/artifacts/contracts/proxies/SafeProxyFactory.sol/SafeProxyFactory.json"; -import SafeL2 from "@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json"; -import SignMessageLib from "@safe-global/safe-smart-account/build/artifacts/contracts/libraries/SignMessageLib.sol/SignMessageLib.json"; -import SimulateTxAccessor from "@safe-global/safe-smart-account/build/artifacts/contracts/accessors/SimulateTxAccessor.sol/SimulateTxAccessor.json"; -import CompatibilityFallbackHandler from "@safe-global/safe-smart-account/build/artifacts/contracts/handler/CompatibilityFallbackHandler.sol/CompatibilityFallbackHandler.json"; -import CreateCall from "@safe-global/safe-smart-account/build/artifacts/contracts/libraries/CreateCall.sol/CreateCall.json"; - -import { DeployFunction } from "hardhat-deploy/types"; - -const deploy: DeployFunction = async ({ deployments, getNamedAccounts, network }) => { - const { deployer } = await getNamedAccounts(); - const { deploy } = deployments; - - await deploy("IDAFallbackHandler", { - from: deployer, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("SafeModuleSetup", { - from: deployer, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("SafeEIP7702ProxyFactory", { - from: deployer, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("ClearStorageHelper", { - from: deployer, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("MultiSend", { - from: deployer, - contract: MultiSend, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("MultiSendCallOnly", { - from: deployer, - contract: MultiSendCallOnly, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("SafeL2", { - from: deployer, - contract: SafeL2, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("SignMessageLib", { - from: deployer, - contract: SignMessageLib, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("SafeProxyFactory", { - from: deployer, - contract: SafeProxyFactory, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("SimulateTxAccessor", { - from: deployer, - contract: SimulateTxAccessor, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("CreateCall", { - from: deployer, - contract: CreateCall, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("CompatibilityFallbackHandler", { - contract: CompatibilityFallbackHandler, - from: deployer, - args: [], - log: true, - deterministicDeployment: true, - }); - - await deploy("SafeLite", { - from: deployer, - args: ["0x0000000071727de22e5e9d8baf0edac6f37da032"], - log: true, - deterministicDeployment: true, - }); -}; - -export default deploy; +import MultiSend from '@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSend.sol/MultiSend.json' +import MultiSendCallOnly from '@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSendCallOnly.sol/MultiSendCallOnly.json' +import SafeProxyFactory from '@safe-global/safe-smart-account/build/artifacts/contracts/proxies/SafeProxyFactory.sol/SafeProxyFactory.json' +import SafeL2 from '@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json' +import SignMessageLib from '@safe-global/safe-smart-account/build/artifacts/contracts/libraries/SignMessageLib.sol/SignMessageLib.json' +import SimulateTxAccessor from '@safe-global/safe-smart-account/build/artifacts/contracts/accessors/SimulateTxAccessor.sol/SimulateTxAccessor.json' +import CompatibilityFallbackHandler from '@safe-global/safe-smart-account/build/artifacts/contracts/handler/CompatibilityFallbackHandler.sol/CompatibilityFallbackHandler.json' +import CreateCall from '@safe-global/safe-smart-account/build/artifacts/contracts/libraries/CreateCall.sol/CreateCall.json' + +import { DeployFunction } from 'hardhat-deploy/types' + +const deploy: DeployFunction = async ({ + deployments, + getNamedAccounts, + network +}) => { + const { deployer } = await getNamedAccounts() + const { deploy } = deployments + + await deploy('IDAFallbackHandler', { + from: deployer, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('SafeModuleSetup', { + from: deployer, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('SafeEIP7702ProxyFactory', { + from: deployer, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('ClearStorageHelper', { + from: deployer, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('MultiSend', { + from: deployer, + contract: MultiSend, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('MultiSendCallOnly', { + from: deployer, + contract: MultiSendCallOnly, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('SafeL2', { + from: deployer, + contract: SafeL2, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('SignMessageLib', { + from: deployer, + contract: SignMessageLib, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('SafeProxyFactory', { + from: deployer, + contract: SafeProxyFactory, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('SimulateTxAccessor', { + from: deployer, + contract: SimulateTxAccessor, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('CreateCall', { + from: deployer, + contract: CreateCall, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('CompatibilityFallbackHandler', { + contract: CompatibilityFallbackHandler, + from: deployer, + args: [], + log: true, + deterministicDeployment: true + }) + + await deploy('SafeLite', { + from: deployer, + args: ['0x0000000071727de22e5e9d8baf0edac6f37da032'], + log: true, + deterministicDeployment: true + }) +} + +export default deploy diff --git a/safe-eip7702-contracts/src/eip7702/helper.ts b/safe-eip7702-contracts/src/eip7702/helper.ts index 17f2831..86bd951 100644 --- a/safe-eip7702-contracts/src/eip7702/helper.ts +++ b/safe-eip7702-contracts/src/eip7702/helper.ts @@ -1,75 +1,102 @@ -import { AddressLike, BytesLike, ethers, keccak256, Provider, Signer, SigningKey } from "ethers"; -import { AuthorizationListEntryAny, encodeRLPAuthorizationEntryUnsigned, serializeEip7702 } from "../utils/encodeRLP"; -import { SafeEIP7702ProxyFactory } from "../../typechain-types"; +import { + AddressLike, + BytesLike, + ethers, + keccak256, + Provider, + Signer, + SigningKey +} from 'ethers' +import { + AuthorizationListEntryAny, + encodeRLPAuthorizationEntryUnsigned, + serializeEip7702 +} from '../utils/encodeRLP' +import { SafeEIP7702ProxyFactory } from '../../typechain-types' export const getAuthorizationList = ( - chainId: bigint, - nonce: bigint, - privateKey: ethers.BytesLike, - authorizer: string, + chainId: bigint, + nonce: bigint, + privateKey: ethers.BytesLike, + authorizer: string ): AuthorizationListEntryAny[] => { - const dataToSign = encodeRLPAuthorizationEntryUnsigned(chainId, authorizer, nonce); - const authHash = ethers.keccak256(dataToSign); - const authSignature = new SigningKey(privateKey).sign(authHash); + const dataToSign = encodeRLPAuthorizationEntryUnsigned( + chainId, + authorizer, + nonce + ) + const authHash = ethers.keccak256(dataToSign) + const authSignature = new SigningKey(privateKey).sign(authHash) - // [[chain_id, address, nonce, y_parity, r, s]] - return [ - { - chainId: chainId, - address: authorizer, - nonce: nonce, - yParity: authSignature.yParity, - r: authSignature.r, - s: authSignature.s, - }, - ]; -}; + // [[chain_id, address, nonce, y_parity, r, s]] + return [ + { + chainId: chainId, + address: authorizer, + nonce: nonce, + yParity: authSignature.yParity, + r: authSignature.r, + s: authSignature.s + } + ] +} export const getSignedTransaction = async ( - provider: Provider, - relayerSigningKey: SigningKey, - authorizationList: AuthorizationListEntryAny[], - to: AddressLike = ethers.ZeroAddress, - value: ethers.BigNumberish = 0, - data: BytesLike = "0x", - nonce?: number + provider: Provider, + relayerSigningKey: SigningKey, + authorizationList: AuthorizationListEntryAny[], + to: AddressLike = ethers.ZeroAddress, + value: ethers.BigNumberish = 0, + data: BytesLike = '0x', + nonce?: number ) => { - const relayerAddress = ethers.computeAddress(relayerSigningKey.publicKey); - const relayerNonce = nonce || await provider.getTransactionCount(relayerAddress); - const tx = { - from: relayerAddress, - nonce: relayerNonce, - gasLimit: ethers.toBeHex(21000000), - gasPrice: ethers.toBeHex(3100), - data: data, - to: to, - value: value, - chainId: (await provider.getNetwork()).chainId, - type: 4, - maxFeePerGas: ethers.toBeHex(30000), - maxPriorityFeePerGas: ethers.toBeHex(30000), - accessList: [], - authorizationList: authorizationList, - }; + const relayerAddress = ethers.computeAddress(relayerSigningKey.publicKey) + const relayerNonce = + nonce || (await provider.getTransactionCount(relayerAddress)) + const tx = { + from: relayerAddress, + nonce: relayerNonce, + gasLimit: ethers.toBeHex(21000000), + gasPrice: ethers.toBeHex(3100), + data: data, + to: to, + value: value, + chainId: (await provider.getNetwork()).chainId, + type: 4, + maxFeePerGas: ethers.toBeHex(30000), + maxPriorityFeePerGas: ethers.toBeHex(30000), + accessList: [], + authorizationList: authorizationList + } - const encodedTx = serializeEip7702(tx, null); - const txHashToSign = ethers.keccak256(encodedTx); - const signature = relayerSigningKey.sign(txHashToSign); - return serializeEip7702(tx, signature); -}; + const encodedTx = serializeEip7702(tx, null) + const txHashToSign = ethers.keccak256(encodedTx) + const signature = relayerSigningKey.sign(txHashToSign) + return serializeEip7702(tx, signature) +} export const calculateProxyAddress = async ( - factory: SafeEIP7702ProxyFactory, - singleton: string, - inititalizer: string, - nonce: number | string, + factory: SafeEIP7702ProxyFactory, + singleton: string, + inititalizer: string, + nonce: number | string ) => { - const salt = ethers.solidityPackedKeccak256(["bytes32", "uint256"], [ethers.solidityPackedKeccak256(["bytes"], [inititalizer]), nonce]); - const factoryAddress = await factory.getAddress(); - const proxyCreationCode = await factory.proxyCreationCode(); + const salt = ethers.solidityPackedKeccak256( + ['bytes32', 'uint256'], + [ethers.solidityPackedKeccak256(['bytes'], [inititalizer]), nonce] + ) + const factoryAddress = await factory.getAddress() + const proxyCreationCode = await factory.proxyCreationCode() - const deploymentCode = ethers.solidityPacked(["bytes", "uint256", "uint256"], [proxyCreationCode, keccak256(inititalizer), singleton]); - return ethers.getCreate2Address(factoryAddress, salt, ethers.keccak256(deploymentCode)); -}; + const deploymentCode = ethers.solidityPacked( + ['bytes', 'uint256', 'uint256'], + [proxyCreationCode, keccak256(inititalizer), singleton] + ) + return ethers.getCreate2Address( + factoryAddress, + salt, + ethers.keccak256(deploymentCode) + ) +} -export const ACCOUNT_CODE_PREFIX = "0xef0100"; +export const ACCOUNT_CODE_PREFIX = '0xef0100' diff --git a/safe-eip7702-contracts/src/eip7702/storage.ts b/safe-eip7702-contracts/src/eip7702/storage.ts index 3a00eea..591cea0 100644 --- a/safe-eip7702-contracts/src/eip7702/storage.ts +++ b/safe-eip7702-contracts/src/eip7702/storage.ts @@ -1,20 +1,32 @@ -import { AddressLike, Provider } from "ethers"; -import { ACCOUNT_CODE_PREFIX } from "./helper"; -import { ethers } from "hardhat"; +import { AddressLike, Provider } from 'ethers' +import { ACCOUNT_CODE_PREFIX } from './helper' +import { ethers } from 'hardhat' -export const isAccountDelegated = async (provider: Provider, account: AddressLike) => { - const codeAtEOA = await provider.getCode(account); - return codeAtEOA.length === 48 && codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX); -}; +export const isAccountDelegated = async ( + provider: Provider, + account: AddressLike +) => { + const codeAtEOA = await provider.getCode(account) + return codeAtEOA.length === 48 && codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX) +} -export const isAccountDelegatedToAddress = async (provider: Provider, account: AddressLike, authority: string) => { - const codeAtEOA = await provider.getCode(account); - return ( - codeAtEOA.length === 48 && codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX) && ethers.getAddress("0x" + codeAtEOA.slice(8)) === authority - ); -}; +export const isAccountDelegatedToAddress = async ( + provider: Provider, + account: AddressLike, + authority: string +) => { + const codeAtEOA = await provider.getCode(account) + return ( + codeAtEOA.length === 48 && + codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX) && + ethers.getAddress('0x' + codeAtEOA.slice(8)) === authority + ) +} -export const getDelegatedToAddress = async (provider: Provider, account: AddressLike): Promise => { - const codeAtEOA = await provider.getCode(account); - return "0x" + codeAtEOA.slice(8); -}; +export const getDelegatedToAddress = async ( + provider: Provider, + account: AddressLike +): Promise => { + const codeAtEOA = await provider.getCode(account) + return '0x' + codeAtEOA.slice(8) +} diff --git a/safe-eip7702-contracts/src/scripts/1_set_code.ts b/safe-eip7702-contracts/src/scripts/1_set_code.ts index 5935c06..34e6e64 100644 --- a/safe-eip7702-contracts/src/scripts/1_set_code.ts +++ b/safe-eip7702-contracts/src/scripts/1_set_code.ts @@ -1,104 +1,164 @@ -import dotenv from "dotenv"; -dotenv.config(); -import { deployments, ethers } from "hardhat"; -import { isAccountDelegatedToAddress } from "../eip7702/storage"; -import { Provider, SigningKey } from "ethers"; -import { printAccountStorage } from "../utils/storageReader"; -import { getSetupData } from "../utils/safe"; -import { calculateProxyAddress, getAuthorizationList, getSignedTransaction } from "../eip7702/helper"; +import dotenv from 'dotenv' +dotenv.config() +import { deployments, ethers } from 'hardhat' +import { isAccountDelegatedToAddress } from '../eip7702/storage' +import { Provider, SigningKey } from 'ethers' +import { printAccountStorage } from '../utils/storageReader' +import { getSetupData } from '../utils/safe' import { - getClearStorageHelper, - getCompatibilityFallbackHandler, - getIDAFallbackHandler, - getSafeEIP7702ProxyFactory, - getSafeModuleSetup, - getSafeSingleton, -} from "../utils/setup"; -import { SafeEIP7702ProxyFactory } from "../../typechain-types"; + calculateProxyAddress, + getAuthorizationList, + getSignedTransaction +} from '../eip7702/helper' +import { + getClearStorageHelper, + getCompatibilityFallbackHandler, + getIDAFallbackHandler, + getSafeEIP7702ProxyFactory, + getSafeModuleSetup, + getSafeSingleton +} from '../utils/setup' +import { SafeEIP7702ProxyFactory } from '../../typechain-types' const setup = async (provider: Provider) => { - await deployments.fixture(); - - const delegator = new ethers.Wallet(process.env.ACCOUNT_PRIVATE_KEY || "", provider); - const relayer = new ethers.Wallet(process.env.RELAYER_PRIVATE_KEY || "", provider); - - const fallbackHandler = await getIDAFallbackHandler(); - const safeSingleton = await getSafeSingleton(); - const safeCompatibilityFallbackHandler = await getCompatibilityFallbackHandler(); - const clearStorageHelper = await getClearStorageHelper(); - const safeModuleSetup = await getSafeModuleSetup(); - const safeEIP7702ProxyFactory: SafeEIP7702ProxyFactory = await getSafeEIP7702ProxyFactory(); - return { - fallbackHandler, - safeSingleton, - safeCompatibilityFallbackHandler, - relayer, - delegator, - clearStorageHelper, - safeModuleSetup, - safeEIP7702ProxyFactory, - }; -}; + await deployments.fixture() + + const delegator = new ethers.Wallet( + process.env.ACCOUNT_PRIVATE_KEY || '', + provider + ) + const relayer = new ethers.Wallet( + process.env.RELAYER_PRIVATE_KEY || '', + provider + ) + + const fallbackHandler = await getIDAFallbackHandler() + const safeSingleton = await getSafeSingleton() + const safeCompatibilityFallbackHandler = + await getCompatibilityFallbackHandler() + const clearStorageHelper = await getClearStorageHelper() + const safeModuleSetup = await getSafeModuleSetup() + const safeEIP7702ProxyFactory: SafeEIP7702ProxyFactory = + await getSafeEIP7702ProxyFactory() + return { + fallbackHandler, + safeSingleton, + safeCompatibilityFallbackHandler, + relayer, + delegator, + clearStorageHelper, + safeModuleSetup, + safeEIP7702ProxyFactory + } +} const main = async () => { - const provider = ethers.provider; - const { safeSingleton, fallbackHandler, relayer, delegator, safeModuleSetup, safeEIP7702ProxyFactory } = await setup(provider); - const pkDelegator = process.env.ACCOUNT_PRIVATE_KEY || ""; - const relayerSigningKey = new SigningKey(process.env.RELAYER_PRIVATE_KEY || ""); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await delegator.getNonce()); - - // Deploy SafeProxy with the delegator as owner - const owners = [delegator]; - const ownerAddresses = await Promise.all(owners.map(async (owner): Promise => await owner.getAddress())); - - const fallbackHandlerAddress = await fallbackHandler.getAddress(); - const data = getSetupData(ownerAddresses, 1, await safeModuleSetup.getAddress(), [fallbackHandlerAddress], fallbackHandlerAddress); - - const proxyAddress = await calculateProxyAddress(safeEIP7702ProxyFactory, await safeSingleton.getAddress(), data, 0); - const isContract = (await provider.getCode(proxyAddress)) === "0x" ? false : true; - - if (!isContract) { - console.log(`Deploying Proxy [${proxyAddress}]`); - const tx = await safeEIP7702ProxyFactory.connect(relayer).createProxyWithNonce(await safeSingleton.getAddress(), data, 0); - await tx.wait(); - console.log(`Proxy deployment transaction hash: [${tx.hash}]`); - } else { - console.log("Proxy already deployed: ", proxyAddress); - } - - const authAddress = proxyAddress; - - const authorizationList = getAuthorizationList(chainId, authNonce, pkDelegator, authAddress); - const encodedSignedTx = await getSignedTransaction(provider, relayerSigningKey, authorizationList); - - const account = await delegator.getAddress(); - - const isAlreadyDelegated = await isAccountDelegatedToAddress(provider, await delegator.getAddress(), authAddress); - if (isAlreadyDelegated && (await provider.getStorage(account, 4)) == ethers.zeroPadValue("0x01", 32)) { - console.log("Account already delegated to Safe Proxy and storage is setup. Returning"); - return; - } - - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - console.log("Set Auth transaction hash", response); - - console.log("Waiting for transaction confirmation"); - await (await provider.getTransaction(response))?.wait(); - - console.log(await delegator.getAddress(), authAddress); - console.log("Code at account: ", await provider.getCode(account)); - - console.log("Account successfully delegated to Safe Proxy"); - - const setupTxResponse = await relayer.sendTransaction({ to: await delegator.getAddress(), data: data }); - await setupTxResponse.wait(); - - await printAccountStorage(provider, account, await safeSingleton.getAddress()); -}; + const provider = ethers.provider + const { + safeSingleton, + fallbackHandler, + relayer, + delegator, + safeModuleSetup, + safeEIP7702ProxyFactory + } = await setup(provider) + const pkDelegator = process.env.ACCOUNT_PRIVATE_KEY || '' + const relayerSigningKey = new SigningKey( + process.env.RELAYER_PRIVATE_KEY || '' + ) + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await delegator.getNonce()) + + // Deploy SafeProxy with the delegator as owner + const owners = [delegator] + const ownerAddresses = await Promise.all( + owners.map(async (owner): Promise => await owner.getAddress()) + ) + + const fallbackHandlerAddress = await fallbackHandler.getAddress() + const data = getSetupData( + ownerAddresses, + 1, + await safeModuleSetup.getAddress(), + [fallbackHandlerAddress], + fallbackHandlerAddress + ) + + const proxyAddress = await calculateProxyAddress( + safeEIP7702ProxyFactory, + await safeSingleton.getAddress(), + data, + 0 + ) + const isContract = + (await provider.getCode(proxyAddress)) === '0x' ? false : true + + if (!isContract) { + console.log(`Deploying Proxy [${proxyAddress}]`) + const tx = await safeEIP7702ProxyFactory + .connect(relayer) + .createProxyWithNonce(await safeSingleton.getAddress(), data, 0) + await tx.wait() + console.log(`Proxy deployment transaction hash: [${tx.hash}]`) + } else { + console.log('Proxy already deployed: ', proxyAddress) + } + + const authAddress = proxyAddress + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + pkDelegator, + authAddress + ) + const encodedSignedTx = await getSignedTransaction( + provider, + relayerSigningKey, + authorizationList + ) + + const account = await delegator.getAddress() + + const isAlreadyDelegated = await isAccountDelegatedToAddress( + provider, + await delegator.getAddress(), + authAddress + ) + if ( + isAlreadyDelegated && + (await provider.getStorage(account, 4)) == ethers.zeroPadValue('0x01', 32) + ) { + console.log( + 'Account already delegated to Safe Proxy and storage is setup. Returning' + ) + return + } + + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + console.log('Set Auth transaction hash', response) + + console.log('Waiting for transaction confirmation') + await (await provider.getTransaction(response))?.wait() + + console.log(await delegator.getAddress(), authAddress) + console.log('Code at account: ', await provider.getCode(account)) + + console.log('Account successfully delegated to Safe Proxy') + + const setupTxResponse = await relayer.sendTransaction({ + to: await delegator.getAddress(), + data: data + }) + await setupTxResponse.wait() + + await printAccountStorage(provider, account, await safeSingleton.getAddress()) +} main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); + console.error(error) + process.exitCode = 1 +}) diff --git a/safe-eip7702-contracts/src/scripts/2_execute.ts b/safe-eip7702-contracts/src/scripts/2_execute.ts index 7261395..fb8c74f 100644 --- a/safe-eip7702-contracts/src/scripts/2_execute.ts +++ b/safe-eip7702-contracts/src/scripts/2_execute.ts @@ -1,44 +1,58 @@ -import dotenv from "dotenv"; -dotenv.config(); -import { deployments, ethers } from "hardhat"; -import { execTransaction } from "../utils/safe"; -import { getSafeAtAddress } from "../utils/setup"; -import { Provider } from "ethers"; +import dotenv from 'dotenv' +dotenv.config() +import { deployments, ethers } from 'hardhat' +import { execTransaction } from '../utils/safe' +import { getSafeAtAddress } from '../utils/setup' +import { Provider } from 'ethers' const setup = async (provider: Provider) => { - await deployments.fixture(); + await deployments.fixture() - const delegator = new ethers.Wallet(process.env.ACCOUNT_PRIVATE_KEY || "", provider); - const relayer = new ethers.Wallet(process.env.RELAYER_PRIVATE_KEY || "", provider); + const delegator = new ethers.Wallet( + process.env.ACCOUNT_PRIVATE_KEY || '', + provider + ) + const relayer = new ethers.Wallet( + process.env.RELAYER_PRIVATE_KEY || '', + provider + ) - return { - relayer, - delegator, - }; -}; + return { + relayer, + delegator + } +} const main = async () => { - const provider = ethers.provider; - const { relayer, delegator } = await setup(ethers.provider); - const owners = [delegator]; - const account = await delegator.getAddress(); - console.log(`Using account [${account}]`); - const safe = await getSafeAtAddress(account); + const provider = ethers.provider + const { relayer, delegator } = await setup(ethers.provider) + const owners = [delegator] + const account = await delegator.getAddress() + console.log(`Using account [${account}]`) + const safe = await getSafeAtAddress(account) - const amount = 1n; - if ((await provider.getBalance(account)) < amount) { - await relayer.sendTransaction({ - to: account, - value: amount, - }); - } + const amount = 1n + if ((await provider.getBalance(account)) < amount) { + await relayer.sendTransaction({ + to: account, + value: amount + }) + } - // Transfer value from the account - const tx = await execTransaction(relayer, owners, safe, await relayer.getAddress(), amount.toString(), "0x", "0"); - console.log(`Transaction hash [${(await tx.wait())?.hash}]`); -}; + // Transfer value from the account + const tx = await execTransaction( + relayer, + owners, + safe, + await relayer.getAddress(), + amount.toString(), + '0x', + '0' + ) + console.log(`Transaction hash [${(await tx.wait())?.hash}]`) +} main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); + console.error(error) + process.exitCode = 1 +}) diff --git a/safe-eip7702-contracts/src/scripts/3_batch_execute.ts b/safe-eip7702-contracts/src/scripts/3_batch_execute.ts index 3b9ee55..291b0f2 100644 --- a/safe-eip7702-contracts/src/scripts/3_batch_execute.ts +++ b/safe-eip7702-contracts/src/scripts/3_batch_execute.ts @@ -1,76 +1,102 @@ -import dotenv from "dotenv"; -dotenv.config(); -import { deployments, ethers } from "hardhat"; -import { execTransaction } from "../utils/safe"; -import { getMultiSend, getSafeAtAddress, getSafeSingleton } from "../utils/setup"; -import { Provider } from "ethers"; -import { encodeMultiSend } from "@safe-global/safe-smart-account/dist/src/utils/multisend"; -import { MetaTransaction } from "@safe-global/safe-smart-account"; -import { isAccountDelegatedToAddress } from "../eip7702/storage"; +import dotenv from 'dotenv' +dotenv.config() +import { deployments, ethers } from 'hardhat' +import { execTransaction } from '../utils/safe' +import { + getMultiSend, + getSafeAtAddress, + getSafeSingleton +} from '../utils/setup' +import { Provider } from 'ethers' +import { encodeMultiSend } from '@safe-global/safe-smart-account/dist/src/utils/multisend' +import { MetaTransaction } from '@safe-global/safe-smart-account' +import { isAccountDelegatedToAddress } from '../eip7702/storage' const setup = async (provider: Provider) => { - await deployments.fixture(); + await deployments.fixture() - const delegator = new ethers.Wallet(process.env.ACCOUNT_PRIVATE_KEY || "", provider); - const relayer = new ethers.Wallet(process.env.RELAYER_PRIVATE_KEY || "", provider); - const multiSend = await getMultiSend(); - const safeSingleton = await getSafeSingleton(); + const delegator = new ethers.Wallet( + process.env.ACCOUNT_PRIVATE_KEY || '', + provider + ) + const relayer = new ethers.Wallet( + process.env.RELAYER_PRIVATE_KEY || '', + provider + ) + const multiSend = await getMultiSend() + const safeSingleton = await getSafeSingleton() - return { - relayer, - delegator, - multiSend, - safeSingleton, - }; -}; + return { + relayer, + delegator, + multiSend, + safeSingleton + } +} const main = async () => { - const provider = ethers.provider; - const { relayer, delegator, multiSend, safeSingleton } = await setup(provider); - const owners = [delegator]; - const account = await delegator.getAddress(); - console.log(`Using account [${account}]`); + const provider = ethers.provider + const { relayer, delegator, multiSend, safeSingleton } = await setup(provider) + const owners = [delegator] + const account = await delegator.getAddress() + console.log(`Using account [${account}]`) - const safeSingletonAddress = await safeSingleton.getAddress(); - const isDelegatedToSafeSingleton = isAccountDelegatedToAddress(provider, account, safeSingletonAddress); + const safeSingletonAddress = await safeSingleton.getAddress() + const isDelegatedToSafeSingleton = isAccountDelegatedToAddress( + provider, + account, + safeSingletonAddress + ) - if (!isDelegatedToSafeSingleton) { - console.log(`Account is not delegated to Safe singleton contract [${safeSingletonAddress}]`); - process.exit(1); - } + if (!isDelegatedToSafeSingleton) { + console.log( + `Account is not delegated to Safe singleton contract [${safeSingletonAddress}]` + ) + process.exit(1) + } - const safe = await getSafeAtAddress(account); + const safe = await getSafeAtAddress(account) - const amount = 1n * 2n; - if ((await provider.getBalance(account)) < amount) { - await relayer.sendTransaction({ - to: account, - value: amount, - }); - } + const amount = 1n * 2n + if ((await provider.getBalance(account)) < amount) { + await relayer.sendTransaction({ + to: account, + value: amount + }) + } - const txs: MetaTransaction[] = [ - { - to: await relayer.getAddress(), - value: 1n, - data: "0x", - operation: 0, - }, - { - to: await relayer.getAddress(), - value: 1n, - data: "0x", - operation: 0, - }, - ]; + const txs: MetaTransaction[] = [ + { + to: await relayer.getAddress(), + value: 1n, + data: '0x', + operation: 0 + }, + { + to: await relayer.getAddress(), + value: 1n, + data: '0x', + operation: 0 + } + ] - const multiSendData = encodeMultiSend(txs); - const calldata = multiSend.interface.encodeFunctionData("multiSend", [multiSendData]); - const tx = await execTransaction(relayer, owners, safe, await multiSend.getAddress(), "0", calldata, "1"); - console.log(`Transaction hash [${(await tx.wait())?.hash}]`); -}; + const multiSendData = encodeMultiSend(txs) + const calldata = multiSend.interface.encodeFunctionData('multiSend', [ + multiSendData + ]) + const tx = await execTransaction( + relayer, + owners, + safe, + await multiSend.getAddress(), + '0', + calldata, + '1' + ) + console.log(`Transaction hash [${(await tx.wait())?.hash}]`) +} main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); + console.error(error) + process.exitCode = 1 +}) diff --git a/safe-eip7702-contracts/src/utils/encodeRLP.ts b/safe-eip7702-contracts/src/utils/encodeRLP.ts index 8777edf..7cc1ae6 100644 --- a/safe-eip7702-contracts/src/utils/encodeRLP.ts +++ b/safe-eip7702-contracts/src/utils/encodeRLP.ts @@ -1,65 +1,110 @@ -import { accessListify, AccessListish, BigNumberish, concat, encodeRlp, getBigInt, Signature, toBeArray } from "ethers"; +import { + accessListify, + AccessListish, + BigNumberish, + concat, + encodeRlp, + getBigInt, + Signature, + toBeArray +} from 'ethers' -export type AuthorizationListEntryAny = { chainId: bigint; address: string; nonce: bigint; yParity: any; r: any; s: any }; +export type AuthorizationListEntryAny = { + chainId: bigint + address: string + nonce: bigint + yParity: any + r: any + s: any +} export type AuthorizationListEntry = { - chainId: Uint8Array; - address: string; - nonce: Uint8Array; - yParity: Uint8Array; - r: Uint8Array; - s: Uint8Array; -}; + chainId: Uint8Array + address: string + nonce: Uint8Array + yParity: Uint8Array + r: Uint8Array + s: Uint8Array +} -export type AuthorizationList = Array; +export type AuthorizationList = Array -export type AuthEntry7702RLPType = [Uint8Array, string, Uint8Array, Uint8Array, Uint8Array, Uint8Array]; +export type AuthEntry7702RLPType = [ + Uint8Array, + string, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array +] -export type Auth7702RLPType = Array; +export type Auth7702RLPType = Array -export type AuthorizationListish = AuthorizationList | Auth7702RLPType; +export type AuthorizationListish = AuthorizationList | Auth7702RLPType const formatNumber = (_value: BigNumberish): Uint8Array => { - const value = getBigInt(_value, "value"); - return toBeArray(value); -}; + const value = getBigInt(_value, 'value') + return toBeArray(value) +} -const formatAccessList = (value: AccessListish): Array<[string, Array]> => { - return accessListify(value).map((set) => [set.address, set.storageKeys]); -}; +const formatAccessList = ( + value: AccessListish +): Array<[string, Array]> => { + return accessListify(value).map((set) => [set.address, set.storageKeys]) +} -export const encodeRLPAuthorizationEntryUnsigned = (chainId: bigint, address: any, nonce: bigint): string => { - // MAGIC = "0x05" defined in ERC-7702 - return concat(["0x05", encodeRlp([formatNumber(chainId), address, formatNumber(nonce)])]); -}; +export const encodeRLPAuthorizationEntryUnsigned = ( + chainId: bigint, + address: any, + nonce: bigint +): string => { + // MAGIC = "0x05" defined in ERC-7702 + return concat([ + '0x05', + encodeRlp([formatNumber(chainId), address, formatNumber(nonce)]) + ]) +} -const formatAuthorizationEntry = (set: AuthorizationListEntryAny): AuthEntry7702RLPType => { - return [formatNumber(set.chainId), set.address, formatNumber(set.nonce), formatNumber(set.yParity), toBeArray(set.r), toBeArray(set.s)]; -}; +const formatAuthorizationEntry = ( + set: AuthorizationListEntryAny +): AuthEntry7702RLPType => { + return [ + formatNumber(set.chainId), + set.address, + formatNumber(set.nonce), + formatNumber(set.yParity), + toBeArray(set.r), + toBeArray(set.s) + ] +} -const formatAuthorizationList = (value: AuthorizationListEntryAny[]): Auth7702RLPType => { - return value.map((set: AuthorizationListEntryAny) => formatAuthorizationEntry(set)); -}; +const formatAuthorizationList = ( + value: AuthorizationListEntryAny[] +): Auth7702RLPType => { + return value.map((set: AuthorizationListEntryAny) => + formatAuthorizationEntry(set) + ) +} export const serializeEip7702 = (tx: any, sig: null | Signature): string => { - const fields: Array = [ - formatNumber(tx.chainId), - formatNumber(tx.nonce), - formatNumber(tx.maxPriorityFeePerGas || 0), - formatNumber(tx.maxFeePerGas || 0), - formatNumber(tx.gasLimit), - tx.to, - formatNumber(tx.value), - tx.data, - formatAccessList(tx.accessList || []), - formatAuthorizationList(tx.authorizationList || []), - ]; + const fields: Array = [ + formatNumber(tx.chainId), + formatNumber(tx.nonce), + formatNumber(tx.maxPriorityFeePerGas || 0), + formatNumber(tx.maxFeePerGas || 0), + formatNumber(tx.gasLimit), + tx.to, + formatNumber(tx.value), + tx.data, + formatAccessList(tx.accessList || []), + formatAuthorizationList(tx.authorizationList || []) + ] - if (sig) { - fields.push(formatNumber(sig.yParity)); - fields.push(toBeArray(sig.r)); - fields.push(toBeArray(sig.s)); - } + if (sig) { + fields.push(formatNumber(sig.yParity)) + fields.push(toBeArray(sig.r)) + fields.push(toBeArray(sig.s)) + } - return concat(["0x04", encodeRlp(fields)]); -}; + return concat(['0x04', encodeRlp(fields)]) +} diff --git a/safe-eip7702-contracts/src/utils/safe.ts b/safe-eip7702-contracts/src/utils/safe.ts index 13c30bd..2c73e31 100644 --- a/safe-eip7702-contracts/src/utils/safe.ts +++ b/safe-eip7702-contracts/src/utils/safe.ts @@ -1,80 +1,133 @@ -import { ethers } from "hardhat"; -import { AddressLike, BigNumberish, ContractTransactionResponse, Provider, Signer } from "ethers"; -import SafeL2 from "@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json"; -import { ISafe } from "@safe-global/safe-smart-account/dist/typechain-types"; +import { ethers } from 'hardhat' +import { + AddressLike, + BigNumberish, + ContractTransactionResponse, + Provider, + Signer +} from 'ethers' +import SafeL2 from '@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json' +import { ISafe } from '@safe-global/safe-smart-account/dist/typechain-types' export const execTransaction = async ( - relayer: Signer, - wallets: Signer[], - safe: ISafe, - to: string, - value: string, - data: string, - operation: string, + relayer: Signer, + wallets: Signer[], + safe: ISafe, + to: string, + value: string, + data: string, + operation: string ): Promise => { - const nonce = await safe.nonce(); + const nonce = await safe.nonce() - const transactionHash = await safe.getTransactionHash( - to, - value, - data, - operation, - 0, - 0, - 0, - ethers.ZeroAddress, - ethers.ZeroAddress, - nonce, - ); - let signatureBytes = "0x"; - let bytesDataHash = ethers.toBeArray(transactionHash); + const transactionHash = await safe.getTransactionHash( + to, + value, + data, + operation, + 0, + 0, + 0, + ethers.ZeroAddress, + ethers.ZeroAddress, + nonce + ) + let signatureBytes = '0x' + let bytesDataHash = ethers.toBeArray(transactionHash) - const comparableArray = await Promise.all(wallets.map(async (x: Signer) => [await x.getAddress(), x])); - comparableArray.sort((a, b) => a[0].toString().localeCompare(b[0].toString(), "en", { sensitivity: "base" })); - const sorted: Signer[] = comparableArray.map((x) => x[1] as Signer); + const comparableArray = await Promise.all( + wallets.map(async (x: Signer) => [await x.getAddress(), x]) + ) + comparableArray.sort((a, b) => + a[0] + .toString() + .localeCompare(b[0].toString(), 'en', { sensitivity: 'base' }) + ) + const sorted: Signer[] = comparableArray.map((x) => x[1] as Signer) - for (let i = 0; i < sorted.length; i++) { - let flatSig = (await sorted[i].signMessage(bytesDataHash)).replace(/1b$/, "1f").replace(/1c$/, "20"); - signatureBytes += flatSig.slice(2); - } + for (let i = 0; i < sorted.length; i++) { + let flatSig = (await sorted[i].signMessage(bytesDataHash)) + .replace(/1b$/, '1f') + .replace(/1c$/, '20') + signatureBytes += flatSig.slice(2) + } - return await safe - .connect(relayer) - .execTransaction(to, value, data, operation, 0, 0, 0, ethers.ZeroAddress, ethers.ZeroAddress, signatureBytes); -}; + return await safe + .connect(relayer) + .execTransaction( + to, + value, + data, + operation, + 0, + 0, + 0, + ethers.ZeroAddress, + ethers.ZeroAddress, + signatureBytes + ) +} -export const readModuleStorageSlot = async (provider: Provider, account: AddressLike, key: string) => { - const moduleStorageSlot = 1n; - return await readMappingStorage(provider, account, moduleStorageSlot, key); -}; +export const readModuleStorageSlot = async ( + provider: Provider, + account: AddressLike, + key: string +) => { + const moduleStorageSlot = 1n + return await readMappingStorage(provider, account, moduleStorageSlot, key) +} -export const readOwnerStorageSlot = async (provider: Provider, account: AddressLike, key: string) => { - const ownerStorageSlot = 2n; - return await readMappingStorage(provider, account, ownerStorageSlot, key); -}; +export const readOwnerStorageSlot = async ( + provider: Provider, + account: AddressLike, + key: string +) => { + const ownerStorageSlot = 2n + return await readMappingStorage(provider, account, ownerStorageSlot, key) +} -export const readMappingStorage = async (provider: Provider, account: AddressLike, storageSlot: bigint, key: string) => { - const paddedKey = ethers.zeroPadValue(key, 32); - const baseSlot = ethers.zeroPadValue(ethers.toBeHex(storageSlot), 32); - const slot = ethers.keccak256(ethers.concat([paddedKey, baseSlot])); - return await provider.getStorage(account, slot); -}; +export const readMappingStorage = async ( + provider: Provider, + account: AddressLike, + storageSlot: bigint, + key: string +) => { + const paddedKey = ethers.zeroPadValue(key, 32) + const baseSlot = ethers.zeroPadValue(ethers.toBeHex(storageSlot), 32) + const slot = ethers.keccak256(ethers.concat([paddedKey, baseSlot])) + return await provider.getStorage(account, slot) +} -export const getSetupData = (owners: AddressLike[], threshold?: number, to?: AddressLike, modules?: AddressLike[], fallbackHandler?: AddressLike, paymentToken: AddressLike = ethers.ZeroAddress, payment: BigNumberish = 0, paymentReceiver: AddressLike = ethers.ZeroAddress): string => { - const safeInterface = new ethers.Interface(SafeL2.abi); - const safeModuleSetupInterface = new ethers.Interface(["function enableModules(address[])"]); - return safeInterface.encodeFunctionData("setup", [ - owners, - threshold || 0, - to || ethers.ZeroAddress, - module !== undefined ? safeModuleSetupInterface.encodeFunctionData("enableModules", [modules]) : ethers.ZeroAddress, - fallbackHandler || ethers.ZeroAddress, - paymentToken, - payment, - paymentReceiver, - ]); -}; +export const getSetupData = ( + owners: AddressLike[], + threshold?: number, + to?: AddressLike, + modules?: AddressLike[], + fallbackHandler?: AddressLike, + paymentToken: AddressLike = ethers.ZeroAddress, + payment: BigNumberish = 0, + paymentReceiver: AddressLike = ethers.ZeroAddress +): string => { + const safeInterface = new ethers.Interface(SafeL2.abi) + const safeModuleSetupInterface = new ethers.Interface([ + 'function enableModules(address[])' + ]) + return safeInterface.encodeFunctionData('setup', [ + owners, + threshold || 0, + to || ethers.ZeroAddress, + module !== undefined + ? safeModuleSetupInterface.encodeFunctionData('enableModules', [modules]) + : ethers.ZeroAddress, + fallbackHandler || ethers.ZeroAddress, + paymentToken, + payment, + paymentReceiver + ]) +} -export const FALLBACK_HANDLER_STORAGE_SLOT = "0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5"; -export const GUARD_STORAGE_SLOT = "0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8"; -export const SENTINEL_ADDRESS = "0x0000000000000000000000000000000000000001"; +export const FALLBACK_HANDLER_STORAGE_SLOT = + '0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5' +export const GUARD_STORAGE_SLOT = + '0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8' +export const SENTINEL_ADDRESS = '0x0000000000000000000000000000000000000001' diff --git a/safe-eip7702-contracts/src/utils/safeLite.ts b/safe-eip7702-contracts/src/utils/safeLite.ts index cfcce0e..6610a3c 100644 --- a/safe-eip7702-contracts/src/utils/safeLite.ts +++ b/safe-eip7702-contracts/src/utils/safeLite.ts @@ -1,55 +1,65 @@ -import { keccak256, toUtf8Bytes, solidityPacked, AbiCoder, BigNumberish, Signature, ethers, Provider, Wallet } from 'ethers'; +import { + keccak256, + toUtf8Bytes, + solidityPacked, + AbiCoder, + BigNumberish, + Signature, + ethers, + Provider, + Wallet +} from 'ethers' -const defaultAbiCoder = AbiCoder.defaultAbiCoder(); +const defaultAbiCoder = AbiCoder.defaultAbiCoder() export const MULTISEND_TYPEHASH = keccak256( - toUtf8Bytes( - "MultiSend(bytes32 data,uint256 nonce)" - ) -); + toUtf8Bytes('MultiSend(bytes32 data,uint256 nonce)') +) export const DOMAIN_TYPEHASH = keccak256( - toUtf8Bytes( - "EIP712Domain(uint256 chainId,address verifyingContract)" - ) -); + toUtf8Bytes('EIP712Domain(uint256 chainId,address verifyingContract)') +) export const getSignature = async ( - provider: Provider, - signer: Wallet, - transactions: string, // assuming transactions is a hex string - nonce: number, - contractAddress: string -): Promise<{ r: string, vs: string }> => { - const { chainId } = await provider.getNetwork(); - const domain = { - chainId: chainId, - verifyingContract: contractAddress, - }; - - const types = { - MultiSend: [ - { name: "data", type: "bytes32" }, - { name: "nonce", type: "uint256" } - ] - }; - - const transactionsHash = keccak256(transactions); - - const message = { - data: transactionsHash, - nonce: nonce - }; - - // Generate the signature - const signature = await signer.signTypedData(domain, types, message); - const s = Signature.from(signature); - return { - r: s.r, - vs: s.yParityAndS - }; + provider: Provider, + signer: Wallet, + transactions: string, // assuming transactions is a hex string + nonce: number, + contractAddress: string +): Promise<{ r: string; vs: string }> => { + const { chainId } = await provider.getNetwork() + const domain = { + chainId: chainId, + verifyingContract: contractAddress + } + + const types = { + MultiSend: [ + { name: 'data', type: 'bytes32' }, + { name: 'nonce', type: 'uint256' } + ] + } + + const transactionsHash = keccak256(transactions) + + const message = { + data: transactionsHash, + nonce: nonce + } + + // Generate the signature + const signature = await signer.signTypedData(domain, types, message) + const s = Signature.from(signature) + return { + r: s.r, + vs: s.yParityAndS + } } export const getStorageSlot = (): string => { - return ethers.toBeHex(BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00") & BigInt(keccak256(toUtf8Bytes("SafeLite")))); -} \ No newline at end of file + return ethers.toBeHex( + BigInt( + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00' + ) & BigInt(keccak256(toUtf8Bytes('SafeLite'))) + ) +} diff --git a/safe-eip7702-contracts/src/utils/setup.ts b/safe-eip7702-contracts/src/utils/setup.ts index fce3361..98dcfd4 100644 --- a/safe-eip7702-contracts/src/utils/setup.ts +++ b/safe-eip7702-contracts/src/utils/setup.ts @@ -1,66 +1,76 @@ -import hre, { ethers } from "hardhat"; -import { IDAFallbackHandler, ISafe } from "../../typechain-types"; -import SafeProxyFactory from "@safe-global/safe-smart-account/build/artifacts/contracts/proxies/SafeProxyFactory.sol/SafeProxyFactory.json"; -import SafeL2 from "@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json"; -import CompatibilityFallbackHandler from "@safe-global/safe-smart-account/build/artifacts/contracts/handler/CompatibilityFallbackHandler.sol/CompatibilityFallbackHandler.json"; -import MultiSendCallOnly from "@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSendCallOnly.sol/MultiSendCallOnly.json"; -import MultiSend from "@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSend.sol/MultiSend.json"; -import { Address } from "hardhat-deploy/types"; +import hre, { ethers } from 'hardhat' +import { IDAFallbackHandler, ISafe } from '../../typechain-types' +import SafeProxyFactory from '@safe-global/safe-smart-account/build/artifacts/contracts/proxies/SafeProxyFactory.sol/SafeProxyFactory.json' +import SafeL2 from '@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json' +import CompatibilityFallbackHandler from '@safe-global/safe-smart-account/build/artifacts/contracts/handler/CompatibilityFallbackHandler.sol/CompatibilityFallbackHandler.json' +import MultiSendCallOnly from '@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSendCallOnly.sol/MultiSendCallOnly.json' +import MultiSend from '@safe-global/safe-smart-account/build/artifacts/contracts/libraries/MultiSend.sol/MultiSend.json' +import { Address } from 'hardhat-deploy/types' export const getIDAFallbackHandler = async (): Promise => { - const fallbackHandler = await hre.deployments.get("IDAFallbackHandler"); - return ethers.getContractAt("IDAFallbackHandler", fallbackHandler.address); -}; + const fallbackHandler = await hre.deployments.get('IDAFallbackHandler') + return ethers.getContractAt('IDAFallbackHandler', fallbackHandler.address) +} export const getSafeSingleton = async () => { - const safe = await hre.deployments.get("SafeL2"); - return ethers.getContractAt(SafeL2.abi, safe.address); -}; + const safe = await hre.deployments.get('SafeL2') + return ethers.getContractAt(SafeL2.abi, safe.address) +} export const getSafeAtAddress = async (address: string): Promise => { - return ethers.getContractAt("ISafe", address); -}; + return ethers.getContractAt('ISafe', address) +} export const getSafeProxyFactory = async () => { - const safeProxyFactory = await hre.deployments.get("SafeProxyFactory"); - return ethers.getContractAt(SafeProxyFactory.abi, safeProxyFactory.address); -}; + const safeProxyFactory = await hre.deployments.get('SafeProxyFactory') + return ethers.getContractAt(SafeProxyFactory.abi, safeProxyFactory.address) +} export const getCompatibilityFallbackHandler = async () => { - const fallbackHandler = await hre.deployments.get("CompatibilityFallbackHandler"); - return ethers.getContractAt(CompatibilityFallbackHandler.abi, fallbackHandler.address); -}; + const fallbackHandler = await hre.deployments.get( + 'CompatibilityFallbackHandler' + ) + return ethers.getContractAt( + CompatibilityFallbackHandler.abi, + fallbackHandler.address + ) +} export const getClearStorageHelper = async () => { - const clearStorageHelper = await hre.deployments.get("ClearStorageHelper"); - return ethers.getContractAt("ClearStorageHelper", clearStorageHelper.address); -}; + const clearStorageHelper = await hre.deployments.get('ClearStorageHelper') + return ethers.getContractAt('ClearStorageHelper', clearStorageHelper.address) +} export const getSafeModuleSetup = async () => { - const safeModuleSetup = await hre.deployments.get("SafeModuleSetup"); - return ethers.getContractAt("SafeModuleSetup", safeModuleSetup.address); -}; + const safeModuleSetup = await hre.deployments.get('SafeModuleSetup') + return ethers.getContractAt('SafeModuleSetup', safeModuleSetup.address) +} export const getSafeEIP7702ProxyFactory = async () => { - const safeEIP7702ProxyFactory = await hre.deployments.get("SafeEIP7702ProxyFactory"); - return ethers.getContractAt("SafeEIP7702ProxyFactory", safeEIP7702ProxyFactory.address); -}; + const safeEIP7702ProxyFactory = await hre.deployments.get( + 'SafeEIP7702ProxyFactory' + ) + return ethers.getContractAt( + 'SafeEIP7702ProxyFactory', + safeEIP7702ProxyFactory.address + ) +} export const getMultiSendCallOnly = async () => { - const multiSendCallOnly = await hre.deployments.get("MultiSendCallOnly"); - return ethers.getContractAt(MultiSendCallOnly.abi, multiSendCallOnly.address); -}; + const multiSendCallOnly = await hre.deployments.get('MultiSendCallOnly') + return ethers.getContractAt(MultiSendCallOnly.abi, multiSendCallOnly.address) +} export const getMultiSend = async () => { - const multiSend = await hre.deployments.get("MultiSend"); - return ethers.getContractAt(MultiSend.abi, multiSend.address); -}; + const multiSend = await hre.deployments.get('MultiSend') + return ethers.getContractAt(MultiSend.abi, multiSend.address) +} export const getSafeLite = async () => { - const safeLite = await hre.deployments.get("SafeLite"); - return ethers.getContractAt("SafeLite", safeLite.address); + const safeLite = await hre.deployments.get('SafeLite') + return ethers.getContractAt('SafeLite', safeLite.address) } export const getSafeLiteAtAddress = async (address: Address) => { - return ethers.getContractAt("SafeLite", address); -} \ No newline at end of file + return ethers.getContractAt('SafeLite', address) +} diff --git a/safe-eip7702-contracts/src/utils/storageReader.ts b/safe-eip7702-contracts/src/utils/storageReader.ts index 7c748e8..a5aaa47 100644 --- a/safe-eip7702-contracts/src/utils/storageReader.ts +++ b/safe-eip7702-contracts/src/utils/storageReader.ts @@ -1,33 +1,59 @@ -import { AddressLike, Provider } from "ethers"; -import { FALLBACK_HANDLER_STORAGE_SLOT, GUARD_STORAGE_SLOT, readModuleStorageSlot, readOwnerStorageSlot, SENTINEL_ADDRESS } from "./safe"; -import { ethers } from "hardhat"; -import { ACCOUNT_CODE_PREFIX } from "../eip7702/helper"; -import SafeL2 from "@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json"; +import { AddressLike, Provider } from 'ethers' +import { + FALLBACK_HANDLER_STORAGE_SLOT, + GUARD_STORAGE_SLOT, + readModuleStorageSlot, + readOwnerStorageSlot, + SENTINEL_ADDRESS +} from './safe' +import { ethers } from 'hardhat' +import { ACCOUNT_CODE_PREFIX } from '../eip7702/helper' +import SafeL2 from '@safe-global/safe-smart-account/build/artifacts/contracts/SafeL2.sol/SafeL2.json' export const readStorage = async (provider: Provider, account: AddressLike) => { - console.log("---- fallback handler ----"); - console.log(await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT)); + console.log('---- fallback handler ----') + console.log(await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT)) - console.log("---- guard ----"); - console.log(await provider.getStorage(account, GUARD_STORAGE_SLOT)); + console.log('---- guard ----') + console.log(await provider.getStorage(account, GUARD_STORAGE_SLOT)) - for (let i = 0; i <= 4; i++) { - console.log(`---- slot ${i} ----`); - console.log(await provider.getStorage(account, i)); - } -}; + for (let i = 0; i <= 4; i++) { + console.log(`---- slot ${i} ----`) + console.log(await provider.getStorage(account, i)) + } +} -export const printAccountStorage = async (provider: Provider, account: AddressLike, safeSingleton: AddressLike | null) => { - await readStorage(provider, account); - console.log("account: ", account.toString()); - console.log("code: ", await provider.getCode(account)); - if (safeSingleton && (await provider.getCode(account)) === ethers.concat([ACCOUNT_CODE_PREFIX, safeSingleton.toString()])) { - const safe = await ethers.getContractAt(SafeL2.abi, account.toString()); - console.log("owners: ", await safe.getOwners()); - console.log("threshold: ", await safe.getThreshold()); - console.log("modules: ", await safe.getModulesPaginated("0x0000000000000000000000000000000000000001", 10)); - } +export const printAccountStorage = async ( + provider: Provider, + account: AddressLike, + safeSingleton: AddressLike | null +) => { + await readStorage(provider, account) + console.log('account: ', account.toString()) + console.log('code: ', await provider.getCode(account)) + if ( + safeSingleton && + (await provider.getCode(account)) === + ethers.concat([ACCOUNT_CODE_PREFIX, safeSingleton.toString()]) + ) { + const safe = await ethers.getContractAt(SafeL2.abi, account.toString()) + console.log('owners: ', await safe.getOwners()) + console.log('threshold: ', await safe.getThreshold()) + console.log( + 'modules: ', + await safe.getModulesPaginated( + '0x0000000000000000000000000000000000000001', + 10 + ) + ) + } - console.log("module pointer at sentinel address: ", await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS)); - console.log("owner pointer at sentinel address: ", await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS)); -}; + console.log( + 'module pointer at sentinel address: ', + await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS) + ) + console.log( + 'owner pointer at sentinel address: ', + await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS) + ) +} diff --git a/safe-eip7702-contracts/test/EIP7702.spec.ts b/safe-eip7702-contracts/test/EIP7702.spec.ts index ecaad5f..abd7cd0 100644 --- a/safe-eip7702-contracts/test/EIP7702.spec.ts +++ b/safe-eip7702-contracts/test/EIP7702.spec.ts @@ -1,301 +1,525 @@ -import { deployments, ethers } from "hardhat"; +import { deployments, ethers } from 'hardhat' import { - getSafeSingleton, - getIDAFallbackHandler, - getSafeEIP7702ProxyFactory, - getCompatibilityFallbackHandler, - getSafeAtAddress, - getClearStorageHelper, - getSafeModuleSetup, -} from "../src/utils/setup"; -import { AddressLike, SigningKey, ZeroAddress } from "ethers"; -import { execTransaction, getSetupData, GUARD_STORAGE_SLOT, readModuleStorageSlot, readOwnerStorageSlot } from "../src/utils/safe"; -import { expect } from "chai"; -import { ACCOUNT_CODE_PREFIX, calculateProxyAddress, getAuthorizationList, getSignedTransaction } from "../src/eip7702/helper"; -import { FALLBACK_HANDLER_STORAGE_SLOT, SENTINEL_ADDRESS } from "../src/utils/safe"; -import { SafeEIP7702ProxyFactory } from "../typechain-types"; -import { isAccountDelegatedToAddress } from "../src/eip7702/storage"; - -describe("EIP7702", () => { - const setupTests = deployments.createFixture(async ({ deployments }) => { - await deployments.fixture(); - const [deployer] = await ethers.getSigners(); - const fallbackHandler = await getIDAFallbackHandler(); - const safeSingleton = await getSafeSingleton(); - const safeCompatibilityFallbackHandler = await getCompatibilityFallbackHandler(); - const clearStorageHelper = await getClearStorageHelper(); - const safeModuleSetup = await getSafeModuleSetup(); - const safeEIP7702ProxyFactory: SafeEIP7702ProxyFactory = await getSafeEIP7702ProxyFactory(); - const delegatorSigningKey = new ethers.Wallet(ethers.Wallet.createRandom().privateKey, ethers.provider); - const delegator = delegatorSigningKey.connect(ethers.provider); - - const pkRelayer = process.env.PK2 || ""; - const relayerSigningKey = new SigningKey(pkRelayer); - - return { - fallbackHandler, - safeSingleton, - safeCompatibilityFallbackHandler, - deployer, - delegator, - clearStorageHelper, - safeModuleSetup, - safeEIP7702ProxyFactory, - delegatorSigningKey, - relayerSigningKey - }; - }); - - const assertEmptyAccountStorage = async (account: AddressLike) => { - const provider = ethers.provider; - expect(await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT)).to.equal(ethers.ZeroHash); - expect(await provider.getStorage(account, GUARD_STORAGE_SLOT)).to.equal(ethers.ZeroHash); - - // Singleton address - expect(await provider.getStorage(account, 0)).to.equal(ethers.ZeroHash); - // Owner count - expect(await provider.getStorage(account, 3)).to.equal(ethers.ZeroHash); - // Threshold - expect(await provider.getStorage(account, 4)).to.equal(ethers.ZeroHash); - - expect(await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS)).to.equal(ethers.ZeroHash); - expect(await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS)).to.equal(ethers.ZeroHash); - }; - - describe("Test SafeEIP7702Proxy", function () { - it("Give authority to SafeEIP7702Proxy with EOA address itself as Safe owner", async () => { - const { safeSingleton, fallbackHandler, deployer, relayerSigningKey, delegator, safeModuleSetup, safeEIP7702ProxyFactory, delegatorSigningKey } = - await setupTests(); - const provider = ethers.provider; - const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await delegatorSigningKey.getNonce()); - - // Deploy SafeProxy with the delegator as owner - const owners = [delegator]; - const ownerAddresses = await Promise.all(owners.map(async (owner): Promise => await owner.getAddress())); - const fallbackHandlerAddress = await fallbackHandler.getAddress(); - const data = getSetupData( - ownerAddresses, - 1, - await safeModuleSetup.getAddress(), - [fallbackHandlerAddress], - fallbackHandlerAddress, - ); - - const proxyAddress = await calculateProxyAddress(safeEIP7702ProxyFactory, await safeSingleton.getAddress(), data, 0); - const isContract = (await provider.getCode(proxyAddress)) === "0x" ? false : true; - - if (!isContract) { - await safeEIP7702ProxyFactory.connect(deployer).createProxyWithNonce(await safeSingleton.getAddress(), data, 0); - } - - const authorizationList = getAuthorizationList(chainId, authNonce, delegatorSigningKey.privateKey, proxyAddress); - const encodedSignedTx = await getSignedTransaction(provider, relayerSigningKey, authorizationList); - - const account = await delegator.getAddress(); - - const isAlreadyDelegated = await isAccountDelegatedToAddress(provider, await delegator.getAddress(), proxyAddress); - expect(isAlreadyDelegated && (await provider.getStorage(account, 4)) == ethers.zeroPadValue("0x01", 32)).to.be.false; - - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - - const txReceipt = await (await provider.getTransaction(response))?.wait(); - - expect(txReceipt?.status === 1, "Transaction failed"); - - expect(await isAccountDelegatedToAddress(provider, await delegator.getAddress(), proxyAddress)).to.be.true; - - - const setupTxResponse = await relayer.sendTransaction({ to: await delegator.getAddress(), data: data }); - const txSetupReceipt = await setupTxResponse.wait(); - expect(txSetupReceipt?.status === 1, "Transaction failed"); - - // const account = await delegator.getAddress(); - expect(await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT)).to.equal( - ethers.zeroPadValue(fallbackHandlerAddress, 32), - ); - expect(await provider.getStorage(account, GUARD_STORAGE_SLOT)).to.equal(ethers.ZeroHash); - // Singleton address - expect(await provider.getStorage(account, 0)).to.equal(ethers.zeroPadValue(await safeSingleton.getAddress(), 32)); - // Owner count - expect(await provider.getStorage(account, 3)).to.equal(ethers.zeroPadValue("0x01", 32)); - // Threshold - expect(await provider.getStorage(account, 4)).to.equal(ethers.zeroPadValue("0x01", 32)); - expect(await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS)).to.equal( - ethers.zeroPadValue(fallbackHandlerAddress, 32), - ); - expect(await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS)).to.equal(ethers.zeroPadValue(ownerAddresses[0], 32)); - - const amount = 1n; - if ((await provider.getBalance(account)) < amount) { - const tx = await relayer.sendTransaction({ - to: account, - value: amount, - }); - await tx.wait(); - } - const tx = await execTransaction(relayer, owners, await getSafeAtAddress(account), await deployer.getAddress(), "1", "0x", "0"); - await tx.wait(); - }); - - it("Give authority to SafeEIP7702Proxy with other address as Safe owner", async () => { - const { safeSingleton, fallbackHandler, deployer, relayerSigningKey, delegator, safeModuleSetup, safeEIP7702ProxyFactory, delegatorSigningKey } = - await setupTests(); - const provider = ethers.provider; - const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await delegatorSigningKey.getNonce()); - - // Deploy SafeProxy with the another address as owner - const safeOwner = new ethers.Wallet(ethers.Wallet.createRandom().privateKey, ethers.provider); - - const owners = [safeOwner]; - const ownerAddresses = await Promise.all(owners.map(async (owner): Promise => await owner.getAddress())); - const fallbackHandlerAddress = await fallbackHandler.getAddress(); - const data = getSetupData( - ownerAddresses, - 1, - await safeModuleSetup.getAddress(), - [fallbackHandlerAddress], - fallbackHandlerAddress, - ); - - const proxyAddress = await calculateProxyAddress(safeEIP7702ProxyFactory, await safeSingleton.getAddress(), data, 0); - const isContract = (await provider.getCode(proxyAddress)) === "0x" ? false : true; - - if (!isContract) { - await safeEIP7702ProxyFactory.connect(deployer).createProxyWithNonce(await safeSingleton.getAddress(), data, 0); - } - - const authorizationList = getAuthorizationList(chainId, authNonce, delegatorSigningKey.privateKey, proxyAddress); - const encodedSignedTx = await getSignedTransaction(provider, relayerSigningKey, authorizationList); - - const account = await delegator.getAddress(); - - const isAlreadyDelegated = await isAccountDelegatedToAddress(provider, await delegator.getAddress(), proxyAddress); - expect(isAlreadyDelegated && (await provider.getStorage(account, 4)) == ethers.zeroPadValue("0x01", 32)).to.be.false; - - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - - const txReceipt = await (await provider.getTransaction(response))?.wait(); - - expect(txReceipt?.status === 1, "Transaction failed"); - - expect(await isAccountDelegatedToAddress(provider, await delegator.getAddress(), proxyAddress)).to.be.true; - - - const setupTxResponse = await relayer.sendTransaction({ to: await delegator.getAddress(), data: data }); - const txSetupReceipt = await setupTxResponse.wait(); - expect(txSetupReceipt?.status === 1, "Transaction failed"); - - // const account = await delegator.getAddress(); - expect(await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT)).to.equal( - ethers.zeroPadValue(fallbackHandlerAddress, 32), - ); - expect(await provider.getStorage(account, GUARD_STORAGE_SLOT)).to.equal(ethers.ZeroHash); - // Singleton address - expect(await provider.getStorage(account, 0)).to.equal(ethers.zeroPadValue(await safeSingleton.getAddress(), 32)); - // Owner count - expect(await provider.getStorage(account, 3)).to.equal(ethers.zeroPadValue("0x01", 32)); - // Threshold - expect(await provider.getStorage(account, 4)).to.equal(ethers.zeroPadValue("0x01", 32)); - expect(await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS)).to.equal( - ethers.zeroPadValue(fallbackHandlerAddress, 32), - ); - expect(await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS)).to.equal(ethers.zeroPadValue(ownerAddresses[0], 32)); - - const amount = 1n; - if ((await provider.getBalance(account)) < amount) { - const tx = await relayer.sendTransaction({ - to: account, - value: amount, - }); - await tx.wait(); - } - const tx = await execTransaction(relayer, owners, await getSafeAtAddress(account), await deployer.getAddress(), "1", "0x", "0"); - await tx.wait(); - }); - - it("Revoke authority and clear storage", async () => { - const { delegator, clearStorageHelper, relayerSigningKey } = await setupTests(); - const provider = ethers.provider; - const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await delegator.getNonce()); - const authAddress = await clearStorageHelper.getAddress(); - - const authorizationList = getAuthorizationList(chainId, authNonce, delegator.privateKey, authAddress); - let encodedSignedTx = await getSignedTransaction(provider, relayerSigningKey, authorizationList); - - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - const txReceipt = await (await provider.getTransaction(response))?.wait(); - expect(txReceipt?.status === 1, "Transaction failed"); - - const codeAtEOA = await provider.getCode(await delegator.getAddress()); - expect(codeAtEOA).to.equal(ethers.concat([ACCOUNT_CODE_PREFIX, await clearStorageHelper.getAddress()])); - - const clearAccountStorage = await ethers.getContractAt("ClearStorageHelper", await delegator.getAddress()); - const txClearStorageResponse = await clearAccountStorage.connect(relayer).clearSafeStorage(); - await txClearStorageResponse.wait(); - - const account = await delegator.getAddress(); - await assertEmptyAccountStorage(account); - }); - - it("Clear storage using onRedelegation()", async () => { - const { safeSingleton, fallbackHandler, deployer, delegator, safeModuleSetup, safeEIP7702ProxyFactory, relayerSigningKey } = - await setupTests(); - - const provider = ethers.provider; - const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider); - - const account = await delegator.getAddress(); - expect(account).to.equal(await delegator.getAddress()); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await delegator.getNonce()); - - // Deploy SafeProxy - const owners = [deployer]; - const ownerAddresses = await Promise.all(owners.map(async (owner): Promise => await owner.getAddress())); - const fallbackHandlerAddress = await fallbackHandler.getAddress(); - const setupData = getSetupData( - ownerAddresses, - 1, - await safeModuleSetup.getAddress(), - [fallbackHandlerAddress], - fallbackHandlerAddress, - ); - - const proxyAddress = await calculateProxyAddress(safeEIP7702ProxyFactory, await safeSingleton.getAddress(), setupData, 0); - const isContract = (await provider.getCode(proxyAddress)) === "0x" ? false : true; - - if (!isContract) { - await safeEIP7702ProxyFactory.connect(deployer).createProxyWithNonce(await safeSingleton.getAddress(), setupData, 0); - } - - const authorizationList = getAuthorizationList(chainId, authNonce, delegator.privateKey, proxyAddress); - const encodedSignedTx = await getSignedTransaction(provider, relayerSigningKey, authorizationList); - - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - const txSetupDataReceipt = await (await provider.getTransaction(response))?.wait(); - expect(txSetupDataReceipt?.status === 1, "Transaction failed"); - const setupTxResponse = await relayer.sendTransaction({ to: account, data: setupData }); - const txSetupReceipt = await setupTxResponse.wait(); - expect(txSetupReceipt?.status === 1, "Transaction failed"); - - const safe = await getSafeAtAddress(account); - const interfaceOnRedelegation = new ethers.Interface(["function onRedelegation()"]); - const data = interfaceOnRedelegation.encodeFunctionData("onRedelegation", []); - const txResponse = await execTransaction(relayer, owners, safe, account, "0", data, "0"); - const txReceipt = await txResponse.wait(); - expect(txReceipt !== null && txReceipt.status === 1, "Transaction failed"); - - await assertEmptyAccountStorage(account); - }); - }); -}); + getSafeSingleton, + getIDAFallbackHandler, + getSafeEIP7702ProxyFactory, + getCompatibilityFallbackHandler, + getSafeAtAddress, + getClearStorageHelper, + getSafeModuleSetup +} from '../src/utils/setup' +import { AddressLike, SigningKey, ZeroAddress } from 'ethers' +import { + execTransaction, + getSetupData, + GUARD_STORAGE_SLOT, + readModuleStorageSlot, + readOwnerStorageSlot +} from '../src/utils/safe' +import { expect } from 'chai' +import { + ACCOUNT_CODE_PREFIX, + calculateProxyAddress, + getAuthorizationList, + getSignedTransaction +} from '../src/eip7702/helper' +import { + FALLBACK_HANDLER_STORAGE_SLOT, + SENTINEL_ADDRESS +} from '../src/utils/safe' +import { SafeEIP7702ProxyFactory } from '../typechain-types' +import { isAccountDelegatedToAddress } from '../src/eip7702/storage' + +describe('EIP7702', () => { + const setupTests = deployments.createFixture(async ({ deployments }) => { + await deployments.fixture() + const [deployer] = await ethers.getSigners() + const fallbackHandler = await getIDAFallbackHandler() + const safeSingleton = await getSafeSingleton() + const safeCompatibilityFallbackHandler = + await getCompatibilityFallbackHandler() + const clearStorageHelper = await getClearStorageHelper() + const safeModuleSetup = await getSafeModuleSetup() + const safeEIP7702ProxyFactory: SafeEIP7702ProxyFactory = + await getSafeEIP7702ProxyFactory() + const delegatorSigningKey = new ethers.Wallet( + ethers.Wallet.createRandom().privateKey, + ethers.provider + ) + const delegator = delegatorSigningKey.connect(ethers.provider) + + const pkRelayer = process.env.PK2 || '' + const relayerSigningKey = new SigningKey(pkRelayer) + + return { + fallbackHandler, + safeSingleton, + safeCompatibilityFallbackHandler, + deployer, + delegator, + clearStorageHelper, + safeModuleSetup, + safeEIP7702ProxyFactory, + delegatorSigningKey, + relayerSigningKey + } + }) + + const assertEmptyAccountStorage = async (account: AddressLike) => { + const provider = ethers.provider + expect( + await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT) + ).to.equal(ethers.ZeroHash) + expect(await provider.getStorage(account, GUARD_STORAGE_SLOT)).to.equal( + ethers.ZeroHash + ) + + // Singleton address + expect(await provider.getStorage(account, 0)).to.equal(ethers.ZeroHash) + // Owner count + expect(await provider.getStorage(account, 3)).to.equal(ethers.ZeroHash) + // Threshold + expect(await provider.getStorage(account, 4)).to.equal(ethers.ZeroHash) + + expect( + await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS) + ).to.equal(ethers.ZeroHash) + expect( + await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS) + ).to.equal(ethers.ZeroHash) + } + + describe('Test SafeEIP7702Proxy', function () { + it('Give authority to SafeEIP7702Proxy with EOA address itself as Safe owner', async () => { + const { + safeSingleton, + fallbackHandler, + deployer, + relayerSigningKey, + delegator, + safeModuleSetup, + safeEIP7702ProxyFactory, + delegatorSigningKey + } = await setupTests() + const provider = ethers.provider + const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider) + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await delegatorSigningKey.getNonce()) + + // Deploy SafeProxy with the delegator as owner + const owners = [delegator] + const ownerAddresses = await Promise.all( + owners.map(async (owner): Promise => await owner.getAddress()) + ) + const fallbackHandlerAddress = await fallbackHandler.getAddress() + const data = getSetupData( + ownerAddresses, + 1, + await safeModuleSetup.getAddress(), + [fallbackHandlerAddress], + fallbackHandlerAddress + ) + + const proxyAddress = await calculateProxyAddress( + safeEIP7702ProxyFactory, + await safeSingleton.getAddress(), + data, + 0 + ) + const isContract = + (await provider.getCode(proxyAddress)) === '0x' ? false : true + + if (!isContract) { + await safeEIP7702ProxyFactory + .connect(deployer) + .createProxyWithNonce(await safeSingleton.getAddress(), data, 0) + } + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + delegatorSigningKey.privateKey, + proxyAddress + ) + const encodedSignedTx = await getSignedTransaction( + provider, + relayerSigningKey, + authorizationList + ) + + const account = await delegator.getAddress() + + const isAlreadyDelegated = await isAccountDelegatedToAddress( + provider, + await delegator.getAddress(), + proxyAddress + ) + expect( + isAlreadyDelegated && + (await provider.getStorage(account, 4)) == + ethers.zeroPadValue('0x01', 32) + ).to.be.false + + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + + const txReceipt = await (await provider.getTransaction(response))?.wait() + + expect(txReceipt?.status === 1, 'Transaction failed') + + expect( + await isAccountDelegatedToAddress( + provider, + await delegator.getAddress(), + proxyAddress + ) + ).to.be.true + + const setupTxResponse = await relayer.sendTransaction({ + to: await delegator.getAddress(), + data: data + }) + const txSetupReceipt = await setupTxResponse.wait() + expect(txSetupReceipt?.status === 1, 'Transaction failed') + + // const account = await delegator.getAddress(); + expect( + await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT) + ).to.equal(ethers.zeroPadValue(fallbackHandlerAddress, 32)) + expect(await provider.getStorage(account, GUARD_STORAGE_SLOT)).to.equal( + ethers.ZeroHash + ) + // Singleton address + expect(await provider.getStorage(account, 0)).to.equal( + ethers.zeroPadValue(await safeSingleton.getAddress(), 32) + ) + // Owner count + expect(await provider.getStorage(account, 3)).to.equal( + ethers.zeroPadValue('0x01', 32) + ) + // Threshold + expect(await provider.getStorage(account, 4)).to.equal( + ethers.zeroPadValue('0x01', 32) + ) + expect( + await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS) + ).to.equal(ethers.zeroPadValue(fallbackHandlerAddress, 32)) + expect( + await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS) + ).to.equal(ethers.zeroPadValue(ownerAddresses[0], 32)) + + const amount = 1n + if ((await provider.getBalance(account)) < amount) { + const tx = await relayer.sendTransaction({ + to: account, + value: amount + }) + await tx.wait() + } + const tx = await execTransaction( + relayer, + owners, + await getSafeAtAddress(account), + await deployer.getAddress(), + '1', + '0x', + '0' + ) + await tx.wait() + }) + + it('Give authority to SafeEIP7702Proxy with other address as Safe owner', async () => { + const { + safeSingleton, + fallbackHandler, + deployer, + relayerSigningKey, + delegator, + safeModuleSetup, + safeEIP7702ProxyFactory, + delegatorSigningKey + } = await setupTests() + const provider = ethers.provider + const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider) + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await delegatorSigningKey.getNonce()) + + // Deploy SafeProxy with the another address as owner + const safeOwner = new ethers.Wallet( + ethers.Wallet.createRandom().privateKey, + ethers.provider + ) + + const owners = [safeOwner] + const ownerAddresses = await Promise.all( + owners.map(async (owner): Promise => await owner.getAddress()) + ) + const fallbackHandlerAddress = await fallbackHandler.getAddress() + const data = getSetupData( + ownerAddresses, + 1, + await safeModuleSetup.getAddress(), + [fallbackHandlerAddress], + fallbackHandlerAddress + ) + + const proxyAddress = await calculateProxyAddress( + safeEIP7702ProxyFactory, + await safeSingleton.getAddress(), + data, + 0 + ) + const isContract = + (await provider.getCode(proxyAddress)) === '0x' ? false : true + + if (!isContract) { + await safeEIP7702ProxyFactory + .connect(deployer) + .createProxyWithNonce(await safeSingleton.getAddress(), data, 0) + } + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + delegatorSigningKey.privateKey, + proxyAddress + ) + const encodedSignedTx = await getSignedTransaction( + provider, + relayerSigningKey, + authorizationList + ) + + const account = await delegator.getAddress() + + const isAlreadyDelegated = await isAccountDelegatedToAddress( + provider, + await delegator.getAddress(), + proxyAddress + ) + expect( + isAlreadyDelegated && + (await provider.getStorage(account, 4)) == + ethers.zeroPadValue('0x01', 32) + ).to.be.false + + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + + const txReceipt = await (await provider.getTransaction(response))?.wait() + + expect(txReceipt?.status === 1, 'Transaction failed') + + expect( + await isAccountDelegatedToAddress( + provider, + await delegator.getAddress(), + proxyAddress + ) + ).to.be.true + + const setupTxResponse = await relayer.sendTransaction({ + to: await delegator.getAddress(), + data: data + }) + const txSetupReceipt = await setupTxResponse.wait() + expect(txSetupReceipt?.status === 1, 'Transaction failed') + + // const account = await delegator.getAddress(); + expect( + await provider.getStorage(account, FALLBACK_HANDLER_STORAGE_SLOT) + ).to.equal(ethers.zeroPadValue(fallbackHandlerAddress, 32)) + expect(await provider.getStorage(account, GUARD_STORAGE_SLOT)).to.equal( + ethers.ZeroHash + ) + // Singleton address + expect(await provider.getStorage(account, 0)).to.equal( + ethers.zeroPadValue(await safeSingleton.getAddress(), 32) + ) + // Owner count + expect(await provider.getStorage(account, 3)).to.equal( + ethers.zeroPadValue('0x01', 32) + ) + // Threshold + expect(await provider.getStorage(account, 4)).to.equal( + ethers.zeroPadValue('0x01', 32) + ) + expect( + await readModuleStorageSlot(provider, account, SENTINEL_ADDRESS) + ).to.equal(ethers.zeroPadValue(fallbackHandlerAddress, 32)) + expect( + await readOwnerStorageSlot(provider, account, SENTINEL_ADDRESS) + ).to.equal(ethers.zeroPadValue(ownerAddresses[0], 32)) + + const amount = 1n + if ((await provider.getBalance(account)) < amount) { + const tx = await relayer.sendTransaction({ + to: account, + value: amount + }) + await tx.wait() + } + const tx = await execTransaction( + relayer, + owners, + await getSafeAtAddress(account), + await deployer.getAddress(), + '1', + '0x', + '0' + ) + await tx.wait() + }) + + it('Revoke authority and clear storage', async () => { + const { delegator, clearStorageHelper, relayerSigningKey } = + await setupTests() + const provider = ethers.provider + const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider) + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await delegator.getNonce()) + const authAddress = await clearStorageHelper.getAddress() + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + delegator.privateKey, + authAddress + ) + let encodedSignedTx = await getSignedTransaction( + provider, + relayerSigningKey, + authorizationList + ) + + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + const txReceipt = await (await provider.getTransaction(response))?.wait() + expect(txReceipt?.status === 1, 'Transaction failed') + + const codeAtEOA = await provider.getCode(await delegator.getAddress()) + expect(codeAtEOA).to.equal( + ethers.concat([ + ACCOUNT_CODE_PREFIX, + await clearStorageHelper.getAddress() + ]) + ) + + const clearAccountStorage = await ethers.getContractAt( + 'ClearStorageHelper', + await delegator.getAddress() + ) + const txClearStorageResponse = await clearAccountStorage + .connect(relayer) + .clearSafeStorage() + await txClearStorageResponse.wait() + + const account = await delegator.getAddress() + await assertEmptyAccountStorage(account) + }) + + it('Clear storage using onRedelegation()', async () => { + const { + safeSingleton, + fallbackHandler, + deployer, + delegator, + safeModuleSetup, + safeEIP7702ProxyFactory, + relayerSigningKey + } = await setupTests() + + const provider = ethers.provider + const relayer = new ethers.Wallet(relayerSigningKey.privateKey, provider) + + const account = await delegator.getAddress() + expect(account).to.equal(await delegator.getAddress()) + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await delegator.getNonce()) + + // Deploy SafeProxy + const owners = [deployer] + const ownerAddresses = await Promise.all( + owners.map(async (owner): Promise => await owner.getAddress()) + ) + const fallbackHandlerAddress = await fallbackHandler.getAddress() + const setupData = getSetupData( + ownerAddresses, + 1, + await safeModuleSetup.getAddress(), + [fallbackHandlerAddress], + fallbackHandlerAddress + ) + + const proxyAddress = await calculateProxyAddress( + safeEIP7702ProxyFactory, + await safeSingleton.getAddress(), + setupData, + 0 + ) + const isContract = + (await provider.getCode(proxyAddress)) === '0x' ? false : true + + if (!isContract) { + await safeEIP7702ProxyFactory + .connect(deployer) + .createProxyWithNonce(await safeSingleton.getAddress(), setupData, 0) + } + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + delegator.privateKey, + proxyAddress + ) + const encodedSignedTx = await getSignedTransaction( + provider, + relayerSigningKey, + authorizationList + ) + + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + const txSetupDataReceipt = await ( + await provider.getTransaction(response) + )?.wait() + expect(txSetupDataReceipt?.status === 1, 'Transaction failed') + const setupTxResponse = await relayer.sendTransaction({ + to: account, + data: setupData + }) + const txSetupReceipt = await setupTxResponse.wait() + expect(txSetupReceipt?.status === 1, 'Transaction failed') + + const safe = await getSafeAtAddress(account) + const interfaceOnRedelegation = new ethers.Interface([ + 'function onRedelegation()' + ]) + const data = interfaceOnRedelegation.encodeFunctionData( + 'onRedelegation', + [] + ) + const txResponse = await execTransaction( + relayer, + owners, + safe, + account, + '0', + data, + '0' + ) + const txReceipt = await txResponse.wait() + expect(txReceipt !== null && txReceipt.status === 1, 'Transaction failed') + + await assertEmptyAccountStorage(account) + }) + }) +}) diff --git a/safe-eip7702-contracts/test/SafeLite.spec.ts b/safe-eip7702-contracts/test/SafeLite.spec.ts index ccc7921..e77ef4c 100644 --- a/safe-eip7702-contracts/test/SafeLite.spec.ts +++ b/safe-eip7702-contracts/test/SafeLite.spec.ts @@ -1,191 +1,312 @@ -import { keccak256, Provider, Signature, SigningKey, Wallet, ZeroAddress } from "ethers"; -import { deployments, ethers } from "hardhat"; -import { getIDAFallbackHandler, getSafeLite, getSafeLiteAtAddress, getSafeSingleton, getCompatibilityFallbackHandler, getClearStorageHelper, getSafeModuleSetup, getSafeEIP7702ProxyFactory } from "../src/utils/setup"; -import { SafeEIP7702ProxyFactory } from "../typechain-types"; -import { getAuthorizationList, getSignedTransaction } from "../src/eip7702/helper"; -import { expect } from "chai"; -import { encodeMultiSend, MetaTransaction } from "@safe-global/safe-smart-account"; -import { getSignature, getStorageSlot } from "../src/utils/safeLite"; - - -describe("SafeLite", () => { - - const setupTests = deployments.createFixture(async ({ deployments }) => { - await deployments.fixture(); - const [deployer] = await ethers.getSigners(); - const fallbackHandler = await getIDAFallbackHandler(); - const safeSingleton = await getSafeSingleton(); - const safeCompatibilityFallbackHandler = await getCompatibilityFallbackHandler(); - const clearStorageHelper = await getClearStorageHelper(); - const safeModuleSetup = await getSafeModuleSetup(); - const safeLite = await getSafeLite(); - const safeEIP7702ProxyFactory: SafeEIP7702ProxyFactory = await getSafeEIP7702ProxyFactory(); - const accountWallet = new ethers.Wallet(ethers.Wallet.createRandom().privateKey, ethers.provider); - - const pkRelayer = process.env.PK2 || ""; - const relayerSigningKey = new SigningKey(pkRelayer); - - const amount = 1_000_000_000_000 - if (await ethers.provider.getBalance(accountWallet.address) < amount) { - const relayerWallet = new ethers.Wallet(relayerSigningKey.privateKey, ethers.provider); - await (await relayerWallet.sendTransaction({ to: accountWallet.address, value: amount })).wait(); - } - - return { - fallbackHandler, - safeSingleton, - safeCompatibilityFallbackHandler, - deployer, - clearStorageHelper, - safeModuleSetup, - safeEIP7702ProxyFactory, - accountWallet, - relayerSigningKey, - safeLite, - provider: ethers.provider - }; - }); - - describe("multisend", () => { - it("Invoke multisend using relayer", async () => { - const { accountWallet, safeLite, relayerSigningKey } = - await setupTests(); - const provider = ethers.provider; - const accountAddress = await accountWallet.getAddress(); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await accountWallet.getNonce()); - - const authorizationList = getAuthorizationList(chainId, authNonce, accountWallet.privateKey, await safeLite.getAddress()); - - const amount1 = BigInt("1"); - const amount2 = BigInt("2"); - const totalAmount = BigInt(amount1 + amount2); - - const transactions: MetaTransaction[] = [{ - to: ZeroAddress, - value: amount1, - data: "0x", - operation: 0 - }, { - to: ZeroAddress, - value: amount2, - data: "0x", - operation: 0 - }]; - - - const encodedTx = encodeMultiSend(transactions); - // Example usage - const nonce: number = 0; - - const { r, vs } = await getSignature(provider, accountWallet, encodedTx, nonce, accountAddress); - - const calldata = safeLite.interface.encodeFunctionData("multiSend", [encodedTx, r, vs]); - const encodedSignedTx = await getSignedTransaction(provider, relayerSigningKey, authorizationList, accountAddress, 0, calldata); - - const accountBalanceBefore = await provider.getBalance(accountAddress); - - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - const txReceipt = await (await provider.getTransaction(response))?.wait(); - const accountBalanceAfter = await provider.getBalance(accountAddress); - expect(txReceipt?.status === 1, "Transaction failed"); - expect(accountBalanceAfter).to.equal(accountBalanceBefore - totalAmount); - - expect(await provider.getCode(accountAddress)).to.equal(ethers.concat(["0xef0100", await safeLite.getAddress()])); - expect(BigInt(await provider.getStorage(accountAddress, getStorageSlot()))).to.equal(1n); - - }) - - it("Invoke multisend (self sponsor)", async () => { - const { accountWallet, safeLite } = - await setupTests(); - const provider = ethers.provider; - const accountAddress = await accountWallet.getAddress(); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await accountWallet.getNonce()); - - const authorizationList = getAuthorizationList(chainId, authNonce, accountWallet.privateKey, await safeLite.getAddress()); - - const amount1 = BigInt("1"); - const amount2 = BigInt("2"); - const totalAmount = BigInt(amount1 + amount2); - - const transactions: MetaTransaction[] = [{ - to: ZeroAddress, - value: amount1, - data: "0x", - operation: 0 - }, { - to: ZeroAddress, - value: amount2, - data: "0x", - operation: 0 - }]; - - const encodedTx = encodeMultiSend(transactions); - // Example usage - const nonce: number = 0; - - const { r, vs } = await getSignature(provider, accountWallet, encodedTx, nonce, accountAddress); - - const calldata = safeLite.interface.encodeFunctionData("multiSend", [encodedTx, r, vs]); - const encodedSignedTx = await getSignedTransaction(provider, new SigningKey(accountWallet.privateKey), authorizationList, accountAddress, 0, calldata, nonce + 1); - - const accountBalanceBefore = await provider.getBalance(accountAddress); - - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - const txReceipt = await (await provider.getTransaction(response))?.wait(); - const accountBalanceAfter = await provider.getBalance(accountAddress); - expect(txReceipt?.status === 1, "Transaction failed"); - expect(accountBalanceAfter).to.be.lessThan(accountBalanceBefore - totalAmount - BigInt(txReceipt?.gasUsed || 0)); - - expect(await provider.getCode(accountAddress)).to.equal(ethers.concat(["0xef0100", await safeLite.getAddress()])); - expect(BigInt(await provider.getStorage(accountAddress, getStorageSlot()))).to.equal(1); +import { + keccak256, + Provider, + Signature, + SigningKey, + Wallet, + ZeroAddress +} from 'ethers' +import { deployments, ethers } from 'hardhat' +import { + getIDAFallbackHandler, + getSafeLite, + getSafeLiteAtAddress, + getSafeSingleton, + getCompatibilityFallbackHandler, + getClearStorageHelper, + getSafeModuleSetup, + getSafeEIP7702ProxyFactory +} from '../src/utils/setup' +import { SafeEIP7702ProxyFactory } from '../typechain-types' +import { + getAuthorizationList, + getSignedTransaction +} from '../src/eip7702/helper' +import { expect } from 'chai' +import { + encodeMultiSend, + MetaTransaction +} from '@safe-global/safe-smart-account' +import { getSignature, getStorageSlot } from '../src/utils/safeLite' + +describe('SafeLite', () => { + const setupTests = deployments.createFixture(async ({ deployments }) => { + await deployments.fixture() + const [deployer] = await ethers.getSigners() + const fallbackHandler = await getIDAFallbackHandler() + const safeSingleton = await getSafeSingleton() + const safeCompatibilityFallbackHandler = + await getCompatibilityFallbackHandler() + const clearStorageHelper = await getClearStorageHelper() + const safeModuleSetup = await getSafeModuleSetup() + const safeLite = await getSafeLite() + const safeEIP7702ProxyFactory: SafeEIP7702ProxyFactory = + await getSafeEIP7702ProxyFactory() + const accountWallet = new ethers.Wallet( + ethers.Wallet.createRandom().privateKey, + ethers.provider + ) + + const pkRelayer = process.env.PK2 || '' + const relayerSigningKey = new SigningKey(pkRelayer) + + const amount = 1_000_000_000_000 + if ((await ethers.provider.getBalance(accountWallet.address)) < amount) { + const relayerWallet = new ethers.Wallet( + relayerSigningKey.privateKey, + ethers.provider + ) + await ( + await relayerWallet.sendTransaction({ + to: accountWallet.address, + value: amount }) - }); - - describe("isValidSignature", async () => { - const setup = deployments.createFixture(async ({ deployments }) => { - const { accountWallet, provider, safeLite, relayerSigningKey } = await setupTests(); - - const chainId = (await provider.getNetwork()).chainId; - const authNonce = BigInt(await accountWallet.getNonce()); - - const authorizationList = getAuthorizationList(chainId, authNonce, accountWallet.privateKey, await safeLite.getAddress()); - const encodedSignedTx = await getSignedTransaction(provider, relayerSigningKey, authorizationList); - const response = await provider.send("eth_sendRawTransaction", [encodedSignedTx]); - await (await provider.getTransaction(response))?.wait(); - - return { - accountWallet - } - }); - - it("Should magic value when signature is valid", async () => { - const { accountWallet } = - await setup(); - - const safeLiteInstance = await getSafeLiteAtAddress(accountWallet.address); - const hash = keccak256("0xdeadbeef"); - const signature = (accountWallet.signingKey).sign(ethers.toBeArray(hash)); - expect(await safeLiteInstance.isValidSignature.staticCall(hash, signature.compactSerialized)).to.equal("0x1626ba7e"); - }); - - it("Should non-magic value when signature is valid", async () => { - const { accountWallet } = - await setup(); - - const safeLiteInstance = await getSafeLiteAtAddress(accountWallet.address); - const signerWallet = new ethers.Wallet(ethers.Wallet.createRandom().privateKey, ethers.provider); - - const hash = keccak256("0xdeadbeef"); - const signature = (signerWallet.signingKey).sign(ethers.toBeArray(hash)); - console.log(signature.compactSerialized, await safeLiteInstance.isValidSignature.staticCall(hash, signature.compactSerialized)); - expect(await safeLiteInstance.isValidSignature.staticCall(hash, signature.compactSerialized)).to.equal("0x00000000"); - - }); - }); - -}) \ No newline at end of file + ).wait() + } + + return { + fallbackHandler, + safeSingleton, + safeCompatibilityFallbackHandler, + deployer, + clearStorageHelper, + safeModuleSetup, + safeEIP7702ProxyFactory, + accountWallet, + relayerSigningKey, + safeLite, + provider: ethers.provider + } + }) + + describe('multisend', () => { + it('Invoke multisend using relayer', async () => { + const { accountWallet, safeLite, relayerSigningKey } = await setupTests() + const provider = ethers.provider + const accountAddress = await accountWallet.getAddress() + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await accountWallet.getNonce()) + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + accountWallet.privateKey, + await safeLite.getAddress() + ) + + const amount1 = BigInt('1') + const amount2 = BigInt('2') + const totalAmount = BigInt(amount1 + amount2) + + const transactions: MetaTransaction[] = [ + { + to: ZeroAddress, + value: amount1, + data: '0x', + operation: 0 + }, + { + to: ZeroAddress, + value: amount2, + data: '0x', + operation: 0 + } + ] + + const encodedTx = encodeMultiSend(transactions) + // Example usage + const nonce: number = 0 + + const { r, vs } = await getSignature( + provider, + accountWallet, + encodedTx, + nonce, + accountAddress + ) + + const calldata = safeLite.interface.encodeFunctionData('multiSend', [ + encodedTx, + r, + vs + ]) + const encodedSignedTx = await getSignedTransaction( + provider, + relayerSigningKey, + authorizationList, + accountAddress, + 0, + calldata + ) + + const accountBalanceBefore = await provider.getBalance(accountAddress) + + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + const txReceipt = await (await provider.getTransaction(response))?.wait() + const accountBalanceAfter = await provider.getBalance(accountAddress) + expect(txReceipt?.status === 1, 'Transaction failed') + expect(accountBalanceAfter).to.equal(accountBalanceBefore - totalAmount) + + expect(await provider.getCode(accountAddress)).to.equal( + ethers.concat(['0xef0100', await safeLite.getAddress()]) + ) + expect( + BigInt(await provider.getStorage(accountAddress, getStorageSlot())) + ).to.equal(1n) + }) + + it('Invoke multisend (self sponsor)', async () => { + const { accountWallet, safeLite } = await setupTests() + const provider = ethers.provider + const accountAddress = await accountWallet.getAddress() + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await accountWallet.getNonce()) + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + accountWallet.privateKey, + await safeLite.getAddress() + ) + + const amount1 = BigInt('1') + const amount2 = BigInt('2') + const totalAmount = BigInt(amount1 + amount2) + + const transactions: MetaTransaction[] = [ + { + to: ZeroAddress, + value: amount1, + data: '0x', + operation: 0 + }, + { + to: ZeroAddress, + value: amount2, + data: '0x', + operation: 0 + } + ] + + const encodedTx = encodeMultiSend(transactions) + // Example usage + const nonce: number = 0 + + const { r, vs } = await getSignature( + provider, + accountWallet, + encodedTx, + nonce, + accountAddress + ) + + const calldata = safeLite.interface.encodeFunctionData('multiSend', [ + encodedTx, + r, + vs + ]) + const encodedSignedTx = await getSignedTransaction( + provider, + new SigningKey(accountWallet.privateKey), + authorizationList, + accountAddress, + 0, + calldata, + nonce + 1 + ) + + const accountBalanceBefore = await provider.getBalance(accountAddress) + + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + const txReceipt = await (await provider.getTransaction(response))?.wait() + const accountBalanceAfter = await provider.getBalance(accountAddress) + expect(txReceipt?.status === 1, 'Transaction failed') + expect(accountBalanceAfter).to.be.lessThan( + accountBalanceBefore - totalAmount - BigInt(txReceipt?.gasUsed || 0) + ) + + expect(await provider.getCode(accountAddress)).to.equal( + ethers.concat(['0xef0100', await safeLite.getAddress()]) + ) + expect( + BigInt(await provider.getStorage(accountAddress, getStorageSlot())) + ).to.equal(1) + }) + }) + + describe('isValidSignature', async () => { + const setup = deployments.createFixture(async ({ deployments }) => { + const { accountWallet, provider, safeLite, relayerSigningKey } = + await setupTests() + + const chainId = (await provider.getNetwork()).chainId + const authNonce = BigInt(await accountWallet.getNonce()) + + const authorizationList = getAuthorizationList( + chainId, + authNonce, + accountWallet.privateKey, + await safeLite.getAddress() + ) + const encodedSignedTx = await getSignedTransaction( + provider, + relayerSigningKey, + authorizationList + ) + const response = await provider.send('eth_sendRawTransaction', [ + encodedSignedTx + ]) + await (await provider.getTransaction(response))?.wait() + + return { + accountWallet + } + }) + + it('Should magic value when signature is valid', async () => { + const { accountWallet } = await setup() + + const safeLiteInstance = await getSafeLiteAtAddress(accountWallet.address) + const hash = keccak256('0xdeadbeef') + const signature = accountWallet.signingKey.sign(ethers.toBeArray(hash)) + expect( + await safeLiteInstance.isValidSignature.staticCall( + hash, + signature.compactSerialized + ) + ).to.equal('0x1626ba7e') + }) + + it('Should non-magic value when signature is valid', async () => { + const { accountWallet } = await setup() + + const safeLiteInstance = await getSafeLiteAtAddress(accountWallet.address) + const signerWallet = new ethers.Wallet( + ethers.Wallet.createRandom().privateKey, + ethers.provider + ) + + const hash = keccak256('0xdeadbeef') + const signature = signerWallet.signingKey.sign(ethers.toBeArray(hash)) + console.log( + signature.compactSerialized, + await safeLiteInstance.isValidSignature.staticCall( + hash, + signature.compactSerialized + ) + ) + expect( + await safeLiteInstance.isValidSignature.staticCall( + hash, + signature.compactSerialized + ) + ).to.equal('0x00000000') + }) + }) +}) From b49daec0268375e320b580b45a203c002c0bacef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 29 Nov 2024 14:38:28 +0100 Subject: [PATCH 4/4] Format code in ui folder --- safe-eip7702-ui/src/App.tsx | 54 +- safe-eip7702-ui/src/api/api.ts | 68 +-- safe-eip7702-ui/src/components/Batch.tsx | 228 +++++--- safe-eip7702-ui/src/components/Delegate.tsx | 526 +++++++++++------- .../src/components/GoToSafeWalletButton.tsx | 35 +- .../src/components/NavigationBar.tsx | 137 +++-- safe-eip7702-ui/src/components/Settings.tsx | 231 +++++--- .../src/components/WalletInput.tsx | 49 +- safe-eip7702-ui/src/context/WalletContext.tsx | 223 +++++--- safe-eip7702-ui/src/main.tsx | 26 +- .../src/safe-eip7702-config/config.ts | 43 +- safe-eip7702-ui/src/theme/darkPalette.ts | 26 +- safe-eip7702-ui/src/theme/lightPalette.ts | 26 +- safe-eip7702-ui/src/theme/safeTheme.ts | 318 ++++++----- safe-eip7702-ui/src/theme/typography.ts | 20 +- safe-eip7702-ui/src/utils/storageReader.ts | 171 +++--- safe-eip7702-ui/src/utils/utils.ts | 150 ++--- safe-eip7702-ui/src/wagmi.ts | 22 +- 18 files changed, 1406 insertions(+), 947 deletions(-) diff --git a/safe-eip7702-ui/src/App.tsx b/safe-eip7702-ui/src/App.tsx index b7b622e..803d616 100644 --- a/safe-eip7702-ui/src/App.tsx +++ b/safe-eip7702-ui/src/App.tsx @@ -1,14 +1,14 @@ -import React from 'react'; -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; -import { ThemeProvider } from '@mui/material/styles'; -import { Container, CssBaseline } from '@mui/material'; -import Delegate from './components/Delegate'; -import Settings from './components/Settings'; -import Home from './components/home/Home'; -import { WalletProvider } from './context/WalletContext'; -import NavigationBar from './components/NavigationBar'; -import SafeThemeProvider from './theme/SafeThemeProvider'; -import Batch from './components/Batch'; +import React from 'react' +import { BrowserRouter as Router, Route, Routes } from 'react-router-dom' +import { ThemeProvider } from '@mui/material/styles' +import { Container, CssBaseline } from '@mui/material' +import Delegate from './components/Delegate' +import Settings from './components/Settings' +import Home from './components/home/Home' +import { WalletProvider } from './context/WalletContext' +import NavigationBar from './components/NavigationBar' +import SafeThemeProvider from './theme/SafeThemeProvider' +import Batch from './components/Batch' const App: React.FC = () => { return ( @@ -16,25 +16,25 @@ const App: React.FC = () => { {(safeTheme) => ( <> - - - - - - - } /> - } /> - } /> - } /> - - - + + + + + + + } /> + } /> + } /> + } /> + + + )} - ); -}; + ) +} -export default App; +export default App diff --git a/safe-eip7702-ui/src/api/api.ts b/safe-eip7702-ui/src/api/api.ts index 7e990ca..a80f8ee 100644 --- a/safe-eip7702-ui/src/api/api.ts +++ b/safe-eip7702-ui/src/api/api.ts @@ -1,5 +1,5 @@ -import { Address } from "viem"; -import { AuthorizationList } from "viem/experimental"; +import { Address } from 'viem' +import { AuthorizationList } from 'viem/experimental' export const relayAuthorization = async ( authorizationList: AuthorizationList, @@ -9,58 +9,58 @@ export const relayAuthorization = async ( ): Promise => { try { const response = await fetch(import.meta.env.VITE_BACKEND_URL, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - authorizationList, - initData, - from, - proxyFactory - }), - }); - return await response.json() + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + authorizationList, + initData, + from, + proxyFactory + }) + }) + return await response.json() } catch (error) { - console.error("Failed to relay authorization:", error); + console.error('Failed to relay authorization:', error) return { error: error - }; + } } -}; +} export const checkRPCStatus = async (rpcUrl: string): Promise => { const data = { - jsonrpc: "2.0", - id: "1", - method: "web3_clientVersion", - params: null, - }; + jsonrpc: '2.0', + id: '1', + method: 'web3_clientVersion', + params: null + } try { const response = await fetch(rpcUrl, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json' }, - body: JSON.stringify(data), - }); + body: JSON.stringify(data) + }) if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); + throw new Error(`HTTP error! status: ${response.status}`) } - const json = await response.json(); + const json = await response.json() // Check if there's a result in the response if (json.result) { - return true; + return true } else { - console.log("Error in RPC response:", json.error || "Unknown error"); - return false; + console.log('Error in RPC response:', json.error || 'Unknown error') + return false } } catch (error) { - console.error("Failed to connect to the Ethereum RPC:", error); - return false; + console.error('Failed to connect to the Ethereum RPC:', error) + return false } -}; +} diff --git a/safe-eip7702-ui/src/components/Batch.tsx b/safe-eip7702-ui/src/components/Batch.tsx index 5c3dde0..592bddb 100644 --- a/safe-eip7702-ui/src/components/Batch.tsx +++ b/safe-eip7702-ui/src/components/Batch.tsx @@ -1,65 +1,91 @@ -import React, { useContext, useState, useEffect } from 'react'; -import { createPimlicoClient } from "permissionless/clients/pimlico"; -import { Button, Typography, TextField, IconButton, Card, CardContent, CardActions, Dialog, DialogContent, DialogTitle, CircularProgress, Alert } from '@mui/material'; -import Grid from "@mui/material/Grid2"; -import DeleteIcon from '@mui/icons-material/Delete'; -import AddIcon from '@mui/icons-material/Add'; -import { WalletContext } from '../context/WalletContext'; -import { FEATURES, safeEIP7702Config } from '../safe-eip7702-config/config'; -import { formatEther, Hex, http, isAddress, isHex, zeroAddress } from 'viem'; -import { toSafeSmartAccount } from 'permissionless/accounts'; -import { createSmartAccountClient } from 'permissionless'; -import { UserOperationCall } from 'viem/account-abstraction'; +import React, { useContext, useState, useEffect } from 'react' +import { createPimlicoClient } from 'permissionless/clients/pimlico' +import { + Button, + Typography, + TextField, + IconButton, + Card, + CardContent, + CardActions, + Dialog, + DialogContent, + DialogTitle, + CircularProgress, + Alert +} from '@mui/material' +import Grid from '@mui/material/Grid2' +import DeleteIcon from '@mui/icons-material/Delete' +import AddIcon from '@mui/icons-material/Add' +import { WalletContext } from '../context/WalletContext' +import { FEATURES, safeEIP7702Config } from '../safe-eip7702-config/config' +import { formatEther, Hex, http, isAddress, isHex, zeroAddress } from 'viem' +import { toSafeSmartAccount } from 'permissionless/accounts' +import { createSmartAccountClient } from 'permissionless' +import { UserOperationCall } from 'viem/account-abstraction' enum BatchError { - SAFE_OWNER_NOT_CONNECTED = "Safe owner not connected", - MISSING_PIMLICO_API_KEY = "Missing Pimlico API Key", - OTHER = "Other" + SAFE_OWNER_NOT_CONNECTED = 'Safe owner not connected', + MISSING_PIMLICO_API_KEY = 'Missing Pimlico API Key', + OTHER = 'Other' } const Batch: React.FC = () => { - const { features, chainId, publicClient, account, safeStorage } = useContext(WalletContext)!; - const [transactions, setTransactions] = useState([{ to: zeroAddress, value: BigInt(0), data: '0x' }]); - const [loading, setLoading] = useState(false); - const [errors, setErrors] = useState([]); - const [dialogOpen, setDialogOpen] = useState(false); - const [transactionUrl, setTransactionUrl] = useState(null); - const [balance, setBalance] = useState(""); + const { features, chainId, publicClient, account, safeStorage } = + useContext(WalletContext)! + const [transactions, setTransactions] = useState([ + { to: zeroAddress, value: BigInt(0), data: '0x' } + ]) + const [loading, setLoading] = useState(false) + const [errors, setErrors] = useState([]) + const [dialogOpen, setDialogOpen] = useState(false) + const [transactionUrl, setTransactionUrl] = useState(null) + const [balance, setBalance] = useState('') useEffect(() => { const fetchBalance = async () => { if (account) { - const balance = await publicClient.getBalance({ address: account.address }); - setBalance(formatEther(balance)); + const balance = await publicClient.getBalance({ + address: account.address + }) + setBalance(formatEther(balance)) } - }; + } - fetchBalance(); - }, [account, publicClient]); + fetchBalance() + }, [account, publicClient]) const handleExecute = async () => { - setLoading(true); - setDialogOpen(true); + setLoading(true) + setDialogOpen(true) try { - const pimlicoApiKey = import.meta.env.VITE_PIMLICO_API_KEY as Hex | undefined + const pimlicoApiKey = import.meta.env.VITE_PIMLICO_API_KEY as + | Hex + | undefined if (!pimlicoApiKey) { - setErrors([...errors, BatchError.MISSING_PIMLICO_API_KEY]); - return; + setErrors([...errors, BatchError.MISSING_PIMLICO_API_KEY]) + return } const pimlicoUrl = `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${pimlicoApiKey}` const pimlicoClient = createPimlicoClient({ - transport: http(pimlicoUrl), + transport: http(pimlicoUrl) }) - if (safeStorage && safeStorage.owners && account && safeStorage.owners.includes(account.address) && safeStorage.threshold === 1n) { + if ( + safeStorage && + safeStorage.owners && + account && + safeStorage.owners.includes(account.address) && + safeStorage.threshold === 1n + ) { const safeAccount = await toSafeSmartAccount({ address: account?.address, owners: [account], client: publicClient, - version: "1.4.1", + version: '1.4.1' }) const smartAccountClient = createSmartAccountClient({ @@ -67,68 +93,92 @@ const Batch: React.FC = () => { paymaster: pimlicoClient, bundlerTransport: http(pimlicoUrl), userOperation: { - estimateFeesPerGas: async () => (await pimlicoClient.getUserOperationGasPrice()).fast, - }, + estimateFeesPerGas: async () => + (await pimlicoClient.getUserOperationGasPrice()).fast + } }) const userOperationHash = await smartAccountClient.sendUserOperation({ calls: transactions }) - const { receipt } = await smartAccountClient.waitForUserOperationReceipt({ - hash: userOperationHash, - }) + const { receipt } = + await smartAccountClient.waitForUserOperationReceipt({ + hash: userOperationHash + }) - const url = `${safeEIP7702Config[chainId].explorer}/tx/${receipt.transactionHash}`; - setTransactionUrl(url); - console.log(`UserOperation included: ${url}`); + const url = `${safeEIP7702Config[chainId].explorer}/tx/${receipt.transactionHash}` + setTransactionUrl(url) + console.log(`UserOperation included: ${url}`) } else { - console.error(`Safe owner for account [${account?.address}] is not the connected account`) + console.error( + `Safe owner for account [${account?.address}] is not the connected account` + ) } } catch (e) { console.error(e) setErrors([...errors, BatchError.OTHER]) } - setLoading(false); + setLoading(false) } const handleAddTransaction = () => { - setTransactions([...transactions, { to: zeroAddress, value: BigInt(0), data: '0x' }]); - }; + setTransactions([ + ...transactions, + { to: zeroAddress, value: BigInt(0), data: '0x' } + ]) + } const handleRemoveTransaction = (index: number) => { - const newTransactions = transactions.filter((_, i) => i !== index); - setTransactions(newTransactions); - }; + const newTransactions = transactions.filter((_, i) => i !== index) + setTransactions(newTransactions) + } - const handleTransactionChange = (index: number, field: keyof UserOperationCall, value: string) => { - const newTransactions = [...transactions]; + const handleTransactionChange = ( + index: number, + field: keyof UserOperationCall, + value: string + ) => { + const newTransactions = [...transactions] if (field === 'value') { - newTransactions[index][field] = BigInt(value); + newTransactions[index][field] = BigInt(value) } else { - newTransactions[index][field] = value as `0x${string}`; + newTransactions[index][field] = value as `0x${string}` } - setTransactions(newTransactions); - }; + setTransactions(newTransactions) + } - const canExecute = () => !features.includes(FEATURES.SUPPORT_4337) - || loading - || !(safeStorage - && account - && safeStorage.owners?.includes(account.address) && safeStorage.threshold === 1n - && safeStorage.modules?.includes(safeEIP7702Config[chainId].addresses.safe4337Module)); + const canExecute = () => + !features.includes(FEATURES.SUPPORT_4337) || + loading || + !( + safeStorage && + account && + safeStorage.owners?.includes(account.address) && + safeStorage.threshold === 1n && + safeStorage.modules?.includes( + safeEIP7702Config[chainId].addresses.safe4337Module + ) + ) return ( - + Batch and relay {!features.includes(FEATURES.SUPPORT_4337) && ( - SUPPORT_4337 is not enabled in the features for this chain. + + SUPPORT_4337 is not enabled in the features for this chain. + )} @@ -141,14 +191,18 @@ const Batch: React.FC = () => { handleTransactionChange(index, 'to', e.target.value)} + onChange={(e) => + handleTransactionChange(index, 'to', e.target.value) + } error={!isAddress(transaction.to)} - helperText={!isAddress(transaction.to) ? 'Invalid address' : ''} + helperText={ + !isAddress(transaction.to) ? 'Invalid address' : '' + } fullWidth sx={{ '& .MuiInputBase-input': { - fontFamily: 'monospace', - }, + fontFamily: 'monospace' + } }} /> @@ -157,7 +211,9 @@ const Batch: React.FC = () => { label="Value" type="number" value={transaction.value?.toString()} - onChange={(e) => handleTransactionChange(index, 'value', e.target.value)} + onChange={(e) => + handleTransactionChange(index, 'value', e.target.value) + } fullWidth /> @@ -165,14 +221,18 @@ const Batch: React.FC = () => { handleTransactionChange(index, 'data', e.target.value)} + onChange={(e) => + handleTransactionChange(index, 'data', e.target.value) + } error={!isHex(transaction.data)} - helperText={!isHex(transaction.data) ? 'Invalid hex data' : ''} + helperText={ + !isHex(transaction.data) ? 'Invalid hex data' : '' + } fullWidth sx={{ '& .MuiInputBase-input': { - fontFamily: 'monospace', - }, + fontFamily: 'monospace' + } }} /> @@ -187,7 +247,6 @@ const Batch: React.FC = () => { ))} - Available balance: {balance} ETH @@ -217,10 +276,21 @@ const Batch: React.FC = () => { setDialogOpen(false)}> - {loading ? "Processing" : Finished execution} + + {loading ? ( + 'Processing' + ) : ( + Finished execution + )} - + {loading && ( @@ -241,7 +311,7 @@ const Batch: React.FC = () => { - ); -}; + ) +} -export default Batch; \ No newline at end of file +export default Batch diff --git a/safe-eip7702-ui/src/components/Delegate.tsx b/safe-eip7702-ui/src/components/Delegate.tsx index 7280126..352caeb 100644 --- a/safe-eip7702-ui/src/components/Delegate.tsx +++ b/safe-eip7702-ui/src/components/Delegate.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from "react"; +import { useContext, useEffect, useState } from 'react' import { Abi, PrivateKeyAccount, @@ -8,12 +8,12 @@ import { http, isAddress, isAddressEqual, - zeroAddress, -} from "viem"; -import { WalletContext } from "../context/WalletContext"; -import { FEATURES, safeEIP7702Config } from "../safe-eip7702-config/config"; -import safeEIP7702Proxy from "../safe-eip7702-config/artifact/SafeEIP7702Proxy.json"; -import safeModuleSetup from "../safe-eip7702-config/artifact/SafeModuleSetup.json"; + zeroAddress +} from 'viem' +import { WalletContext } from '../context/WalletContext' +import { FEATURES, safeEIP7702Config } from '../safe-eip7702-config/config' +import safeEIP7702Proxy from '../safe-eip7702-config/artifact/SafeEIP7702Proxy.json' +import safeModuleSetup from '../safe-eip7702-config/artifact/SafeModuleSetup.json' import { Button, Typography, @@ -26,222 +26,275 @@ import { SelectChangeEvent, Box, Tooltip, - Dialog, -} from "@mui/material"; -import Grid from "@mui/material/Grid2"; -import { eip7702Actions } from "viem/experimental"; -import { ACCOUNT_CODE_PREFIX, getProxyAddress, getShortAddress, getShortTransactionHash } from "../utils/utils"; -import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; -import { relayAuthorization } from "../api/api"; -import { Link } from "react-router-dom"; -import DoneIcon from "@mui/icons-material/Done"; -import AddIcon from "@mui/icons-material/Add"; -import InfoOutlined from '@mui/icons-material/InfoOutlined'; -import DefaultConfigurationDialog from "./dialogs/DefaultConfigurationDialog"; -import GoToSafeWalletButton from './GoToSafeWalletButton'; + Dialog +} from '@mui/material' +import Grid from '@mui/material/Grid2' +import { eip7702Actions } from 'viem/experimental' +import { + ACCOUNT_CODE_PREFIX, + getProxyAddress, + getShortAddress, + getShortTransactionHash +} from '../utils/utils' +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline' +import { relayAuthorization } from '../api/api' +import { Link } from 'react-router-dom' +import DoneIcon from '@mui/icons-material/Done' +import AddIcon from '@mui/icons-material/Add' +import InfoOutlined from '@mui/icons-material/InfoOutlined' +import DefaultConfigurationDialog from './dialogs/DefaultConfigurationDialog' +import GoToSafeWalletButton from './GoToSafeWalletButton' declare global { interface BigInt { - toJSON(): Number; + toJSON(): Number } } BigInt.prototype.toJSON = function () { - return Number(this); -}; + return Number(this) +} function Delegate() { - const { features, authorizations, chainId, account, setAuthorizations, safeStorage } = useContext(WalletContext)!; - - const [proxyAddress, setProxyAddress] = useState<`0x${string}`>(); - const [errorMessage, setErrorMessage] = useState(); - const [threshold, setThreshold] = useState(1); - const [owners, setOwners] = useState([account?.address || ""]); - const [initData, setInitData] = useState<`0x${string}`>(); - const [isWaitingForTransactionHash, setIsWaitingForTransactionHash] = useState(false); - const [isWaitingForTransactionReceipt, setIsWaitingForTransactionReceipt] = useState(false); - const [transactionHash, setTransactionHash] = useState<`0x${string}`>(); - const [proxyCreationSalt, setProxyCreationSalt] = useState(0n); - const [nonce, setNonce] = useState(0); - const [signed, setSigned] = useState(false); - const [isProxyDeployed, setIsProxyDeployed] = useState(false); - const [delegatee, setDelegatee] = useState(); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(); - const [canSign, setCanSign] = useState(false); - const [success, setSuccess] = useState(false); - const [openDialog, setOpenDialog] = useState(false); - const [openDialogTransactionStatus, setOpenDialogTransactionStatus] = useState(false); - - const proxyFactory = safeEIP7702Config[chainId]?.addresses.proxyFactory; + const { + features, + authorizations, + chainId, + account, + setAuthorizations, + safeStorage + } = useContext(WalletContext)! + + const [proxyAddress, setProxyAddress] = useState<`0x${string}`>() + const [errorMessage, setErrorMessage] = useState() + const [threshold, setThreshold] = useState(1) + const [owners, setOwners] = useState([account?.address || '']) + const [initData, setInitData] = useState<`0x${string}`>() + const [isWaitingForTransactionHash, setIsWaitingForTransactionHash] = + useState(false) + const [isWaitingForTransactionReceipt, setIsWaitingForTransactionReceipt] = + useState(false) + const [transactionHash, setTransactionHash] = useState<`0x${string}`>() + const [proxyCreationSalt, setProxyCreationSalt] = useState(0n) + const [nonce, setNonce] = useState(0) + const [signed, setSigned] = useState(false) + const [isProxyDeployed, setIsProxyDeployed] = useState(false) + const [delegatee, setDelegatee] = useState() + const [loading, setLoading] = useState(false) + const [error, setError] = useState() + const [canSign, setCanSign] = useState(false) + const [success, setSuccess] = useState(false) + const [openDialog, setOpenDialog] = useState(false) + const [openDialogTransactionStatus, setOpenDialogTransactionStatus] = + useState(false) + + const proxyFactory = safeEIP7702Config[chainId]?.addresses.proxyFactory const isDelegatedToSafeAccount = () => { - return delegatee && safeStorage && safeStorage.singleton && isAddressEqual(safeEIP7702Config[chainId].addresses.safeSingleton, safeStorage.singleton); + return ( + delegatee && + safeStorage && + safeStorage.singleton && + isAddressEqual( + safeEIP7702Config[chainId].addresses.safeSingleton, + safeStorage.singleton + ) + ) } useEffect(() => { - setInitData(calculateInitData() as `0x${string}`); - }, [threshold, owners]); + setInitData(calculateInitData() as `0x${string}`) + }, [threshold, owners]) useEffect(() => { - if (proxyAddress === undefined || isWaitingForTransactionHash || isWaitingForTransactionReceipt || authorizations.length > 0 || delegatee !== undefined) { - setCanSign(false); + if ( + proxyAddress === undefined || + isWaitingForTransactionHash || + isWaitingForTransactionReceipt || + authorizations.length > 0 || + delegatee !== undefined + ) { + setCanSign(false) } else { - setCanSign(true); + setCanSign(true) } - }, [proxyAddress, isWaitingForTransactionHash, isWaitingForTransactionReceipt, authorizations, delegatee]); + }, [ + proxyAddress, + isWaitingForTransactionHash, + isWaitingForTransactionReceipt, + authorizations, + delegatee + ]) useEffect(() => { if (proxyAddress) { - (async () => { - const proxyCode = await publicClient.getCode({ address: proxyAddress }); + ;(async () => { + const proxyCode = await publicClient.getCode({ address: proxyAddress }) if (proxyCode) { - setIsProxyDeployed(true); + setIsProxyDeployed(true) } else { - setIsProxyDeployed(false); + setIsProxyDeployed(false) } - })(); + })() } - }, [proxyAddress]); + }, [proxyAddress]) useEffect(() => { - if (proxyFactory && chainId && nonce !== undefined) calculateProxyAddress(); - }, [proxyFactory, chainId, initData]); + if (proxyFactory && chainId && nonce !== undefined) calculateProxyAddress() + }, [proxyFactory, chainId, initData]) useEffect(() => { if (account) { - (async () => { - - setOwners([account?.address]); - setThreshold(1); - setInitData(calculateInitData() as `0x${string}`); + ;(async () => { + setOwners([account?.address]) + setThreshold(1) + setInitData(calculateInitData() as `0x${string}`) try { const publicClient = createPublicClient({ - transport: http(safeEIP7702Config[chainId].rpc), - }); + transport: http(safeEIP7702Config[chainId].rpc) + }) const transactionCount = await publicClient.getTransactionCount({ - address: account.address, - }); - setNonce(transactionCount); + address: account.address + }) + setNonce(transactionCount) - const accountCode = await publicClient.getCode({ address: account.address }); + const accountCode = await publicClient.getCode({ + address: account.address + }) if (accountCode && accountCode.startsWith(ACCOUNT_CODE_PREFIX)) { - setDelegatee(accountCode); + setDelegatee(accountCode) } else { - setDelegatee(undefined); + setDelegatee(undefined) } - } catch (e) { - console.error("RPC error", e); + console.error('RPC error', e) } - })(); + })() } - }, [account]); + }, [account]) const walletClient = createWalletClient({ - transport: http(safeEIP7702Config[chainId].rpc), - }).extend(eip7702Actions()); + transport: http(safeEIP7702Config[chainId].rpc) + }).extend(eip7702Actions()) const publicClient = createPublicClient({ - transport: http(safeEIP7702Config[chainId].rpc), - }); + transport: http(safeEIP7702Config[chainId].rpc) + }) const validateOwners = (): boolean => { - const uniqueOwners = new Set(owners); - return uniqueOwners.size === owners.length && owners.every((owner) => isAddress(owner)); - }; + const uniqueOwners = new Set(owners) + return ( + uniqueOwners.size === owners.length && + owners.every((owner) => isAddress(owner)) + ) + } const handleThresholdChange = (event: SelectChangeEvent) => { - const value = parseInt(event.target.value, 10); - if (!isNaN(value) && value > 0) setThreshold(value); - else alert("Threshold must be a positive number."); - }; + const value = parseInt(event.target.value, 10) + if (!isNaN(value) && value > 0) setThreshold(value) + else alert('Threshold must be a positive number.') + } const handleOwnerChange = (index: number, value: string) => { - const updatedOwners = [...owners]; - updatedOwners[index] = value; - setOwners(updatedOwners); - }; + const updatedOwners = [...owners] + updatedOwners[index] = value + setOwners(updatedOwners) + } - const addOwner = () => setOwners([...owners, ""]); + const addOwner = () => setOwners([...owners, '']) const removeOwner = (index: number) => { - const updatedOwners = owners.filter((_, i) => i !== index); - setOwners(updatedOwners); - }; + const updatedOwners = owners.filter((_, i) => i !== index) + setOwners(updatedOwners) + } const calculateInitData = () => { - if (!chainId || owners.length === 0 || !validateOwners() || threshold > owners.length) return; + if ( + !chainId || + owners.length === 0 || + !validateOwners() || + threshold > owners.length + ) + return const moduleSetupData = encodeFunctionData({ abi: safeModuleSetup.abi, - functionName: "enableModules", - args: [[safeEIP7702Config[chainId].addresses.fallbackHandler]], - }); + functionName: 'enableModules', + args: [[safeEIP7702Config[chainId].addresses.fallbackHandler]] + }) const setupCalldata = encodeFunctionData({ abi: safeEIP7702Proxy.abi as Abi, - functionName: "setup", + functionName: 'setup', args: [ owners, threshold, safeEIP7702Config[chainId].addresses.moduleSetup, moduleSetupData, safeEIP7702Config[chainId].addresses.fallbackHandler, - "0x" + "00".repeat(20), + '0x' + '00'.repeat(20), 0, - "0x" + "00".repeat(20), - ], - }); + '0x' + '00'.repeat(20) + ] + }) - return setupCalldata; - }; + return setupCalldata + } const handleConvertToSmartAccount = async () => { - setOpenDialogTransactionStatus(true); - setError(undefined); - setSuccess(false); + setOpenDialogTransactionStatus(true) + setError(undefined) + setSuccess(false) if (!authorizations.length) { - setErrorMessage("Authorization not signed"); - return; + setErrorMessage('Authorization not signed') + return } - setLoading(true); - setIsWaitingForTransactionHash(true); + setLoading(true) + setIsWaitingForTransactionHash(true) - const result = await relayAuthorization(authorizations, initData, proxyFactory, account?.address || zeroAddress); + const result = await relayAuthorization( + authorizations, + initData, + proxyFactory, + account?.address || zeroAddress + ) - setIsWaitingForTransactionHash(false); + setIsWaitingForTransactionHash(false) if (result.txHash) { - setTransactionHash(result.txHash); - setIsWaitingForTransactionReceipt(true); + setTransactionHash(result.txHash) + setIsWaitingForTransactionReceipt(true) try { - const transactionReceipt = await publicClient.waitForTransactionReceipt({ - hash: result.txHash, - pollingInterval: parseInt(import.meta.env.VITE_TRANSACTION_POOLING_INTERVAL) || 12_000, - }); - console.log("Transaction receipt", transactionReceipt); - - if (transactionReceipt.status === "success") { - setSuccess(true); + const transactionReceipt = await publicClient.waitForTransactionReceipt( + { + hash: result.txHash, + pollingInterval: + parseInt(import.meta.env.VITE_TRANSACTION_POOLING_INTERVAL) || + 12_000 + } + ) + console.log('Transaction receipt', transactionReceipt) + + if (transactionReceipt.status === 'success') { + setSuccess(true) } else { - setError("Transaction failed"); - setSuccess(false); + setError('Transaction failed') + setSuccess(false) } } catch (e) { - console.error("Failed to execute transaction", e); - setError("Failed to execute transaction"); + console.error('Failed to execute transaction', e) + setError('Failed to execute transaction') } - setIsWaitingForTransactionReceipt(false); + setIsWaitingForTransactionReceipt(false) } else { - setError("Failed to relay authorization"); - console.error("Request to relay authorization failed:", result.error); + setError('Failed to relay authorization') + console.error('Request to relay authorization failed:', result.error) } - setLoading(false); - }; + setLoading(false) + } const handleSignAuthorization = async (chainId: number) => { if (account && proxyAddress) { @@ -249,21 +302,21 @@ function Delegate() { account: account as PrivateKeyAccount, contractAddress: proxyAddress, nonce: nonce, - chainId: chainId, - }); + chainId: chainId + }) - setAuthorizations([authorization]); - setSigned(true); - setSuccess(false); + setAuthorizations([authorization]) + setSigned(true) + setSuccess(false) } - }; + } const calculateProxyAddress = async () => { if (!proxyFactory || !chainId || !initData) { - setProxyAddress(undefined); - setSigned(false); - setAuthorizations([]); - return; + setProxyAddress(undefined) + setSigned(false) + setAuthorizations([]) + return } const calculatedProxyAddress = getProxyAddress( @@ -271,26 +324,31 @@ function Delegate() { safeEIP7702Config[chainId].addresses.safeSingleton, initData, proxyCreationSalt - ); + ) - setProxyAddress(calculatedProxyAddress); + setProxyAddress(calculatedProxyAddress) if (signed && calculatedProxyAddress !== proxyAddress) { - setSigned(false); - setAuthorizations([]); + setSigned(false) + setAuthorizations([]) } - }; + } const handleOpenDialog = () => { - setOpenDialog(true); - }; + setOpenDialog(true) + } const handleCloseDialog = () => { - setOpenDialog(false); - }; + setOpenDialog(false) + } return ( - + Account Setup @@ -304,25 +362,33 @@ function Delegate() { handleOwnerChange(index, e.target.value)} placeholder="Enter singer address" margin="normal" - error={!isAddress(owner) || owners.indexOf(owner) !== owners.lastIndexOf(owner)} + error={ + !isAddress(owner) || + owners.indexOf(owner) !== owners.lastIndexOf(owner) + } helperText={ - (!isAddress(owner) && "Invalid address") || - (owners.indexOf(owner) !== owners.lastIndexOf(owner) && "Duplicate singer address") + (!isAddress(owner) && 'Invalid address') || + (owners.indexOf(owner) !== owners.lastIndexOf(owner) && + 'Duplicate singer address') } sx={{ '& .MuiInputBase-input': { - fontFamily: 'monospace', - }, + fontFamily: 'monospace' + } }} /> - removeOwner(index)}> + removeOwner(index)} + > @@ -339,7 +405,11 @@ function Delegate() { - {owners.map((owner, index) => ( {index + 1} @@ -348,16 +418,19 @@ function Delegate() { - + - + View other default configuration - + - + @@ -365,10 +438,14 @@ function Delegate() { {isProxyDeployed ? ( - Proxy {getShortAddress(proxyAddress)} already deployed + + Proxy{' '} + + {getShortAddress(proxyAddress)} + {' '} + already deployed + ) : ( - Relayer will deploy proxy at address: {getShortAddress(proxyAddress)} + Relayer will deploy proxy at address:{' '} + + {getShortAddress(proxyAddress)} + )} @@ -393,20 +479,30 @@ function Delegate() { ) : null} {isDelegatedToSafeAccount() && ( - View more}> + View more} + > - Account already delegated and uses Safe singleton: - {getShortAddress((safeStorage?.singleton || zeroAddress))} + Account already delegated and uses Safe singleton:{' '} + + {getShortAddress(safeStorage?.singleton || zeroAddress)} )} {delegatee && !isDelegatedToSafeAccount() && ( - View more}> + View more} + > - Account already delegated to address: - {getShortAddress(("0x" + delegatee.slice(8)) as `0x${string}`)} + Account already delegated to address:{' '} + + {getShortAddress(('0x' + delegatee.slice(8)) as `0x${string}`)} @@ -420,17 +516,23 @@ function Delegate() { fullWidth endIcon={authorizations.length > 0 ? : null} > - {authorizations.length === 0 ? "Sign Authorization" : "Authorization Signed"} + {authorizations.length === 0 + ? 'Sign Authorization' + : 'Authorization Signed'} {isDelegatedToSafeAccount() && ( @@ -439,14 +541,27 @@ function Delegate() { )} - setOpenDialogTransactionStatus(false)}> + setOpenDialogTransactionStatus(false)} + > - + {transactionHash && ( Transaction hash:  - + {getShortTransactionHash(transactionHash)} @@ -455,7 +570,9 @@ function Delegate() { {isWaitingForTransactionHash || isWaitingForTransactionReceipt ? ( - Waiting for transaction to confirm + + Waiting for transaction to confirm + ) : null} @@ -466,7 +583,7 @@ function Delegate() { )} {error && ( - {error} + {error} )} @@ -474,20 +591,24 @@ function Delegate() { - Transaction executed. EOA now can be used in Safe wallet. + + Transaction executed. EOA now can be used in Safe wallet. + - - { - features.includes(FEATURES.SAFE_WALLET) && + + {features.includes(FEATURES.SAFE_WALLET) && ( - } - - { - features.includes(FEATURES.SUPPORT_4337) && + )} + {features.includes(FEATURES.SUPPORT_4337) && ( - ); -}; + + ) +} -export default GoToSafeWalletButton; \ No newline at end of file +export default GoToSafeWalletButton diff --git a/safe-eip7702-ui/src/components/NavigationBar.tsx b/safe-eip7702-ui/src/components/NavigationBar.tsx index d1f9b55..df488e5 100644 --- a/safe-eip7702-ui/src/components/NavigationBar.tsx +++ b/safe-eip7702-ui/src/components/NavigationBar.tsx @@ -1,76 +1,91 @@ -import React, { useContext, useEffect, useState } from "react"; -import { AppBar, Toolbar, Button, Typography, Box, IconButton, MenuItem, Menu, Tooltip } from "@mui/material"; -import { Link } from "react-router-dom"; -import { WalletContext } from "../context/WalletContext"; -import ChangeAccountDialog from "./dialogs/ChangeAccountDialog"; -import { safeEIP7702Config } from "../safe-eip7702-config/config"; -import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline"; -import ErrorIcon from "@mui/icons-material/Error"; -import { checkRPCStatus } from "../api/api"; -import { getShortAddress } from "../utils/utils"; -import { zeroAddress } from "viem"; +import React, { useContext, useEffect, useState } from 'react' +import { + AppBar, + Toolbar, + Button, + Typography, + Box, + IconButton, + MenuItem, + Menu, + Tooltip +} from '@mui/material' +import { Link } from 'react-router-dom' +import { WalletContext } from '../context/WalletContext' +import ChangeAccountDialog from './dialogs/ChangeAccountDialog' +import { safeEIP7702Config } from '../safe-eip7702-config/config' +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline' +import ErrorIcon from '@mui/icons-material/Error' +import { checkRPCStatus } from '../api/api' +import { getShortAddress } from '../utils/utils' +import { zeroAddress } from 'viem' const NavigationBar: React.FC = () => { - const { isPrivateKeyValid, account, chainId, setChainId } = useContext(WalletContext)!; - const [anchorEl, setAnchorEl] = React.useState(null); - const [chainMenuAnchorEl, setChainMenuAnchorEl] = React.useState(null); // State for chain selection menu - const open = Boolean(anchorEl); - const chainMenuOpen = Boolean(chainMenuAnchorEl); - const [disconnectDialogOpen, setDisconnectDialogOpen] = useState(false); // State for controlling the dialog - const [connected, setConnected] = useState(false); + const { isPrivateKeyValid, account, chainId, setChainId } = + useContext(WalletContext)! + const [anchorEl, setAnchorEl] = React.useState(null) + const [chainMenuAnchorEl, setChainMenuAnchorEl] = + React.useState(null) // State for chain selection menu + const open = Boolean(anchorEl) + const chainMenuOpen = Boolean(chainMenuAnchorEl) + const [disconnectDialogOpen, setDisconnectDialogOpen] = useState(false) // State for controlling the dialog + const [connected, setConnected] = useState(false) // Handle main menu open/close const handleMenuOpen = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; + setAnchorEl(event.currentTarget) + } const handleMenuClose = () => { - setAnchorEl(null); - }; + setAnchorEl(null) + } // Handle chain menu open/close const handleChainMenuOpen = (event: React.MouseEvent) => { - setChainMenuAnchorEl(event.currentTarget); - }; + setChainMenuAnchorEl(event.currentTarget) + } const handleChainMenuClose = () => { - setChainMenuAnchorEl(null); - }; + setChainMenuAnchorEl(null) + } const handleChainSelect = (selectedChainId: string) => { - setChainId(parseInt(selectedChainId)); // Update the selected chainId in context - handleChainMenuClose(); // Close the chain selection menu - }; + setChainId(parseInt(selectedChainId)) // Update the selected chainId in context + handleChainMenuClose() // Close the chain selection menu + } const handleChangeClick = () => { - handleMenuClose(); // Close the menu first - setDisconnectDialogOpen(true); // Open the dialog - }; + handleMenuClose() // Close the menu first + setDisconnectDialogOpen(true) // Open the dialog + } const handleDisconnectConfirm = () => { - setDisconnectDialogOpen(false); - }; + setDisconnectDialogOpen(false) + } useEffect(() => { - (async () => { - const rpcUrl = safeEIP7702Config[chainId].rpc; - const isLive = await checkRPCStatus(rpcUrl); - setConnected(isLive); - })(); - }, [chainId]); + ;(async () => { + const rpcUrl = safeEIP7702Config[chainId].rpc + const isLive = await checkRPCStatus(rpcUrl) + setConnected(isLive) + })() + }, [chainId]) return ( - - + + {/* Left Section: App Name and Buttons */} - - + window.location.href = '/'} - > + onClick={() => (window.location.href = '/')} + > EOA-->Safe - + {loading ? ( - + @@ -108,7 +159,9 @@ const Settings: React.FC = () => { {isDelegatedToSafeSingleton && ( - This account is delegated to Safe Singleton + + This account is delegated to Safe Singleton + )} @@ -117,7 +170,13 @@ const Settings: React.FC = () => { Safe Singleton - {safeStorage?.singleton} + + {safeStorage?.singleton} + @@ -125,7 +184,13 @@ const Settings: React.FC = () => { Fallbackhandler - {safeStorage?.fallbackHandler} + + {safeStorage?.fallbackHandler} + @@ -133,7 +198,13 @@ const Settings: React.FC = () => { Threshold - {safeStorage?.threshold.toString()} + + {safeStorage?.threshold.toString()} + @@ -141,7 +212,13 @@ const Settings: React.FC = () => { Safe nonce - {safeStorage?.nonce.toString()} + + {safeStorage?.nonce.toString()} + @@ -149,7 +226,13 @@ const Settings: React.FC = () => { Owner count - {safeStorage?.ownerCount.toString()} + + {safeStorage?.ownerCount.toString()} + @@ -157,7 +240,13 @@ const Settings: React.FC = () => { Delegatee - {isDelegated && accountCode ? "0x" + accountCode.slice(8) : ""} + + {isDelegated && accountCode ? '0x' + accountCode.slice(8) : ''} + @@ -165,7 +254,15 @@ const Settings: React.FC = () => { Owners - {safeStorage?.owners?.map((owner) => {owner})} + + {safeStorage?.owners?.map((owner) => ( + {owner} + ))} + @@ -173,13 +270,21 @@ const Settings: React.FC = () => { Modules - {safeStorage?.modules?.map((module) => {module})} + + {safeStorage?.modules?.map((module) => ( + {module} + ))} + )} - ); -}; + ) +} -export default Settings; +export default Settings diff --git a/safe-eip7702-ui/src/components/WalletInput.tsx b/safe-eip7702-ui/src/components/WalletInput.tsx index 4b8c796..1389dac 100644 --- a/safe-eip7702-ui/src/components/WalletInput.tsx +++ b/safe-eip7702-ui/src/components/WalletInput.tsx @@ -1,23 +1,22 @@ -import React, { useContext } from 'react'; -import { Button, TextField, Typography } from '@mui/material'; -import Grid from '@mui/material/Grid2'; -import { WalletContext } from '../context/WalletContext'; +import React, { useContext } from 'react' +import { Button, TextField, Typography } from '@mui/material' +import Grid from '@mui/material/Grid2' +import { WalletContext } from '../context/WalletContext' const WalletInput: React.FC = () => { - const { privateKey, setPrivateKey, isPrivateKeyValid, account} = useContext(WalletContext)!; + const { privateKey, setPrivateKey, isPrivateKeyValid, account } = + useContext(WalletContext)! return ( - - + container + direction="column" + justifyContent="center" + alignItems="center" + component="span" + > + + Private Key { */} - - {!isPrivateKeyValid ? + + {!isPrivateKeyValid ? ( - Private key must start with 0x and be a valid 66-character hexadecimal string. - : - Account: {account?.address} - } + Private key must start with 0x and be a valid 66-character hexadecimal + string. + + ) : ( + Account: {account?.address} + )} - ); -}; + ) +} -export default WalletInput; +export default WalletInput diff --git a/safe-eip7702-ui/src/context/WalletContext.tsx b/safe-eip7702-ui/src/context/WalletContext.tsx index ef683e8..114774b 100644 --- a/safe-eip7702-ui/src/context/WalletContext.tsx +++ b/safe-eip7702-ui/src/context/WalletContext.tsx @@ -1,58 +1,88 @@ -import React, { createContext, useState, ReactNode, useEffect } from 'react'; -import { Address, Chain, createPublicClient, defineChain, getContract, http, isAddress, isAddressEqual, isHex, PrivateKeyAccount, PublicClient } from 'viem'; // Import viem library for validation -import { privateKeyToAccount } from 'viem/accounts'; -import { Authorization } from 'viem/experimental'; -import { defaultChainId, FEATURES, safeEIP7702Config } from '../safe-eip7702-config/config'; -import { readStorage, SafeStorage, SENTINEL_ADDRESS } from '../utils/storageReader'; -import safeArtifact from "../safe-eip7702-config/artifact/Safe.json"; +import React, { createContext, useState, ReactNode, useEffect } from 'react' +import { + Address, + Chain, + createPublicClient, + defineChain, + getContract, + http, + isAddress, + isAddressEqual, + isHex, + PrivateKeyAccount, + PublicClient +} from 'viem' // Import viem library for validation +import { privateKeyToAccount } from 'viem/accounts' +import { Authorization } from 'viem/experimental' +import { + defaultChainId, + FEATURES, + safeEIP7702Config +} from '../safe-eip7702-config/config' +import { + readStorage, + SafeStorage, + SENTINEL_ADDRESS +} from '../utils/storageReader' +import safeArtifact from '../safe-eip7702-config/artifact/Safe.json' interface WalletContextType { - privateKey: `0x${string}` | undefined; - setPrivateKey: (key: `0x${string}` | undefined) => void; - isPrivateKeyValid: boolean; - account: PrivateKeyAccount | undefined; - setAccount: (account: any) => void; - authorizations: Authorization[]; - setAuthorizations: (authorizations: Authorization[]) => void; - chainId: number; - setChainId: (chainId: number) => void; - features: FEATURES[]; - publicClient: PublicClient; - loading: boolean, - safeStorage: SafeStorage | undefined, + privateKey: `0x${string}` | undefined + setPrivateKey: (key: `0x${string}` | undefined) => void + isPrivateKeyValid: boolean + account: PrivateKeyAccount | undefined + setAccount: (account: any) => void + authorizations: Authorization[] + setAuthorizations: (authorizations: Authorization[]) => void + chainId: number + setChainId: (chainId: number) => void + features: FEATURES[] + publicClient: PublicClient + loading: boolean + safeStorage: SafeStorage | undefined accountCode: string | undefined } // Create the context -export const WalletContext = createContext(undefined); +export const WalletContext = createContext( + undefined +) // Create a provider component -export const WalletProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const [privateKey, setPrivateKey] = useState<`0x${string}`>(import.meta.env.VITE_PRIVATE_KEY); - const [isPrivateKeyValid, setIsPrivateKeyValid] = useState(true); - const [account, setAccount] = useState(privateKeyToAccount(import.meta.env.VITE_PRIVATE_KEY)); - const [authorizations, setAuthorizations] = useState([]); - const [chainId, setChainId] = useState(defaultChainId); - const [features, setFeatures] = useState([]); - const [loading, setLoading] = useState(false); - const [safeStorage, setSafeStorage] = useState(); - const [accountCode, setAccountCode] = useState(); - const [chain, setChain] = useState(); - - const [publicClient, setPublicClient] = useState(createPublicClient({ - transport: http(safeEIP7702Config[chainId].rpc), - })); +export const WalletProvider: React.FC<{ children: ReactNode }> = ({ + children +}) => { + const [privateKey, setPrivateKey] = useState<`0x${string}`>( + import.meta.env.VITE_PRIVATE_KEY + ) + const [isPrivateKeyValid, setIsPrivateKeyValid] = useState(true) + const [account, setAccount] = useState( + privateKeyToAccount(import.meta.env.VITE_PRIVATE_KEY) + ) + const [authorizations, setAuthorizations] = useState([]) + const [chainId, setChainId] = useState(defaultChainId) + const [features, setFeatures] = useState([]) + const [loading, setLoading] = useState(false) + const [safeStorage, setSafeStorage] = useState() + const [accountCode, setAccountCode] = useState() + const [chain, setChain] = useState() + + const [publicClient, setPublicClient] = useState( + createPublicClient({ + transport: http(safeEIP7702Config[chainId].rpc) + }) + ) // Function to validate the private key const validatePrivateKey = (key: `0x${string}` | undefined) => { if (key && key.startsWith('0x') && isHex(key) && key.length === 66) { - setIsPrivateKeyValid(true); - setAccount(privateKeyToAccount(key)); + setIsPrivateKeyValid(true) + setAccount(privateKeyToAccount(key)) } else { - setIsPrivateKeyValid(false); + setIsPrivateKeyValid(false) } - setPrivateKey(key as `0x${string}`); - }; + setPrivateKey(key as `0x${string}`) + } useEffect(() => { setFeatures(safeEIP7702Config[chainId].features) @@ -61,69 +91,94 @@ export const WalletProvider: React.FC<{ children: ReactNode }> = ({ children }) id: chainId, name: safeEIP7702Config[chainId].name, nativeCurrency: { - name: "Ethereum", - symbol: "ETH", - decimals: 18, + name: 'Ethereum', + symbol: 'ETH', + decimals: 18 }, rpcUrls: { default: { http: [safeEIP7702Config[chainId].rpc as string], - webSocket: undefined, - }, + webSocket: undefined + } }, - testnet: safeEIP7702Config[chainId].testnet, - }); - - setChain(chain); - setPublicClient(createPublicClient({ - chain: chain, - transport: http(safeEIP7702Config[chainId].rpc), - })); - - }, [chainId]); + testnet: safeEIP7702Config[chainId].testnet + }) + + setChain(chain) + setPublicClient( + createPublicClient({ + chain: chain, + transport: http(safeEIP7702Config[chainId].rpc) + }) + ) + }, [chainId]) const loadStorage = async () => { - const accountAddress = account.address; + const accountAddress = account.address if (!account || !isAddress(accountAddress)) { - setAccountCode(undefined); - setSafeStorage(undefined); - return; - }; - setLoading(true); - let storage = await readStorage(publicClient, accountAddress); - console.log(`Storage values for account [${accountAddress}]:`, storage); - const accountCode = await publicClient.getCode({ address: accountAddress }); - - if (isAddressEqual(storage.singleton, safeEIP7702Config[chainId].addresses.safeSingleton)) { - + setAccountCode(undefined) + setSafeStorage(undefined) + return + } + setLoading(true) + let storage = await readStorage(publicClient, accountAddress) + console.log(`Storage values for account [${accountAddress}]:`, storage) + const accountCode = await publicClient.getCode({ address: accountAddress }) + + if ( + isAddressEqual( + storage.singleton, + safeEIP7702Config[chainId].addresses.safeSingleton + ) + ) { const contract = getContract({ address: accountAddress, abi: safeArtifact.abi, - client: publicClient, - }); + client: publicClient + }) - storage.owners = ((await contract.read.getOwners()) as Address[]) || []; + storage.owners = ((await contract.read.getOwners()) as Address[]) || [] // Assumes that there are no more than 10 modules enabled. - const modulesResult = (await contract.read.getModulesPaginated([SENTINEL_ADDRESS, 10])) as Address[][]; + const modulesResult = (await contract.read.getModulesPaginated([ + SENTINEL_ADDRESS, + 10 + ])) as Address[][] if (modulesResult && modulesResult.length > 0) { - storage.modules = modulesResult[0]; + storage.modules = modulesResult[0] } } - setAccountCode(accountCode); - setSafeStorage(storage); - setLoading(false); - }; + setAccountCode(accountCode) + setSafeStorage(storage) + setLoading(false) + } useEffect(() => { - (async () => { - await loadStorage(); - })(); - }, [account]); + ;(async () => { + await loadStorage() + })() + }, [account]) return ( - + {children} - ); -}; + ) +} diff --git a/safe-eip7702-ui/src/main.tsx b/safe-eip7702-ui/src/main.tsx index b40921e..78bdc74 100644 --- a/safe-eip7702-ui/src/main.tsx +++ b/safe-eip7702-ui/src/main.tsx @@ -1,24 +1,24 @@ -import { Buffer } from "buffer"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import React from "react"; -import ReactDOM from "react-dom/client"; -import { WagmiProvider } from "wagmi"; +import { Buffer } from 'buffer' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import React from 'react' +import ReactDOM from 'react-dom/client' +import { WagmiProvider } from 'wagmi' -import App from "./App.tsx"; -import { config } from "./wagmi.ts"; +import App from './App.tsx' +import { config } from './wagmi.ts' -import "./index.css"; +import './index.css' -globalThis.Buffer = Buffer; +globalThis.Buffer = Buffer -const queryClient = new QueryClient(); +const queryClient = new QueryClient() -ReactDOM.createRoot(document.getElementById("root")!).render( +ReactDOM.createRoot(document.getElementById('root')!).render( - , -); + +) diff --git a/safe-eip7702-ui/src/safe-eip7702-config/config.ts b/safe-eip7702-ui/src/safe-eip7702-config/config.ts index ab3bbff..9d27312 100644 --- a/safe-eip7702-ui/src/safe-eip7702-config/config.ts +++ b/safe-eip7702-ui/src/safe-eip7702-config/config.ts @@ -1,4 +1,3 @@ - export enum FEATURES { SUPPORT_4337, SAFE_WALLET @@ -7,19 +6,19 @@ export enum FEATURES { export const safeEIP7702Config: any = { 7042905162: { rpc: import.meta.env.VITE_PECTRA_RPC_URL, - name: "pectra-devnet", - explorer: "https://explorer.pectra-devnet-4.ethpandaops.io", + name: 'pectra-devnet', + explorer: 'https://explorer.pectra-devnet-4.ethpandaops.io', addresses: { - proxyFactory: "0xE60EcE6588DCcFb7373538034963B4D20a280DB0", - safeSingleton: "0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB", - fallbackHandler: "0x4fFeBe9E5af056a73555223E9319Ae94D43461C0", - moduleSetup: "0x2204DcA7d254897ae6d815D2189032db87F50Bba", - multiSend: "0xd58De9D288831482346fA36e6bdc16925d9cFC85", - multiSendCallOnly: "0x4873593fC8e788eFc06287327749fdDe08C0146b" + proxyFactory: '0xE60EcE6588DCcFb7373538034963B4D20a280DB0', + safeSingleton: '0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB', + fallbackHandler: '0x4fFeBe9E5af056a73555223E9319Ae94D43461C0', + moduleSetup: '0x2204DcA7d254897ae6d815D2189032db87F50Bba', + multiSend: '0xd58De9D288831482346fA36e6bdc16925d9cFC85', + multiSendCallOnly: '0x4873593fC8e788eFc06287327749fdDe08C0146b' }, features: [FEATURES.SAFE_WALLET] }, - [parseInt(import.meta.env.VITE_NETWORK_ID) || ""]: { + [parseInt(import.meta.env.VITE_NETWORK_ID) || '']: { rpc: import.meta.env.VITE_RPC_URL, name: import.meta.env.VITE_NETWORK_NAME, explorer: import.meta.env.VITE_EXPLORER_URL, @@ -34,20 +33,20 @@ export const safeEIP7702Config: any = { features: [FEATURES.SAFE_WALLET] }, 911867: { - rpc: "https://odyssey.ithaca.xyz", - name: "ithaca", - explorer: "https://odyssey-explorer.ithaca.xyz", + rpc: 'https://odyssey.ithaca.xyz', + name: 'ithaca', + explorer: 'https://odyssey-explorer.ithaca.xyz', addresses: { - proxyFactory: "0xE60EcE6588DCcFb7373538034963B4D20a280DB0", - safeSingleton: "0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB", - fallbackHandler: "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226", - moduleSetup: "0x2204DcA7d254897ae6d815D2189032db87F50Bba", - multiSend: "0xd58De9D288831482346fA36e6bdc16925d9cFC85", - multiSendCallOnly: "0x4873593fC8e788eFc06287327749fdDe08C0146b", - safe4337Module: "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226" + proxyFactory: '0xE60EcE6588DCcFb7373538034963B4D20a280DB0', + safeSingleton: '0xCfaA26AD40bFC7E3b1642E1888620FC402b95dAB', + fallbackHandler: '0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226', + moduleSetup: '0x2204DcA7d254897ae6d815D2189032db87F50Bba', + multiSend: '0xd58De9D288831482346fA36e6bdc16925d9cFC85', + multiSendCallOnly: '0x4873593fC8e788eFc06287327749fdDe08C0146b', + safe4337Module: '0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226' }, features: [FEATURES.SAFE_WALLET, FEATURES.SUPPORT_4337] } -}; +} -export const defaultChainId = parseInt(import.meta.env.VITE_DEFAULT_CHAIN_ID); +export const defaultChainId = parseInt(import.meta.env.VITE_DEFAULT_CHAIN_ID) diff --git a/safe-eip7702-ui/src/theme/darkPalette.ts b/safe-eip7702-ui/src/theme/darkPalette.ts index 46818fd..7741eec 100644 --- a/safe-eip7702-ui/src/theme/darkPalette.ts +++ b/safe-eip7702-ui/src/theme/darkPalette.ts @@ -2,64 +2,64 @@ const darkPalette = { text: { primary: '#FFFFFF', secondary: '#636669', - disabled: '#636669', + disabled: '#636669' }, primary: { dark: '#0cb259', main: '#12FF80', - light: '#A1A3A7', + light: '#A1A3A7' }, secondary: { dark: '#636669', main: '#FFFFFF', light: '#B0FFC9', - background: '#1B2A22', + background: '#1B2A22' }, border: { main: '#636669', light: '#303033', - background: '#121312', + background: '#121312' }, error: { dark: '#AC2C3B', main: '#FF5F72', light: '#FFB4BD', - background: '#2F2527', + background: '#2F2527' }, success: { dark: '#028D4C', main: '#00B460', light: '#81C784', - background: '#1F2920', + background: '#1F2920' }, info: { dark: '#52BFDC', main: '#5FDDFF', light: '#B7F0FF', - background: '#19252C', + background: '#19252C' }, warning: { dark: '#C04C32', main: '#FF8061', light: '#FFBC9F', - background: '#2F2318', + background: '#2F2318' }, background: { default: '#121312', main: '#121312', paper: '#1C1C1C', - light: '#1B2A22', + light: '#1B2A22' }, backdrop: { - main: '#636669', + main: '#636669' }, logo: { main: '#FFFFFF', - background: '#303033', + background: '#303033' }, static: { - main: '#121312', - }, + main: '#121312' + } } export default darkPalette diff --git a/safe-eip7702-ui/src/theme/lightPalette.ts b/safe-eip7702-ui/src/theme/lightPalette.ts index a47daca..8846bfe 100644 --- a/safe-eip7702-ui/src/theme/lightPalette.ts +++ b/safe-eip7702-ui/src/theme/lightPalette.ts @@ -2,64 +2,64 @@ const lightPalette = { text: { primary: '#121312', secondary: '#A1A3A7', - disabled: '#DDDEE0', + disabled: '#DDDEE0' }, primary: { dark: '#3c3c3c', main: '#121312', - light: '#636669', + light: '#636669' }, secondary: { dark: '#0FDA6D', main: '#12FF80', light: '#B0FFC9', - background: '#EFFFF4', + background: '#EFFFF4' }, border: { main: '#A1A3A7', light: '#DCDEE0', - background: '#F4F4F4', + background: '#F4F4F4' }, error: { dark: '#AC2C3B', main: '#FF5F72', light: '#FFB4BD', - background: '#FFE6EA', + background: '#FFE6EA' }, success: { dark: '#028D4C', main: '#00B460', light: '#72F5B8', - background: '#EFFAF1', + background: '#EFFAF1' }, info: { dark: '#52BFDC', main: '#5FDDFF', light: '#B7F0FF', - background: '#EFFCFF', + background: '#EFFCFF' }, warning: { dark: '#C04C32', main: '#FF8061', light: '#FFBC9F', - background: '#FFF1E0', + background: '#FFF1E0' }, background: { default: '#F4F4F4', main: '#F4F4F4', paper: '#FFFFFF', - light: '#EFFFF4', + light: '#EFFFF4' }, backdrop: { - main: '#636669', + main: '#636669' }, logo: { main: '#121312', - background: '#EEEFF0', + background: '#EEEFF0' }, static: { - main: '#121312', - }, + main: '#121312' + } } export default lightPalette diff --git a/safe-eip7702-ui/src/theme/safeTheme.ts b/safe-eip7702-ui/src/theme/safeTheme.ts index e9108c3..5dc17e6 100644 --- a/safe-eip7702-ui/src/theme/safeTheme.ts +++ b/safe-eip7702-ui/src/theme/safeTheme.ts @@ -74,19 +74,27 @@ const createSafeTheme = (mode: PaletteMode): Theme => { return createTheme({ palette: { mode: isDarkMode ? 'dark' : 'light', - ...colors, + ...colors }, spacing: base, shape: { - borderRadius: 6, + borderRadius: 6 }, shadows: [ 'none', - isDarkMode ? `0 0 2px ${shadowColor}` : `0 1px 4px ${shadowColor}0a, 0 4px 10px ${shadowColor}14`, - isDarkMode ? `0 0 2px ${shadowColor}` : `0 1px 4px ${shadowColor}0a, 0 4px 10px ${shadowColor}14`, - isDarkMode ? `0 0 2px ${shadowColor}` : `0 2px 20px ${shadowColor}0a, 0 8px 32px ${shadowColor}14`, - isDarkMode ? `0 0 2px ${shadowColor}` : `0 8px 32px ${shadowColor}0a, 0 24px 60px ${shadowColor}14`, - ...Array(20).fill('none'), + isDarkMode + ? `0 0 2px ${shadowColor}` + : `0 1px 4px ${shadowColor}0a, 0 4px 10px ${shadowColor}14`, + isDarkMode + ? `0 0 2px ${shadowColor}` + : `0 1px 4px ${shadowColor}0a, 0 4px 10px ${shadowColor}14`, + isDarkMode + ? `0 0 2px ${shadowColor}` + : `0 2px 20px ${shadowColor}0a, 0 8px 32px ${shadowColor}14`, + isDarkMode + ? `0 0 2px ${shadowColor}` + : `0 8px 32px ${shadowColor}0a, 0 24px 60px ${shadowColor}14`, + ...Array(20).fill('none') ] as Shadows, typography, components: { @@ -94,17 +102,17 @@ const createSafeTheme = (mode: PaletteMode): Theme => { styleOverrides: { head: ({ theme }) => ({ ...theme.typography.body1, - color: theme.palette.primary.light, - }), - }, + color: theme.palette.primary.light + }) + } }, MuiButton: { variants: [ { props: { size: 'stretched' }, style: { - padding: '12px 48px', - }, + padding: '12px 48px' + } }, { props: { variant: 'danger' }, @@ -113,19 +121,19 @@ const createSafeTheme = (mode: PaletteMode): Theme => { color: theme.palette.error.main, '&:hover': { color: theme.palette.error.dark, - backgroundColor: theme.palette.error.light, - }, - }), - }, + backgroundColor: theme.palette.error.light + } + }) + } ], styleOverrides: { sizeSmall: { fontSize: '14px', - padding: '8px 24px', + padding: '8px 24px' }, sizeMedium: { fontSize: '16px', - padding: '12px 24px', + padding: '12px 24px' }, root: ({ theme }) => ({ borderRadius: theme.shape.borderRadius, @@ -134,17 +142,17 @@ const createSafeTheme = (mode: PaletteMode): Theme => { borderColor: theme.palette.primary.main, textTransform: 'none', '&:hover': { - boxShadow: 'none', - }, + boxShadow: 'none' + } }), outlined: { border: '2px solid', '&:hover': { - border: '2px solid', - }, + border: '2px solid' + } }, - sizeLarge: { fontSize: '16px' }, - }, + sizeLarge: { fontSize: '16px' } + } }, MuiAccordion: { variants: [ @@ -155,13 +163,13 @@ const createSafeTheme = (mode: PaletteMode): Theme => { boxShadow: '0', '&:not(:last-of-type)': { borderRadius: '0 !important', - borderBottom: `1px solid ${theme.palette.border.light}`, + borderBottom: `1px solid ${theme.palette.border.light}` }, '&:last-of-type': { - borderBottomLeftRadius: '8px', - }, - }), - }, + borderBottomLeftRadius: '8px' + } + }) + } ], styleOverrides: { root: ({ theme }) => ({ @@ -171,48 +179,48 @@ const createSafeTheme = (mode: PaletteMode): Theme => { overflow: 'hidden', '&::before': { - content: 'none', + content: 'none' }, '&:hover': { - borderColor: theme.palette.secondary.light, + borderColor: theme.palette.secondary.light }, '&:hover > .MuiAccordionSummary-root': { - background: theme.palette.background.light, + background: theme.palette.background.light }, '&.Mui-expanded': { margin: 0, - borderColor: theme.palette.secondary.light, + borderColor: theme.palette.secondary.light }, '&.Mui-expanded > .MuiAccordionSummary-root': { - background: theme.palette.background.light, - }, - }), - }, + background: theme.palette.background.light + } + }) + } }, MuiAccordionSummary: { styleOverrides: { root: { '&.Mui-expanded': { - minHeight: '48px', - }, + minHeight: '48px' + } }, content: { '&.Mui-expanded': { - margin: '12px 0', - }, - }, - }, + margin: '12px 0' + } + } + } }, MuiAccordionDetails: { styleOverrides: { root: ({ theme }) => ({ - padding: theme.spacing(2), - }), - }, + padding: theme.spacing(2) + }) + } }, MuiCard: { styleOverrides: { @@ -220,143 +228,143 @@ const createSafeTheme = (mode: PaletteMode): Theme => { borderRadius: theme.shape.borderRadius, boxSizing: 'border-box', border: '2px solid transparent', - boxShadow: 'none', - }), - }, + boxShadow: 'none' + }) + } }, MuiDialog: { defaultProps: { - fullWidth: true, - }, + fullWidth: true + } }, MuiDialogContent: { styleOverrides: { root: ({ theme }) => ({ - padding: theme.spacing(3), - }), - }, + padding: theme.spacing(3) + }) + } }, MuiDivider: { styleOverrides: { root: ({ theme }) => ({ - borderColor: theme.palette.border.light, - }), - }, + borderColor: theme.palette.border.light + }) + } }, MuiPaper: { defaultProps: { - elevation: 0, + elevation: 0 }, styleOverrides: { outlined: ({ theme }) => ({ borderWidth: 2, - borderColor: theme.palette.border.light, + borderColor: theme.palette.border.light }), root: ({ theme }) => ({ borderRadius: theme.shape.borderRadius, - backgroundImage: 'none', - }), - }, + backgroundImage: 'none' + }) + } }, MuiPopover: { defaultProps: { - elevation: 2, + elevation: 2 }, styleOverrides: { paper: { - overflow: 'visible', - }, - }, + overflow: 'visible' + } + } }, MuiIconButton: { styleOverrides: { sizeSmall: { - padding: '4px', - }, - }, + padding: '4px' + } + } }, MuiToggleButton: { styleOverrides: { root: { - textTransform: 'none', - }, - }, + textTransform: 'none' + } + } }, MuiChip: { styleOverrides: { colorSuccess: ({ theme }) => ({ backgroundColor: theme.palette.secondary.light, - height: '24px', - }), - }, + height: '24px' + }) + } }, MuiAlert: { styleOverrides: { standardError: ({ theme }) => ({ '& .MuiAlert-icon': { - color: theme.palette.error.main, + color: theme.palette.error.main }, '&.MuiPaper-root': { - backgroundColor: theme.palette.error.background, + backgroundColor: theme.palette.error.background }, - border: `1px solid ${theme.palette.error.main}`, + border: `1px solid ${theme.palette.error.main}` }), standardInfo: ({ theme }) => ({ '& .MuiAlert-icon': { - color: theme.palette.info.main, + color: theme.palette.info.main }, '&.MuiPaper-root': { - backgroundColor: theme.palette.info.background, + backgroundColor: theme.palette.info.background }, - border: `1px solid ${theme.palette.info.main}`, + border: `1px solid ${theme.palette.info.main}` }), standardSuccess: ({ theme }) => ({ '& .MuiAlert-icon': { - color: theme.palette.success.main, + color: theme.palette.success.main }, '&.MuiPaper-root': { - backgroundColor: theme.palette.success.background, + backgroundColor: theme.palette.success.background }, - border: `1px solid ${theme.palette.success.main}`, + border: `1px solid ${theme.palette.success.main}` }), standardWarning: ({ theme }) => ({ '& .MuiAlert-icon': { - color: theme.palette.warning.main, + color: theme.palette.warning.main }, '&.MuiPaper-root': { - backgroundColor: theme.palette.warning.background, + backgroundColor: theme.palette.warning.background }, - border: `1px solid ${theme.palette.warning.main}`, + border: `1px solid ${theme.palette.warning.main}` }), root: ({ theme }) => ({ color: theme.palette.text.primary, - padding: '12px 16px', - }), - }, + padding: '12px 16px' + }) + } }, MuiTableHead: { styleOverrides: { root: ({ theme }) => ({ '& .MuiTableCell-root': { - borderBottom: `1px solid ${theme.palette.border.light}`, + borderBottom: `1px solid ${theme.palette.border.light}` }, [theme.breakpoints.down('sm')]: { '& .MuiTableCell-root:first-of-type': { - paddingRight: theme.spacing(1), + paddingRight: theme.spacing(1) }, '& .MuiTableCell-root:not(:first-of-type):not(:last-of-type)': { paddingLeft: theme.spacing(1), - paddingRight: theme.spacing(1), + paddingRight: theme.spacing(1) }, '& .MuiTableCell-root:last-of-type': { - paddingLeft: theme.spacing(1), - }, - }, - }), - }, + paddingLeft: theme.spacing(1) + } + } + }) + } }, MuiTableBody: { styleOverrides: { @@ -364,64 +372,64 @@ const createSafeTheme = (mode: PaletteMode): Theme => { '& .MuiTableCell-root': { paddingTop: theme.spacing(1), paddingBottom: theme.spacing(1), - borderBottom: 'none', + borderBottom: 'none' }, [theme.breakpoints.down('sm')]: { '& .MuiTableCell-root:first-of-type': { - paddingRight: theme.spacing(1), + paddingRight: theme.spacing(1) }, '& .MuiTableCell-root:not(:first-of-type):not(:last-of-type)': { paddingLeft: theme.spacing(1), - paddingRight: theme.spacing(1), + paddingRight: theme.spacing(1) }, '& .MuiTableCell-root:last-of-type': { - paddingLeft: theme.spacing(1), - }, + paddingLeft: theme.spacing(1) + } }, '& .MuiTableRow-root': { transition: 'background-color 0.2s', '&:not(:last-of-type)': { - borderBottom: `1px solid ${theme.palette.border.light}`, - }, + borderBottom: `1px solid ${theme.palette.border.light}` + } }, '& .MuiTableRow-root:hover': { - backgroundColor: theme.palette.background.light, + backgroundColor: theme.palette.background.light }, '& .MuiTableRow-root.Mui-selected': { - backgroundColor: theme.palette.background.light, - }, - }), - }, + backgroundColor: theme.palette.background.light + } + }) + } }, MuiCheckbox: { styleOverrides: { root: ({ theme }) => ({ - color: theme.palette.primary.main, - }), - }, + color: theme.palette.primary.main + }) + } }, MuiOutlinedInput: { styleOverrides: { notchedOutline: ({ theme }) => ({ - borderColor: theme.palette.border.main, + borderColor: theme.palette.border.main }), root: ({ theme }) => ({ - borderColor: theme.palette.border.main, - }), - }, + borderColor: theme.palette.border.main + }) + } }, MuiSvgIcon: { styleOverrides: { fontSizeSmall: { width: '1rem', - height: '1rem', - }, - }, + height: '1rem' + } + } }, MuiFilledInput: { styleOverrides: { @@ -433,21 +441,21 @@ const createSafeTheme = (mode: PaletteMode): Theme => { '&:hover, &:focus, &.Mui-focused': { backgroundColor: theme.palette.background.paper, - borderColor: theme.palette.primary.main, - }, - }), - }, + borderColor: theme.palette.primary.main + } + }) + } }, MuiSelect: { defaultProps: { MenuProps: { sx: { '& .MuiPaper-root': { - overflow: 'auto', - }, - }, - }, - }, + overflow: 'auto' + } + } + } + } }, MuiTooltip: { styleOverrides: { @@ -456,54 +464,60 @@ const createSafeTheme = (mode: PaletteMode): Theme => { color: theme.palette.background.main, backgroundColor: theme.palette.text.primary, '& .MuiLink-root': { - color: isDarkMode ? theme.palette.background.main : theme.palette.secondary.main, - textDecorationColor: isDarkMode ? theme.palette.background.main : theme.palette.secondary.main, + color: isDarkMode + ? theme.palette.background.main + : theme.palette.secondary.main, + textDecorationColor: isDarkMode + ? theme.palette.background.main + : theme.palette.secondary.main }, '& .MuiLink-root:hover': { - color: isDarkMode ? theme.palette.text.secondary : theme.palette.secondary.light, - }, + color: isDarkMode + ? theme.palette.text.secondary + : theme.palette.secondary.light + } }), arrow: ({ theme }) => ({ - color: theme.palette.text.primary, - }), - }, + color: theme.palette.text.primary + }) + } }, MuiBackdrop: { styleOverrides: { root: ({ theme }) => ({ - backgroundColor: alpha(theme.palette.backdrop.main, 0.75), - }), - }, + backgroundColor: alpha(theme.palette.backdrop.main, 0.75) + }) + } }, MuiSwitch: { defaultProps: { - color: isDarkMode ? undefined : 'success', + color: isDarkMode ? undefined : 'success' }, styleOverrides: { thumb: () => ({ boxShadow: - '0px 2px 6px -1px rgba(0, 0, 0, 0.2), 0px 1px 4px rgba(0, 0, 0, 0.14), 0px 1px 4px rgba(0, 0, 0, 0.14)', - }), - }, + '0px 2px 6px -1px rgba(0, 0, 0, 0.2), 0px 1px 4px rgba(0, 0, 0, 0.14), 0px 1px 4px rgba(0, 0, 0, 0.14)' + }) + } }, MuiLink: { styleOverrides: { root: ({ theme }) => ({ fontWeight: 700, '&:hover': { - color: theme.palette.primary.light, - }, - }), - }, + color: theme.palette.primary.light + } + }) + } }, MuiLinearProgress: { styleOverrides: { root: ({ theme }) => ({ - backgroundColor: theme.palette.border.light, - }), - }, - }, - }, + backgroundColor: theme.palette.border.light + }) + } + } + } }) } diff --git a/safe-eip7702-ui/src/theme/typography.ts b/safe-eip7702-ui/src/theme/typography.ts index 3058ccd..539962f 100644 --- a/safe-eip7702-ui/src/theme/typography.ts +++ b/safe-eip7702-ui/src/theme/typography.ts @@ -7,44 +7,44 @@ const typography: TypographyOptions = { h1: { fontSize: '32px', lineHeight: '36px', - fontWeight: 700, + fontWeight: 700 }, h2: { fontSize: '27px', lineHeight: '34px', - fontWeight: 700, + fontWeight: 700 }, h3: { fontSize: '24px', - lineHeight: '30px', + lineHeight: '30px' }, h4: { fontSize: '20px', - lineHeight: '26px', + lineHeight: '26px' }, h5: { fontSize: '16px', - fontWeight: 700, + fontWeight: 700 }, body1: { fontSize: '16px', - lineHeight: '22px', + lineHeight: '22px' }, body2: { fontSize: '14px', - lineHeight: '20px', + lineHeight: '20px' }, caption: { fontSize: '12px', lineHeight: '16px', - letterSpacing: '0.4px', + letterSpacing: '0.4px' }, overline: { fontSize: '11px', lineHeight: '14px', textTransform: 'uppercase', - letterSpacing: '1px', - }, + letterSpacing: '1px' + } } export default typography diff --git a/safe-eip7702-ui/src/utils/storageReader.ts b/safe-eip7702-ui/src/utils/storageReader.ts index 0f0fb59..34a0d35 100644 --- a/safe-eip7702-ui/src/utils/storageReader.ts +++ b/safe-eip7702-ui/src/utils/storageReader.ts @@ -1,84 +1,131 @@ -import { PublicClient, Address, keccak256, encodePacked, pad, Hex, toHex, zeroHash, sliceHex, hexToBigInt } from 'viem'; +import { + PublicClient, + Address, + keccak256, + encodePacked, + pad, + Hex, + toHex, + zeroHash, + sliceHex, + hexToBigInt +} from 'viem' -export const FALLBACK_HANDLER_STORAGE_SLOT = "0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5"; -export const GUARD_STORAGE_SLOT = "0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8"; -export const SENTINEL_ADDRESS = "0x0000000000000000000000000000000000000001"; +export const FALLBACK_HANDLER_STORAGE_SLOT = + '0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5' +export const GUARD_STORAGE_SLOT = + '0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8' +export const SENTINEL_ADDRESS = '0x0000000000000000000000000000000000000001' // Function to read module storage slot export const readModuleStorageSlot = async ( - client: PublicClient, - account: Address, - key: string + client: PublicClient, + account: Address, + key: string ): Promise => { - const moduleStorageSlot = 1n; - return await readMappingStorage(client, account, moduleStorageSlot, key); -}; + const moduleStorageSlot = 1n + return await readMappingStorage(client, account, moduleStorageSlot, key) +} // Function to read owner storage slot export const readOwnerStorageSlot = async ( - client: PublicClient, - account: Address, - key: string + client: PublicClient, + account: Address, + key: string ): Promise => { - const ownerStorageSlot = 2n; - return await readMappingStorage(client, account, ownerStorageSlot, key); -}; + const ownerStorageSlot = 2n + return await readMappingStorage(client, account, ownerStorageSlot, key) +} // Function to read a mapping storage slot export const readMappingStorage = async ( - client: PublicClient, - account: Address, - storageSlot: bigint, - key: string + client: PublicClient, + account: Address, + storageSlot: bigint, + key: string ): Promise<`0x${string}`> => { - // Pad the key to 32 bytes - const paddedKey = pad(key as Hex, { size: 32 }); - - // Convert storage slot to hex and pad it to 32 bytes - const baseSlot = pad(`0x${storageSlot.toString(16)}` as Hex, { size: 32 }); - - // Compute the storage slot using keccak256(key + slot) - const slot = keccak256(encodePacked(['bytes32', 'bytes32'], [paddedKey, baseSlot])); - - const value = await client.getStorageAt({ address: account, slot }) || zeroHash; - console.log(`Storage slot: [${slot}] value [${value}]`); + // Pad the key to 32 bytes + const paddedKey = pad(key as Hex, { size: 32 }) + + // Convert storage slot to hex and pad it to 32 bytes + const baseSlot = pad(`0x${storageSlot.toString(16)}` as Hex, { size: 32 }) + + // Compute the storage slot using keccak256(key + slot) + const slot = keccak256( + encodePacked(['bytes32', 'bytes32'], [paddedKey, baseSlot]) + ) - // Fetch and return the storage value at the computed slot - return value; -}; + const value = + (await client.getStorageAt({ address: account, slot })) || zeroHash + console.log(`Storage slot: [${slot}] value [${value}]`) + + // Fetch and return the storage value at the computed slot + return value +} export type SafeStorage = { - fallbackHandler?: Address, - singleton: Address, - nonce: bigint, - guard?: Address, - ownerCount: bigint, - owners?: Address[], - modules?: Address[], - threshold: bigint, + fallbackHandler?: Address + singleton: Address + nonce: bigint + guard?: Address + ownerCount: bigint + owners?: Address[] + modules?: Address[] + threshold: bigint } -export const readStorage = async (client: PublicClient, account: Address): Promise => { - // Fallback Handler Storage - const fallbackHandler = sliceHex(await client.getStorageAt({ address: account, slot: FALLBACK_HANDLER_STORAGE_SLOT }) || zeroHash, 12, 32); +export const readStorage = async ( + client: PublicClient, + account: Address +): Promise => { + // Fallback Handler Storage + const fallbackHandler = sliceHex( + (await client.getStorageAt({ + address: account, + slot: FALLBACK_HANDLER_STORAGE_SLOT + })) || zeroHash, + 12, + 32 + ) - // Guard Storage - const guard = sliceHex(await client.getStorageAt({ address: account, slot: GUARD_STORAGE_SLOT }) || zeroHash, 12, 32); + // Guard Storage + const guard = sliceHex( + (await client.getStorageAt({ + address: account, + slot: GUARD_STORAGE_SLOT + })) || zeroHash, + 12, + 32 + ) - // Reading slots 0 to 4 - const singleton = sliceHex(await client.getStorageAt({ address: account, slot: toHex(0) }) || zeroHash, 12, 32); + // Reading slots 0 to 4 + const singleton = sliceHex( + (await client.getStorageAt({ address: account, slot: toHex(0) })) || + zeroHash, + 12, + 32 + ) - const ownerCount = hexToBigInt(await client.getStorageAt({ address: account, slot: toHex(3) }) || zeroHash); - const threshold = hexToBigInt(await client.getStorageAt({ address: account, slot: toHex(4) })|| zeroHash); - const nonce = hexToBigInt(await client.getStorageAt({ address: account, slot: toHex(5) })|| zeroHash); + const ownerCount = hexToBigInt( + (await client.getStorageAt({ address: account, slot: toHex(3) })) || + zeroHash + ) + const threshold = hexToBigInt( + (await client.getStorageAt({ address: account, slot: toHex(4) })) || + zeroHash + ) + const nonce = hexToBigInt( + (await client.getStorageAt({ address: account, slot: toHex(5) })) || + zeroHash + ) - // Return as a JSON object - return { - fallbackHandler, - guard, - singleton, - ownerCount, - threshold, - nonce, - }; -}; \ No newline at end of file + // Return as a JSON object + return { + fallbackHandler, + guard, + singleton, + ownerCount, + threshold, + nonce + } +} diff --git a/safe-eip7702-ui/src/utils/utils.ts b/safe-eip7702-ui/src/utils/utils.ts index 70ce186..fcdf106 100644 --- a/safe-eip7702-ui/src/utils/utils.ts +++ b/safe-eip7702-ui/src/utils/utils.ts @@ -1,94 +1,114 @@ -import { encodePacked, keccak256, getContractAddress, PublicClient, Address } from 'viem' -import safeEIP7702Proxy from "../safe-eip7702-config/artifact/SafeEIP7702Proxy.json"; +import { + encodePacked, + keccak256, + getContractAddress, + PublicClient, + Address +} from 'viem' +import safeEIP7702Proxy from '../safe-eip7702-config/artifact/SafeEIP7702Proxy.json' -export const ACCOUNT_CODE_PREFIX = "0xef0100"; +export const ACCOUNT_CODE_PREFIX = '0xef0100' export const getProxyAddress = ( - factory: `0x${string}`, - singleton: `0x${string}`, - inititalizer: `0x${string}`, - nonce: bigint, - proxyCreationCode?: `0x${string}`, + factory: `0x${string}`, + singleton: `0x${string}`, + inititalizer: `0x${string}`, + nonce: bigint, + proxyCreationCode?: `0x${string}` ) => { - const salt = keccak256(encodePacked(["bytes32", "uint256"], [keccak256(encodePacked(["bytes"], [inititalizer])), nonce])); - - if(!proxyCreationCode){ - proxyCreationCode = safeEIP7702Proxy.bytecode as `0x${string}`; - } + const salt = keccak256( + encodePacked( + ['bytes32', 'uint256'], + [keccak256(encodePacked(['bytes'], [inititalizer])), nonce] + ) + ) - const deploymentCode = encodePacked(["bytes", "uint256", "uint256"], [proxyCreationCode || "0x", keccak256(inititalizer) as any, singleton as any]); - return getContractAddress({ - bytecode: deploymentCode, - from: factory, - opcode: 'CREATE2', - salt: salt, - }); -}; + if (!proxyCreationCode) { + proxyCreationCode = safeEIP7702Proxy.bytecode as `0x${string}` + } -export type MultiSendTransaction = { - to: `0x${string}`, - value: bigint, - data: `0x${string}`, - operation: number, + const deploymentCode = encodePacked( + ['bytes', 'uint256', 'uint256'], + [ + proxyCreationCode || '0x', + keccak256(inititalizer) as any, + singleton as any + ] + ) + return getContractAddress({ + bytecode: deploymentCode, + from: factory, + opcode: 'CREATE2', + salt: salt + }) } -export type MultiSendTransactions = MultiSendTransaction[]; +export type MultiSendTransaction = { + to: `0x${string}` + value: bigint + data: `0x${string}` + operation: number +} -export const getMultiSendCallData = (transactions: MultiSendTransactions): `0x${string}` => { +export type MultiSendTransactions = MultiSendTransaction[] - // Encode the transactions into the format required by MultiSend contract - let packedTransactions = '0x'; // Start with empty hex string - for (const tx of transactions) { +export const getMultiSendCallData = ( + transactions: MultiSendTransactions +): `0x${string}` => { + // Encode the transactions into the format required by MultiSend contract + let packedTransactions = '0x' // Start with empty hex string + for (const tx of transactions) { const encodedTx = encodePacked( - ['uint8', 'address', 'uint256', 'uint256', 'bytes'], - [tx.operation, tx.to, tx.value, BigInt(tx.data.length), tx.data] - ); - packedTransactions += encodedTx.slice(2); // Append the packed transaction data - } - return packedTransactions as `0x${string}`; - + ['uint8', 'address', 'uint256', 'uint256', 'bytes'], + [tx.operation, tx.to, tx.value, BigInt(tx.data.length), tx.data] + ) + packedTransactions += encodedTx.slice(2) // Append the packed transaction data + } + return packedTransactions as `0x${string}` } - // Function to check if the account is delegated -export const isAccountDelegated = async (client: PublicClient, account: Address): Promise => { - const codeAtEOA = await client.getCode({ address: account }); - return codeAtEOA?.length === 48 && codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX); -}; +export const isAccountDelegated = async ( + client: PublicClient, + account: Address +): Promise => { + const codeAtEOA = await client.getCode({ address: account }) + return codeAtEOA?.length === 48 && codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX) +} // Function to check if the account is delegated to a specific address export const isAccountDelegatedToAddress = async ( - client: PublicClient, - account: Address, - authority: Address + client: PublicClient, + account: Address, + authority: Address ): Promise => { - const codeAtEOA = await client.getCode({ address: account }); + const codeAtEOA = await client.getCode({ address: account }) - return ( - codeAtEOA?.length === 48 && - codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX) && - `0x${codeAtEOA.slice(8)}`.toLowerCase() === authority.toLowerCase() - ); -}; + return ( + codeAtEOA?.length === 48 && + codeAtEOA.startsWith(ACCOUNT_CODE_PREFIX) && + `0x${codeAtEOA.slice(8)}`.toLowerCase() === authority.toLowerCase() + ) +} // Function to get the address the account is delegated to export const getDelegatedToAddress = async ( - client: PublicClient, - account: Address + client: PublicClient, + account: Address ): Promise
=> { - const codeAtEOA = await client.getCode({ address: account }); + const codeAtEOA = await client.getCode({ address: account }) - if (!codeAtEOA || codeAtEOA.length < 48) { - throw new Error('Invalid code length'); - } - - return `0x${codeAtEOA.slice(8)}` as Address; -}; + if (!codeAtEOA || codeAtEOA.length < 48) { + throw new Error('Invalid code length') + } + + return `0x${codeAtEOA.slice(8)}` as Address +} export const getShortAddress = (address: Address): string => { - return `${address.slice(0, 6)}...${address.slice(-4)}`; + return `${address.slice(0, 6)}...${address.slice(-4)}` } export const getShortTransactionHash = (address: Address): string => { - return `${address.slice(0, 10)}...${address.slice(-10)}`; -} \ No newline at end of file + return `${address.slice(0, 10)}...${address.slice(-10)}` +} diff --git a/safe-eip7702-ui/src/wagmi.ts b/safe-eip7702-ui/src/wagmi.ts index 1da3c5f..358fc60 100644 --- a/safe-eip7702-ui/src/wagmi.ts +++ b/safe-eip7702-ui/src/wagmi.ts @@ -1,7 +1,7 @@ -import { defineChain } from "viem"; -import { http, createConfig } from "wagmi"; -import { mainnet, sepolia } from "wagmi/chains"; -import { coinbaseWallet, injected } from "wagmi/connectors"; +import { defineChain } from 'viem' +import { http, createConfig } from 'wagmi' +import { mainnet, sepolia } from 'wagmi/chains' +import { coinbaseWallet, injected } from 'wagmi/connectors' // export const pectraDevnet = defineChain({ // id: 7042905162, @@ -23,18 +23,18 @@ export const config = createConfig({ chains: [mainnet, sepolia], connectors: [ injected(), - coinbaseWallet(), + coinbaseWallet() // walletConnect({ projectId: import.meta.env.VITE_WC_PROJECT_ID }), ], transports: { [mainnet.id]: http(), - [sepolia.id]: http(), - // [pectraDevnet.id]: http(), - }, -}); + [sepolia.id]: http() + // [pectraDevnet.id]: http(), + } +}) -declare module "wagmi" { +declare module 'wagmi' { interface Register { - config: typeof config; + config: typeof config } }