将结构数组传递给 Solidity Contract 的构造函数

Passing a Struct Array to constructor of Solidity Contract

我正在构建一个可靠的 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 方面得到了很好的数据数组,但是当我将它传递到合同中时似乎出现了一些错误。 我在这里错过了什么?

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

...you cannot pass struct to a constructor as it will be receiving its input outside from the blockchain. Only the private and internal functions can expect structs as input parameters.

解决方案是使用“原始”数据类型作为输入。对于您的特定情况,可以修改构造函数以接受一个 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 对象的数量。

实际上,您实际上可以将结构作为参数传递给 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。

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