本项目基于EIP-7702标准,实现了一个主控EOA + 多子EOA的多重委托架构,突破单个EOA只能委托一个合约的限制,实现不同功能的隔离和组合。
- 主控EOA:拥有私钥,负责发起操作和管理子EOA
- 子EOA:通过EIP-7702升级为智能账户,执行特定功能,私钥已丢弃
- RecordContractPlatform:记录主控EOA与子EOA的映射关系和功能标签
用户操作 → 点击前端"添加x功能"按钮 → 随机生成EOA私钥 → 立刻私钥签名授权给对应的合约 → 发送类型4(7702)升级交易 → 成功后立即丢弃私钥
前端调用 eth_signAuthorization(viem - signAuthorization) → 子EOA签名授权对应功能的逻辑合约 → 通过钱包(json-RPC)发送类型4(7702)升级交易升级为智能账户 → 丢弃私钥
主控EOA 通过钱包(json-RPC)→ RecordContractPlatform.addChild() → 记录子EOA地址和功能标签
前端 → RecordContractPlatform.getChildren() → 获取所有子EOA信息 → 展示功能列表
用户选择功能 → 前端构造交易 → 发送到对应子EOA → 执行智能合约逻辑
1. 生成子EOA
// 生成新的EOA地址对
async function generateChildEOA() {
const wallet = ethers.Wallet.createRandom();
return {
address: wallet.address,
privateKey: wallet.privateKey // 升级后需要安全销毁
};
}2. EIP-7702授权签名
// 调用钱包进行授权签名
async function signAuthorization(childAddress, logicContractAddress) {
const authorization = {
chainId: await provider.getNetwork().chainId,
address: logicContractAddress,
nonce: await provider.getTransactionCount(childAddress)
};
return await wallet.signAuthorization(authorization);
}3. 升级子EOA
// 发送EIP-7702升级交易
async function upgradeChildEOA(childAddress, logicContractAddress, authorization) {
const tx = {
type: 4, // EIP-7702交易类型
to: childAddress,
authorizationList: [authorization],
data: "0x", // 空数据,仅升级
};
return await mainEOA.sendTransaction(tx);
}1. 注册子EOA到记录平台
async function registerChildEOA(childAddress, role) {
const recordPlatform = new ethers.Contract(
RECORD_PLATFORM_ADDRESS,
RecordContractPlatformABI,
mainEOASigner
);
return await recordPlatform.addChild(childAddress, role);
}2. 查询子EOA列表
async function getChildEOAs(ownerAddress) {
const recordPlatform = new ethers.Contract(
RECORD_PLATFORM_ADDRESS,
RecordContractPlatformABI,
provider
);
return await recordPlatform.getChildren(ownerAddress);
}3. 调用子EOA功能
async function callChildEOAFunction(childAddress, functionData) {
// 直接向子EOA地址发送交易,会触发delegatecall到逻辑合约
const tx = {
to: childAddress,
data: functionData,
value: ethers.parseEther("0") // 根据需要设置
};
return await mainEOASigner.sendTransaction(tx);
}1. 主控账户信息展示
// 展示主控EOA地址、余额等基本信息
function MainAccountInfo({ address, balance }) {
return (
<div className="main-account">
<h3>主控账户</h3>
<p>地址: {address}</p>
<p>余额: {balance} ETH</p>
</div>
);
}2. 子账户列表展示
// 展示所有子EOA及其功能标签
function ChildAccountsList({ children, onCallFunction }) {
return (
<div className="child-accounts">
<h3>功能账户列表</h3>
{children.map(child => (
<div key={child.childEOA} className="child-account">
<p>地址: {child.childEOA}</p>
<p>功能: {child.role}</p>
<button onClick={() => onCallFunction(child)}>
执行{child.role}
</button>
</div>
))}
</div>
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract RecordContractPlatform {
struct Child {
address childEOA;
string role;
uint256 createdAt;
}
// 主控EOA -> 子EOA数组的映射
mapping(address => Child[]) public children;
// 事件定义
event ChildAdded(address indexed owner, address indexed childEOA, string role);
event ChildRemoved(address indexed owner, address indexed childEOA);
// 添加子EOA记录
function addChild(address childEOA, string memory role) external {
require(childEOA != address(0), "Invalid child address");
require(bytes(role).length > 0, "Role cannot be empty");
children[msg.sender].push(Child({
childEOA: childEOA,
role: role,
createdAt: block.timestamp
}));
emit ChildAdded(msg.sender, childEOA, role);
}
// 查询子EOA列表
function getChildren(address owner) external view returns (Child[] memory) {
return children[owner];
}
// 获取子EOA数量
function getChildrenCount(address owner) external view returns (uint256) {
return children[owner].length;
}
// 移除子EOA记录(可选功能)
function removeChild(address childEOA) external {
Child[] storage ownerChildren = children[msg.sender];
for (uint256 i = 0; i < ownerChildren.length; i++) {
if (ownerChildren[i].childEOA == childEOA) {
// 将最后一个元素移到当前位置,然后删除最后一个
ownerChildren[i] = ownerChildren[ownerChildren.length - 1];
ownerChildren.pop();
emit ChildRemoved(msg.sender, childEOA);
break;
}
}
}
}contract BatchTxLogic {
address public immutable owner;
constructor(address _owner) {
owner = _owner;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call");
_;
}
function batchTransfer(
address[] calldata recipients,
uint256[] calldata amounts
) external payable onlyOwner {
require(recipients.length == amounts.length, "Arrays length mismatch");
for (uint256 i = 0; i < recipients.length; i++) {
payable(recipients[i]).transfer(amounts[i]);
}
}
}contract RecoveryLogic {
address public immutable owner;
mapping(address => uint256) public recoveryRequests;
uint256 public constant RECOVERY_DELAY = 7 days;
constructor(address _owner) {
owner = _owner;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call");
_;
}
function initiateRecovery(address newOwner) external onlyOwner {
recoveryRequests[newOwner] = block.timestamp + RECOVERY_DELAY;
}
function executeRecovery(address newOwner) external {
require(
recoveryRequests[newOwner] > 0 &&
block.timestamp >= recoveryRequests[newOwner],
"Recovery not ready"
);
// 转移所有资产到新地址
payable(newOwner).transfer(address(this).balance);
}
}contract SubscriptionLogic {
address public immutable owner;
struct Subscription {
address service;
uint256 amount;
uint256 interval;
uint256 nextPayment;
bool active;
}
mapping(uint256 => Subscription) public subscriptions;
uint256 public subscriptionCount;
constructor(address _owner) {
owner = _owner;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call");
_;
}
function createSubscription(
address service,
uint256 amount,
uint256 interval
) external onlyOwner {
subscriptions[subscriptionCount] = Subscription({
service: service,
amount: amount,
interval: interval,
nextPayment: block.timestamp + interval,
active: true
});
subscriptionCount++;
}
function executePayment(uint256 subscriptionId) external {
Subscription storage sub = subscriptions[subscriptionId];
require(sub.active, "Subscription not active");
require(block.timestamp >= sub.nextPayment, "Payment not due");
payable(sub.service).transfer(sub.amount);
sub.nextPayment = block.timestamp + sub.interval;
}
}async function setupBatchTxChild() {
// 步骤1: 生成子EOA
const childEOA = await generateChildEOA();
console.log("生成子EOA:", childEOA.address);
// 步骤2: 部署或获取批量交易逻辑合约地址
const batchTxLogicAddress = "0x..."; // 预部署的合约地址
// 步骤3: 创建授权签名
const childSigner = new ethers.Wallet(childEOA.privateKey, provider);
const authorization = await signAuthorization(
childEOA.address,
batchTxLogicAddress
);
// 步骤4: 执行EIP-7702升级
await upgradeChildEOA(childEOA.address, batchTxLogicAddress, authorization);
// 步骤5: 安全销毁私钥
// 注意:实际应用中需要安全的内存清理
childEOA.privateKey = null;
// 步骤6: 注册到记录平台
await registerChildEOA(childEOA.address, "batchTx");
console.log("批量交易子EOA设置完成");
}async function executeBatchTransfer(recipients, amounts) {
// 步骤1: 查询批量交易子EOA地址
const calls = [
{
to: '0x0000000000000000000000000000000000000111' as `0x${string}`,
value: parseEther('0.00001')
},
{
to: '0x0000000000000000000000000000000000000222' as `0x${string}`,
value: parseEther('0.00001')
}
];
const result = await walletClient.sendCalls({
account: wallet.address as `0x${string}`,
calls,
experimental_fallback: true // 启用兼容性回退
});
console.log("批量转账交易已发送:", result);
return tx;
}- 立即销毁:子EOA升级后必须立即安全销毁私钥
- 提醒警告:明确告知用户子EOA私钥已丢弃,请放心使用
- Owner验证:所有逻辑合约必须验证调用者为主控EOA
- 访问控制:RecordContractPlatform只允许主控EOA管理自己的记录
- 状态保护:子EOA的关键状态变量应设为immutable或有严格的修改控制
- 一次性升级:子EOA升级后不可更改,需要新功能时创建新子EOA
- 逻辑合约审计:确保所有逻辑合约经过充分测试和审计
- 授权验证:确认EIP-7702授权签名的有效性
- 余额监控:定期检查各子EOA的余额状态
- 恢复机制:实现紧急资产恢复功能
- 多签保护:对于高价值操作考虑添加多重签名保护
本架构通过主控EOA + 多子EOA + 记录合约的设计,成功实现了EIP-7702的多重委托功能,解决了单个EOA只能委托一个合约的限制。前端负责用户交互和签名操作,合约端提供功能逻辑和状态管理,两者通过明确定义的接口进行交互,构建了一个安全、灵活、可扩展的智能账户管理系统。