Created
September 14, 2021 06:06
-
-
Save mlegls/62feb7656056f83281bdd71c6b785434 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity >=0.7.0 <0.9.0; | |
/** | |
* @title Lootbox | |
* @dev Matrix lootbox contract | |
*/ | |
contract Lootbox { | |
enum TokenType{ NULL, NONFUNGIBLE, SEMIFUNGIBLE, FUNGIBLE } | |
struct LootboxItem { | |
TokenType tokenType; | |
string name; | |
uint32 unitWeight; | |
bool pity; | |
} | |
// maps id to rate ranges | |
mapping(uint => uint32[2]) public normalRates; | |
mapping(uint => uint32[2]) public pityRates; | |
LootboxItem[] public pool; | |
function _fakeRandint(uint range_end) private view returns (uint randint_) { | |
randint_ = uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty))) % (range_end + 1); | |
} | |
function _removeItem(uint _index) private { | |
LootboxItem storage toRemove = pool[_index]; | |
// change ranges of all other items first because unitWeight may be deleted | |
for (uint i=_index+1; i<pool.length; i++) { | |
if (pool[i].tokenType == TokenType.NULL) continue; | |
normalRates[_index][0] -= toRemove.unitWeight; | |
normalRates[_index][1] -= toRemove.unitWeight; | |
if (pool[i].pity) { | |
pityRates[_index][0] -= toRemove.unitWeight; | |
pityRates[_index][1] -= toRemove.unitWeight; | |
} | |
} | |
// remove relevant item | |
if (toRemove.tokenType == TokenType.SEMIFUNGIBLE) { | |
if (normalRates[_index][1] >= normalRates[_index][0] + toRemove.unitWeight) { | |
normalRates[_index][1] -= toRemove.unitWeight; | |
if (toRemove.pity) pityRates[_index][1] -= toRemove.unitWeight; | |
} else { | |
pool[_index].tokenType = TokenType.NULL; | |
} | |
} else if (toRemove.tokenType == TokenType.NONFUNGIBLE) { | |
pool[_index].tokenType = TokenType.NULL; | |
} else revert("Trying to remove null item"); | |
} | |
function addNft(string memory _name, uint32 _weight, bool _pity) public { | |
uint32 rangeEnd = (pool.length>0) ? normalRates[pool.length-1][1] + 1 : 1; | |
LootboxItem memory item = LootboxItem({tokenType: TokenType.NONFUNGIBLE, name: _name, unitWeight: _weight, pity: _pity}); | |
pool.push(item); | |
normalRates[pool.length-1] = [rangeEnd, rangeEnd+_weight-1]; | |
if (_pity) { | |
uint32 pityRangeEnd = (pool.length>0) ? pityRates[pool.length-1][1] + 1 : 1; | |
pityRates[pool.length-1] = [pityRangeEnd, pityRangeEnd+_weight-1]; | |
} | |
} | |
function addNft(string memory _name, uint32 _weight) public { | |
addNft(_name, _weight, false); | |
} | |
function addSft(string memory _name, uint32 _weight, uint32 _qty, bool _pity) public { | |
uint32 rangeEnd = (pool.length>0) ? normalRates[pool.length-1][1] + 1 : 1; | |
LootboxItem memory item = LootboxItem({tokenType: TokenType.SEMIFUNGIBLE, name: _name, unitWeight: _weight, pity: _pity}); | |
pool.push(item); | |
normalRates[pool.length-1] = [rangeEnd, rangeEnd+(_weight*_qty)-1]; | |
if (_pity) { | |
uint32 pityRangeEnd = (pool.length>0) ? pityRates[pool.length-1][1] + 1 : 1; | |
pityRates[pool.length-1] = [pityRangeEnd, pityRangeEnd+(_weight*_qty)-1]; | |
} | |
} | |
function addSft(string memory _name, uint32 _weight, uint32 _qty) public { | |
addSft(_name, _weight, _qty, false); | |
} | |
function _open() private returns (LootboxItem memory item_) { | |
uint rangeEnd; | |
for (uint i=pool.length-1; i>=0; i--) { | |
if (pool[i].tokenType == TokenType.NULL) continue; | |
if (i==0 && pool[0].tokenType == TokenType.NULL) revert("Box empty"); | |
rangeEnd = normalRates[i][1]; | |
break; | |
} | |
uint32 randIndex = uint32(_fakeRandint(rangeEnd-1)) + 1; | |
for (uint i=0; i<pool.length; i++) { | |
if (pool[i].tokenType == TokenType.NULL) continue; | |
if (randIndex <= normalRates[i][1]) { | |
item_ = pool[i]; | |
_removeItem(i); | |
} | |
} | |
} | |
function _pityOpen() private returns (LootboxItem memory item_) { | |
uint rangeEnd; | |
for (uint i=pool.length-1; i>=0; i--) { | |
if (pool[i].tokenType == TokenType.NULL) continue; | |
if (!pool[i].pity) continue; | |
rangeEnd = pityRates[i][1]; | |
break; | |
} | |
uint32 randIndex = uint32(_fakeRandint(rangeEnd-1)) + 1; | |
for (uint i=0; i<pool.length; i++) { | |
if (pool[i].tokenType == TokenType.NULL) continue; | |
if (!pool[i].pity) continue; | |
if (randIndex <= pityRates[i][1]) { | |
item_ = pool[i]; | |
_removeItem(i); | |
} | |
} | |
} | |
function open() public returns (LootboxItem memory item_) { | |
item_ = _open(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment