Skip to content

Commit

Permalink
Merge pull request #1 from eludius18/feat-arbitrum-deployment
Browse files Browse the repository at this point in the history
Feat arbitrum deployment
  • Loading branch information
eludius18 authored Feb 14, 2024
2 parents 3278b2d + dd3c2a5 commit 31816c5
Show file tree
Hide file tree
Showing 19 changed files with 302 additions and 126 deletions.
Empty file modified .gitignore
100644 → 100755
Empty file.
27 changes: 11 additions & 16 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,15 @@ This project is an implementation of the Account Abstraction proposal (EIP-4337)

Follow these steps to deploy the contracts:

1. Start a local Hardhat network in a new terminal window using `npx hardhat node --no-deploy`. This starts a local Ethereum network for development purposes
1. Compile the contracts using `npx hardhat compile`. This step is necessary before you can deploy the contracts

2. In another terminal window, navigate to the project directory
2. Deploy the `AccountFactory` contract using `npx hardhat deploy --network arbitrum --tags AccountFactory`. This deploys the `AccountFactory`

3. Compile the contracts using `npx hardhat compile`. This step is necessary before you can deploy the contracts
3. Deploy the `EntryPoint` contract using `npx hardhat deploy --network arbitrum --tags EntryPoint`. This deploys the `EntryPoint` (When using Alchemy Bundler, interactions should be made through its EntryPoint Contract)

4. Deploy the `EntryPoint` contract using `npx hardhat deploy --network localhost --tags EntryPoint`. This deploys the `EntryPoint`
4. Deploy the `Paymaster` contract using `npx hardhat deploy --network arbitrum --tags Paymaster`. This deploys the `Paymaster`

5. Deploy the `AccountFactory` contract using `npx hardhat deploy --network localhost --tags AccountFactory`. This deploys the `AccountFactory`

6. Deploy the `Paymaster` contract using `npx hardhat deploy --network localhost --tags Paymaster`. This deploys the `Paymaster`

> **Note:** The `--network localhost` flag is used to specify that the contracts should be deployed to the local Hardhat network that you started in step 1
> **Note:** The `--network arbitrum` flag is used to specify that the contracts should be deployed to the Arbitrum Sepolia network that you started in step 1

## Setting Up Environment Variables
Expand All @@ -53,36 +49,35 @@ Before running the scripts, you need to set up your environment variables. Follo
2. Open the `.env` file and add the following variables:

```env
FACTORY_NONCE= 1
FACTORY_ADDRESS=<YOUR_ACCOUNT_FACTORY_CONTRACT_ADDRESS>
ENTRYPOINT_ADDRESS=<YOUR_ENTRYPOINT_CONTRACT_ADDRESS>
ENTRYPOINT_ADDRESS=<YOUR_ENTRYPOINT_CONTRACT_ADDRESS> --> When using Alchemy Bundler, interactions should be made through its EntryPoint Contract
PAYMASTER_ADDRESS=<YOUR_PAYMASTER_CONTRACT_ADDRESS>
RPC_URL_ARBITRUM=<YOUR_ARBITRUM_RPC_URL>
PRIVATE_KEY=<YOUR_PRIVATE_KEY>
```


## Executing the Scripts

Follow these steps to execute the scripts:

1. Run the `1-execute.ts` script using `npx hardhat run --network localhost scripts/1-execute.ts`. This script interacts with the deployed contracts for the first execution.
1. Run the `1-execute.ts` script using `npx hardhat run --network arbitrum scripts/1-execute.js`. This script interacts with the deployed contracts for the first execution.

2. Add `ACCOUNT_ADDRESS` to the `.env` file. The value for `ACCOUNT_ADDRESS` should be the `Sender` address logged to the console when executing the previous step. It should look like this: `ACCOUNT_ADDRESS=<YOUR_ACCOUNT_ADDRESS>`.

2. Run the `2-execute.ts` script using `npx hardhat run --network localhost scripts/2-execute.ts`. This script interacts with the deployed contracts for subsequent executions.


## Checking Account Balances

You may want to run the `nonceAccount.ts` script for getting the nonce. You can do this by running:

```bash
npx hardhat run --network localhost scripts/nonceAccount.ts
npx hardhat run --network arbitrum scripts/nonceAccount.ts
```

You may want to check the balance of the deployed contracts. You can do this by running the `accountBalance.ts` script:

```bash
npx hardhat run --network localhost scripts/accountBalance.ts
npx hardhat run --network arbitrum scripts/accountBalance.ts

```

