pragma solidity ^0.4.24; import "./AlphabetToken.sol"; import "openzeppelin-solidity/contracts/token/ERC20/BasicToken.sol"; import "openzeppelin-solidity/contracts/token/ERC20/BurnableToken.sol"; import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract AlphabetPack is BasicToken, BurnableToken, Ownable { AlphabetToken alphabetToken; string public constant NAME = "Alphabet Pack Token"; string public constant SYMBOL = "PAC"; uint8 public constant DECIMALS = 18; uint16 public constant INITIAL_SUPPLY = 13000; uint8 public constant NFTS_PER_PACK = 3; uint16 public constant MAX_BLOCKS = 256; event PackPurchased(address to); event PackUnwrapped(uint256[] tokenIds); uint packFee = 0.001 ether; uint unwrapCount; mapping(address => uint32) unwrapRandomNumbers; constructor() public { // TODO: require(((NFT_SUPPLY * 26) / 3) == packSupply); alphabetToken = new AlphabetToken(address(this)); string memory error = "NFT Supply must be divisible by the supply of Packs"; require(((alphabetToken.NFT_SUPPLY() * alphabetToken.ALPHABET_SIZE()) / NFTS_PER_PACK) == INITIAL_SUPPLY, error); totalSupply_ = INITIAL_SUPPLY; balances[address(this)] = INITIAL_SUPPLY; unwrapCount = 0; } function() external payable { buy(msg.value / packFee); } function withdraw() external onlyOwner returns(bool) { owner.transfer(address(this).balance); return true; } function setPackFee(uint _fee) external onlyOwner { packFee = _fee; } function name() public pure returns(string) { return NAME; } function symbol() public pure returns(string) { return SYMBOL; } function decimals() public pure returns(uint8) { return DECIMALS; } function beginUnwrap() public { require(balanceOf(msg.sender) > 0, "Must have at least one pack"); // Burn one token to stake burn(1); unwrapRandomNumbers[msg.sender] = uint32(block.number); unwrapCount = unwrapCount.add(1); } function endUnwrap() public { require(block.number > unwrapRandomNumbers[msg.sender] + NFTS_PER_PACK, "Must have enough blocks available for 3 packs"); require(block.number < unwrapRandomNumbers[msg.sender] + MAX_BLOCKS, "Must be within 256 future blocks"); uint256[] memory randNums = new uint256[](NFTS_PER_PACK); uint16[] memory counts = getAlphabetCountArr(); for (uint i = 0; i < randNums.length; i++) { uint16 totalWeight = 0; for (uint j = 0; j < counts.length; j++) { totalWeight += 1000 - counts[j]; } bytes32 rand = blockhash(unwrapRandomNumbers[msg.sender] + i); bytes32 output = keccak256(abi.encodePacked(rand, unwrapCount)); uint256 random = uint256(output) % totalWeight; for (uint k = 0; k < counts.length; k++) { random -= 1000 - counts[k]; if (random < 0) { counts[k] = counts[k] + 1; randNums[i] = k; break; } } } /* for (uint i = 0; i < randNums.length; i++) { randNums[i] = (uint256(blockhash(unwrapRandomNumbers[msg.sender] + i + unwrapCount)) % alphabetToken.ALPHABET_SIZE()); } */ unwrap(randNums); } function buy(uint256 count) public payable { require(msg.value >= packFee * count, "Must send the correct amount per pack"); require(balanceOf(address(this)) > 0, "Contract must have enough packs left"); balances[address(this)] = balances[address(this)].sub(count); balances[msg.sender] = balances[msg.sender].add(count); emit PackPurchased(msg.sender); } function getTokensArray(address _owner) public view returns (uint256[]) { return alphabetToken.nonFungiblesOfOwnerByIndex(_owner); } function getOwnerBalance(address _owner) public view returns (uint256) { return alphabetToken.nonFungiblesOfOwnerByIndex(_owner).length; } function getSupply() public view returns (uint256 total) { total = alphabetToken.totalSupply(); } function getAlphabetSupply(uint256 _letter) public view returns (uint16) { return alphabetToken.getAlphabetCount(_letter); } function getAlphabetCountArr() public view returns (uint16[]) { return alphabetToken.getCountArray(); } function getCountPacksLeft() public view returns (uint256) { return balanceOf(address(this)); } function unwrap(uint256[] randNums) internal { for (uint i = 0; i < randNums.length; i++) { require(getAlphabetSupply(randNums[i]) <= alphabetToken.NFT_SUPPLY(), "Must have enough letters to mint"); } for (uint j = 0; j < randNums.length; j++) { alphabetToken.mint(msg.sender, randNums[j]); } emit PackUnwrapped(randNums); } }
0.4.24