5月29日 22:35

Solidity 中 delegatecall 和 call 有什么区别?代理合约怎么实现?

call 在被调用合约的上下文执行,msg.sender 是调用者,存储读写被调用合约的 storage。delegatecall 在调用者的上下文执行,msg.sender 保持原始调用者不变,存储读写调用者的 storage——代码是别人的,存储是自己的。代理合约就是靠 delegatecall 实现的:代理合约存数据,逻辑合约存代码,fallback 函数 delegatecall 到逻辑合约,逻辑合约操作的是代理的 storage。

追问

透明代理和 UUPS 有什么区别?

透明代理(Transparent Proxy):代理合约的 admin 调管理函数、user 调业务逻辑,在代理中用 if (msg.sender == admin) 分流,无函数选择器冲突风险但 gas 多约 2000。UUPS:升级逻辑写在逻辑合约里,代理更轻量,但逻辑合约忘了写 _authorizeUpgrade 就永远锁死——安全性依赖逻辑合约代码质量。

存储碰撞怎么防?

代理合约和逻辑合约的存储布局必须对齐——变量声明顺序一致,新版本只能追加变量不能删除或插入。用 OpenZeppelin 的 StorageSlot 或 EIP-1967 规定特定 slot 存代理管理数据(如 bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)),避免和业务变量撞 slot。

代理合约升级时旧数据会丢吗?

不会。数据存在代理合约的 storage 里,逻辑合约升级只是换了 delegatecall 的目标地址,代理的 storage 不变。但新逻辑合约的存储布局必须兼容旧布局——删除或重排变量会导致旧数据错位。

为什么不直接用 call 替代 delegatecall?

call 执行后状态变更在被调用合约上,调用者(代理)的 storage 不变——等于白调了。代理模式的核心就是"数据在代理、逻辑在实现",只有 delegatecall 能让实现合约操作代理的 storage。

Beacon 代理是什么?

多个代理合约共享同一个逻辑合约地址,升级时改一处全部生效。结构:Proxy → Beacon(存 implementation 地址)→ Logic Contract。好处是管理 100 个代理只需一次升级,gas 省。坏处是多一层间接调用。

标签:Solidity