abc-blockchain 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/PUBLISHING.md +89 -0
  3. package/README.md +144 -0
  4. package/SECURITY.md +41 -0
  5. package/dist/branding.d.ts +7 -0
  6. package/dist/branding.js +14 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +31 -0
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +15 -0
  11. package/dist/create-project.d.ts +3 -0
  12. package/dist/create-project.js +55 -0
  13. package/dist/create-project.test.d.ts +1 -0
  14. package/dist/create-project.test.js +29 -0
  15. package/dist/logger.d.ts +7 -0
  16. package/dist/logger.js +15 -0
  17. package/dist/package-manager.d.ts +3 -0
  18. package/dist/package-manager.js +14 -0
  19. package/dist/template.d.ts +3 -0
  20. package/dist/template.js +30 -0
  21. package/package.json +72 -0
  22. package/templates/hardhat-erc4337/.env.example +7 -0
  23. package/templates/hardhat-erc4337/.eslintrc.cjs +14 -0
  24. package/templates/hardhat-erc4337/.github/workflows/ci.yml +36 -0
  25. package/templates/hardhat-erc4337/.github/workflows/release.yml +26 -0
  26. package/templates/hardhat-erc4337/.husky/pre-commit +1 -0
  27. package/templates/hardhat-erc4337/.prettierrc.json +7 -0
  28. package/templates/hardhat-erc4337/README.md.tmpl +98 -0
  29. package/templates/hardhat-erc4337/contracts/AccountFactory.sol +45 -0
  30. package/templates/hardhat-erc4337/contracts/EntryPoint.sol +69 -0
  31. package/templates/hardhat-erc4337/contracts/SmartAccount.sol +93 -0
  32. package/templates/hardhat-erc4337/contracts/Token.sol +15 -0
  33. package/templates/hardhat-erc4337/contracts/interfaces/IEntryPoint.sol +24 -0
  34. package/templates/hardhat-erc4337/contracts/interfaces/IPaymasterHook.sol +12 -0
  35. package/templates/hardhat-erc4337/deployments/.gitkeep +1 -0
  36. package/templates/hardhat-erc4337/hardhat.config.ts +41 -0
  37. package/templates/hardhat-erc4337/ignition/modules/AccountAbstraction.ts +14 -0
  38. package/templates/hardhat-erc4337/package.json.tmpl +62 -0
  39. package/templates/hardhat-erc4337/scripts/createAccount.ts +33 -0
  40. package/templates/hardhat-erc4337/scripts/deploy.ts +50 -0
  41. package/templates/hardhat-erc4337/scripts/lib/bundler.ts +17 -0
  42. package/templates/hardhat-erc4337/scripts/lib/env.ts +18 -0
  43. package/templates/hardhat-erc4337/scripts/lib/logger.ts +8 -0
  44. package/templates/hardhat-erc4337/slither.config.json +4 -0
  45. package/templates/hardhat-erc4337/tasks/accounts.ts +17 -0
  46. package/templates/hardhat-erc4337/test/SmartAccount.ts +32 -0
  47. package/templates/hardhat-erc4337/tsconfig.json +15 -0
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+ security-events: write
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 22
20
+ cache: npm
21
+ - run: npm ci
22
+ - run: npm run ci
23
+
24
+ codeql:
25
+ runs-on: ubuntu-latest
26
+ permissions:
27
+ security-events: write
28
+ packages: read
29
+ actions: read
30
+ contents: read
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ - uses: github/codeql-action/init@v3
34
+ with:
35
+ languages: javascript-typescript
36
+ - uses: github/codeql-action/analyze@v3
@@ -0,0 +1,26 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: write
9
+ id-token: write
10
+
11
+ jobs:
12
+ release:
13
+ runs-on: ubuntu-latest
14
+ if: github.repository_owner != ''
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 22
20
+ cache: npm
21
+ registry-url: https://registry.npmjs.org
22
+ - run: npm ci
23
+ - run: npm test
24
+ - run: npm publish --provenance --access public
25
+ env:
26
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1 @@
1
+ npm run format:check && npm run lint && npm test
@@ -0,0 +1,7 @@
1
+ {
2
+ "printWidth": 100,
3
+ "singleQuote": false,
4
+ "trailingComma": "none",
5
+ "semi": true,
6
+ "plugins": ["prettier-plugin-solidity"]
7
+ }
@@ -0,0 +1,98 @@
1
+ # {{projectName}}
2
+
3
+ ERC-4337 Hardhat development environment generated by ABC Blockchain.
4
+
5
+ Creator: {{creator}}
6
+ Framework Signature: {{signature}}
7
+
8
+ ## What Is Included
9
+
10
+ - Hardhat with TypeScript
11
+ - Ethers v6
12
+ - OpenZeppelin contracts
13
+ - Upgrade-ready smart account template
14
+ - Deterministic account factory
15
+ - EntryPoint integration scaffold
16
+ - UserOperation type support
17
+ - Paymaster hook interface
18
+ - Bundler RPC integration point
19
+ - Deployment manifests
20
+ - GitHub Actions CI and release workflow
21
+ - ESLint, Prettier, Husky-ready scripts
22
+ - Slither-ready configuration
23
+
24
+ ## Quick Start
25
+
26
+ ```bash
27
+ cp .env.example .env
28
+ npm install
29
+ npm run ci
30
+ npm test
31
+ npm run deploy:local
32
+ ```
33
+
34
+ ## Project Structure
35
+
36
+ ```text
37
+ {{projectName}}/
38
+ ├── contracts/
39
+ │ ├── interfaces/
40
+ │ ├── SmartAccount.sol
41
+ │ ├── AccountFactory.sol
42
+ │ ├── EntryPoint.sol
43
+ │ └── Token.sol
44
+ ├── scripts/
45
+ ├── deployments/
46
+ ├── ignition/
47
+ ├── tasks/
48
+ ├── test/
49
+ ├── .github/workflows/
50
+ ├── hardhat.config.ts
51
+ ├── tsconfig.json
52
+ ├── .env.example
53
+ ├── README.md
54
+ └── package.json
55
+ ```
56
+
57
+ ## Security Notes
58
+
59
+ - Do not commit `.env`.
60
+ - Use dedicated deployer keys with limited funds.
61
+ - Run `npm audit` and Slither before production deployments.
62
+ - Replace the included development EntryPoint with the canonical audited EntryPoint for production networks.
63
+ - Review upgrade authorization and owner custody before deploying account implementations.
64
+ - Validate bundler and paymaster provider trust assumptions.
65
+
66
+ ## Commands
67
+
68
+ ```bash
69
+ npm run build
70
+ npm test
71
+ npm run deploy:local
72
+ npm run deploy:sepolia
73
+ npm run lint
74
+ npm run format:check
75
+ npm run audit
76
+ ```
77
+
78
+ ## Environment
79
+
80
+ ```bash
81
+ PRIVATE_KEY=
82
+ SEPOLIA_RPC_URL=
83
+ MAINNET_RPC_URL=
84
+ ETHERSCAN_API_KEY=
85
+ REPORT_GAS=false
86
+ BUNDLER_RPC_URL=
87
+ PAYMASTER_RPC_URL=
88
+ ```
89
+
90
+ ## Production Checklist
91
+
92
+ - Replace development EntryPoint with a canonical ERC-4337 EntryPoint deployment.
93
+ - Add invariant and fuzz tests for account validation and nonce handling.
94
+ - Run static analysis with Slither.
95
+ - Run gas reports on all account execution paths.
96
+ - Confirm UUPS upgrade controls with a multisig or governance process.
97
+ - Add deployment approvals and protected environments in GitHub.
98
+ - Pin dependency ranges before audited releases.
@@ -0,0 +1,45 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
5
+ import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
6
+ import {IEntryPoint} from "./interfaces/IEntryPoint.sol";
7
+ import {SmartAccount} from "./SmartAccount.sol";
8
+
9
+ contract AccountFactory {
10
+ SmartAccount public immutable accountImplementation;
11
+ IEntryPoint public immutable entryPoint;
12
+
13
+ event AccountCreated(address indexed account, address indexed owner, uint256 salt);
14
+
15
+ constructor(IEntryPoint factoryEntryPoint) {
16
+ entryPoint = factoryEntryPoint;
17
+ accountImplementation = new SmartAccount();
18
+ }
19
+
20
+ function createAccount(address owner, uint256 salt) external returns (SmartAccount account) {
21
+ address predicted = getAccountAddress(owner, salt);
22
+ if (predicted.code.length > 0) {
23
+ return SmartAccount(payable(predicted));
24
+ }
25
+
26
+ bytes memory initializer = abi.encodeCall(SmartAccount.initialize, (owner, entryPoint));
27
+ bytes memory bytecode = abi.encodePacked(
28
+ type(ERC1967Proxy).creationCode,
29
+ abi.encode(address(accountImplementation), initializer)
30
+ );
31
+
32
+ account = SmartAccount(payable(Create2.deploy(0, bytes32(salt), bytecode)));
33
+ emit AccountCreated(address(account), owner, salt);
34
+ }
35
+
36
+ function getAccountAddress(address owner, uint256 salt) public view returns (address) {
37
+ bytes memory initializer = abi.encodeCall(SmartAccount.initialize, (owner, entryPoint));
38
+ bytes memory bytecode = abi.encodePacked(
39
+ type(ERC1967Proxy).creationCode,
40
+ abi.encode(address(accountImplementation), initializer)
41
+ );
42
+
43
+ return Create2.computeAddress(bytes32(salt), keccak256(bytecode));
44
+ }
45
+ }
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {IEntryPoint} from "./interfaces/IEntryPoint.sol";
5
+
6
+ contract EntryPoint is IEntryPoint {
7
+ mapping(address => uint256) private deposits;
8
+
9
+ event UserOperationHandled(address indexed sender, bytes32 indexed userOpHash, bool success);
10
+ event Deposited(address indexed account, uint256 amount);
11
+ event Withdrawn(address indexed account, address indexed withdrawAddress, uint256 amount);
12
+
13
+ receive() external payable {
14
+ depositTo(msg.sender);
15
+ }
16
+
17
+ function handleOps(
18
+ UserOperation[] calldata ops,
19
+ address payable beneficiary
20
+ ) external override {
21
+ uint256 collected;
22
+ for (uint256 i = 0; i < ops.length; i++) {
23
+ bytes32 opHash = getUserOpHash(ops[i]);
24
+ (bool success, ) = ops[i].sender.call(ops[i].callData);
25
+ emit UserOperationHandled(ops[i].sender, opHash, success);
26
+ collected += ops[i].preVerificationGas;
27
+ }
28
+
29
+ if (collected > 0 && address(this).balance >= collected) {
30
+ beneficiary.transfer(collected);
31
+ }
32
+ }
33
+
34
+ function getUserOpHash(UserOperation calldata userOp) public view override returns (bytes32) {
35
+ return
36
+ keccak256(
37
+ abi.encode(
38
+ userOp.sender,
39
+ userOp.nonce,
40
+ keccak256(userOp.initCode),
41
+ keccak256(userOp.callData),
42
+ userOp.callGasLimit,
43
+ userOp.verificationGasLimit,
44
+ userOp.preVerificationGas,
45
+ userOp.maxFeePerGas,
46
+ userOp.maxPriorityFeePerGas,
47
+ keccak256(userOp.paymasterAndData),
48
+ block.chainid,
49
+ address(this)
50
+ )
51
+ );
52
+ }
53
+
54
+ function depositTo(address account) public payable override {
55
+ deposits[account] += msg.value;
56
+ emit Deposited(account, msg.value);
57
+ }
58
+
59
+ function balanceOf(address account) external view override returns (uint256) {
60
+ return deposits[account];
61
+ }
62
+
63
+ function withdrawTo(address payable withdrawAddress, uint256 amount) external override {
64
+ require(deposits[msg.sender] >= amount, "Insufficient deposit");
65
+ deposits[msg.sender] -= amount;
66
+ withdrawAddress.transfer(amount);
67
+ emit Withdrawn(msg.sender, withdrawAddress, amount);
68
+ }
69
+ }
@@ -0,0 +1,93 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
5
+ import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
6
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
7
+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
8
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
9
+ import {IEntryPoint} from "./interfaces/IEntryPoint.sol";
10
+
11
+ contract SmartAccount is Initializable, OwnableUpgradeable, UUPSUpgradeable {
12
+ using ECDSA for bytes32;
13
+
14
+ error NotEntryPoint();
15
+ error ExecutionFailed(bytes result);
16
+ error InvalidSignature();
17
+
18
+ IEntryPoint public entryPoint;
19
+ uint256 public nonce;
20
+
21
+ event EntryPointUpdated(address indexed entryPoint);
22
+ event AccountExecuted(address indexed target, uint256 value, bytes data);
23
+
24
+ modifier onlyEntryPoint() {
25
+ if (msg.sender != address(entryPoint)) revert NotEntryPoint();
26
+ _;
27
+ }
28
+
29
+ constructor() {
30
+ _disableInitializers();
31
+ }
32
+
33
+ function initialize(address initialOwner, IEntryPoint accountEntryPoint) external initializer {
34
+ __Ownable_init(initialOwner);
35
+ entryPoint = accountEntryPoint;
36
+ emit EntryPointUpdated(address(accountEntryPoint));
37
+ }
38
+
39
+ receive() external payable {}
40
+
41
+ function execute(address target, uint256 value, bytes calldata data) external onlyEntryPoint {
42
+ _call(target, value, data);
43
+ }
44
+
45
+ function executeBatch(
46
+ address[] calldata targets,
47
+ uint256[] calldata values,
48
+ bytes[] calldata payloads
49
+ ) external onlyEntryPoint {
50
+ require(
51
+ targets.length == values.length && targets.length == payloads.length,
52
+ "Length mismatch"
53
+ );
54
+ for (uint256 i = 0; i < targets.length; i++) {
55
+ _call(targets[i], values[i], payloads[i]);
56
+ }
57
+ }
58
+
59
+ function validateUserOp(
60
+ IEntryPoint.UserOperation calldata userOp,
61
+ bytes32 userOpHash,
62
+ uint256 missingAccountFunds
63
+ ) external onlyEntryPoint returns (uint256 validationData) {
64
+ bytes32 digest = MessageHashUtils.toEthSignedMessageHash(userOpHash);
65
+ address recovered = ECDSA.recover(digest, userOp.signature);
66
+ if (recovered != owner()) revert InvalidSignature();
67
+
68
+ require(userOp.nonce == nonce, "Invalid nonce");
69
+ unchecked {
70
+ nonce++;
71
+ }
72
+
73
+ if (missingAccountFunds > 0) {
74
+ (bool sent, ) = payable(msg.sender).call{value: missingAccountFunds}("");
75
+ require(sent, "Prefund failed");
76
+ }
77
+
78
+ return 0;
79
+ }
80
+
81
+ function updateEntryPoint(IEntryPoint newEntryPoint) external onlyOwner {
82
+ entryPoint = newEntryPoint;
83
+ emit EntryPointUpdated(address(newEntryPoint));
84
+ }
85
+
86
+ function _call(address target, uint256 value, bytes calldata data) internal {
87
+ (bool success, bytes memory result) = target.call{value: value}(data);
88
+ if (!success) revert ExecutionFailed(result);
89
+ emit AccountExecuted(target, value, data);
90
+ }
91
+
92
+ function _authorizeUpgrade(address) internal override onlyOwner {}
93
+ }
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
6
+
7
+ contract Token is ERC20, Ownable {
8
+ constructor(address initialOwner) ERC20("ABC Demo Token", "ABC") Ownable(initialOwner) {
9
+ _mint(initialOwner, 1_000_000 ether);
10
+ }
11
+
12
+ function mint(address to, uint256 amount) external onlyOwner {
13
+ _mint(to, amount);
14
+ }
15
+ }
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.24;
3
+
4
+ interface IEntryPoint {
5
+ struct UserOperation {
6
+ address sender;
7
+ uint256 nonce;
8
+ bytes initCode;
9
+ bytes callData;
10
+ uint256 callGasLimit;
11
+ uint256 verificationGasLimit;
12
+ uint256 preVerificationGas;
13
+ uint256 maxFeePerGas;
14
+ uint256 maxPriorityFeePerGas;
15
+ bytes paymasterAndData;
16
+ bytes signature;
17
+ }
18
+
19
+ function handleOps(UserOperation[] calldata ops, address payable beneficiary) external;
20
+ function getUserOpHash(UserOperation calldata userOp) external view returns (bytes32);
21
+ function depositTo(address account) external payable;
22
+ function balanceOf(address account) external view returns (uint256);
23
+ function withdrawTo(address payable withdrawAddress, uint256 amount) external;
24
+ }
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.24;
3
+
4
+ import {IEntryPoint} from "./IEntryPoint.sol";
5
+
6
+ interface IPaymasterHook {
7
+ function validatePaymasterUserOp(
8
+ IEntryPoint.UserOperation calldata userOp,
9
+ bytes32 userOpHash,
10
+ uint256 maxCost
11
+ ) external returns (bytes memory context, uint256 validationData);
12
+ }
@@ -0,0 +1,41 @@
1
+ import hardhatEthers from "@nomicfoundation/hardhat-ethers";
2
+ import hardhatMocha from "@nomicfoundation/hardhat-mocha";
3
+ import accountsTask from "./tasks/accounts.js";
4
+ import { defineConfig } from "hardhat/config";
5
+ import { env } from "./scripts/lib/env.js";
6
+
7
+ const accounts = env.PRIVATE_KEY ? [env.PRIVATE_KEY] : [];
8
+
9
+ export default defineConfig({
10
+ plugins: [hardhatEthers, hardhatMocha],
11
+ tasks: [accountsTask],
12
+ solidity: {
13
+ version: "0.8.24",
14
+ settings: {
15
+ optimizer: {
16
+ enabled: true,
17
+ runs: 200
18
+ },
19
+ viaIR: true
20
+ }
21
+ },
22
+ networks: {
23
+ localhost: {
24
+ type: "http",
25
+ url: "http://127.0.0.1:8545",
26
+ chainId: 31337
27
+ },
28
+ sepolia: {
29
+ type: "http",
30
+ url: env.SEPOLIA_RPC_URL,
31
+ chainId: 11155111,
32
+ accounts
33
+ },
34
+ mainnet: {
35
+ type: "http",
36
+ url: env.MAINNET_RPC_URL,
37
+ chainId: 1,
38
+ accounts
39
+ }
40
+ }
41
+ });
@@ -0,0 +1,14 @@
1
+ // Install @nomicfoundation/hardhat-ignition when enabling Ignition deployments.
2
+ // The module shape is kept here so deployment ownership stays explicit.
3
+ import { buildModule } from "hardhat/ignition";
4
+
5
+ const AccountAbstractionModule = buildModule("AccountAbstractionModule", (m) => {
6
+ const deployer = m.getAccount(0);
7
+ const entryPoint = m.contract("EntryPoint");
8
+ const factory = m.contract("AccountFactory", [entryPoint]);
9
+ const token = m.contract("Token", [deployer]);
10
+
11
+ return { entryPoint, factory, token };
12
+ });
13
+
14
+ export default AccountAbstractionModule;
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "ERC-4337 development environment generated by ABC Blockchain.",
6
+ "author": "{{creator}}",
7
+ "license": "MIT",
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "hardhat compile",
11
+ "clean": "hardhat clean",
12
+ "test": "hardhat test",
13
+ "ci": "npm run format:check && npm run lint && npm run typecheck && npm test && npm run audit",
14
+ "deploy:local": "hardhat run scripts/deploy.ts --network localhost",
15
+ "deploy:sepolia": "hardhat run scripts/deploy.ts --network sepolia",
16
+ "lint": "eslint \"{scripts,tasks,test,ignition}/**/*.ts\"",
17
+ "format": "prettier --write .",
18
+ "format:check": "prettier --check .",
19
+ "typecheck": "tsc --noEmit",
20
+ "audit": "npm audit --audit-level=moderate",
21
+ "slither": "slither . --config-file slither.config.json"
22
+ },
23
+ "abcBlockchain": {
24
+ "creator": "{{creator}}",
25
+ "signature": "{{signature}}",
26
+ "generatedBy": "abc-blockchain",
27
+ "telemetry": false
28
+ },
29
+ "dependencies": {
30
+ "@openzeppelin/contracts": "5.0.2",
31
+ "@openzeppelin/contracts-upgradeable": "5.0.2",
32
+ "dotenv": "16.4.5",
33
+ "ethers": "6.13.1",
34
+ "zod": "3.23.8"
35
+ },
36
+ "devDependencies": {
37
+ "@nomicfoundation/hardhat-ethers": "4.0.12",
38
+ "@nomicfoundation/hardhat-mocha": "3.0.20",
39
+ "@types/chai": "4.3.16",
40
+ "@types/mocha": "10.0.7",
41
+ "@types/node": "22.10.2",
42
+ "@typescript-eslint/eslint-plugin": "7.16.0",
43
+ "@typescript-eslint/parser": "7.16.0",
44
+ "chai": "4.4.1",
45
+ "eslint": "8.57.0",
46
+ "eslint-config-prettier": "9.1.0",
47
+ "hardhat": "3.7.0",
48
+ "husky": "9.1.1",
49
+ "prettier": "3.3.3",
50
+ "prettier-plugin-solidity": "1.3.1",
51
+ "ts-node": "10.9.2",
52
+ "typescript": "5.5.3"
53
+ },
54
+ "overrides": {
55
+ "diff": "8.0.3",
56
+ "serialize-javascript": "7.0.5",
57
+ "ws": "8.20.1"
58
+ },
59
+ "engines": {
60
+ "node": ">=22.0.0"
61
+ }
62
+ }
@@ -0,0 +1,33 @@
1
+ import { network } from "hardhat";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { log } from "./lib/logger.js";
5
+
6
+ async function main() {
7
+ const connection = await network.create();
8
+ const { ethers } = connection;
9
+ const [owner] = await ethers.getSigners();
10
+ const chainId = Number((await ethers.provider.getNetwork()).chainId);
11
+ const deployment = JSON.parse(
12
+ await fs.readFile(path.join("deployments", `${chainId}.json`), "utf8")
13
+ ) as { contracts: { AccountFactory: string } };
14
+
15
+ const factory = await ethers.getContractAt("AccountFactory", deployment.contracts.AccountFactory);
16
+ const salt = 0n;
17
+ const predicted = await factory.getAccountAddress(owner.address, salt);
18
+ const tx = await factory.createAccount(owner.address, salt);
19
+ await tx.wait();
20
+
21
+ log.info("account:created", {
22
+ owner: owner.address,
23
+ smartAccount: predicted
24
+ });
25
+ await connection.close();
26
+ }
27
+
28
+ main().catch((error) => {
29
+ log.error("account:create:failed", {
30
+ error: error instanceof Error ? error.message : String(error)
31
+ });
32
+ process.exitCode = 1;
33
+ });
@@ -0,0 +1,50 @@
1
+ import { network } from "hardhat";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { log } from "./lib/logger.js";
5
+
6
+ async function main() {
7
+ const connection = await network.create();
8
+ const { ethers } = connection;
9
+ const [deployer] = await ethers.getSigners();
10
+ log.info("deploy:start", { deployer: deployer.address });
11
+
12
+ const EntryPoint = await ethers.getContractFactory("EntryPoint");
13
+ const entryPoint = await EntryPoint.deploy();
14
+ await entryPoint.waitForDeployment();
15
+
16
+ const AccountFactory = await ethers.getContractFactory("AccountFactory");
17
+ const accountFactory = await AccountFactory.deploy(await entryPoint.getAddress());
18
+ await accountFactory.waitForDeployment();
19
+
20
+ const Token = await ethers.getContractFactory("Token");
21
+ const token = await Token.deploy(deployer.address);
22
+ await token.waitForDeployment();
23
+
24
+ const deployment = {
25
+ network: (await ethers.provider.getNetwork()).name,
26
+ chainId: Number((await ethers.provider.getNetwork()).chainId),
27
+ deployer: deployer.address,
28
+ contracts: {
29
+ EntryPoint: await entryPoint.getAddress(),
30
+ AccountFactory: await accountFactory.getAddress(),
31
+ Token: await token.getAddress()
32
+ },
33
+ creator: "{{creator}}",
34
+ signature: "{{signature}}"
35
+ };
36
+
37
+ await fs.mkdir("deployments", { recursive: true });
38
+ await fs.writeFile(
39
+ path.join("deployments", `${deployment.chainId}.json`),
40
+ JSON.stringify(deployment, null, 2)
41
+ );
42
+
43
+ log.info("deploy:complete", deployment);
44
+ await connection.close();
45
+ }
46
+
47
+ main().catch((error) => {
48
+ log.error("deploy:failed", { error: error instanceof Error ? error.message : String(error) });
49
+ process.exitCode = 1;
50
+ });
@@ -0,0 +1,17 @@
1
+ import { JsonRpcProvider } from "ethers";
2
+ import { env } from "./env.js";
3
+
4
+ export type BundlerClient = {
5
+ sendUserOperation(userOperation: unknown, entryPoint: string): Promise<string>;
6
+ };
7
+
8
+ export function createBundlerClient(): BundlerClient | undefined {
9
+ if (!env.BUNDLER_RPC_URL) return undefined;
10
+ const provider = new JsonRpcProvider(env.BUNDLER_RPC_URL);
11
+
12
+ return {
13
+ async sendUserOperation(userOperation, entryPoint) {
14
+ return provider.send("eth_sendUserOperation", [userOperation, entryPoint]) as Promise<string>;
15
+ }
16
+ };
17
+ }
@@ -0,0 +1,18 @@
1
+ import "dotenv/config";
2
+ import { z } from "zod";
3
+
4
+ const EnvSchema = z.object({
5
+ PRIVATE_KEY: z.string().optional(),
6
+ SEPOLIA_RPC_URL: z.string().url().optional().default("http://127.0.0.1:8545"),
7
+ MAINNET_RPC_URL: z.string().url().optional().default("http://127.0.0.1:8545"),
8
+ ETHERSCAN_API_KEY: z.string().optional().default(""),
9
+ REPORT_GAS: z
10
+ .string()
11
+ .optional()
12
+ .default("false")
13
+ .transform((value) => value === "true"),
14
+ BUNDLER_RPC_URL: z.string().url().optional(),
15
+ PAYMASTER_RPC_URL: z.string().url().optional()
16
+ });
17
+
18
+ export const env = EnvSchema.parse(process.env);