这是 Solidity 高级开发中很重要的两个概念,通常用于:
精细控制合约行为(比如合约间通信、控制 gas、避免 fallback 限制)
编写高性能或底层逻辑的合约(例如代理合约、MEV、Gas 优化等)
我们逐一讲清楚:
Solidity 提供了几种**“低级”函数调用方式**,用于替代标准的函数调用。这些函数更原始、更灵活,但使用不当容易出 bug(比如失败不报错、容易被 reentrancy 攻击)。
低级函数 | 用途 |
---|---|
address.call |
向地址发交易,可以带 data(常用于代理合约) |
address.delegatecall |
在调用者上下文中执行另一个合约的代码(代理合约核心) |
address.staticcall |
和 call 类似,但只读(不允许写入链上状态) |
address.send |
转 ETH,失败返回 false,不会 revert |
address.transfer |
转 ETH,gas 固定 2300,失败会 revert |
call
(bool success, bytes memory data) = address(target).call(
abi.encodeWithSignature("doSomething(uint256)", 123)
);
success
是是否成功的标志
data
是返回的字节数据(需要用 abi.decode
解码)
delegatecall
(bool success, ) = address(lib).delegatecall(
abi.encodeWithSignature("increment()")
);
执行的是 lib
合约的函数
但使用的是 当前合约的 storage
常用于 可升级合约(proxy pattern)
不会自动 revert
,必须检查 success
否则可能吞错
容易遭遇重入攻击(尤其是 call 后调用外部合约)
可读性差,调试难
Solidity 支持使用 Yul 或旧版本的汇编语言 直接写 EVM 操作码。这可以让你:
完全掌控执行逻辑
做极致 gas 优化
使用 Solidity 无法访问的底层功能
assembly {
let x := add(1, 2)
sstore(0x0, x)
}
这是 Yul 风格汇编(推荐)。这里:
add
是 EVM 操作码(加法)
sstore
把值写入 storage
function getCaller() public view returns (address caller) {
assembly {
caller := caller()
}
}
等价于 msg.sender
,但这是纯操作码形式。
场景 | 为什么要用汇编? |
---|---|
Gas 极致优化 | 比 Solidity 更轻量的操作方式 |
操作内存 / 存储地址控制 | Solidity 语法无法表达的东西 |
与其它语言/合约兼容(例如代理) | 手动构造函数选择器 / calldata |
名称 | 是什么 | 使用场景 |
---|---|---|
低级调用 | call、delegatecall、staticcall 等原始调用方式 | 跨合约通信、代理合约 |
内联汇编 | 写原始的 EVM 操作码 | 性能极致、底层访问、操作存储或 calldata |
没有特殊需求,避免用低级调用和汇编(容易出错)
如果做代理合约、升级逻辑、MEV、DeFi 细节处理,这些是必备工具
内联汇编在 Solidity 0.8+ 中推荐用 Yul 风格(可读性更强)
如果你想,我可以给你写一个代理合约 + delegatecall 示例,或者手动拼接 calldata 的内联汇编 demo。需要吗?