【问题标题】:Passing a Struct Array to constructor of Solidity Contract将结构数组传递给 Solidity Contract 的构造函数
【发布时间】:2023-02-17 18:23:30
【问题描述】:

我正在用 solidity 构建一个 NFT 智能合约,我试图在部署合约时将 Array of Structs 传递给构造函数。但是我收到以下错误。

TypeError: Cannot read property 'length' of undefined

联系代码是:

contract MetropolisWorldGenesis {

    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    struct PropertyAttributes {
        uint id;
        string name;
        string description;
        string image;
        Properties properties;
    }

    struct Properties {
        string tower;
        string disctrict;
        string neighborhood;
        string primary_type;
        string sub_type_1;
        string sub_type_2;
        string structure;
        string feature_1;
        string feature_2;
        string feature_3;
        string feature_4;
        string feature_5;
        string rarity;
        // string internal_id;
    }

    //store a list of all the NFT's available to mint. 
    //this is built on when the constructor is called. 
    PropertyAttributes[] defaultProperties;

    //store which has been minted. 
    mapping(uint => bool) public MintedNfts;

    //map the nft tokenid to the atributes 
    mapping(uint256 => PropertyAttributes) public nftAttributes;

    constructor(PropertyAttributes[] memory props) { 
        console.log("OK I am making the contract, just this once mind");

        for (uint i = 0; i < props.length; i += 1){
             defaultProperties.push(props[i]);

             PropertyAttributes memory p = defaultProperties[i];
             console.log("Done initializing %s w/ HP %s, img %s", p.name, p.description, p.image);
        
    } 
}

我使用以下方式调用它:

const main = async () => {

    // make up the data from t he json 
    const nftList = require('./nft_list_finalv2.json')
    
    let props = []

    for(var i=0; i < nftList['nfts'].length;i+=1){
        
        x = nftList['nfts'][i]['metadata']
        props.push(x)
    }
    
    console.log(props.length)

    // deply the contract will the data made above. 
    const propertyContractFactory = await hre.ethers.getContractFactory('MetropolisWorldGenesis');
    const propertyContract = await propertyContractFactory.deploy(
        props
    );
    await propertyContract.deployed();
    console.log("Contract deployed to:", propertyContract.address);
  };
  
  const runMain = async () => {
    try {
      await main();
      process.exit(0);
    } catch (error) {
      console.log(error);
      process.exit(1);
    }
  };
  
  runMain();

Json 文件是一个结构如下的项目数组。

{ 'nfts':[
     {
            "id": 1,
            "metadata": {
                "id": 1,
                "name": "tester",
                "description": "Rtestt",
                "image": "",
                "properties": {
                    "tower": "Tower 1",
                    "district": "Hir",
                    "neighborhood": "Fres",
                    "primary_type": "Whause",
                    "sub_type_1": "Aboned",
                    "sub_type_2": "Fors",
                    "structure": "Dark brick",
                    "feature_1": "Df",
                    "feature_2": "Gins",
                    "feature_3": "Castes",
                    "feature_4": "Cloors",
                    "feature_5": "e walls",
                    "rarity": "",
                    "internal_id": "Tower 1_1"
                }
            },
            "price": 10,
            "ipfs": "",
            "img_name": "WqmYMT02j.png",
            "map_ref": "Z"
        },
....
]}

我在 javascript 端得到了很好的数据数组,但是当我将它传递给合同时似乎出现了一些错误。 我在这里错过了什么?

【问题讨论】:

  • ./nft_list_finalv2.json 文件的内容是什么?
  • 我编辑了问题以包含 json 格式

标签: solidity ethers.js hardhat


【解决方案1】:

实际上,您实际上可以将结构作为 arg 传递给 solidity 中的构造函数。我在我的一份合同中自己做:


struct UserConfig {
        address user;
        address userToken;
        uint userSlippage; 
    }

    struct FixedConfig { 
        address inbox;
        address ops;
        address PYY;
        address emitter;
        uint maxGas;
    }

FixedConfig fxConfig;
VariableConfig varConfig;


constructor(
        FixedConfig memory fxConfig_,
        VariableConfig memory varConfig_
    ) {
        fxConfig = fxConfig_;
        varConfig = varConfig_;
    }

您会将其作为数组传递到 ethers.js 上。

我遇到的问题,以及我最初是如何在这篇文章中结束的,是当你将数组传递给以太坊上的deploy() 时,它会改变合约中结构体上变量的顺序。所以我想弄清楚为什么会这样。

【讨论】:

  • 谢谢,很高兴知道。对订单问题满意吗?
  • 不知道那个
  • 我没有看到这种行为。 Ethers/Hardhat -> solidity 保持秩序。您使用的是什么版本?
  • 溶胶 0.8.14 @ian
  • “你将它作为数组传递到 ethers.js”是什么意思?如果您只是在那里将其声明为普通结构,合约如何知道它是一个数组?
【解决方案2】:

您也可以将结构传递给外部和公共可见性函数。
如果你的函数是这样的:

struct YourStruct {
    uint x;
    string y;
}
function func(
    uint a,
    uint b,
    string memory c,
    YourStruct memory d
    ) external;

然后你可以像这样传递这些:

let args = [
    1,
    2,
    "c",
    {
        x: 3,
        y: "y"
    }
]
contract.func(...args);

只有 struct 需要一个具有键值对的对象,您也可以对嵌套结构遵循这一点。
我希望它会有所帮助!

【讨论】:

    【解决方案3】:

    上次我检查时无法将 struct 传递给 constructor function (see here this 2018 answer)。它说:

    ...您不能将结构传递给构造函数,因为它将从区块链外部接收输入。只有私有和内部函数可以期望结构作为输入参数。

    解决方案是使用“原始”数据类型作为输入。对于您的特定情况,可以修改构造函数以接受一个 NFT 对象,例如:

    constructor (
      int NFTsId,
      int MetaId,
      string MetaName,
      string MetaDescription,
      string MetaImage,
      string PropertiesTower,
      string PropertiesDistrict,
      ...,
      uint MetaPrice,
      string MetaIPFS,
      ...
    ) {
      // Assign values
    }
    

    或者你可以编写构造函数来接收数组中的多个输入并根据它们的索引位置重新定位它们(​​第一个 MetaId 与第一个 MetaName ...):

    constructor (
      int[] memory NFTsIds,
      int[] memory MetaIds,
      string[] memory MetaNames,
      string[] memory MetaDescriptions,
      string[] memory MetaImages,
      string[] memory PropertiesTowers,
      string[] memory PropertiesDistricts,
      ...,
      uint[] memory MetaPrices,
      string[] memory MetaIPFSs,
      ...
    ) {  
      for (uint256 i=0; i < NFTsId.length; i ++){
        NFTsId = NFTsIds[i];
        ...
        // Assign values
      }
    }
    

    需要指出的是,for 循环会消耗大量气体,具体取决于发送给构造函数的 NFT 对象的数量。

    【讨论】:

    • 非常感谢,这是有道理的,最初我有数组方法,但我遇到了一个堆栈太深的错误,我希望用结构修复它,看起来我需要重新考虑。
    • 我使用的是 0.8.9,将结构作为构造函数参数传递没有任何问题。我正在使用结构来避免堆栈太深的错误。我遇到的问题是我现在在 Etherscan 上验证时遇到错误。我想弄清楚这是否相关。
    猜你喜欢
    • 1970-01-01
    • 2018-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-24
    • 1970-01-01
    相关资源
    最近更新 更多