Solidity实践-一个简单的盲盒合约

数组操作库,用于删除数组元素,返回一个新数组

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

library ArrayUtil {
    //根据值获取元素在数组中的索引
    function getIdx(uint[] memory arr, uint val) internal pure returns(uint) {
        uint index = type(uint).max;
        for (uint i; i < arr.length; i++){
            if(val == arr[i]){
                index = i;
                break;
            }
        }
        return index;
    }

    //删除指定索引的数组元素
    function removeAtIndex(uint[] memory array, uint index) internal pure returns (uint[] memory) {
        if(index >= array.length){
            return array;
        }
        if(array.length <= 1){
            return new uint[](0);
        }
        uint _len = array.length - 1;
        uint[] memory newArray = new uint[](_len);
        for(uint n; n < newArray.length; n++){
            if(n < index){
                newArray[n] = array[n];
            }else{
                newArray[n] = array[n + 1];
            }
        }
        return newArray;
    }

    // 根据值从数组中删除对应元素
    function removeValue(uint[] memory array, uint value) internal pure returns (uint[] memory) {
        uint idx = getIdx(array, value);
        return removeAtIndex(array, idx);
    }
}

盲盒合约

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./library/ArrayUtil.sol";

contract BlindBox {
    using SafeMath for uint;
    using Address for address;
    using SafeERC20 for IERC20;

    address public factory;

    uint private _rand = 1;
    address private feeTo; // 购买盲盒的费用接收地址
    address private settleToken; // 结算token
    // 记录盲盒中的token以及剩余总量
    struct ShardStore {
        address token;
        uint256 amount;
    }
    mapping(uint256 => ShardStore) public shardStore;
    // 记录盲盒不同id盒子每次发放的数量
    mapping(uint256 => uint256) public rewardAmount; // 每次发放数量 idx => amount
    uint256 public price; // 盲盒价格

    event Buy(uint256, uint256);

    constructor() {
        factory = msg.sender;
    }

    // 生成指定范围随机数
    function _getRandom(uint256 _start, uint256 _end) private returns(uint256) {
        if(_start == _end){
            return _start;
        }
        uint256 _length = _end - _start;
        uint256 random = uint256(keccak256(abi.encodePacked(block.difficulty, block.timestamp, _rand)));
        random = random % _length + _start;
        _rand++;
        return random;
    }

    // 购买盲盒
    function buy() external virtual returns(uint256 shardId){
        require(settleToken != address(0), 'Tips: 0001');
        require(feeTo != address(0), 'Tips: 0003');
        // 生成1-100的随机数
        uint256 rd = _getRandom(0, 100);
        // 74%机率买到0号盒子
        if(rd >= 98 && rd < 100){
            // 2%机率买到盒子3
            shardId = 3;
        } else if(rd >= 92 && rd < 98){
            // 6%机率买到盒子2
            shardId = 2;
        } else if(rd >= 74 && rd < 92){
            // 18%机率买到盒子1
            shardId = 1;
        }
        // 根据id获取盒子信息
        ShardStore memory _shardStore = shardStore[shardId];
        // 验证盒子对应发放数量>0
        require(rewardAmount[shardId] > 0, 'Tips: 0004');
        // 验证价格>0
        require(price > 0, 'Tips: 0002');
        // 验证盒子中token数量>发放数量
        require(_shardStore.amount >= rewardAmount[shardId], 'Tips: 0001');
        // 支付到feeTo
        uint256 amount = price;
        IERC20(settleToken).safeTransferFrom(msg.sender, feeTo, amount);
        // 发放盲盒token
        IERC20(_shardStore.token).safeTransfer(msg.sender, rewardAmount[shardId]);
        // 盒子中剩余token数量-发放数量
        shardStore[shardId].amount -= rewardAmount[shardId];

        emit Buy(shardId, rewardAmount[shardId]);
    }

    // 往盒子中放入token,shardId的值为 0-3
    function addShard(uint256 shardId, address token, uint256 amount) external virtual returns(bool){
        require(msg.sender == factory, 'Tips: 0008');
        require(shardStore[shardId].amount == 0, 'Tip: 0001');
        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        shardStore[shardId] = ShardStore(token, amount);

        return true;
    }

    // 撤出盒子中剩余的token
    function removeShard(uint256 shardId) external virtual returns(bool){
        require(msg.sender == factory, 'Tips: 0008');
        if(shardStore[shardId].amount > 0){
            IERC20(shardStore[shardId].token).safeTransfer(msg.sender, shardStore[shardId].amount);
        }
        delete shardStore[shardId];

        return true;
    }

    // 设置费用接收地址feeTo
    function setFeeTo(address _feeTo) external virtual returns(bool){
        require(msg.sender == factory, 'Tips: 0008');
        feeTo = _feeTo;
        return true;
    }

    // 设置结算token
    function setSettleToken(address _settleToken) external virtual returns(bool){
        require(msg.sender == factory, 'Tips: 0009');
        settleToken = _settleToken;
        return true;
    }

    // 设置盲盒价格和不同盒子每次发放数量
    function setPrice(uint256 _price, uint256 _idx, uint256 _rewardAmount) external virtual returns(bool){
        require(msg.sender == factory, 'Tips: 0009');
        price = _price;
        rewardAmount[_idx] = _rewardAmount;
        return true;
    }

    // 重置发放数量为0
    function removeRewardAmount(uint256 _idx) external virtual returns(bool){
        require(msg.sender == factory, 'Tips: 0009');
        delete rewardAmount[_idx];
        return true;
    }

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值