Repository: CryptoBlades/cryptoblades
File: contracts/Blacksmith.sol (Solidity)

Code before:
        itemFlatPrices[ITEM_WEAPON_RENAME] = 0.1 ether;
        itemFlatPrices[ITEM_CHARACTER_RENAME] = 0.1 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = 0.2 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = 0.2 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = 0.2 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = 0.2 ether;
    }

    function migrateTo_bcdf4c(CBKLandSale _cbkLandSale) external {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");

        cbkLandSale = _cbkLandSale;
    }

    /* ========== VIEWS ========== */

    /* ========== MUTATIVE FUNCTIONS ========== */

    function recoverToken(address tokenAddress, uint256 amount) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");

        IERC20(tokenAddress).safeTransfer(msg.sender, amount);
    }

    function getSeed(uint seedId) internal pure returns (uint256 seed) {
        seed = RandomUtil.combineSeeds(seedId, uint(1));
    }

    function hasSeed(uint seedId) public view returns (bool) {
        return SafeRandoms(links[LINK_SAFE_RANDOMS]).hasSingleSeedRequest(tx.origin, getSeed(seedId));
    }

    function generateSeed() public {
        require(itemFlatPrices[ITEM_SHIELD] > 0);
        payCurrency(msg.sender, itemFlatPrices[ITEM_SHIELD], CURRENCY_SKILL);
        SafeRandoms(links[LINK_SAFE_RANDOMS]).requestSingleSeed(tx.origin, getSeed(uint(SHIELD_SEED)));
    }

    function purchaseShield() public {
        uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];
        if(shieldType != 0) {
            require(numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] > 0);
            numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] -= 1;
        }
        uint256 seed = SafeRandoms(links[LINK_SAFE_RANDOMS]).popSingleSeed(msg.sender, getSeed(uint(SHIELD_SEED)), true, false);
        shields.mint(msg.sender, shieldType, seed);
    }

    /* ========== MODIFIERS ========== */

    modifier onlyGame() {

Review comment (suggestion):
Just realised, that we technically have the ability to offer different types of shield purchases (1 type at a time atm), so we ought to mix the type number into the seed too.

numberParameters[VAR_PURCHASE_SHIELD_TYPE]
Should be mixed into the seed generation. The currently used function takes two arguments, there are a few different ways to get around this:
1. there's a function that takes an array, it may be a bit fiddly to pass an array though
2. we could add more functions with more params, i believe this won't inflate existing contracts as we're touching a library, only referenced functions get included AFAIK
3. using keccak256(abi.encodePacked(<any number of arguments>)) directly, instead of the call to the library
4. a nested call ie RandomUtil.combineSeeds(uint(seedId,RandomUtil.combineSeeds(1,shieldType)), a bit hacky perhaps but it works. Very important that the orders of params need to be laid out in the same order if reused elsewhere

Worth mentioning that the array solution and calling abi.encodepacked result in the same output, while the nested combination doesn't.
<img width="1197" alt="2022-08-22_15-02-00_mstsc" src="https://user-images.githubusercontent.com/31659034/185927914-9ad9ee18-94d0-41f1-b25d-e39b8fba46a3.png">

I have no preference, and I couldn't find examples of us packing more than 3 at once (only iteratively like for mintN etc, that's a different use case). Use whichever you like

EDIT: Derp, realised you did the array solution below. Might as well use it here i guess

Code after:
        itemFlatPrices[ITEM_WEAPON_RENAME] = 0.1 ether;
        itemFlatPrices[ITEM_CHARACTER_RENAME] = 0.1 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_FIRE] = 0.2 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_EARTH] = 0.2 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_WATER] = 0.2 ether;
        itemFlatPrices[ITEM_CHARACTER_TRAITCHANGE_LIGHTNING] = 0.2 ether;
    }

    function migrateTo_bcdf4c(CBKLandSale _cbkLandSale) external {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");

        cbkLandSale = _cbkLandSale;
    }

    /* ========== VIEWS ========== */

    /* ========== MUTATIVE FUNCTIONS ========== */

    function recoverToken(address tokenAddress, uint256 amount) public {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Not admin");

        IERC20(tokenAddress).safeTransfer(msg.sender, amount);
    }

    function getSeed(uint seedId, uint shieldType) internal pure returns (uint256 seed) {
        uint[] memory seeds = new uint[](3);
        seeds[0] = seedId;
        seeds[1] = uint(1);
        seeds[2] = shieldType;
        seed = RandomUtil.combineSeeds(seeds);
    }

    function hasSeed(uint seedId, uint shieldType) public view returns (bool) {
        return SafeRandoms(links[LINK_SAFE_RANDOMS]).hasSingleSeedRequest(msg.sender, getSeed(seedId, shieldType));
    }

    function generateShieldSeed() external {
        require(itemFlatPrices[ITEM_SHIELD] > 0);
        uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];
        payCurrency(msg.sender, itemFlatPrices[ITEM_SHIELD], CURRENCY_SKILL);
        SafeRandoms(links[LINK_SAFE_RANDOMS]).requestSingleSeed(msg.sender, getSeed(uint(SHIELD_SEED), shieldType));
    }

    function claimShield() external {
        uint256 shieldType = numberParameters[VAR_PURCHASE_SHIELD_TYPE];
        if(shieldType != 0) {
            require(numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] > 0);
            numberParameters[VAR_PURCHASE_SHIELD_SUPPLY] -= 1;
        }
        uint256 seed = SafeRandoms(links[LINK_SAFE_RANDOMS]).popSingleSeed(msg.sender, getSeed(uint(SHIELD_SEED), shieldType), true, false);
        shields.mint(msg.sender, shieldType, seed);