becrypto/contracts/logic/MysteryBoxProxy.sol
2022-08-18 10:54:42 +08:00

161 lines
4.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "../interfaces/IBEERC721.sol";
import "../core/HasSignature.sol";
contract MysteryBoxProxy is Ownable, Initializable, HasSignature {
IBEERC721 public box;
IBEERC721 public hero;
IBEERC721 public equip;
IBEERC721 public chip;
uint8 public constant TYPE_NONE = 0;
uint8 public constant TYPE_HERO = 1;
uint8 public constant TYPE_EQUIP = 2;
uint8 public constant TYPE_CHIP = 3;
address public executor;
mapping(bytes => bool) public usedSignatures;
event TokenMinted(
address contractAddress,
address to,
uint256 indexed tokenId
);
event BoxOpened(
address indexed to,
uint256 indexed boxId,
uint256 val,
uint256[3] ids,
uint8[3] types
);
constructor() HasSignature("MysteryBoxProxy", "1") {}
function init(address[4] calldata _erc721s) external initializer onlyOwner {
hero = IBEERC721(_erc721s[0]);
equip = IBEERC721(_erc721s[1]);
chip = IBEERC721(_erc721s[2]);
box = IBEERC721(_erc721s[3]);
}
/**
* @dev update executor
*/
function updateExecutor(address account) external onlyOwner {
require(account != address(0), "address can not be zero");
executor = account;
}
function mintBoxTo(address to, uint256 tokenId) external onlyOwner {
require(to != address(0), "to address can not be zero");
box.mint(to, tokenId);
emit TokenMinted(address(box), to, tokenId);
}
function openBox(
uint256 boxId,
uint256[3] calldata ids,
uint256 saltNonce,
bytes calldata signature
) external {
require(ids.length == 3, "MysteryBoxProxy: amount of token id mismatch");
require(
!usedSignatures[signature],
"MysteryBoxProxy: signature used. please send another transaction with new signature"
);
address owner = msg.sender;
require(
box.ownerOf(boxId) == owner,
"MysteryBoxProxy: only owner can open this box"
);
bytes32 criteriaMessageHash = getMessageHash(
boxId,
ids[0],
ids[1],
ids[2],
saltNonce
);
checkSigner(executor, criteriaMessageHash, signature);
// open box
box.burn(owner, boxId);
usedSignatures[signature] = true;
uint256[3] memory results = [ids[0], 0, 0];
uint8[3] memory types = [TYPE_HERO, TYPE_NONE, TYPE_NONE];
mint721WithType(owner, ids[0], types[0]);
results[0] = ids[0];
uint256 val = rand(owner, saltNonce, 100);
if (val >= 70 && val < 90) {
types[1] = TYPE_CHIP;
mint721WithType(owner, ids[1], types[1]);
results[1] = ids[1];
} else if (val >= 90 && val < 98) {
types[1] = TYPE_EQUIP;
mint721WithType(owner, ids[1], types[1]);
results[1] = ids[1];
} else if (val >= 98) {
types[1] = TYPE_EQUIP;
mint721WithType(owner, ids[1], types[1]);
results[1] = ids[1];
types[2] = TYPE_CHIP;
mint721WithType(owner, ids[2], types[2]);
results[2] = ids[2];
}
emit BoxOpened(owner, boxId, val, results, types);
}
function mint721WithType(
address to,
uint256 tokenId,
uint256 typeNum
) private {
if (typeNum == 1) {
hero.mint(to, tokenId);
} else if (typeNum == 2) {
equip.mint(to, tokenId);
} else if (typeNum == 3) {
chip.mint(to, tokenId);
}
}
function rand(
address owner,
uint256 nonce,
uint256 _length
) internal view returns (uint256) {
uint256 random = uint256(
keccak256(
abi.encodePacked(owner, nonce, block.difficulty, block.timestamp)
)
);
return random % _length;
}
function getMessageHash(
uint256 _boxId,
uint256 _firstToken,
uint256 _secondToken,
uint256 _thirdToken,
uint256 _saltNonce
) public pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
_boxId,
_firstToken,
_secondToken,
_thirdToken,
_saltNonce
)
);
}
}