161 lines
4.1 KiB
Solidity
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
|
|
)
|
|
);
|
|
}
|
|
}
|