Solidity

在松露中呼叫備份函式來委託呼叫

  • November 16, 2018

我正在通過Solidity 中的 DelegateProxy玩可升級合約。

該模式假定MutableForwarder合約部署一次,所有後續對可升級合約的呼叫都對 Forwarder 的回退函式進行,而 Forwarder 又使用委託呼叫來呼叫目標合約。

我很難呼叫回退函式:

var payload = testContract.contract.construct.getData(1);

return web3.eth.sendTransaction({
 from: accounts[0],
 to: testContractForwarder.address,
 data: payload
});

執行部署腳本的結果:


Replacing TestContract...
 TestContract: 0x93d8c7d954a930f2eadf6fc68058e5c43ebe988f
Deploying TestContractForwarder...
 TestContractForwarder: 0xd46539cae056345ce547e65c3e8d8ec764ed5d1d
@@@ TestContractForwarder target: 0x93d8c7d954a930f2eadf6fc68058e5c43ebe988f

將目標連結到轉發器後,下一個事務通過,但沒有效果:


@@@ TestContract/construct tx 0x11bea1b01e4977747bfd8b604eaf9fc3ebc5dae2b42bde7e918f811fbad1fd53 successful
--- TestContract/value : 0
--- TestContract/wasConstructed : false

直接呼叫合約,使用相同的有效負載成功並設置值。

複製的儲存庫:https ://github.com/fbielejec/truffle-forwarder

您誤解了轉發器在使用delegatecall. 什麼delegatecall是使用其他地方的程式碼在使用的契約的上下文中執行delegatecall

因此,如果您的實例TestContractForwarder“委託呼叫” 的程式碼TestContract,那麼您可以期望**TestContractForwarder**更新您的實例的儲存。

讓我們查看您的遷移腳本。你寫:


var payload = testContract.contract.construct.getData(1);
return web3.eth.sendTransaction({
   from: address,
   to: testContractForwarder.address,
   data: payload,
   gas: gas
});

您可以更改為:


return TestContract.at(testContractForwarder.address)
   .construct(1, { from: address, gas: gas })

然後,當您要確認更改時,您需要更改:


   return TestContract.deployed ();
}).then ((instance) => {
   return [instance.value (), instance.wasConstructed ()];
}).then ((promises) => {
   return Promise.all (promises);

進入:


   return TestContractForwarder.deployed (); // <-- Notice the difference
}).then ((instance) => {
   var myContract = TestContract.at(instance.address);
   return Promise.all([myContract.value (), myContract.wasConstructed ()]);

所以現在你有問題了。你看,MutableForwarder儲存target在儲存槽0,也TestContract儲存value在儲存0。因此,當您呼叫 時.construct,實際上是覆蓋了該target欄位。

要修復它,您需要TestContract在 storage slot 有一個虛擬欄位0。像這樣:


contract TestContract {

 uint private dummyTargetDontUpdate; // Storage slo 0
 uint public value; // slot 1
 bool public wasConstructed; // slot 2

我重寫了您的遷移腳本:


 deployer.deploy (TestContract, opts)
   .then (instance => {
     linkBytecode(TestContractForwarder, forwarderTargetPlaceholder, instance.address);
     return deployer.deploy(TestContractForwarder, opts);
   })
   .then (instance => instance.target())
   .then (res => {
     console.log ("@@@ TestContractForwarder target:",  res);
     return Promise.all([ TestContract.deployed(), TestContractForwarder.deployed() ]);
   })
   .then (([testContract, testContractForwarder]) => TestContract.at(testContractForwarder.address)
       .construct(1, { from: address, gas: gas }))
   .then (tx => {
     console.log ("@@@ TestContract/construct tx", tx.tx, "successful");
     return TestContractForwarder.deployed();
   })
   .then (instance => {
     var myContract = TestContract.at(instance.address);
     return Promise.all([ myContract.value(), myContract.wasConstructed() ]);
   })
   .then (([res1, res2]) => {
     console.log ("--- TestContract/value :", res1.c [0]);
     console.log ("--- TestContract/wasConstructed :", res2);
     console.log ("Done");
   });

引用自:https://ethereum.stackexchange.com/questions/62477