Contract-Development

無效的操作碼 - 將 bytes32 數組傳遞給從另一個合約的另一個方法呼叫的合約方法

  • March 16, 2022

幾個小時以來,我一直在努力解決這個問題,我將不勝感激:)

這是明確說明問題的最終編輯:是否有適當的方法將 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 字節(完全適合 a bytes32),或者更多(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 中玩耍以查看它的工作原理。

  1. 部署 A
  2. 複製A的地址
  3. 部署 B,將 A 粘貼到建構子中
  4. 嘗試使用 B.test["0x1","0x2"]將其傳遞給數組。

希望能幫助到你。

我剛剛在 Remix(Solidity 版本 0.4.24)中嘗試了你的契約,它工作正常。我可以通過provideStuffcontract方法寫東西B,然後通過 contract 閱讀A。不過,我需要將以下方法添加到契約A中以從中讀取內容:

function get (uint i, uint j) public view returns (bytes32) {
 return myStructs [i].stuff [j];
}

因為 Solidity 生成的預設 getter 不讀取動態數組。

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