概述
tx.origin与msg.sender是solidity中容易令人迷惑的两个变量,尤其是当我们直接调用合约时两者的值是相同的。为了更清晰的说明两者的关系我们需要构造合约间的链式调用,如下:
EOA -> Contract A -> Contract B -> Contract C
这里先说明结论:tx.origin始终保持是EOA,msg.sender是其直接调用者的地址。如:合约B中msg.sender的值为合约A的地址,合约C中msg.sender的值为合约B的地址。
简单来说,前者是原始的交易发起者的外部地址(EOA),后者是方法的直接调用者(可以是EOA也可以是合约地址)。以下我们通过简单的合约示例来观察两者值的变化。
抽丝剥茧
下面通过合约直接调用、合约间调用和合约间链式调用的形式由浅入深逐步揭开tx.origin和msg.sender的神秘面纱。
直接调用
此处我们直接通过外部账户(EOA)来调用合约。
代码
contract AA {
constructor() {
console.log("Contract AA's address:", address(this));
}
fallback() external {
console.log(
"In AA fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
}
}
上面代码中在构造函数中输出了合约地址,在fallback函数中输出了msg.sender和tx.origin的值。
执行
操作过程如下:
1. 部署合约;
2. 执行合约的fallback方法;
由上面的执行结果(箭头1)我们可以看出,当直接调用合约时msg.sender和tx.origin的值是相同的。
注:我们构造了一个calldata参数来调用函数方法,该参数前4个字节标示要调用的方法。其计算方法是先对方法签名计算hash值(keccak256),然后截取hash的前四个字节(8个16进制字符),最后补0至32个字节。此处我们调用合约中的fallback方法,因此随机8个16进制数即可。
合约间调用
此处我们通过合约间调用来观察两者的值。
代码
contract AA {
constructor() {
console.log("Contract AA's address:", address(this));
}
fallback() external {
console.log(
"In AA fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
}
function remoteCall(address _instance) external {
console.log(
"In BB fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
(bool sucess, ) = _instance.call(
abi.encodeWithSignature("nonExistingFunction()")
);
require(sucess, "call error");
}
}
contract BB {
constructor() {
console.log("Contract BB's address:", address(this));
}
fallback() external {
console.log(
"In BB fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
}
}
以上代码合约AA中的remoteCall方法接受一个合约地址(在执行过程中我们输入合约BB的地址),用于调用其它合约的方法,此处我们调用的是合约中不存在的一个方法,因此目标合约的fallback方法会被触发。
执行
操作过程如下:
1. 部署合约AA;
2. 部署合约BB;
3. 以合约BB的地址为参数来调用合约AA的remoteCall方法;
由上面执行结果我们可能看出,当合约AA调用合约BB中的方法时,合约B中的msg.sender为合约AA的地址。
合约间链式调用
代码
此处我们实现文章最开始描述的链式调用,即:
EOA -> Contract A -> Contract B -> Contract C
contract AA {
constructor() {
console.log("Contract AA's address:", address(this));
}
fallback() external {
console.log(
"In AA fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
}
function remoteCall(address _instance, address _instance2) external {
console.log(
"In AA fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
(bool sucess, ) = _instance.call(
abi.encodeWithSignature("remoteCall(address)", _instance2)
);
require(sucess, "call error");
}
}
contract BB {
constructor() {
console.log("Contract BB's address:", address(this));
}
fallback() external {
console.log(
"In BB fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
}
function remoteCall(address _instance) external {
console.log(
"In BB fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
(bool sucess, ) = _instance.call(
abi.encodeWithSignature("nonExistingFunction()")
);
require(sucess, "call error");
}
}
contract CC {
constructor() {
console.log("Contract CC's address:", address(this));
}
fallback() external {
console.log(
"In CC fallback msg.sender:[%s] tx.origin:[%s] ",
msg.sender,
tx.origin
);
}
}
上述代码中合约AA通过remoteCall方法接收两个地址,分别是合约BB、CC的地址,其中调用过程为:合约AA的remoteCall -> 合约BB的remoteCall -> 合约CC的fallback
执行
操作过程如下:
1. 部署合约AA;
2. 部署合约BB;
3. 部署合约CC;
4. 调用合约AA的remoteCall方法,其参数分别为合约BB地址、合约CC地址;
文章来源:https://www.toymoban.com/news/detail-777861.html
由上图的执行过程,我们可以验证文章开头处的结论:tx.origin始终保持不变,其值是交易发起者的外部地址(EOA),msg.sender是其直接调用者的地址。文章来源地址https://www.toymoban.com/news/detail-777861.html
到了这里,关于solidity tx.origin和msg.sender那些事儿的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!