ERC 4337
PreviousERC-721: The Standard for Non-Fungible Tokens (NFTs)NextERC 6551: Token Bound Accounts (TBA)
Last updated
+-------------------------------------------------------+
| Ethereum Network |
| (Settlement Layer) |
+-------------------------------------------------------+
| |
| |
+------------+ +----------------+
| EntryPoint |<---[Executes]--| Bundler |
| (Singleton SC)| | (Relayer) |
+------------+ +----------------+
^ |
| [Validation & Execution] |
+----------------+ +-----------------+
| Smart Contract | | Paymaster |
| Wallet | | (Optional Gas |
| (User Account) | | Sponsor) |
+----------------+ +-----------------+
^
|
+---------------+
| UserOp | <---- Transaction sent by User
+---------------+
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
interface IEntryPoint {
function handleOps(address user, bytes calldata txData) external;
}
contract ERC4337Wallet is Ownable {
using ECDSA for bytes32;
address public entryPoint;
constructor(address _entryPoint) {
entryPoint = _entryPoint;
}
function validateUserOp(
bytes32 hash,
bytes memory signature
) external view returns (bool) {
address signer = hash.toEthSignedMessageHash().recover(signature);
return signer == owner();
}
function executeTransaction(
address to,
uint256 value,
bytes calldata data
) external onlyOwner {
(bool success, ) = to.call{value: value}(data);
require(success, "Tx failed");
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract EntryPoint {
mapping(address => bool) public authorizedWallets;
function handleOps(
address user,
bytes calldata txData
) external {
require(authorizedWallets[user], "Unauthorized wallet");
(bool success, ) = user.call(txData);
require(success, "Tx failed");
}
function registerWallet(address wallet) external {
authorizedWallets[wallet] = true;
}
}
import { ethers } from "ethers";
async function bundleTransactions(userOps) {
const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_API_KEY");
const signer = new ethers.Wallet("YOUR_PRIVATE_KEY", provider);
const entryPoint = new ethers.Contract(
"ENTRYPOINT_CONTRACT_ADDRESS",
["function handleOps(address user, bytes txData)"],
signer
);
for (let op of userOps) {
await entryPoint.handleOps(op.user, op.txData);
}
}
bundleTransactions(userOps);
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IERC20 {
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
contract Paymaster {
address public token;
address public relayer;
constructor(address _token, address _relayer) {
token = _token;
relayer = _relayer;
}
function payForUserOp(address user, uint256 amount) external {
IERC20(token).transferFrom(user, relayer, amount);
}
}