Skip to main content

Lootbox

Lootbox

Contract module that enables implementation of unlockable lootboxes by routing unlock requests to a number of deployed Crafter contracts. By configuring multiple Crafter contracts that take in the lootbox (typically some ERC721 token) as their sole input and each have different output asset bundles, a developer can route the output of a certain lootbox to a myriad of possible output bundles, effectively designating the inputted token as a lootbox.

Configuration for Crafter contracts is designated by two {Ingredient}[]. One array is the inputs and the other is the outputs. The contract allows for the inputs to be redeemed for the outputs, craftableAmount times.

struct Ingredient {
TokenType token;
ConsumableType consumableType;
address contractAddr;
uint256[] amounts;
uint256[] tokenIds;
}

Configuration is set in the initializers and cannot be edited once the contract has been launched Other configurations will require their own contract to be deployed

However, craftableAmount can be dynamically updated through the {deposit} and {withdraw} functions which are only accessible to DEFAULT_ADMIN_ROLE

Each Ingredient has a consumableType field.* This field is for the inputs elements and ignored by the outputs elements. ERC20 and ERC1155 inputs elements can be unaffected or burned. unaffected will check for ownership/balance while burned will send the asset(s) to the burnAddress. ERC721 inputs can be NTime or burned. NTime allows for a specfic tokenId to only be used 'n times', as defined by contract deployer.

ERC20 inputs and outputs elements should have one number in the amounts array denoting ERC20 token amount requirement.tokenIds should be empty. NTime consumable type ERC721 inputs should have empty tokenIds and amounts[0] equal to n - the maximum number of times the input can be used. Burned ERC721 inputs elements should have empty amounts and tokenIds array. This contract accepts all tokenIds from an ERC721 contract as inputs. ERC721 outputs elements must have empty amounts array. tokenIds array length should be craftableAmount. The tokenIds array will contain the tokenIds to be transferred out when {craft} is called. Important to note that output transfers will be from the end of the array since .pop() is used.

ERC1155 inputs and outputs elements should have the length of amounts and tokenIds array be the same. The indices will be linked where each index denotes how much of each ERC1155 tokenId is required.

This module is used through composition. It can be deployed to create crafting logic with asset contracts that are already on chain and active; plug-and-play, so to speak.

Configuration for a lootbox requires the developer enter _crafterContracts, an address array of deployed Crafter contracts that makes up the pool of potential contracts routed to.* The probabilities array must be of the same length, as it describes the probability distribution of the random variable that determines a randomly chosen contract.

A VRFBeacon is deployed and used to inject randomness, while a Chainlink Keeper is used to autonomously check if a random number has been returned by the off-chain VRFBeacon coordinator. Upon receiving a random number, the Keeper then calls the _unlock() function to randomly select a Crafter contract and call its craft() function.

version

string version

ERC165TAG

bytes4 ERC165TAG

crafterContracts

address[] crafterContracts

probabilities

uint256[] probabilities

vrfBeacon

address vrfBeacon

lootboxIdToEpochBlock

mapping(uint256 => uint256) lootboxIdToEpochBlock

queueIndex

uint256 queueIndex

upkeepQueue

uint256[] upkeepQueue

constructor

constructor() public

initialize

function initialize(address _admin, address[] _crafterContracts, uint8[] _probabilities, address _vrfBeacon, address _forwarder) external

Create Lootbox instance

NameTypeDescription
_adminaddressthe admin/owner of the contract
_crafterContractsaddress[]array of crafterContract address, each with unique recipe
_probabilitiesuint8[]array of cumulative probabilities associated with using a contract from crafterContracts
_vrfBeaconaddress
_forwarderaddressaddress for trusted forwarder for open GSN integration

proxyInitialize

function proxyInitialize(address _admin, address[] _crafterContracts, uint8[] _probabilities, address _vrfBeacon, address _forwarder) external

Initializes contract through beacon proxy (replaces constructor in proxy pattern)

__Lootbox_init

function __Lootbox_init(address _admin, address[] _crafterContracts, uint8[] _probabilities, address _vrfBeacon, address _forwarder) internal

performs validations that _inputs are valid and creates the configuration

__Lootbox_init_unchained

function __Lootbox_init_unchained(address[] _crafterContracts, uint8[] _probabilities, address _vrfBeacon) internal

performs validations that _inputs and _outputs are valid and creates the configuration

requestUnlock

function requestUnlock(uint256 lootboxId) external returns (uint256 requestId, uint256 epochBlockNumber)

Makes a request for random number from VRFBeacon, adds unlock request to queue - specific to lootbox token id

NameTypeDescription
lootboxIduint256the token ID of the lootbox asset to be unlocked
NameTypeDescription
requestIduint256the id given to the request by the VRFBeacon
epochBlockNumberuint256the epoch block number at the time request is made

checkUpkeep

function checkUpkeep(bytes) external view returns (bool upkeepNeeded, bytes performData)

Run by Chainlink Keeper to check whether random number has been returned by VRFBeacon

NameTypeDescription
upkeepNeededboolwhether a random number has been returned
performDatabytesencoding of random number and queue index

performUpkeep

function performUpkeep(bytes performData) external

Run by Chainlink Keeper to perform unlock on a lootbox id requiring upkeep

NameTypeDescription
performDatabytesencoding of random number and queue index via checkUpkeep

_unlock

function _unlock(uint256 lootboxId, uint256 randomSeed) internal

Internal function called by performUpkeep() to call randomly-selected Crafter contract

NameTypeDescription
lootboxIduint256the id of the lootbox to unlock
randomSeeduint256the random seed generated by the VRFBeacon to be used to source a random number

getEpochBlock

function getEpochBlock(uint256 lootboxId) public view returns (uint256)

Used for testing only

Returns epoch block during which requestUnlock was called on a lootbox ID

NameTypeDescription
lootboxIduint256the ID of the lootbox to unlock
NameTypeDescription
[0]uint256uint256 the epoch block corresponding to lootboxId

getRandomContract

function getRandomContract(uint256 lootboxId, uint256 randomSeed) external view returns (uint256 selectedContract)

Used for testing only

Returns the randomly selected contract from the contracts array

NameTypeDescription
lootboxIduint256the ID of the lootbox to unlock, used as nonce in random number generation
randomSeeduint256the random seed taken from checkUpkeep
NameTypeDescription
selectedContractuint256the randomly selected contract's index in contracts array

supportsInterface

function supportsInterface(bytes4 interfaceId) public view virtual returns (bool)

ERC165 Support

NameTypeDescription
interfaceIdbytes4hash of the interface testing for
NameTypeDescription
[0]boolbool whether interface is supported