智能合约中,使用delegatecall(),需要很慎重
简单解释下delegatecall:
Aaddress.delegatecall(abi.encode(keccak256("test()"))
delegatecall其实就是当前合约将Aaddress所指向的合约代码的test()代码作为当前合约的一部分来执行,即完全使用当前合约的环境来执行,脱离Aaddress合约本身
慎重的理由:
1.delegatecall或者call()都是错误执行也不会抛出任何异常的,因此很不友好
2.巨坑,delegatecall执行时,被借用的方法中如果有变量赋值,那么赋值的变量会直接顺序从当前合约合约中获取变量并且赋值,不在乎变量名,会导致当前合约变量被莫名改变了。危险,示例代码如下
3.待续.....
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Calltest {
address public sameAddress;
address public sameAddress2;
event Test(address who,address contractNow);
event Test2(address sameAddress2);
// 获取当前合约地址,并赋值
function test() public returns(address){
sameAddress=address(this);
sameAddress2=address(this);
emit Test(msg.sender,sameAddress);
emit Test2(sameAddress2);
return sameAddress;
}
}
contract Compare {
address public sameAddress;
uint256 public temp_int;
address public testaddress;
// 构造时 将合约Calltest的部署地址传入
constructor(address _addressOfCalltest) {
testaddress = _addressOfCalltest;
}
// 合约调用合约的第二种方式 call方式调用,代表调用testaddress地址对应合约的test()方法,并且变更testaddress地址对应的状态数据 sameAddress
function useCall() public {
testaddress.call(abi.encode(keccak256("test()")));
}
// delegatecall 方法调用,官方叫做委托调用
// 个人理解:其实就是借用testaddress合约地址的test()代码片段,拿到当前合约(Compare)来执行;
// 也就是说执行时,把test()的代码作为了Compare的一部分代码,
// 注意:
// test方法依赖的event、变量都能够正常执行,
// 但是注意变量的赋值,会直接顺序从当前合约中的变量,逐个被赋值,不挑剔数据类型是否匹配,只挑顺序,会非常危险,
// 也就是sameAddress被赋值,预期内;但是t_int会被赋值当前合约地址,要警惕该陷阱。
// 同事注意delegatecall执行错误是不抛异常的
function useDelegatecall() public {
testaddress.delegatecall(abi.encode(keccak256("test()")));
}
}
测试方式
测试环境 remix + Remix VM(London)
1. 部署Calltest
2. 部署Compare时,传入Calltest的地址
3.执行Compare的useDelegatecall(),会发现测试结果,temp_int居然变成了当前合约Compare的地址