概述
????????tx.origin與msg.sender是solidity中容易令人迷惑的兩個(gè)變量,尤其是當(dāng)我們直接調(diào)用合約時(shí)兩者的值是相同的。為了更清晰的說明兩者的關(guān)系我們需要構(gòu)造合約間的鏈?zhǔn)秸{(diào)用,如下:
EOA -> Contract A -> Contract B -> Contract C
這里先說明結(jié)論:tx.origin始終保持是EOA,msg.sender是其直接調(diào)用者的地址。如:合約B中msg.sender的值為合約A的地址,合約C中msg.sender的值為合約B的地址。
????????簡(jiǎn)單來說,前者是原始的交易發(fā)起者的外部地址(EOA),后者是方法的直接調(diào)用者(可以是EOA也可以是合約地址)。以下我們通過簡(jiǎn)單的合約示例來觀察兩者值的變化。
抽絲剝繭
下面通過合約直接調(diào)用、合約間調(diào)用和合約間鏈?zhǔn)秸{(diào)用的形式由淺入深逐步揭開tx.origin和msg.sender的神秘面紗。
直接調(diào)用
此處我們直接通過外部賬戶(EOA)來調(diào)用合約。
代碼
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
);
}
}
上面代碼中在構(gòu)造函數(shù)中輸出了合約地址,在fallback函數(shù)中輸出了msg.sender和tx.origin的值。
執(zhí)行
操作過程如下:
1. 部署合約;
2. 執(zhí)行合約的fallback方法;
由上面的執(zhí)行結(jié)果(箭頭1)我們可以看出,當(dāng)直接調(diào)用合約時(shí)msg.sender和tx.origin的值是相同的。
注:我們構(gòu)造了一個(gè)calldata參數(shù)來調(diào)用函數(shù)方法,該參數(shù)前4個(gè)字節(jié)標(biāo)示要調(diào)用的方法。其計(jì)算方法是先對(duì)方法簽名計(jì)算hash值(keccak256),然后截取hash的前四個(gè)字節(jié)(8個(gè)16進(jìn)制字符),最后補(bǔ)0至32個(gè)字節(jié)。此處我們調(diào)用合約中的fallback方法,因此隨機(jī)8個(gè)16進(jìn)制數(shù)即可。
合約間調(diào)用
此處我們通過合約間調(diào)用來觀察兩者的值。
代碼
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方法接受一個(gè)合約地址(在執(zhí)行過程中我們輸入合約BB的地址),用于調(diào)用其它合約的方法,此處我們調(diào)用的是合約中不存在的一個(gè)方法,因此目標(biāo)合約的fallback方法會(huì)被觸發(fā)。
執(zhí)行
操作過程如下:
1. 部署合約AA;
2. 部署合約BB;
3. 以合約BB的地址為參數(shù)來調(diào)用合約AA的remoteCall方法;
由上面執(zhí)行結(jié)果我們可能看出,當(dāng)合約AA調(diào)用合約BB中的方法時(shí),合約B中的msg.sender為合約AA的地址。
合約間鏈?zhǔn)秸{(diào)用
代碼
此處我們實(shí)現(xiàn)文章最開始描述的鏈?zhǔn)秸{(diào)用,即:
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方法接收兩個(gè)地址,分別是合約BB、CC的地址,其中調(diào)用過程為:合約AA的remoteCall -> 合約BB的remoteCall -> 合約CC的fallback
執(zhí)行
操作過程如下:
1. 部署合約AA;
2. 部署合約BB;
3. 部署合約CC;
4. 調(diào)用合約AA的remoteCall方法,其參數(shù)分別為合約BB地址、合約CC地址;
文章來源:http://www.zghlxwxcb.cn/news/detail-777861.html
由上圖的執(zhí)行過程,我們可以驗(yàn)證文章開頭處的結(jié)論:tx.origin始終保持不變,其值是交易發(fā)起者的外部地址(EOA),msg.sender是其直接調(diào)用者的地址。文章來源地址http://www.zghlxwxcb.cn/news/detail-777861.html
到了這里,關(guān)于solidity tx.origin和msg.sender那些事兒的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!