EIP-1167是一种标准化的代理合约实现,旨在通过最小的字节码和委托调用的方式实现合约逻辑的复用。这种模式不仅节省部署和存储成本,还提供了开发者灵活的合约管理能力。
EIP-1167定义了一种代理合约标准,代理合约的作用是将用户的调用委托到另一个逻辑合约(目标合约)。它通过 delegatecall 操作将调用转发给目标合约,从而实现代码复用,同时代理自身不存储逻辑,只存储状态。
代理合约的主要特点
轻量高效 :代理合约的字节码极短,部署成本低。
逻辑复用 :多个代理可以复用一个逻辑合约,节省存储。
工厂合约模型 :支持创建交易中的克隆初始化(通过工厂合约模型)。
易于升级 :通过调整代理指向的目标地址,可以实现逻辑的动态升级。
代理合约的字节码模板如下:
0x363d3d373d3d3d363d73<logic_contract_address>5af43d82803e903d91602b57fd5bf3
function createProxyBytecode(address logic) public pure returns (bytes memory) {
return abi.encodePacked(
hex"363d3d373d3d3d363d73",
logic,
hex"5af43d82803e903d91602b57fd5bf3"
);
}
● 代理合约的部署
● 函数调用的转发
● 逻辑合约的复用
● 可升级性
● 多实例工厂模式:工厂合约可以快速部署多个代理合约实例,这些实例共享同一个逻辑合约 ● 可升级合约:可以通过设置存储中的逻辑合约地址,实现合约逻辑的动态升级 ● 模块化合约:通过代理合约实现模块化设计,各模块分离逻辑代码和状态存储,提高合约的可维护性
● 优点:
● 缺点
以下是一个完整的实现案例,包括逻辑合约、代理工厂合约以及交互流程。 ● 逻辑合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract LogicContract {
uint256 public value;
function setValue(uint256 _value) external {
value = _value;
}
function getValue() external view returns (uint256) {
return value;
}
}
● 代理工厂合约
0x14: SHA3 操作码(也称为 KECCAK256)
0x28 : 将字节值 0x28(十进制 40)推入栈。PUSH1 是一字节操作码,用于将紧接的单字节值推入栈中
0x37: CALLDATACOPY,将调用数据(calldata)从输入中复制到内存。通常用于在合约中处理调用参数。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MinimalProxyFactory {
event ProxyCreated(address proxy);
function createProxy(address logic) external returns (address) {
bytes20 targetBytes = bytes20(logic);
address proxy;
assembly {
let ptr := mload(0x40) // Free memory pointer
mstore(ptr, 0x3d602d80600a3d3981f3) // Prefix
mstore(add(ptr, 0x14), targetBytes) // Logic address
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3) // Suffix
proxy := create(0, ptr, 0x37) // Deploy proxy
}
require(proxy != address(0), "Proxy deployment failed");
emit ProxyCreated(proxy);
return proxy;
}
}
● 交互示例
逻辑合约和工厂合约: 部署 LogicContract 和 MinimalProxyFactory
使用工厂部署代理合约 : 调用 createProxy 方法,传入逻辑合约地址
address proxy = factory.createProxy(logicAddress);
const proxyContract = new ethers.Contract(proxyAddress, LogicContract.abi, signer);
// 设置值
await proxyContract.setValue(42);
// 获取值
const value = await proxyContract.getValue();
console.log("Value:", value); // 42
EIP-1167 是以太坊合约开发中不可或缺的工具,极大地提高了资源利用效率和开发灵活性。
OZ 实现了EIP-1167 标准的一个库,提供了使用 create 和 create2 操作码部署最小代理合约(即 Clone)的功能,同时支持通过 create2 操作码预测代理合约地址。
2.1 clone 函数
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// 将 implementation 地址压缩存储在 0x00 和 0x20 内存位置,并拼接代理合约字节码。
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
// 使用 `create` 操作码部署代理合约。
instance := create(0, 0x09, 0x37)
}
// 如果部署失败,抛出错误。
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
作用
2.2 cloneDeterministic 函数
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// 将 implementation 地址拼接代理合约字节码,存储到内存中。
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
// 使用 create2 操作码部署合约。
instance := create2(0, 0x09, 0x37, salt)
}
// 如果部署失败,抛出错误。
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
作用
2.3 predictDeterministicAddress 函数
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
// 部署者地址
mstore(add(ptr, 0x38), deployer)
// 后置字节码
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
// 实现地址(implementation)
mstore(add(ptr, 0x14), implementation)
// 前置字节码
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
// salt
mstore(add(ptr, 0x58), salt)
// 计算合约地址
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
作用
EIP-1167 提供了一种标准化的代理合约实现,广泛应用于工厂模式和模块化合约。通过代理技术,可以显著节约部署成本,提高合约逻辑复用性,同时提升开发灵活性。它是以太坊生态中不可或缺的工具,促进了智能合约的高效实现和模块化设计。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!