Expand Down
Empty file modified contracts/Account.sol
100644 → 100755
Empty file.
23 changes: 21 additions & 2 deletions contracts/AccountFactory.sol
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,30 @@
pragma solidity ^0.8.9;

import "./Account.sol";
import "@account-abstraction/contracts/interfaces/IAccount.sol";
import "@openzeppelin/contracts/utils/Create2.sol";

contract AccountFactory {

function createAccount(address owner) external returns (address) {
Account account = new Account(owner);
return address(account);

bytes32 salt = bytes32(uint256(uint160(owner)));
bytes memory byteCode = abi.encodePacked(type(Account).creationCode, abi.encode(owner));

address addr = Create2.computeAddress(salt, keccak256(byteCode));
uint256 codeLenght = addr.code.length;
if (codeLenght > 0) {
return addr;
}
return deploy(salt, byteCode);
}

function deploy(bytes32 salt, bytes memory bytecode) internal returns (address addr) {
require(bytecode.length != 0, "Create2: bytecode length is zero");
/// @solidity memory-safe-assembly
assembly {
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
}
}
Empty file modified contracts/Paymaster.sol
100644 → 100755
Empty file.
Empty file modified deploy/deploy-accountFactory.ts
100644 → 100755
Empty file.
Empty file modified deploy/deploy-entryPoint.ts
100644 → 100755
Empty file.
Empty file modified deploy/deploy-paymaster.ts
100644 → 100755
Empty file.
4 changes: 4 additions & 0 deletions hardhat.config.ts
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ const config: HardhatUserConfig = {
deployer: 0
},
networks: {
arbitrum: {
url: process.env.RPC_URL_ARBITRUM,
accounts:[`0x${process.env.PRIVATE_KEY}`],
},
hardhat: {
mining: {
auto: true,
Expand Down
Empty file modified package-lock.json
100644 → 100755
Empty file.
86 changes: 86 additions & 0 deletions scripts/1-execute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const hre = require("hardhat");
require("dotenv").config();

const FACTORY_ADDRESS = "0x0E0a6a938b5FCe4766e104d31BaCcB47dE827911";
const PAYMASTER_ADDRESS = "0x7f35B3259dB9ce146eD52bC762ac320e1b3C73D6";
const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";

async function main() {
const [signer0, signer1] = await hre.ethers.getSigners();
const address0 = await signer0.getAddress();

const entryPoint = await hre.ethers.getContractAt("EntryPoint", ENTRYPOINT_ADDRESS);

const AccountFactory = await hre.ethers.getContractFactory("AccountFactory");
let initCode =
FACTORY_ADDRESS +
AccountFactory.interface
.encodeFunctionData("createAccount", [address0])
.slice(2);
let sender;
try {
await entryPoint.getSenderAddress(initCode);
} catch (e) {
sender = "0x" + e.data.slice(-40);
}
console.log(`Sender Address: ${sender}`);

const code = await hre.ethers.provider.getCode(sender);
if (code !== "0x") {
initCode = "0x";
}

const Account = await hre.ethers.getContractFactory("Account");
const callData = Account.interface.encodeFunctionData("execute");

const userOp = {
sender,
nonce: "0x" + (await entryPoint.getNonce(sender, 0)).toString(16),
initCode,
callData,
paymasterAndData: PAYMASTER_ADDRESS,
signature:
"0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
};

const { preVerificationGas, verificationGasLimit, callGasLimit } =
await hre.ethers.provider.send("eth_estimateUserOperationGas", [
userOp,
ENTRYPOINT_ADDRESS,
]);

const { maxFeePerGas } = await hre.ethers.provider.getFeeData();

const maxPriorityFeePerGas = await ethers.provider.send(
"rundler_maxPriorityFeePerGas"
);

userOp.preVerificationGas = preVerificationGas;
userOp.verificationGasLimit = verificationGasLimit;
userOp.callGasLimit = callGasLimit;
userOp.maxFeePerGas = "0x" + maxFeePerGas.toString(16);
userOp.maxPriorityFeePerGas = maxPriorityFeePerGas;

const userOpHash = await entryPoint.getUserOpHash(userOp);
userOp.signature = await signer0.signMessage(hre.ethers.getBytes(userOpHash));

const opHash = await hre.ethers.provider.send("eth_sendUserOperation", [
userOp,
ENTRYPOINT_ADDRESS,
]);

setTimeout(async () => {
const { transactionHash } = await hre.ethers.provider.send(
"eth_getUserOperationByHash",
[opHash]
);

console.log(transactionHash);
}, 5000);

}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
108 changes: 77 additions & 31 deletions scripts/1-execute.ts
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,65 +1,111 @@
const hre = require("hardhat");
import dotenv from 'dotenv';


dotenv.config();

const FACTORY_NONCE = process.env.FACTORY_NONCE;
const FACTORY_ADDRESS = process.env.FACTORY_ADDRESS;
const ENTRYPOINT_ADDRESS = process.env.ENTRYPOINT_ADDRESS;
const PAYMASTER_ADDRESS = process.env.PAYMASTER_ADDRESS;

async function main() {
const entryPoint = await hre.ethers.getContractAt("EntryPoint", ENTRYPOINT_ADDRESS);

const sender = hre.ethers.getCreateAddress({
from: FACTORY_ADDRESS,
nonce: FACTORY_NONCE
});

const entryPoint = await hre.ethers.getContractAt("EntryPoint", ENTRYPOINT_ADDRESS);

const AccountFactory = await hre.ethers.getContractFactory("AccountFactory");

const [signer0] = await hre.ethers.getSigners();
const [signer0, signer1] = await hre.ethers.getSigners();
const address0 = await signer0.getAddress();

const initCode =
let initCode =
FACTORY_ADDRESS +
AccountFactory.interface
.encodeFunctionData("createAccount", [address0])
.slice(2);

let sender
try {
await entryPoint.getSenderAddress(initCode);
} catch (e: any) {
sender = "0x" + e.data.slice(-40);
}

console.log("InitCode: ", initCode);
console.log("Sender: ", {sender});
console.log({ initCode });

const Account = await hre.ethers.getContractFactory("Account");
const code = await hre.ethers.provider.getCode(sender);
console.log({ code });
if (code !== "0x") {
initCode = "0x";
}

await entryPoint.depositTo(PAYMASTER_ADDRESS, {
value: hre.ethers.parseEther("200"),
});
console.log({ sender });

const userOp = {
const Account = await hre.ethers.getContractFactory("Account");

type UserOp = {
sender: string | undefined;
nonce: string;
callGasLimit: any | string;
verificationGasLimit: any;
preVerificationGas: any;
maxPriorityFeePerGas: any;
maxFeePerGas: any;
initCode: any;
callData: any;
paymasterAndData: string | undefined;
signature: string;
};

const userOp: UserOp = {
sender,
nonce: await entryPoint.getNonce(sender, 0),
initCode,
nonce: "0x" + (await entryPoint.getNonce(sender, 0)).toString(16),
initCode: "0x",
callData: Account.interface.encodeFunctionData("execute"),
callGasLimit: 400_000,
verificationGasLimit: 400_000,
preVerificationGas: 100_000,
maxFeePerGas: hre.ethers.parseUnits("100", "gwei"),
maxPriorityFeePerGas: hre.ethers.parseUnits("50", "gwei"),
callGasLimit: undefined,
verificationGasLimit: undefined,
preVerificationGas: undefined,
maxFeePerGas: undefined,
maxPriorityFeePerGas: undefined,
paymasterAndData: PAYMASTER_ADDRESS,
signature: "0x"
signature:
"0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
};

const { preVerificationGas, verificationGasLimit, callGasLimit } =
await hre.ethers.provider.send("eth_estimateUserOperationGas", [
userOp,
ENTRYPOINT_ADDRESS,
]);

userOp.preVerificationGas = preVerificationGas;
userOp.verificationGasLimit = verificationGasLimit;
userOp.callGasLimit = callGasLimit;

const { maxFeePerGas } = await hre.ethers.provider.getFeeData();
userOp.maxFeePerGas = "0x" + maxFeePerGas.toString(16);

const maxPriorityFeePerGas = await hre.ethers.provider.send(
"rundler_maxPriorityFeePerGas"
);

userOp.maxPriorityFeePerGas = maxPriorityFeePerGas;

const userOpsHash = await entryPoint.getUserOpHash(userOp);
userOp.signature = await signer0.signMessage(hre.ethers.getBytes(userOpsHash));

console.log("EntryPoint NONCE: ", await entryPoint.getNonce(sender, 0),);
const opHash = await hre.ethers.provider.send(
"eth_sendUserOperation", [
userOp,
ENTRYPOINT_ADDRESS
]);

const tx = await entryPoint.handleOps([userOp], address0);
const receipt = await tx.wait();
console.log(receipt);

process.exit(0);
setTimeout(async () => {
const { transactionHash } = await hre.ethers.provider.send(
"eth_getUserOperationByHash",
[opHash]
);

console.log(transactionHash);
}, 5000);
}

main().catch((error) => {
Expand Down
Loading

0 comments on commit 31816c5

Please sign in to comment.