最小代理
为每个"代理实例",用一次固定模板+目标合约地址生成一个转发合约。
他不做逻辑处理,他仅仅是将用户的信息转发到逻辑合约中。
最小代理合约他只是搬运工,他将用户数据传到逻辑合约中来。逻辑合约只做处理,所有数据在代理合约中。就好比将用户swap信息传递到逻辑合约中的swap方法中去,并进行执行。或许你可以理解为,你在remix中部署了一个合约,这个合约中存在很多方法。你不需要没错调用某一个方法而进行重新部署。
所有数据、状态变量、余额,其实都存在代理合约的存储空间内。
为什么?因为:
- 代理使用delegatecall转发,delegatecall的特点是:
-
- 逻辑用目标合约。
- 存储用自己(代理合约)。
代理合约为什么能支持多用户
因为每个代理合约本身就是一个完整的合约账户:
- 它有独立的以太坊地址;
- 它有自己的ETH余额(可以接收ETH);
- 它的存储槽里可以存放变量(如用户余额、状态标识等)。
逻辑合约与代理合约
即使逻辑是共享的,每个代理依然是"独立的个体"。
- 逻辑合约 = 公司总部,掌管所有业务流程。
- 最小代理合约 = 在各地开设的分公司(省钱建的简易分公司)(业务实例):
-
- 每家分公司(代理)有自己的账本(数据);
- 但都靠总部(逻辑合约)指挥,按照总部的业务规则运作。
虽然分公司(代理)设施简单,但它们都是独立存在、独立运营的"业务实例"。

工厂合约和最小代理区分
工厂合约 和最小代理合约 确实都能"批量生成合约",但它们定位完全不同。
工厂合约 是"造合约的工厂",最小代理合约是"节省成本的产品设计"。
|----------|---------------|-----------------------|
| 类型 | 工厂合约(Factory) | 最小代理合约(Minimal Proxy) |
| 本质 | 造合约的工具(部署者) | 一个独立的合约实例(业务壳) |
| 作用 | 批量生成合约(创建实例) | 作为业务合约接受调用 |
| 本身是否业务实例 | 否,本身不参与任何业务 | 是,自己就是独立的合约实例 |
| 部署成本 | 正常合约部署成本 | 极低成本(只是一段代理代码) |
| 依赖关系 | 工厂可以部署任何类型的合约 | 必须依赖某个逻辑合约 |
| 功能 | 管理生成过程 | 实际承载业务,负责转发请求处理 |
最大区别就在:
- 最小代理:
-
- 有独立的以太坊地址;
- 有自己的ETH余额;
- 存储数据在自己身上;
- 用户直接与它交互,它是业务提供者。
- 工厂合约只是创建合约
-
- 批量部署
- 记录合约地址
- 管理合约
MultiCall原理
简单来说就是:可以一次性抓取大量数据。降低一次一次去抓取需要花费的时间和gas。
MultiCall 只读取,不做写操作
会在很多地方使用到,比如: 假设你有一个钱包地址,想一次性查询它在多个ERC20代币的余额。 MultiCall做法:一次调用MultiCall合约,打包10个balanceOf()
调用,链上统一执行,结果一次返回。
流程:
-
输入 :你给合约传入一个查询数组(例如:查询3个不同合约的余额或总供应量)。
-
操作:
-
合约会依次调用每个目标合约 ,查询它们的指定数据(例如:
balanceOf(address)
或totalSupply()
)。 -
输出 :所有查询的结果会一起返回给你,避免多次查询和浪费Gas。
/**
*Submitted for verification at Etherscan.io on 2019-11-14
*/// hevm: flattened sources of /nix/store/im7ll7dx8gsw2da9k5xwbf8pbjfli2hc-multicall-df1e59d/src/Multicall.sol
pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;////// /nix/store/im7ll7dx8gsw2da9k5xwbf8pbjfli2hc-multicall-df1e59d/src/Multicall.sol
/* pragma solidity >=0.5.0; /
/ pragma experimental ABIEncoderV2; *//// @title Multicall - Aggregate results from multiple read-only function calls
/// @author Michael Elliot mike@makerdao.com
/// @author Joshua Levine joshua@makerdao.com
/// @author Nick Johnson arachnid@notdot.netcontract Multicall {
struct Call {
address target;
bytes callData;
}
//传入打包的数据,里面有地址和方法
function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) {
blockNumber = block.number;
returnData = new bytes;
for(uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
require(success);
returnData[i] = ret;
}
}
// Helper functions
function getEthBalance(address addr) public view returns (uint256 balance) {
balance = addr.balance;
}
function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
blockHash = blockhash(blockNumber);
}
function getLastBlockHash() public view returns (bytes32 blockHash) {
blockHash = blockhash(block.number - 1);
}
function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
timestamp = block.timestamp;
}
function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
difficulty = block.difficulty;
}
function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
gaslimit = block.gaslimit;
}
function getCurrentBlockCoinbase() public view returns (address coinbase) {
coinbase = block.coinbase;
}
}