creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/UpbitRandomGenerator.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\n/*\n   __  __      __    _ __\n  / / / /___  / /_  (_) /_\n / / / / __ \\/ __ \\/ / __/\n/ /_/ / /_/ / /_/ / / /_\n\\____/ .___/_.___/_/\\__/\n    /_/\n    ____                  __\n   / __ \\____ _____  ____/ /___  ____ ___\n  / /_/ / __ `/ __ \\/ __  / __ \\/ __ `__ \\\n / _, _/ /_/ / / / / /_/ / /_/ / / / / / /\n/_/ |_|\\__,_/_/ /_/\\__,_/\\____/_/ /_/ /_/\n\n   ______                           __\n  / ____/__  ____  ___  _________ _/ /_____  _____\n / / __/ _ \\/ __ \\/ _ \\/ ___/ __ `/ __/ __ \\/ ___/\n/ /_/ /  __/ / / /  __/ /  / /_/ / /_/ /_/ / /\n\\____/\\___/_/ /_/\\___/_/   \\__,_/\\__/\\____/_/\n\n*/\n\n/**\n * @title UpbitRandomGenerator\n * @dev Generate approximated random numbers by blockhash\n */\ncontract UpbitRandomGenerator {\n    /*\n     * @dev Mapping of block number to block hash\n     * @notice This is used to store block hashes for generating random numbers\n     *        as blockhash can only be used for the most recent 256 blocks\n     */\n    mapping(uint256 => bytes32) public cachedBlockHashes;\n\n    event RandomNumbers(\n        uint256 indexed max,\n        uint256 indexed count,\n        uint256 indexed blockNumber,\n        string salt,\n        uint256[] randomNumbers\n    );\n\n    error AlreadyCachedBlockHash();\n    error InvalidCount();\n    error InvalidBlockNumber();\n\n    receive() external payable {\n        // solhint-disable-next-line reason-string\n        revert();\n    }\n\n    /**\n     * @notice Record random numbers\n     * @param max Maximum number to generate random numbers for\n     * @param count Number of random numbers to generate\n     * @param blockNumber Block number to use for generating random numbers\n     * @param salt Salt to use for generating random numbers\n     * @dev This function generates random numbers and emits an event with the generated random numbers\n     */\n    function recordRandomNumbers(\n        uint256 max,\n        uint256 count,\n        uint256 blockNumber,\n        string calldata salt\n    ) external {\n        if (cachedBlockHashes[blockNumber] == bytes32(0))\n            cacheBlockhash(blockNumber);\n\n        emit RandomNumbers(\n            max,\n            count,\n            blockNumber,\n            salt,\n            getRandomNumbers(max, count, blockNumber, salt)\n        );\n    }\n\n    /**\n     * @notice Check if my number is a winner\n     * @param max Maximum number to generate random numbers for\n     * @param count Number of random numbers to generate\n     * @param blockNumber Block number to use for generating random numbers\n     * @param salt Salt to use for generating random numbers\n     * @param myNumber My number to check if it is a winner\n     * @return bool True if the winning number is in the random numbers\n     */\n    function checkIfImWinner(\n        uint256 max,\n        uint256 count,\n        uint256 blockNumber,\n        string calldata salt,\n        uint256 myNumber\n    ) external view returns (bool) {\n        uint256[] memory randomNumbers = getRandomNumbers(\n            max,\n            count,\n            blockNumber,\n            salt\n        );\n        for (uint256 i = 0; i < count; i++) {\n            if (randomNumbers[i] == myNumber) return true;\n        }\n        return false;\n    }\n\n    /**\n     * @notice Store block hash for generating random numbers\n     * @param blockNumber Block number to store block hash for\n     * @dev This function stores block hash for generating random numbers. This function was designed\n     * to be called internally, but it can also be called externally if necessary, such as when it needs\n     * to be invoked in advance and recordRandomNumbers is to be executed later.\n     */\n    function cacheBlockhash(uint256 blockNumber) public {\n        if (blockhash(blockNumber) == bytes32(0)) revert InvalidBlockNumber();\n        if (cachedBlockHashes[blockNumber] != bytes32(0))\n            revert AlreadyCachedBlockHash();\n        cachedBlockHashes[blockNumber] = blockhash(blockNumber);\n    }\n\n    /**\n     * @notice Generate random numbers\n     * @param max Maximum number to generate random numbers for\n     * @param count Number of random numbers to generate\n     * @param blockNumber Block number to use for generating random numbers\n     * @param salt Salt to use for generating random numbers\n     * @return randomNumbers Array of random numbers\n     * @dev This function generates random numbers using block hash and salt. It uses bitwise operations for\n     * memory efficiency.\n     */\n    function getRandomNumbers(\n        uint256 max,\n        uint256 count,\n        uint256 blockNumber,\n        string calldata salt\n    ) public view returns (uint256[] memory) {\n        if (count == 0 || max < count) revert InvalidCount();\n\n        bytes32 blockHash = cachedBlockHashes[blockNumber];\n        if (blockHash == bytes32(0)) revert InvalidBlockNumber();\n\n        uint256 maxNumber = max + 1;\n        uint256 nonce = 0;\n        bytes32 saltBytes = keccak256(abi.encodePacked(salt));\n\n        uint256[] memory randomNumbers = new uint256[](count);\n        /**\n         * @dev Local array to store drawn numbers, optimized for memory efficiency.\n         * Each uint256 is treated as like an array of 256 boolean values,\n         * with each bit representing whether a number has been drawn (1) or not (0).\n         * randomNumbersBitwise[0] is used to store the drawn number 0-255\n         * randomNumbersBitwise[1] is used to store the drawn number 256-511\n         * randomNumbersBitwise[2] is used to store the drawn number 512-767\n         * and so on\n         */\n        uint256[] memory randomNumbersBitwise = new uint256[](\n            (maxNumber >> 8) + 1\n        );\n\n        // Exclude 0 from the random numbers\n        randomNumbersBitwise[0] = 1;\n        for (uint256 i = 0; i < count; ) {\n            uint256 draw;\n\n            /*\n             * Below assembly code is used to generate random numbers with the following steps:\n             * 1. Load the block hash, count, i, nonce, and salt bytes into memory\n             * 2. Generate a random number using keccak256 hash of the memory\n             * 3. Calculate the index and mask for the randomNumbersBitwise array\n             * 4. Load the drawn number from the randomNumbersBitwise array\n             * 5. Check if the number has been drawn\n             * 6. If the number has not been drawn, store the number in the randomNumbers array\n             * 7. If the number has been drawn, increment the nonce, and repeat the process\n             * 8. Repeat the process until the required number of random numbers are generated\n             */\n            assembly {\n                let m := mload(0x40)\n                mstore(m, blockHash)\n                mstore(add(m, 0x20), count)\n                mstore(add(m, 0x40), i)\n                mstore(add(m, 0x60), nonce)\n                mstore(add(m, 0x80), saltBytes)\n                draw := mod(keccak256(m, 0xa0), maxNumber)\n\n                let index := add(shr(8, draw), 1)\n                let mask := shl(and(draw, 0xff), 1)\n                let drawnNumber := mload(\n                    add(randomNumbersBitwise, mul(0x20, index))\n                )\n\n                switch and(drawnNumber, mask)\n                case 0 {\n                    mstore(add(randomNumbers, mul(0x20, add(i, 1))), draw)\n                    mstore(\n                        add(randomNumbersBitwise, mul(index, 0x20)),\n                        or(drawnNumber, mask)\n                    )\n                    i := add(i, 1)\n                }\n                default {\n                    nonce := add(nonce, 1)\n                }\n            }\n        }\n        return randomNumbers;\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 1000000
      },
      "viaIR": true,
      "evmVersion": "paris",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "metadata": {
        "useLiteralContent": true
      },
      "libraries": {}
    }
  }}