無效的操作碼 - 將 bytes32 數組傳遞給從另一個合約的另一個方法呼叫的合約方法
幾個小時以來,我一直在努力解決這個問題,我將不勝感激:)
這是明確說明問題的最終編輯:是否有適當的方法將 bytes32 數組傳遞給合約 B 上的方法,該方法本身實例化合約 A(從其地址)並將 bytes32 數組傳遞給該合約的方法? 將數組直接傳遞給合約 A 的方法工作得很好,但是將它傳遞給合約 B 的方法,然後再將它傳遞給合約 A 的方法,當數組有多個字節 32 時,它就不起作用了**。**
這是堅固性的限制嗎?還是我做錯了什麼(堅固或使用我的javascript web3js程式碼)?或者這是 testrpc 的一個錯誤?
完整的細節如下。讓我知道您是否可以重現該問題,或者是否可以正常工作或其他問題。
我有一個契約 A 和一個繼承自它的契約 B。我想要做的是從已經部署的合約 A 地址實例化合約 B 中的合約 A,並呼叫該實例的方法。
我知道如何做到這一點(或者至少,我認為我知道;也許這不是正確的方法?)並且它有效。真正的問題在於 bytes32 數組。
語境
首先,這是合約程式碼。
contract A { struct MyStruct { address user; bytes32 [] stuff; } MyStruct [] public myStructs; function addStuff (bytes32 [] _stuff) public { myStructs.push ( MyStruct ({user: msg.sender, stuff: _stuff}) ); } } contract B is A { function provideStuff (address to, bytes32 [] stuff) public { A(to).addStuff(stuff); } }
一點上下文:
stuff
是一個十六進製字元串(可以是 256 位 = 32 字節(完全適合 abytes32
),或者更多(512 等);因此是數組bytes32[]
)。
contractA
首先,我使用錢包地址部署一個合約 A(返回一個實例)anAddress
。這給了我,當我的合約被部署時,智能合約地址contractAaddress
。
addStuff
使用 bytes32 數組呼叫非常有效:gasEstimate = contractA.addStuff.estimateGas(getBytes32Array(stuff), {from: anAddress); contractA.addStuff(getBytes32Array(stuff), {from: anAddress, gas: gasEstimate});
注意 - getBytes32Array
我有一個函式,
getBytes32Array
它從給定的十六進製字元串輸出一個 Bytes32 數組(實際上是十六進製字元串):function getBytes32Array(string){ var splittedString = string.match(/.{1,64}/g); for(var i=0; i< splittedString.length; i++){ splittedString[i] = "0x"+splittedString[i]; } return splittedString; }
這使我可以簡單地輸入我的十六進製字元串,例如
e0f89ca8eae95281590977802df657506a151304234d15570c12cc26263a8b7a2bf140bc9f80baa93879634717d9c2cf08cc6c96492c1b56c053ae54546f4f20
(512 位)並獲取["0xe0f89ca8eae95281590977802df657506a151304234d15570c12cc26263a8b7a","0x2bf140bc9f80baa93879634717d9c2cf08cc6c96492c1b56c053ae54546f4f20"]
將被理解為包含兩個字節 32 的字節 32 數組。進入真正的問題
以這種方式呼叫
addStuff
與包含多個字節 32 的字節 32 數組完美配合。現在,第二,我正在
contractB
使用錢包地址部署我的合約 B(返回一個實例)anotherAddress
。這給了我,當我的合約被部署時,智能合約地址contractBaddress
。我想做的是呼叫
provideStuff
合約 B 的方法,將部署的合約 AcontractAaddress
和 的地址提供給它stuff
,它應該從地址實例化合約 A 並addStuff
使用stuff
提供的呼叫。(參見合約 B 的可靠性程式碼)gasEstimate = contractB.provideStuff.estimateGas(contractAaddress, getBytes32Array(stuff), {from: anotherAddress}); contractB.provideStuff(contractAaddress, getBytes32Array(stuff), {from: anotherAddress, gas: gasEstimate});
但這每次都會給我一個無效的操作碼……儘管estimateGas工作(不是很奇怪嗎?)……當我的bytes32數組只有一個bytes32時,這****確實有效。更奇怪的是,當它有一個非常短的十六進製字元串的第二個 bytes32 時,它也可以工作(參見PS1)。但是我無法使用 x bytes32 的數組獲得一致的行為,每個 32 字節(64 個字元)十六進製字元串(甚至更短)……
提前致謝。
PS1:如果東西是 256 位十六進制值或稍長一點(我嘗試使用硬編碼
["0x256BitValueHere","0xverySmallValueHere"]
,0xverySmallValueHere
不能超過幾個字元(可能是 10,我不記得),則前面的範例工作正常,之後它會觸發無效操作再次編碼。我也嘗試了幾個小值["0xverySmallValueHere", "0xverySmallValueHere", "0xverySmallValueHere", "0xverySmallValueHere"]
,它也不起作用。PS2:使用 testrpc 在本地工作。
編輯:一切都在 Remix 中完美執行。所以這不是 Solidity 程式碼問題。
您將“實例化”契約 A 與“繼承自”契約 A 混淆了。
繼承更像是類庫,其中 B 接受 A 的狀態變數和函式。在這種情況下,沒有單獨的 A 可以交談
contract B is A
- 只有 B 恰好包含來自 A 的原始碼。既然你想讓兩個合約互相對話,那麼先部署A,然後在B中討論。最簡單的方法是在B的源文件中包含A的副本,這樣編譯器就可以“看到”並理解ABI(函式簽名)。沒有
B is A
,但您可以將變數轉換為“be”類型contract A
,如下所示:
A a;
(有點像uint x
)。既然已經確定了“a”的“類型”,那麼另一個重要的因素就是它的地址。假設你已經在某個地方部署了 A,你應該知道它的地址。您可以將其傳遞給 B 的建構子並完成實例化“a”。
function B(address aAddress) public { a = A(aAddress); // so "a" is the "A" found at "aAddress" }
偉大的。現在您可以使用 來回發送消息
a.function(args)
。這裡有一個類似於你的小修補:
pragma solidity 0.4.17; contract A { event LogTest(address sender, bytes32 element); function test(bytes32[] X) public returns(bool success) { // This unbounded loop is an anti-pattern, but it shows it working at small scales. for(uint i=0; i<X.length; i++) { LogTest(msg.sender, X[i]); } return true; } } contract B { A a; // variable "a" cast as type "A" which is the contract shown above function B(address aAddress) public { a = A(aAddress); // instantiate an instance of "a" already deployed at the address passed in } function testIt(bytes32[] array) public returns(bool success) { return a.test(array); // send a message to contract "a" } }
您可以在 Remix 中玩耍以查看它的工作原理。
- 部署 A
- 複製A的地址
- 部署 B,將 A 粘貼到建構子中
- 嘗試使用 B.test
["0x1","0x2"]
將其傳遞給數組。希望能幫助到你。
我剛剛在 Remix(Solidity 版本 0.4.24)中嘗試了你的契約,它工作正常。我可以通過
provideStuff
contract方法寫東西B
,然後通過 contract 閱讀A
。不過,我需要將以下方法添加到契約A
中以從中讀取內容:function get (uint i, uint j) public view returns (bytes32) { return myStructs [i].stuff [j]; }
因為 Solidity 生成的預設 getter 不讀取動態數組。