diff --git a/src/tokens/ERC1155/README.md b/src/tokens/ERC1155/README.md index 36b13d8a..c255f4c9 100644 --- a/src/tokens/ERC1155/README.md +++ b/src/tokens/ERC1155/README.md @@ -18,6 +18,37 @@ This folder contains contracts that are pre-configured for specific use cases. The `ERC1155Items` contract is a preset that configures the `ERC1155BaseToken` contract to allow minting of tokens. It adds a `MINTER_ROLE` and a `mint(address to, uint256 amount)` function that can only be called by accounts with the `MINTER_ROLE`. +### Packs + +The `ERC1155Pack` contract is a preset that extends `ERC1155Items` to provide a pack opening mechanism for ERC1155 tokens. It implements a commit-reveal scheme to ensure fair and verifiable pack opening. + +Pack contents are managed by accounts with the `PACK_ADMIN_ROLE` using the `setPacksContent(bytes32 _merkleRoot, uint256 _supply, uint256 packId)` function. The merkle root contains all possible pack contents, and the supply determines how many packs can be opened. + +#### Flow + +The pack opening process works as follows: + +1. User calls `commit(uint256 packId)` to burn their pack and create a commitment +2. After at least one block, anyone may call `reveal(address user, PackContent calldata packContent, bytes32[] calldata proof, uint256 packId)` with a merkle proof of the selected pack content +3. The contract verifies the proof and mints the revealed tokens to the user +4. As a safety feature, if reveal isn't called while the block hash is still available (before it expires), they can call `refundPack(address user, uint256 packId)` to get their pack back + +> [!NOTE] +> By allowing the `reveal` function to be called from anyone, a third party can ensure every committed pack is revealed before the blockhash becomes unaccessible on chain. + +#### Randomization Mechanism + +The pack opening uses a commit-reveal scheme with block hash randomness to ensure fair and unpredictable selection: + +1. **Commitment Phase**: When a user calls `commit(uint256 packId)`, the contract records the current block number plus one (`block.number + 1`) as the commitment block +2. **Random Seed Generation**: During reveal, the contract uses the block hash of the commitment block combined with the user's address to generate a random seed: `keccak256(abi.encode(blockHash, user))` +3. **Index Selection**: The random seed is used to select an index from the remaining available pack contents: `randomSeed % remainingSupply[packId]` +4. **Fisher-Yates Shuffle**: The contract maintains an `_availableIndices` mapping that implements a Fisher-Yates shuffle algorithm, ensuring each pack content can only be selected once and maintaining uniform distribution +5. **Verification**: The selected index corresponds to a specific pack content in the merkle tree. The merkle leaf is constructed as `keccak256(abi.encode(revealIdx, packContent))`, which must be provided as a proof during the reveal phase + +> [!WARNING] +> This randomization technique is susceptible to attacks by entities that can control sequential blocks (such as large mining pools or validators). An attacker with significant hash power could potentially manipulate block hashes to influence the randomness outcome. + ## Utilities This folder contains contracts that work in conjunction with other ERC1155 contracts. @@ -68,3 +99,4 @@ The following roles are defined: | `MINT_ADMIN_ROLE` | Can set minting logic. | `0x4c02318d8c3aadc98ccf18aebbf3126f651e0c3f6a1de5ff8edcf6724a2ad5c2` | | `WITHDRAW_ROLE` | Withdraw tokens from the contract. | `0x5d8e12c39142ff96d79d04d15d1ba1269e4fe57bb9d26f43523628b34ba108ec` | | `IMPLICIT_MODE_ADMIN_ROLE` | Update settings for implicit mode validation. | `0x70649ec320b507febad3e8ef750e5f580b9ae32f9f50d4c7b121332c81971530` | +| `PACK_ADMIN_ROLE` | Can manage pack contents and settings. | `0xbaa5ee745de68a3095827d2ee7dd2043afc932834d02cc1b8be3da78577f6c1a` |