Solidity
在松露中呼叫備份函式來委託呼叫
我正在通過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
直接呼叫合約,使用相同的有效負載成功並設置值。
您誤解了轉發器在使用
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"); });