数组操作库,用于删除数组元素,返回一个新数组
// 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;
}
}