Skip to content

Instantly share code, notes, and snippets.

@mlegls
Created September 14, 2021 06:06
Show Gist options
  • Save mlegls/62feb7656056f83281bdd71c6b785434 to your computer and use it in GitHub Desktop.
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=
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