Solidity

如何將大量數據填充到struct的映射中

  • January 29, 2022

我正在開發一款進入 Solidity 的遊戲,我希望能夠將元數據填充到我的智能合約中,以便能夠直接從遊戲功能中訪問它們。

所以我有一個基本的 ERC721 開始:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract GameEngine is ERC721Enumerable, Ownable {

 string baseURI;
 uint256 public maxSupply = 10000;

 bool isPopulated = false;
 struct Character {
   uint8 race;
   uint8 strength;
   uint8 agility;
   uint8 hp;
   uint8 xp;
   /*..*/
 }
 mapping(uint => Character) characters;

 constructor(
   string memory _initBaseURI
 ) ERC721("GameCharacters", "GC") {
   _baseURI = _initBaseURI;
 }

 /* All basic functions for ERC721: mint, tokenURI, ...*/
}

現在……我希望能夠預先填充characters映射中的所有字元結構。我可以實現兩種方式:

添加單個的功能

問題:但我必須呼叫它 10.000 次(或更多)!而且費用會很高。。

function populateOne(uint8 race, uint8 strength, uint8 agility, uint8 hp, /*...*/ ) external onlyOwner {
   require(!isPopulated, "Already Populated");
   characters[characters.length] = Character(race, strength, agility, hp, 0, /*...*/ );
}
function setPopulated() external onlyOwner { 
   isPopulated = true;
}

多合一功能

問題:不確定我能否使用 10,20 或 50 個 10k、20k 或 100k 項的數組呼叫此函式。

// All array must be the same size (maxSupply)
function populateAll(uint8[] races, uint8[] strengths, uint8[] agilities, uint8[] hps, /*...*/ ) external onlyOwner {
   require(!isPopulated, "Already Populated");
   for (uint256 i = 0; i < type.length; i++) {
       characters[i] = Character(races[i], strengths[i], agilities[i], hps[i], 0, /*...*/ );
   }
   isPopulated = true;
}

我錯過了什麼 ?有什麼解決辦法呢?感謝您的幫助,文章,程式碼,..我正在接受一切:p

您對這兩種解決方案的理解都是正確的。有幾件事情需要考慮。

解決方案 1

這很容易實現,但正如您所提到的,您將為每筆交易支付汽油費。您可以使用multicall將多個呼叫批處理到單個事務中,但對於大量呼叫來說會非常昂貴。

解決方案 2

這是有道理的,但不可能提供任意大的數據作為輸入。雖然EIP-1985規定calldata最大大小約為 4GB,但您會更早地達到塊氣體限制。

解決方案 3

您可以實施解決方案 2,但採用批處理格式。假設您決定批量大小為 100,您添加了前 100 個令牌的元數據。在下一次呼叫中,您添加下一個 100。您可以根據每個批次的氣體使用量來決定批次大小。這樣,您可以將呼叫次數減少到最低限度,同時保持在氣體限制以下。

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