将结构数组发送到不同的合约

Send array of structs to a different contract

提到 here that ABIEncoderV2 should make it possible to pass structs from contract to contract. Solidity latest ABI spec 提到可以将元组数组作为输入。你能说一下在下面的示例代码中应该如何将结构数组/元组 [] 从 TupleArrayFactory 发送到 TupleArray 吗?

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract TupleArrayFactory {
    TupleArray newTuple;

    function createTuple() public {
        newTuple = new TupleArray("trait0", "display0", 0);
       
        // newTuple.pushAttribute([["trait1","display2",2]]);
        newTuple.pushAttribute(["trait1","display2",2]);
        // TypeError: Unable to deduce common type for array elements.
    }
}

contract TupleArray {
    struct Attribute {
        string trait_type;
        string display_type;
        uint8 value;
    }
    
    Attribute[] public attributes;
    Attribute[] public tempAttributeArr;

    constructor(string memory trait_type, string memory display_type, uint8 value) payable {
        Attribute memory Attribute0 = Attribute(trait_type, display_type, value);
        tempAttributeArr.push(Attribute0);
        pushAttribute(tempAttributeArr);
        
        //pushAttribute([trait_type, display_type, value]);
        // TypeError: Unable to deduce common type for array elements.
    }
    
    function pushAttribute(Attribute[] memory _attr) public payable {
        attributes.push(_attr[0]);
    }
}

这是您示例的工作版本:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract TupleArrayFactory {
    TupleArray newTuple;

    function createTuple() public {
        newTuple = new TupleArray("trait0", "display0", 0);

        TupleArray.Attribute[] memory attributes = new TupleArray.Attribute[](1);
        attributes[0] = TupleArray.Attribute("trait1", "display2", 2);
        newTuple.pushAttribute(attributes);
    }
}

contract TupleArray {
    struct Attribute {
        string trait_type;
        string display_type;
        uint8 value;
    }

    Attribute[] public attributes;

    constructor(string memory trait_type, string memory display_type, uint8 value) payable {
        Attribute[] memory tempAttributeArr = new Attribute[](1);
        tempAttributeArr[0] = Attribute(trait_type, display_type, value);
        pushAttribute(tempAttributeArr);
    }

    function pushAttribute(Attribute[] memory _attr) public payable {
        attributes.push(_attr[0]);
    }
}

一些备注:

  1. pushAttribute([["trait1","display2",2]]) 将不起作用,因为数组不能隐式转换为结构。您必须调用结构构造函数。
  2. 即使那样 pushAttribute([TupleArray.Attribute("trait1","display2",2)]) 也不会工作,因为 Solidity currently does not have dynamic array literals。如果要将动态数组传递给函数,则必须为其创建一个变量。
  3. 您的 pushAttribute() 接受一个数组但忽略除第一个元素以外的所有元素。那么为什么要把它变成一个数组呢?我假设这是为了这个例子,但如果不是,你应该让函数只接受一个结构。
  4. tempAttributeArr 存储起来是可行的,但不是必需的。可以放在内存里,比较便宜

Solidity latest ABI spec mentions that it is possible to have tuple array as a input.

您链接到的文档仅描述元组(例如用于 return 命名函数参数或 return 值)如何在描述 ABI 的 JSON 中格式化。

也许您指的是该页面上的其他部分?通常,结构被编码为元组 ABI,这可能是那里混淆的根源。但这只是意味着,如果您自己对参数进行编码(例如,当您使用链下代码发布交易时),您应该将结构编码为元组。如果您只是在链上调用外部函数,则不必这样做。事实上你不能,因为结构和元组不能相互转换。当函数需要结构并且结构类型必须与函数签名中指定的完全相同时,您必须使用实际结构。