6

I want to create a new string array with elements from another array. After that, my randomization will go through this new array. What is the best way to do this?

Here's example code piece:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract EXAMPLE is ERC721 {
    string[] public assets;
    constructor() ERC721("Example", "EXMP") {
        assets = ["sword","arrow","shield","cape"];
    }
    function attack() public view returns(uint256 damage){
        string[] memory weapons = assets[0:2];
    }
}

I receive two error messages:

Type string storage ref[] storage ref slice is not implicitly convertible to expected type string memory[] memory.

Index range access is only supported for dynamic calldata arrays.

Note that:

  • I do not want to store this new array in storage
  • But even if I made it string[] storage, it still gives the same error: Type string storage ref[] storage ref slice is not implicitly convertible to expected type string storage ref[] storage pointer.
  • I tried adding string(assets[0:2]) which also didn't work.
  • I tried adding arguments to the function, but that brought up different error messages.

I'm open to completely different approaches too, as long as they're cheap on storage & EVM.

2
  • You will need to loop over your array and assign the values to your new array. Slices are only supported for calldata arrays.
    – Richard
    Jan 15, 2022 at 22:26
  • @Richard thanks. Damn this sounds pretty expensive.. Any ideas on how to do this cheaper (even if not via arrays)? Thought about mapping but not sure how that'd look like.
    – Chainleft
    Jan 15, 2022 at 23:53

2 Answers 2

5

The majority of gas cost is caused by SLOADing the values saved in storage. There's nothing you can do to prevent that if you want to read from storage.

Having acknowledged that, a loop is the simplest solution:

    function attack() public view returns(uint256 damage){
        string[] memory weapons = new string[](3);
        for (uint256 i; i < 3; ) {
            weapons[i] = assets[i];
            unchecked {++i;}
        }
    }
3

As the documentation states "As of now, array slices are only implemented for calldata arrays.", we can only use array slicing with arrays passed as calldata to our function. This is ideal in case we want to slice msg.data passed to our function to have more control over it. Like extracting a function signature from the passed data, slicing the passed data into desired chunks, etc.

In the future Solidity may allow array slicing in memory and storage arrays.

So, right now, the best we can do to copy a range of a storage array is, as RiccardoS says, to loop through it and make the copy manually.

1
  • Is it possible to do it with assembly?
    – Ryan Sea
    Apr 22, 2023 at 17:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.