编译器
0.8.23+commit.f704f362
文件 1 的 28:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 28:BurnItDAO.sol
pragma solidity ^0.8.23;
import {ERC721PsiBurnable, ERC721Psi} from "./ERC721Psi/extension/ERC721PsiBurnable.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {PullPayment} from "./OpenZeppelin4/PullPayment.sol";
import {LibPRNG} from "solady/src/utils/LibPRNG.sol";
import {LibString} from "solady/src/utils/LibString.sol";
import {LibBitSet, ILibBitSet64Filter} from "./LibBitSet.sol";
import {LibShared} from "./LibShared.sol";
import {LibConfig} from "./LibConfig.sol";
import {LibGame, GameStatus} from "./LibGame.sol";
import {LibWinners} from "./LibWinners.sol";
import {LibRefundable} from "./LibRefundable.sol";
import {TimeLock} from "./TimeLock.sol";
import {LibUser} from "./LibUser.sol";
import "./Constants.sol";
contract BurnItDAO is
ERC721PsiBurnable,
PullPayment,
Ownable,
ReentrancyGuard,
TimeLock,
ILibBitSet64Filter
{
using LibPRNG for LibPRNG.PRNG;
using LibBitSet for LibBitSet.Set;
using LibGame for LibGame.Game;
using LibUser for LibUser.User;
using LibWinners for LibWinners.Winners;
using LibRefundable for LibRefundable.MintData;
using LibShared for uint256;
using LibConfig for uint256;
using LibGame for uint256;
using LibString for uint256;
using LibShared for uint32;
using LibString for uint8;
uint256 private immutable _tokenPrice;
uint256 private immutable _config;
LibGame.Game private _game;
LibWinners.Winners private _winners;
LibRefundable.MintData private _mints;
mapping(address => LibUser.User) private _users;
uint256 private _claimSeed;
address payable private _teamAddress;
address payable private _drawAddress;
string private _baseTokenURI;
event Claim(address indexed from, uint32 indexed gameRound, uint256 indexed tokenId);
event Commit(address indexed from, uint32 indexed gameRound);
event Reveal(address indexed from, uint32 indexed gameRound);
error ErrorClaimInvalidBurn(uint256 tokenId);
error ErrorClaimInvalidOwner();
error ErrorClaimInvalidToken(uint256 tokenId);
error ErrorClaimInvalidUser();
error ErrorClaimMaxWallet();
error ErrorClaimPermissionDenied();
error ErrorClaimRoundClosed();
error ErrorClaimUnavailable();
error ErrorCommitInvalidUser();
error ErrorDoNotSendDirectEth();
error ErrorDrawAddress();
error ErrorGameNotRunning();
error ErrorInvalidTokenURI();
error ErrorMintComplete();
error ErrorMintExpired();
error ErrorMintMaxTokens();
error ErrorMintMaxWallet();
error ErrorMintNotActive();
error ErrorMintResetting();
error ErrorMintTxAmount();
error ErrorMintTxPrice();
error ErrorNonRefundable();
error ErrorTeamAddress();
error ErrorTokenPrice();
error ErrorTransferDenied();
error ErrorTransferInvalidBalance();
error ErrorTransferInvalidUser();
constructor(
string memory name,
string memory symbol,
uint32 contractId,
uint256 tokenPrice,
uint16 maxTokens,
uint16 maxWallet,
uint16 maxAmount,
uint8 teamSplit,
address payable teamAddress,
address payable drawAddress,
string memory baseTokenURI
)
ERC721Psi(name, symbol)
Ownable(msg.sender)
PullPayment()
TimeLock(MINT_START_LOCK, MINT_LAST_LOCK)
{
if (tokenPrice == 0) revert ErrorTokenPrice();
_tokenPrice = tokenPrice;
_config = LibConfig.initConfig(
tokenPrice,
contractId,
maxTokens,
maxWallet,
maxAmount,
teamSplit);
_claimSeed = uint256(uint160(msg.sender));
_setInternalAddresses(teamAddress, drawAddress);
_setBaseTokenURI(baseTokenURI);
_game.initGame(_nextTokenId());
}
function setWallets(
address payable teamAddress,
address payable drawAddress
) external nonReentrant onlyOwner {
_setInternalAddresses(teamAddress, drawAddress);
}
function setBaseURI(
string memory newURI
) external onlyOwner {
_setBaseTokenURI(newURI);
}
fallback() external {
revert ErrorDoNotSendDirectEth();
}
function mint(
uint16 amount
) external payable nonReentrant {
uint256 gameData = _game.data;
uint32 gameRound = gameData.getGameRound();
GameStatus status = _game.getStatus(gameData);
if (amount > _config.maxAmount()) revert ErrorMintTxAmount();
if (msg.value < _tokenPrice * amount) revert ErrorMintTxPrice();
if (block.timestamp <= _game.virtualResetEndTime(status)) revert ErrorMintResetting();
if (_isGameExpired(status)) revert ErrorMintExpired();
if (gameData.gameState() != GAME_STATE_OFFLINE && status > GameStatus.RUNNING) {
if (gameData.resetEndTime() == 0) {
_finalizeGame(gameRound);
}
gameRound = _game.resetGame(_nextTokenId());
} else if (status != GameStatus.MINTING) revert ErrorMintNotActive();
unchecked {
LibUser.User storage user = _users[msg.sender];
uint256 userData = user.initUser(msg.sender, gameRound);
if (userData.getLiveCount() + amount > _config.maxWallet()) revert ErrorMintMaxWallet();
LibBitSet.Set storage gameTokens = _game.tokens[MINT_LIVE_INDEX];
uint16 total = uint16(gameTokens.length()) + amount;
uint16 maxTokens = _config.maxTokens();
if (total > maxTokens) revert ErrorMintMaxTokens();
gameTokens.addBatch(_nextTokenId(), amount);
user.data = userData.addLiveCount(amount);
_mint(msg.sender, amount);
uint256 teamAmount = (msg.value * _config.teamSplit()) / 100;
uint256 userAmount = msg.value - teamAmount;
_game.prizePool += userAmount;
_mints.addRefundableAmount(gameRound >> OFFSET_GAME_NUMBER, msg.sender, userAmount);
_asyncTransfer(_teamAddress, teamAmount);
if (total == maxTokens) {
_game.startGame();
resetTimeLock();
}
}
timeLock();
}
function commit(
bytes32 hash
) external nonReentrant {
uint256 gameData = _game.data;
uint32 gameRound = gameData.getGameRound();
LibUser.User storage user = _users[msg.sender];
if (user.isInvalid(gameRound)) {
revert ErrorClaimInvalidUser();
}
GameStatus status = _game.getStatus();
if (status != GameStatus.PENDING && status != GameStatus.RUNNING) {
revert ErrorGameNotRunning();
}
if (status == GameStatus.PENDING) {
if (gameData.pauseEndTime() != 0) {
unchecked { gameRound++; }
}
_game.startRound(gameRound);
status = GameStatus.RUNNING;
}
user.commit(gameRound, status, hash);
emit Commit(msg.sender, gameRound);
}
function reveal(
bytes memory secret
) external nonReentrant {
uint32 gameRound = _game.data.getGameRound();
_users[msg.sender].reveal(gameRound, _game.getStatus(), secret);
_randomSeed(bytes32(secret));
emit Reveal(msg.sender, gameRound);
}
function claim(
uint256 tokenId
) external nonReentrant {
LibUser.User storage user = _users[msg.sender];
LibUser.User memory claimUser = user;
uint256 claimData = claimUser.data;
uint256 gameData = _game.data;
uint32 gameRound = gameData.getGameRound();
uint256 roundEndTime = gameData.roundEndTime();
LibBitSet.Set storage liveTokens = _game.tokens[gameRound.liveIndex()];
uint256 liveCount = liveTokens.length();
if (ownerOf(tokenId) != msg.sender) revert ErrorClaimInvalidOwner();
if (liveCount <= 1) revert ErrorClaimUnavailable();
if (claimData.getSafeCount() >= _config.maxWallet()) revert ErrorClaimMaxWallet();
if (roundEndTime <= block.timestamp) revert ErrorClaimRoundClosed();
if (claimData.getGameRound() != gameRound) revert ErrorClaimInvalidUser();
if (claimUser.lastCommit <= REVEAL_THRESHOLD) revert ErrorClaimPermissionDenied();
if (!liveTokens.remove(tokenId)) revert ErrorClaimInvalidToken(tokenId);
uint256 safeCount = _game.tokens[gameRound.safeIndex()].add(tokenId);
claimData = claimData.subLiveCount(1);
claimData = claimData.addSafeCount(1);
unchecked {
liveCount -= 1;
uint256 burnId = liveTokens.removeAt(_randomN(tokenId, liveCount));
if (burnId == LibBitSet.NOT_FOUND) revert ErrorClaimInvalidBurn(burnId);
address burnAddress = ownerOf(burnId);
_burn(burnId);
liveCount -= 1;
gameData += 1;
if (burnAddress != msg.sender) {
uint256 burnData = _users[burnAddress].initUser(burnAddress, gameRound);
_users[burnAddress].data = burnData.subLiveCount(1) + 1;
} else {
claimData = claimData.subLiveCount(1) + 1;
}
emit Claim(msg.sender, gameRound, tokenId);
if (claimData.getSafeCount() != safeCount) {
gameData = gameData.setMultiUser();
}
if (liveCount <= 1) {
gameData = gameData.clearRoundEndTime();
}
if ((liveCount > 1) || (gameData.isMultiUser() && (safeCount > 1))) {
uint256 pauseTime = LibShared.max(safeCount << TOKEN_DELAY_PAUSE, MIN_PAUSE_TIME);
gameData = gameData.setPauseEndTime(
LibShared.max(gameData.roundEndTime(), block.timestamp) + pauseTime
);
}
else {
gameData = gameData.clearPauseEndTime() | gameData.setResetEndTime(block.timestamp + MIN_RESET_TIME);
uint256 prize = _game.prizePool;
_winners.recordWinner(tokenId, prize, gameRound, msg.sender);
_asyncTransfer(msg.sender, prize);
emit LibGame.GameOver(gameRound, msg.sender);
}
}
user.data = claimData;
_game.data = gameData;
}
function finalize(
) external nonReentrant {
_finalizeGame(_game.data.getGameRound());
}
function refund(
uint32 gameNumber,
address payable owner
) external nonReentrant {
uint256 amount = _mints.removeRefundableAmount(gameNumber, owner);
if (amount == 0) revert ErrorNonRefundable();
_asyncTransfer(owner, amount);
}
function cancel(
) external nonReentrant timeLocked {
if (_game.getStatus() != GameStatus.MINTING) revert ErrorMintNotActive();
if ( _game.liveTokenCount() >= _config.maxTokens()) revert ErrorMintComplete();
_mints.cancelMint(_game.data.getGameNumber(), _game.prizePool);
_game.cancelGame(_nextTokenId());
resetTimeLock();
}
function config(
) external view returns (uint256 tokenPrice, uint256 data, address drawAddress) {
return (_tokenPrice, _config, _drawAddress);
}
function getRefundAmount(
uint256 gameNumber,
address owner
) external view returns (uint256) {
return _mints.getRefundableAmount(gameNumber, owner);
}
function canCancelGame(
) external view returns (uint256) {
if (_game.getStatus() != GameStatus.MINTING) return TimeLock.MAX_LOCK;
return timeLockLeft();
}
function cancelledGames(
) external view returns (uint256[] memory) {
return _mints.cancelledMints();
}
function totalCancelledGames(
) external view returns (uint256) {
return _mints.totalCancelledMints();
}
function cancelledGameAtIndex(
uint256 index
) external view returns (uint256) {
return _mints.cancelledMintAtIndex(index);
}
function isGameFinalized(
) external view returns (bool) {
(, bool finalized) = _isGameFinalized(_game.data.getGameRound());
return finalized;
}
function isGameExpired(
) external view returns (bool) {
return _isGameExpired(_game.getStatus());
}
function getGameInfo(
) external view returns (uint256) {
return _game.gameInfo();
}
function getUserInfo(
address userAddress
) external view returns (uint256) {
return _users[userAddress].getUserInfo(_game);
}
function getTokenStatus(
uint256 tokenId
) external view returns (uint8) {
uint8 status = _getVirtualTokenStatus(tokenId);
if (status == TOKEN_STATUS_SECURE && _game.data.hasPauseExpired()) {
status = TOKEN_STATUS_ACTIVE;
} else if ((status & TOKEN_STATUS_BURNED) != 0) {
status = TOKEN_STATUS_BURNED;
} else if ((status & TOKEN_STATUS_WINNER) != 0) {
status = TOKEN_STATUS_WINNER;
}
return status;
}
function isTokenOwner(
address owner,
uint256 idx
) external view override returns (bool) {
return ownerOf(idx) == owner;
}
function liveTokenOfOwner(
address owner
) external view returns (uint256) {
uint256 data = _users[owner].data;
uint32 gameRound = _game.data.getGameRound();
if ((gameRound != data.getGameRound()) || (data.getLiveCount() == 0))
return LibBitSet.NOT_FOUND;
return _game.tokens[gameRound.liveIndex()].findFirstOfOwner(owner, this);
}
function totalWinners(
) external view returns (uint256) {
uint256 total = _winners.totalWinners();
(uint256 tokenId, bool finalized) = _isGameFinalized(_game.data.getGameRound());
if (finalized || tokenId == LibBitSet.NOT_FOUND) return total;
return total + 1;
}
function getWinnerAtIndex(
uint256 index
) external view returns (LibWinners.Winner memory) {
if (index == _winners.totalWinners()) {
return _virtualWinner(_game.data.getGameRound());
}
return _winners.getWinnerAt(index);
}
function getWinner(
uint32 gameNumber
) external view returns (LibWinners.Winner memory) {
uint32 gameRound = _game.data.getGameRound();
uint32 currentGame = gameRound >> OFFSET_GAME_NUMBER;
if (gameNumber > currentGame) {
gameNumber = currentGame;
}
LibWinners.Winner memory winner = _winners.getWinner(gameNumber);
if (winner.data != 0 || gameNumber != currentGame) {
return winner;
}
return _virtualWinner(gameRound);
}
function tokenURI(
uint256 tokenId
) public view override returns (string memory) {
if (!_exists(tokenId)) {
revert OperatorQueryForNonexistentToken();
}
uint8 slug = _getVirtualTokenStatus(tokenId);
if (slug == TOKEN_STATUS_BANNED || ((slug & TOKEN_STATUS_BURNED) != 0)) {
slug = TOKEN_STATUS_BURNED;
}
if (slug == TOKEN_STATUS_SECURE && _game.data.hasPauseExpired()) {
slug = TOKEN_STATUS_ACTIVE;
}
if ((slug & TOKEN_STATUS_WINNER) != 0) {
slug = TOKEN_STATUS_WINNER;
tokenId = _winners.getWinnerId(tokenId);
} else {
tokenId %= _config.maxTokens();
}
return string(abi.encodePacked(
_baseTokenURI, slug.toString(), URI_SLASH, tokenId.toString()
));
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public payable override {
if (!_approveTransfer(from, to, tokenId)) return;
super._transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public payable override {
if (!_approveTransfer(from, to, tokenId)) return;
super._safeTransfer(from, to, tokenId, _data);
}
function _setInternalAddresses(
address payable teamAddress,
address payable drawAddress
) internal {
if (teamAddress == address(0)) revert ErrorTeamAddress();
if (drawAddress == address(0)) revert ErrorDrawAddress();
_teamAddress = teamAddress;
_drawAddress = drawAddress;
}
function _finalizeGame(
uint32 gameRound
) internal returns (bool) {
(uint256 tokenId, bool finalized) = _isGameFinalized(gameRound);
if (finalized) return false;
address winnerAddress = tokenId >= FORFEIT_TOKEN_ID ?
_drawAddress : ownerOf(tokenId);
uint256 prize = _game.prizePool;
_winners.recordWinner(tokenId, prize, gameRound, winnerAddress);
_asyncTransfer(winnerAddress, prize);
emit LibGame.GameOver(gameRound, winnerAddress);
return true;
}
function _isGameFinalized(
uint32 gameRound
) internal view returns (uint256, bool) {
uint256 tokenId = _game.isGameOver(gameRound);
return (tokenId, tokenId == LibBitSet.NOT_FOUND || _winners.hasWinner(tokenId));
}
function _isGameExpired(
GameStatus status
) internal view returns (bool) {
return (status == GameStatus.MINTING) && timeLockExpired();
}
function _getVirtualTokenStatus(
uint256 tokenId
) internal view returns (uint8) {
if (_winners.hasWinner(tokenId)) return TOKEN_STATUS_WINNER;
uint8 status = _game.getTokenStatus(tokenId);
if ((status & (TOKEN_STATUS_ACTIVE | TOKEN_STATUS_SECURE)) != 0) {
uint256 winnerId = _game.isGameOver(_game.data.getGameRound());
if (winnerId != LibBitSet.NOT_FOUND) {
return status | (winnerId == tokenId ? TOKEN_STATUS_WINNER : TOKEN_STATUS_BURNED);
}
}
return status;
}
function _approveTransfer(
address from,
address to,
uint256 tokenId
) internal returns (bool) {
if ((from == to) || !_isApprovedOrOwner(msg.sender, tokenId)) revert TransferFromIncorrectOwner();
uint8 tokenStatus = _getVirtualTokenStatus(tokenId);
if (tokenStatus == TOKEN_STATUS_BANNED || ((tokenStatus & TOKEN_STATUS_BURNED) != 0)) {
_burn(tokenId);
return false;
}
if ((tokenStatus & TOKEN_STATUS_WINNER) != 0) return true;
if (_game.getStatus() == GameStatus.RUNNING) revert ErrorTransferDenied();
uint32 gameRound = _game.data.getGameRound();
LibUser.User storage fromUser = _users[from];
if (fromUser.isInvalid(gameRound)) revert ErrorTransferInvalidUser();
uint256 fromData = fromUser.initUser(from, gameRound);
if ((tokenStatus & (TOKEN_STATUS_ACTIVE | TOKEN_STATUS_QUEUED)) != 0) {
if (fromData.getLiveCount() == 0) revert ErrorTransferInvalidBalance();
fromData = fromData.subLiveCount(1);
} else if ((tokenStatus & TOKEN_STATUS_SECURE) != 0) {
if (fromData.getSafeCount() == 0) revert ErrorTransferInvalidBalance();
fromData = fromData.subSafeCount(1);
}
fromUser.data = fromData;
LibUser.User storage toUser = _users[to];
uint256 toData = toUser.initUser(to, gameRound);
if ((tokenStatus & (TOKEN_STATUS_ACTIVE | TOKEN_STATUS_QUEUED)) != 0) {
toData = toData.addLiveCount(1);
} else if ((tokenStatus & TOKEN_STATUS_SECURE) != 0) {
toData = toData.addSafeCount(1);
}
toUser.data = toData;
return true;
}
function _setBaseTokenURI(
string memory newURI
) private {
if (bytes(newURI).length < 10) revert ErrorInvalidTokenURI();
_baseTokenURI = newURI;
}
function _virtualUserOf(
address owner
) private view returns (LibUser.User storage, uint32) {
LibUser.User storage user = _users[owner];
uint32 gameRound =_game.data.getGameRound();
return (user, user.isExpired(gameRound) ? 0 : gameRound);
}
function _virtualWinner(
uint32 gameRound
) private view returns (LibWinners.Winner memory) {
LibWinners.Winner memory winner;
uint256 tokenId = _game.isGameOver(gameRound);
if (tokenId == LibBitSet.NOT_FOUND) return winner;
bool draw = tokenId >= FORFEIT_TOKEN_ID;
address winAddress = draw ? _drawAddress : ownerOf(tokenId);
gameRound ^= (gameRound & uint32(LibShared.MASK_ROUND));
winner.data = _winners.packWinnerData(gameRound, winAddress);
winner.tokenId = draw ? 0 : tokenId;
winner.prize = _game.prizePool;
return winner;
}
function _randomSeed(
bytes32 seed
) private {
unchecked {
_claimSeed = uint256(keccak256(abi.encodePacked(
_claimSeed, block.prevrandao, msg.sender, block.timestamp, seed, tx.gasprice,
blockhash(block.number - (uint256(seed) % (block.number < 255 ? block.number - 1 : 255) + 1)))));
}
}
function _randomN(
uint256 nonce,
uint256 max
) private view returns (uint256 result) {
return LibPRNG.PRNG({state: uint256(keccak256(
abi.encodePacked(nonce, block.prevrandao, msg.sender, block.timestamp, _claimSeed)
))}).uniform(max);
}
}
文件 3 的 28:Constants.sol
pragma solidity ^0.8.23;
uint256 constant FORFEIT_TOKEN_ID = 1e6;
uint256 constant GAME_STATE_OFFLINE = 0x0;
uint256 constant GAME_STATE_STARTED = 0x1;
uint256 constant GAME_STATE_VIRTUAL = 0x2;
uint256 constant DATA_OFFSET_SAFE_COUNT = 16;
uint256 constant DATA_OFFSET_LIVE_COUNT = 32;
uint256 constant DATA_OFFSET_GAME_ROUND = 224;
uint256 constant DATA_OFFSET_ROUND_COUNT = 224;
uint256 constant DATA_OFFSET_GAME_NUMBER = 232;
uint256 constant REVEAL_THRESHOLD = 0xFFFFFFFF;
uint32 constant OFFSET_GAME_NUMBER = 8;
uint32 constant MIN_ROUND_TIME = 60 minutes;
uint32 constant MIN_PAUSE_TIME = 60 minutes;
uint32 constant MIN_RESET_TIME = 24 hours;
uint32 constant MINT_START_LOCK = 365 days;
uint32 constant MINT_LAST_LOCK = 90 days;
uint16 constant TOKEN_DELAY_ROUND = 4;
uint16 constant TOKEN_DELAY_PAUSE = 5;
uint8 constant TOKEN_STATUS_BANNED = 0x00;
uint8 constant TOKEN_STATUS_BURNED = 0x02;
uint8 constant TOKEN_STATUS_QUEUED = 0x04;
uint8 constant TOKEN_STATUS_ACTIVE = 0x08;
uint8 constant TOKEN_STATUS_SECURE = 0x10;
uint8 constant TOKEN_STATUS_WINNER = 0x20;
uint8 constant MINT_LIVE_INDEX = 0;
string constant URI_SLASH = "/";
文件 4 的 28:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 5 的 28:ERC721Psi.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "solady/src/utils/LibBitmap.sol";
import "./interface/IERC721Psi.sol";
interface ERC721Psi__IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
contract ERC721Psi is IERC721Psi {
using Address for address;
using Strings for uint256;
using LibBitmap for LibBitmap.Bitmap;
LibBitmap.Bitmap private _batchHead;
string private _name;
string private _symbol;
mapping(uint256 => address) internal _owners;
uint256 internal _currentIndex;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
_currentIndex = _startTokenId();
}
function _startTokenId() internal view virtual returns (uint256) {
return 0;
}
function _nextTokenId() internal view virtual returns (uint256) {
return _currentIndex;
}
function _totalMinted() internal view virtual returns (uint256) {
return _currentIndex - _startTokenId();
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function balanceOf(address owner)
public
view
virtual
override
returns (uint)
{
if(owner == address(0)) revert BalanceQueryForZeroAddress();
uint count;
for( uint i = _startTokenId(); i < _nextTokenId(); ++i ){
if(_exists(i)){
if( owner == ownerOf(i)){
++count;
}
}
}
return count;
}
function ownerOf(uint256 tokenId)
public
view
virtual
override
returns (address)
{
(address owner, ) = _ownerAndBatchHeadOf(tokenId);
return owner;
}
function _ownerAndBatchHeadOf(uint256 tokenId) internal view returns (address owner, uint256 tokenIdBatchHead){
if (!_exists(tokenId)) revert OwnerQueryForNonexistentToken();
tokenIdBatchHead = _getBatchHead(tokenId);
owner = _owners[tokenIdBatchHead];
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if( !_exists(tokenId)) revert URIQueryForNonexistentToken();
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
function approve(address to, uint256 tokenId) public payable virtual override {
address owner = ownerOf(tokenId);
if (_msgSenderERC721Psi() != owner) {
if (!isApprovedForAll(owner, _msgSenderERC721Psi())) {
revert ApprovalCallerNotOwnerNorApproved();
}
}
_approve(to, tokenId);
}
function getApproved(uint256 tokenId)
public
view
virtual
override
returns (address)
{
if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved)
public
virtual
override
{
_operatorApprovals[_msgSenderERC721Psi()][operator] = approved;
emit ApprovalForAll(_msgSenderERC721Psi(), operator, approved);
}
function isApprovedForAll(address owner, address operator)
public
view
virtual
override
returns (bool)
{
return _operatorApprovals[owner][operator];
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
_transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public payable virtual override {
_safeTransfer(from, to, tokenId, _data);
}
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_transfer(from, to, tokenId);
if (!_checkOnERC721Received(from, to, tokenId, 1, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
}
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return tokenId < _nextTokenId() && _startTokenId() <= tokenId;
}
error OperatorQueryForNonexistentToken();
function _isApprovedOrOwner(address spender, uint256 tokenId)
internal
view
virtual
returns (bool)
{
if (!_exists(tokenId)) revert OperatorQueryForNonexistentToken();
address owner = ownerOf(tokenId);
return (spender == owner ||
getApproved(tokenId) == spender ||
isApprovedForAll(owner, spender));
}
function _safeMint(address to, uint256 quantity) internal virtual {
_safeMint(to, quantity, "");
}
function _safeMint(
address to,
uint256 quantity,
bytes memory _data
) internal virtual {
_mint(to, quantity);
uint256 end = _currentIndex;
if (!_checkOnERC721Received(address(0), to, end - quantity, quantity, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
if (_currentIndex != end) revert();
}
function _mint(
address to,
uint256 quantity
) internal virtual {
uint256 nextTokenId = _nextTokenId();
if (quantity == 0) revert MintZeroQuantity();
if (to == address(0)) revert MintToZeroAddress();
_beforeTokenTransfers(address(0), to, nextTokenId, quantity);
_currentIndex += quantity;
_owners[nextTokenId] = to;
_batchHead.set(nextTokenId);
uint256 toMasked;
uint256 end = nextTokenId + quantity;
assembly {
toMasked := and(to, _BITMASK_ADDRESS)
log4(
0,
0,
_TRANSFER_EVENT_SIGNATURE,
0,
toMasked,
nextTokenId
)
for {
let tokenId := add(nextTokenId, 1)
} iszero(eq(tokenId, end)) {
tokenId := add(tokenId, 1)
} {
log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
}
}
_afterTokenTransfers(address(0), to, nextTokenId, quantity);
}
function _transfer(
address from,
address to,
uint256 tokenId
) internal virtual {
(address owner, uint256 tokenIdBatchHead) = _ownerAndBatchHeadOf(tokenId);
if (owner != from) revert TransferFromIncorrectOwner();
if (!_isApprovedOrOwner(_msgSenderERC721Psi(), tokenId)) {
revert TransferCallerNotOwnerNorApproved();
}
if (to == address(0)) revert TransferToZeroAddress();
_beforeTokenTransfers(from, to, tokenId, 1);
_approve(address(0), tokenId);
uint256 subsequentTokenId = tokenId + 1;
if(!_batchHead.get(subsequentTokenId) &&
subsequentTokenId < _nextTokenId()
) {
_owners[subsequentTokenId] = from;
_batchHead.set(subsequentTokenId);
}
_owners[tokenId] = to;
if(tokenId != tokenIdBatchHead) {
_batchHead.set(tokenId);
}
emit Transfer(from, to, tokenId);
_afterTokenTransfers(from, to, tokenId, 1);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ownerOf(tokenId), to, tokenId);
}
function _checkOnERC721Received(
address from,
address to,
uint256 startTokenId,
uint256 quantity,
bytes memory _data
) private returns (bool r) {
if (to.code.length > 0) {
r = true;
for(uint256 tokenId = startTokenId; tokenId < startTokenId + quantity; tokenId++){
try ERC721Psi__IERC721Receiver(to).onERC721Received( _msgSenderERC721Psi(), from, tokenId, _data) returns (bytes4 retval) {
r = r && retval == ERC721Psi__IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert TransferToNonERC721ReceiverImplementer();
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
return r;
} else {
return true;
}
}
function _getBatchHead(uint256 tokenId) internal view returns (uint256 tokenIdBatchHead) {
tokenIdBatchHead = _batchHead.findLastSet(tokenId);
}
function totalSupply() public virtual override view returns (uint256) {
return _totalMinted();
}
function tokensOfOwner(address owner) external view virtual returns (uint256[] memory) {
unchecked {
uint256 tokenIdsIdx;
uint256 tokenIdsLength = balanceOf(owner);
uint256[] memory tokenIds = new uint256[](tokenIdsLength);
for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
if (_exists(i)) {
if (ownerOf(i) == owner) {
tokenIds[tokenIdsIdx++] = i;
}
}
}
return tokenIds;
}
}
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
function _msgSenderERC721Psi() internal view virtual returns (address) {
return msg.sender;
}
}
文件 6 的 28:ERC721PsiBurnable.sol
pragma solidity ^0.8.0;
import "solady/src/utils/LibBitmap.sol";
import "../ERC721Psi.sol";
abstract contract ERC721PsiBurnable is ERC721Psi {
using LibBitmap for LibBitmap.Bitmap;
LibBitmap.Bitmap private _burnedToken;
function _burn(uint256 tokenId) internal virtual {
address from = ownerOf(tokenId);
_beforeTokenTransfers(from, address(0), tokenId, 1);
_burnedToken.set(tokenId);
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
}
function _exists(uint256 tokenId) internal view override virtual returns (bool){
if(_burnedToken.get(tokenId)) {
return false;
}
return super._exists(tokenId);
}
function ownerOf(uint256 tokenId)
public
view
virtual
override
returns (address)
{
if (_burnedToken.get(tokenId)) {
return address(0);
}
else {
return super.ownerOf(tokenId);
}
}
function totalSupply() public view virtual override returns (uint256) {
return _currentIndex - _burned() - _startTokenId();
}
function _burned() internal view returns (uint256 burned){
return _burnedToken.popCount( _startTokenId(), _totalMinted());
}
}
文件 7 的 28:EnumerableMap.sol
pragma solidity ^0.8.20;
import {EnumerableSet} from "./EnumerableSet.sol";
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
error EnumerableMapNonexistentKey(bytes32 key);
struct Bytes32ToBytes32Map {
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 key => bytes32) _values;
}
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
if (value == 0 && !contains(map, key)) {
revert EnumerableMapNonexistentKey(key);
}
return value;
}
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
assembly {
result := store
}
return result;
}
}
文件 8 的 28:EnumerableSet.sol
pragma solidity ^0.8.20;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping(bytes32 value => uint256) _positions;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 position = set._positions[value];
if (position != 0) {
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
set._values[valueIndex] = lastValue;
set._positions[lastValue] = position;
}
set._values.pop();
delete set._positions[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly {
result := store
}
return result;
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}
文件 9 的 28:Escrow.sol
pragma solidity ^0.8.0;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
contract Escrow is Ownable {
using Address for address payable;
event Deposited(address indexed payee, uint256 weiAmount);
event Withdrawn(address indexed payee, uint256 weiAmount);
mapping(address => uint256) private _deposits;
constructor() Ownable(msg.sender) {}
function depositsOf(address payee) public view returns (uint256) {
return _deposits[payee];
}
function deposit(address payee) public payable virtual onlyOwner {
uint256 amount = msg.value;
_deposits[payee] += amount;
emit Deposited(payee, amount);
}
function withdraw(address payable payee) public virtual onlyOwner {
uint256 payment = _deposits[payee];
_deposits[payee] = 0;
payee.sendValue(payment);
emit Withdrawn(payee, payment);
}
}
文件 10 的 28:IERC721Psi.sol
pragma solidity ^0.8.0;
interface IERC721Psi {
error ApprovalCallerNotOwnerNorApproved();
error ApprovalQueryForNonexistentToken();
error BalanceQueryForZeroAddress();
error MintToZeroAddress();
error MintZeroQuantity();
error OwnerQueryForNonexistentToken();
error TransferCallerNotOwnerNorApproved();
error TransferFromIncorrectOwner();
error TransferToNonERC721ReceiverImplementer();
error TransferToZeroAddress();
error URIQueryForNonexistentToken();
function totalSupply() external view returns (uint256);
function supportsInterface(bytes4 interfaceId) external view returns (bool);
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external payable;
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external payable;
function transferFrom(
address from,
address to,
uint256 tokenId
) external payable;
function approve(address to, uint256 tokenId) external payable;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 11 的 28:LibBit.sol
pragma solidity ^0.8.4;
library LibBit {
function fls(uint256 x) internal pure returns (uint256 r) {
assembly {
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
function clz(uint256 x) internal pure returns (uint256 r) {
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
}
}
function ffs(uint256 x) internal pure returns (uint256 r) {
assembly {
let b := and(x, add(not(x), 1))
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, b)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
r := or(r, shl(5, lt(0xffffffff, shr(r, b))))
r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
function popCount(uint256 x) internal pure returns (uint256 c) {
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
function isPo2(uint256 x) internal pure returns (bool result) {
assembly {
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
function reverseBits(uint256 x) internal pure returns (uint256 r) {
assembly {
let m := not(0)
r := x
for { let s := 128 } 1 {} {
m := xor(m, shl(s, m))
r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
s := shr(1, s)
if iszero(s) { break }
}
}
}
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
assembly {
let m := not(0)
r := x
for { let s := 128 } 1 {} {
m := xor(m, shl(s, m))
r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
s := shr(1, s)
if eq(s, 4) { break }
}
}
}
function rawAnd(bool x, bool y) internal pure returns (bool z) {
assembly {
z := and(x, y)
}
}
function and(bool x, bool y) internal pure returns (bool z) {
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}
function rawOr(bool x, bool y) internal pure returns (bool z) {
assembly {
z := or(x, y)
}
}
function or(bool x, bool y) internal pure returns (bool z) {
assembly {
z := or(iszero(iszero(x)), iszero(iszero(y)))
}
}
function rawToUint(bool b) internal pure returns (uint256 z) {
assembly {
z := b
}
}
function toUint(bool b) internal pure returns (uint256 z) {
assembly {
z := iszero(iszero(b))
}
}
}
文件 12 的 28:LibBitSet.sol
pragma solidity ^0.8.23;
import {LibBit} from "solady/src/utils/LibBit.sol";
interface ILibBitSet64Filter {
function isTokenOwner(address owner, uint256 idx) external view returns (bool);
}
library LibBitSet {
uint256 public constant NOT_FOUND = type(uint256).max;
uint256 private constant MAX_MASK = type(uint256).max;
uint256 private constant MAX_INDEX = 16383;
uint256 private constant FIXED_LENGTH = 64;
uint16 public constant MAX_POP_COUNT = 256;
uint16 private constant MASK_LENGTH = 0xFFFF;
uint8 private constant MAX_BIT_COUNT = 255;
uint8 private constant MASK_BIT_COUNT = 0xFF;
uint8 private constant INDEX_SHIFT = 8;
uint8 private constant INDEX_MASK = 0xFF;
uint8 private constant INDEX_NOT_FOUND = 0xFF;
uint8 private constant OFFSET_BUCKET_FLAGS = 192;
struct Set {
uint256 offset;
uint256 count;
uint256[2] popCounts;
uint256[64] map;
}
function add(Set storage self,
uint256 tokenId
) internal returns (uint256) {
uint256 count = self.count;
unchecked {
tokenId -= self.offset;
if (tokenId > MAX_INDEX) return uint16(count);
uint8 bucket = uint8(tokenId >> INDEX_SHIFT);
uint256 bitIndex = tokenId & INDEX_MASK;
uint256 mask = 1 << bitIndex;
uint256 bitmap = self.map[bucket];
if ((bitmap & mask) == 0) {
bitmap |= mask;
self.map[bucket] = bitmap;
if (bitmap != MAX_MASK) {
_addPopCount(self, bucket, 1);
}
count = ((count & ~(1 << (bucket + OFFSET_BUCKET_FLAGS))) ^ (((~(bitmap + 1) & bitmap) >> MAX_BIT_COUNT)
<< (bucket + OFFSET_BUCKET_FLAGS))) + 1;
self.count = count;
}
}
return uint16(count);
}
function addBatch(Set storage self,
uint256 startId,
uint256 amount
) internal {
unchecked {
startId -= self.offset;
if (startId > MAX_INDEX) return;
uint256[2] memory popCounts = self.popCounts;
uint256 mask = 0;
uint256 delta = 0;
uint256 count = self.count + amount;
uint8 bits = 0;
uint8 bucket = uint8(startId >> INDEX_SHIFT);
uint8 shift = uint8(startId & INDEX_MASK);
while (amount > 0) {
delta = MAX_POP_COUNT - shift;
if (amount >= delta) {
mask = MAX_MASK << shift;
bits = MAX_BIT_COUNT - shift;
count |= (1 << (bucket + OFFSET_BUCKET_FLAGS));
amount -= delta;
shift = 0;
} else {
mask = ((1 << amount) - 1) << shift;
bits = uint8(amount);
count &= ~(1 << (bucket + OFFSET_BUCKET_FLAGS));
amount = 0;
}
_addPopCounts(popCounts, bucket, bits);
self.map[bucket] |= mask;
bucket++;
}
self.popCounts = popCounts;
self.count = count;
}
}
function remove(Set storage self,
uint256 tokenId
) internal returns (bool) {
unchecked {
tokenId -= self.offset;
uint8 bucket = uint8(tokenId >> INDEX_SHIFT);
if (bucket >= FIXED_LENGTH) return false;
uint256 bitmap = self.map[bucket];
uint256 mask = 1 << (tokenId & INDEX_MASK);
if ((bitmap & mask) == 0) return false;
uint256 count = self.count;
uint256 offset = bucket + OFFSET_BUCKET_FLAGS;
if (((count >> offset) & 1) == 0) {
_subPopCount(self, bucket, 1);
}
self.count = (count - 1) & ~(1 << offset);
self.map[bucket] = bitmap & ~mask;
return true;
}
}
function removeAt(Set storage self,
uint256 index
) internal returns (uint256) {
uint256 count = self.count;
if (index >= uint16(count)) return NOT_FOUND;
uint256[2] memory popCounts = self.popCounts;
(uint256 bucket, uint16 remaining) = _positionOfIndex(count, popCounts, uint16(index));
if (bucket == INDEX_NOT_FOUND) return NOT_FOUND;
uint256 bit;
uint256 pos;
uint256 offset;
uint256 bitmap = self.map[bucket];
unchecked {
while (bitmap != 0) {
bit = bitmap & (~bitmap + 1);
pos = LibBit.fls(bit);
if (remaining == 0) {
offset = bucket + OFFSET_BUCKET_FLAGS;
if (((count >> offset) & 1) == 0) {
_subPopCounts(popCounts, uint8(bucket), 1);
self.popCounts = popCounts;
}
self.count = (count - 1) & ~(1 << offset);
self.map[bucket] ^= bit;
return ((bucket << INDEX_SHIFT) | pos) + self.offset;
}
remaining--;
bitmap ^= bit;
}
}
return NOT_FOUND;
}
function contains(Set storage self,
uint256 tokenId
) internal view returns (bool isSet) {
unchecked {
tokenId -= self.offset;
uint256 bucket = tokenId >> INDEX_SHIFT;
if (bucket >= FIXED_LENGTH) return false;
uint256 bit = (self.map[bucket] >> (tokenId & INDEX_MASK)) & 1;
assembly {
isSet := bit
}
}
}
function at(Set storage self,
uint256 index
) internal view returns (uint256) {
if (index >= uint16(self.count)) return NOT_FOUND;
uint256 len = 0;
uint256 bitmap;
uint256 bitsCount;
uint256 remaining;
uint256 bucket;
uint256 bit;
uint256 pos;
unchecked {
for (bucket = 0; bucket < FIXED_LENGTH; ++bucket) {
bitmap = self.map[bucket];
bitsCount = LibBit.popCount(bitmap);
if (len + bitsCount > index) {
remaining = index - len;
while (bitmap != 0) {
bit = bitmap & (~bitmap + 1);
pos = LibBit.fls(bit);
if (remaining == 0) {
return ((bucket << INDEX_SHIFT) | pos) + self.offset;
}
remaining--;
bitmap ^= bit;
}
}
len += bitsCount;
}
}
return NOT_FOUND;
}
function findFirst(Set storage self
) internal view returns (uint256) {
if (self.count == 0) return NOT_FOUND;
uint256 bitmap;
uint256 lsb;
unchecked {
for (uint256 bucket = 0; bucket < FIXED_LENGTH; bucket++) {
bitmap = self.map[bucket];
if (bitmap != 0) {
lsb = LibBit.ffs(bitmap);
return (bucket << INDEX_SHIFT | lsb) + self.offset;
}
}
}
return NOT_FOUND;
}
function findFirstOfOwner(Set storage self,
address owner,
ILibBitSet64Filter filter
) internal view returns (uint256) {
uint256 count = self.count;
if (count == 0) return NOT_FOUND;
unchecked {
uint256 offset = self.offset;
uint256 bitmap;
uint256 tokenId;
uint256 pos;
for (uint256 bucket = 0; bucket < FIXED_LENGTH; bucket++) {
bitmap = self.map[bucket];
while (bitmap != 0) {
pos = LibBit.ffs(bitmap);
tokenId = ((bucket << INDEX_SHIFT) | pos) + offset;
if (filter.isTokenOwner(owner, tokenId)) return tokenId;
bitmap &= ~(1 << pos);
}
}
}
return NOT_FOUND;
}
function findNearest(Set storage self,
uint256 index
) internal view returns (uint256) {
if (self.count == 0) return NOT_FOUND;
unchecked {
index -= self.offset;
uint256 bucket = index >> INDEX_SHIFT;
uint256 bitIndex = index & INDEX_MASK;
uint256 bitmap = bucket < FIXED_LENGTH ? self.map[bucket] : 0;
if ((bitmap >> bitIndex) & 1 == 1) {
return index + self.offset;
}
for (uint256 i = bucket; i < FIXED_LENGTH; i++) {
bitmap = self.map[i];
if (bitmap != 0) {
return (i << INDEX_SHIFT) | LibBit.fls(bitmap) + self.offset;
}
}
}
return NOT_FOUND;
}
function findLast(Set storage self) internal view returns (uint256) {
if (self.count == 0) return NOT_FOUND;
for (uint256 bucket = FIXED_LENGTH; bucket > 0; bucket--) {
if (self.map[bucket - 1] != 0) {
uint256 bitIndex = LibBit.fls(self.map[bucket - 1]);
return ((bucket - 1) << INDEX_SHIFT) + bitIndex + self.offset;
}
}
return 0;
}
function mapRange(Set storage self
) internal view returns (uint256 start, uint256 len) {
unchecked {
for (uint256 i = 0; i < FIXED_LENGTH; i++) {
if (self.map[i] != 0) {
start = (i << INDEX_SHIFT) + LibBit.ffs(self.map[i]);
break;
}
}
for (uint256 i = FIXED_LENGTH; i > 0; i--) {
if (self.map[i - 1] != 0) {
len = ((i - 1) << INDEX_SHIFT) + LibBit.fls(self.map[i - 1]);
break;
}
}
len += 1;
}
return (start, len);
}
function getRange(Set storage self,
uint256 start,
uint256 stop
) internal view returns (uint256[] memory) {
unchecked {
uint256 count = uint16(self.count);
stop = (stop > count) ? count : stop;
uint256 startBucket = (start - self.offset) >> INDEX_SHIFT;
uint256 endBucket = (stop - self.offset) >> INDEX_SHIFT;
uint256 arraySize = stop - start + 1;
uint256[] memory result = new uint256[](arraySize);
uint256 resultIndex = 0;
uint256 bucketBits;
for (uint256 i = startBucket; i <= endBucket && i < FIXED_LENGTH; ++i) {
bucketBits = self.map[i];
if (bucketBits == 0) continue;
for (uint256 j = 0; j < MAX_POP_COUNT; ++j) {
uint256 bitIndex = (i << INDEX_SHIFT) + j + self.offset;
if (bitIndex < start) continue;
if (bitIndex > stop) break;
if ((bucketBits & (1 << j)) != 0) {
result[resultIndex++] = bitIndex;
}
}
}
if (resultIndex < arraySize) {
assembly {
mstore(result, resultIndex)
}
}
return result;
}
}
function rangeLength(Set storage self
) internal view returns (uint256) {
unchecked {
uint256 bitmap;
for (uint256 bucket = (FIXED_LENGTH - 1); bucket >= 0; bucket--) {
bitmap = self.map[bucket];
if (bitmap != 0) {
return (bucket << INDEX_SHIFT) + LibBit.fls(bitmap) + 1;
}
}
}
return 0;
}
function mapLength(Set storage
) internal pure returns (uint256) {
return FIXED_LENGTH;
}
function length(Set storage self
) internal view returns (uint256) {
return self.count & MASK_LENGTH;
}
function trim(Set storage self
) internal {
uint256 len = self.map.length;
while (len > 0 && self.map[len - 1] == 0) {
delete self.map[--len];
}
}
function values(Set storage self
) internal view returns (uint256[] memory result) {
result = new uint256[](uint16(self.count));
uint256 index = 0;
uint256 offset = self.offset;
for (uint256 i = 0; i < FIXED_LENGTH; i++) {
uint256 bucket = self.map[i];
if (bucket == 0) continue;
for (uint256 j = 0; j < MAX_POP_COUNT; j++) {
if ((bucket & (1 << j)) == 0) continue;
result[index] = ((i << INDEX_SHIFT) | j) + offset;
index++;
}
}
}
function _addPopCount(Set storage self,
uint8 slot,
uint8 amount
) private {
unchecked {
self.popCounts[slot >> 5] += uint256(amount & INDEX_MASK) << ((slot & 31) << 3);
}
}
function _addPopCounts(
uint256[2] memory popCounts,
uint8 slot,
uint8 amount
) private pure {
unchecked {
popCounts[slot >> 5] += uint256(amount & INDEX_MASK) << ((slot & 31) << 3);
}
}
function _subPopCount(Set storage self,
uint8 slot,
uint8 amount
) private {
unchecked {
self.popCounts[slot >> 5] -= uint256(amount & INDEX_MASK) << ((slot & 31) << 3);
}
}
function _subPopCounts(
uint256[2] memory popCounts,
uint8 slot,
uint8 amount
) private pure {
unchecked {
popCounts[slot >> 5] -= uint256(amount & INDEX_MASK) << ((slot & 31) << 3);
}
}
function _getPopCount(
uint256 count,
uint256[2] memory popCounts,
uint256 slot
) private pure returns (uint16) {
return ((count >> (slot + OFFSET_BUCKET_FLAGS)) & 1) == 1 ? MAX_POP_COUNT :
uint16((popCounts[slot >> 5] >> ((slot & 31) << 3)) & MASK_BIT_COUNT);
}
function _positionOfIndex(
uint256 count,
uint256[2] memory popCounts,
uint16 index
) private pure returns (uint8 bucket, uint16 ) {
unchecked {
if (index > uint16(count)) return (INDEX_NOT_FOUND, 0);
uint16 total = 0;
uint16 popCount = 0;
uint16 next = 0;
for (bucket = 0; bucket < FIXED_LENGTH; ++bucket) {
popCount = _getPopCount(count, popCounts, bucket);
next = total + popCount;
if (next > index) return (bucket, index - total);
total = next;
}
return (INDEX_NOT_FOUND, 0);
}
}
}
文件 13 的 28:LibBitmap.sol
pragma solidity ^0.8.4;
import {LibBit} from "./LibBit.sol";
library LibBitmap {
uint256 internal constant NOT_FOUND = type(uint256).max;
struct Bitmap {
mapping(uint256 => uint256) map;
}
function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) {
uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1;
assembly {
isSet := b
}
}
function set(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] |= (1 << (index & 0xff));
}
function unset(Bitmap storage bitmap, uint256 index) internal {
bitmap.map[index >> 8] &= ~(1 << (index & 0xff));
}
function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) {
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let shift := and(index, 0xff)
let storageValue := xor(sload(storageSlot), shl(shift, 1))
newIsSet := and(1, shr(shift, storageValue))
sstore(storageSlot, storageValue)
}
}
function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal {
assembly {
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, index))
let storageSlot := keccak256(0x00, 0x40)
let storageValue := sload(storageSlot)
let shift := and(index, 0xff)
sstore(
storageSlot,
or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet))))
)
}
}
function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
assembly {
let max := not(0)
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, max)))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), max)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max))))
}
}
function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal {
assembly {
let shift := and(start, 0xff)
mstore(0x20, bitmap.slot)
mstore(0x00, shr(8, start))
if iszero(lt(add(shift, amount), 257)) {
let storageSlot := keccak256(0x00, 0x40)
sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0)))))
let bucket := add(mload(0x00), 1)
let bucketEnd := add(mload(0x00), shr(8, add(amount, shift)))
amount := and(add(amount, shift), 0xff)
shift := 0
for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } {
mstore(0x00, bucket)
sstore(keccak256(0x00, 0x40), 0)
}
mstore(0x00, bucket)
}
let storageSlot := keccak256(0x00, 0x40)
sstore(
storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0)))))
)
}
}
function popCount(Bitmap storage bitmap, uint256 start, uint256 amount)
internal
view
returns (uint256 count)
{
unchecked {
uint256 bucket = start >> 8;
uint256 shift = start & 0xff;
if (!(amount + shift < 257)) {
count = LibBit.popCount(bitmap.map[bucket] >> shift);
uint256 bucketEnd = bucket + ((amount + shift) >> 8);
amount = (amount + shift) & 0xff;
shift = 0;
for (++bucket; bucket != bucketEnd; ++bucket) {
count += LibBit.popCount(bitmap.map[bucket]);
}
}
count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount));
}
}
function findLastSet(Bitmap storage bitmap, uint256 before)
internal
view
returns (uint256 setBitIndex)
{
uint256 bucket;
uint256 bucketBits;
assembly {
setBitIndex := not(0)
bucket := shr(8, before)
mstore(0x00, bucket)
mstore(0x20, bitmap.slot)
let offset := and(0xff, not(before))
bucketBits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40))))
if iszero(or(bucketBits, iszero(bucket))) {
for {} 1 {} {
bucket := add(bucket, setBitIndex)
mstore(0x00, bucket)
bucketBits := sload(keccak256(0x00, 0x40))
if or(bucketBits, iszero(bucket)) { break }
}
}
}
if (bucketBits != 0) {
setBitIndex = (bucket << 8) | LibBit.fls(bucketBits);
assembly {
setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, before)))
}
}
}
}
文件 14 的 28:LibConfig.sol
pragma solidity ^0.8.23;
import {LibShared} from "./LibShared.sol";
import "./Constants.sol";
library LibConfig {
uint256 private constant CONFIG_OFFSET_MAX_TOKENS = 0;
uint256 private constant CONFIG_OFFSET_MAX_WALLET = 16;
uint256 private constant CONFIG_OFFSET_MAX_AMOUNT = 32;
uint256 private constant CONFIG_OFFSET_CONTEST_ID = 48;
uint256 private constant CONFIG_OFFSET_CONFIG_VER = 80;
uint256 private constant CONFIG_OFFSET_TEAM_SPLIT = 88;
uint256 private constant CONFIG_OFFSET_PRIZE_POOL = 96;
uint256 private constant MASK_MAX_TOKENS = 0xFFFF;
uint256 private constant MASK_MAX_WALLET = 0xFFFF;
uint256 private constant MASK_MAX_AMOUNT = 0xFFFF;
uint256 private constant MASK_PRIZE_POOL = 0xFFFF;
uint256 private constant MASK_CONTEST_ID = 0xFFFF;
uint256 private constant MASK_TEAM_SPLIT = 0xFF;
uint256 private constant MASK_CONFIG_VER = 0xFF;
uint256 private constant SCALAR_GWEI = 1e9;
uint16 private constant CONTEST_ID_OFFSET_SERIES = 16;
uint16 private constant MIN_TOKENS = 2;
uint16 private constant MAX_TOKENS = 2**14;
uint8 private constant MAX_TEAM_SPLIT = 50;
uint8 private constant CONFIG_VERSION = 1;
error ErrorMaxAmount();
error ErrorMaxTokens();
error ErrorMaxWallet();
error ErrorTeamSplit();
function initConfig(
uint256 _tokenPrice,
uint32 _contestId,
uint16 _maxTokens,
uint16 _maxWallet,
uint16 _maxAmount,
uint8 _teamSplit
) internal pure returns (uint256) {
if ((_maxTokens < MIN_TOKENS) || (_maxTokens > MAX_TOKENS)) revert ErrorMaxTokens();
if ((_maxWallet == 0) || (_maxWallet > _maxTokens)) revert ErrorMaxWallet();
if ((_maxAmount == 0) || (_maxAmount > _maxWallet)) revert ErrorMaxAmount();
if (_teamSplit > MAX_TEAM_SPLIT) revert ErrorTeamSplit();
uint64 prizePool = uint64(((_tokenPrice * _maxTokens * (100 - _teamSplit)) / 100) / SCALAR_GWEI);
if (_contestId <= type(uint16).max) {
_contestId = (uint32(_maxTokens | uint16(prizePool / SCALAR_GWEI)) << CONTEST_ID_OFFSET_SERIES) | _contestId;
}
return
(uint256(_maxTokens) << CONFIG_OFFSET_MAX_TOKENS) |
(uint256(_maxWallet) << CONFIG_OFFSET_MAX_WALLET) |
(uint256(_maxAmount) << CONFIG_OFFSET_MAX_AMOUNT) |
(uint256(_contestId) << CONFIG_OFFSET_CONTEST_ID) |
(uint256(CONFIG_VERSION) << CONFIG_OFFSET_CONFIG_VER) |
(uint256(_teamSplit) << CONFIG_OFFSET_TEAM_SPLIT) |
(uint256(prizePool) << CONFIG_OFFSET_PRIZE_POOL);
}
function teamSplit(uint256 data) internal pure returns (uint8) {
return uint8((data >> CONFIG_OFFSET_TEAM_SPLIT) & MASK_TEAM_SPLIT);
}
function maxAmount(uint256 data) internal pure returns (uint16) {
return uint16((data >> CONFIG_OFFSET_MAX_AMOUNT) & MASK_MAX_AMOUNT);
}
function maxWallet(uint256 data) internal pure returns (uint16) {
return uint16((data >> CONFIG_OFFSET_MAX_WALLET) & MASK_MAX_WALLET);
}
function maxTokens(uint256 data) internal pure returns (uint16) {
return uint16((data >> CONFIG_OFFSET_MAX_TOKENS) & MASK_MAX_TOKENS);
}
}
文件 15 的 28:LibGame.sol
pragma solidity ^0.8.23;
import {LibBitSet} from "./LibBitSet.sol";
import {LibShared} from "./LibShared.sol";
import "./Constants.sol";
enum GameStatus {
MINTING,
PAUSING,
PENDING,
RUNNING,
FORFEIT,
WINNERS
}
library LibGame {
using LibBitSet for LibBitSet.Set;
using LibShared for uint256;
using LibShared for uint32;
uint256 private constant GAME_OFFSET_RESET_TIME = 112;
uint256 private constant GAME_OFFSET_PAUSE_TIME = 144;
uint256 private constant GAME_OFFSET_ROUND_TIME = 176;
uint256 private constant GAME_OFFSET_MULTI_USER = 208;
uint256 private constant GAME_OFFSET_GAME_STATE = 216;
uint256 private constant INFO_OFFSET_GAME_STATE = 0;
uint256 private constant INFO_OFFSET_BLOCK_TIME = 8;
uint256 private constant INFO_OFFSET_RESET_TIME = 40;
uint256 private constant INFO_OFFSET_PAUSE_TIME = 72;
uint256 private constant INFO_OFFSET_ROUND_TIME = 104;
uint256 private constant INFO_OFFSET_PRIZE_POOL = 136;
uint256 private constant INFO_OFFSET_GAMESTATUS = 168;
uint256 private constant INFO_OFFSET_BURN_COUNT = 176;
uint256 private constant INFO_OFFSET_SAFE_COUNT = 192;
uint256 private constant INFO_OFFSET_LIVE_COUNT = 208;
uint256 private constant INFO_OFFSET_GAME_ROUND = 224;
uint256 private constant MASK_MULTI = 0xFF;
uint256 private constant MASK_STATE = 0xFF;
uint256 private constant MASK_BURNED = 0xFFFF;
uint256 private constant MASK_TIMESTAMP = 0xFFFFFFFF;
uint256 private constant MASK_PENDING = 0xFFFFFFFFFFFFFFFFFFFFFFFF;
uint256 private constant SCALAR_BWEI = 1e14;
struct Game {
uint256 data;
uint256 prizePool;
LibBitSet.Set[2] tokens;
}
event GameStart(uint32 indexed gameRound);
event GameCancelled(uint32 indexed gameRound, uint256 mintCount);
event GameOver(uint32 indexed gameRound, address winner);
event RoundStart(uint32 indexed gameRound);
function nextGameRound(Game storage self
) internal view returns (uint32) {
return _nextGameRound(self.data);
}
function initGame(Game storage self,
uint256 startTokenId
) internal returns (uint32) {
return _initGame(self, self.data, startTokenId);
}
function startGame(Game storage self
) internal {
_startGame(self);
}
function startRound(Game storage self,
uint32 gameRound
) internal {
_startRound(self, gameRound);
}
function resetGame(Game storage self,
uint256 startTokenId
) internal returns (uint32) {
return _resetGame(self, self.data, startTokenId);
}
function cancelGame(Game storage self,
uint256 startTokenId
) internal {
uint256 data = self.data;
emit GameCancelled(data.getGameRound(), data.getLiveCount());
_resetGame(self, data, startTokenId);
}
function getStatus(Game storage self
) internal view returns (GameStatus) {
return _getStatus(self, self.data);
}
function getStatus(Game storage self,
uint256 data
) internal view returns (GameStatus) {
return _getStatus(self, data);
}
function isGameOver(Game storage self,
uint32 gameRound
) internal view returns (uint256) {
return _isGameOver(self, gameRound);
}
function liveTokenCount(Game storage self
) internal view returns (uint16) {
return _liveTokenCount(self, self.data);
}
function virtualLiveTokenCount(Game storage self
) internal view returns (uint16) {
return _virtualLiveTokenCount(self, self.data);
}
function virtualSafeTokenCount(Game storage self
) internal view returns (uint16) {
return _virtualSafeTokenCount(self, self.data);
}
function virtualBurnTokenCount(Game storage self
) internal view returns (uint16) {
return _virtualBurnTokenCount(self, self.data);
}
function getTokenStatus(Game storage self,
uint256 tokenId
) internal view returns (uint8) {
return _getTokenStatus(self, tokenId);
}
function virtualResetEndTime(Game storage self,
GameStatus status
) internal view returns (uint32) {
return _virtualResetEndTime(self.data, status);
}
function gameInfo(Game storage self
) internal view returns (uint256) {
return _gameInfo(self);
}
function _nextGameRound(
uint256 data
) private pure returns (uint32) {
unchecked {
return (data.getGameRound() + (uint32(1) << OFFSET_GAME_NUMBER)) | 1;
}
}
function _initGame(Game storage self,
uint256 data,
uint256 startTokenId
) private returns (uint32) {
uint256 gameRound = _nextGameRound(data);
self.data = gameRound << DATA_OFFSET_GAME_ROUND;
self.tokens[0].offset = self.tokens[1].offset = startTokenId;
return uint32(gameRound);
}
function _startGame(Game storage self
) private {
uint256 data = self.data;
data = _clearEndTimes(data) | (GAME_STATE_STARTED << GAME_OFFSET_GAME_STATE);
self.data = data;
emit GameStart(data.getGameRound());
}
function _startRound(Game storage self,
uint32 gameRound
) private {
uint256 data = self.data;
if (block.timestamp <= roundEndTime(data)) return;
unchecked {
LibBitSet.Set[2] storage tokens = self.tokens;
if (data.getGameRound() != gameRound) {
uint8 prevIndex = data.getLiveIndex();
data = data.addBurnCount(uint16(tokens[prevIndex].length()));
delete tokens[prevIndex];
tokens[prevIndex].offset = tokens[1 - prevIndex].offset;
data = data.setGameRound(gameRound);
}
data = _clearMultiUser(data);
self.data = setRoundEndTime(data, block.timestamp +
LibShared.max(tokens[gameRound.liveIndex()].length() << TOKEN_DELAY_ROUND, MIN_ROUND_TIME));
emit RoundStart(gameRound);
}
}
function _resetGame(Game storage self,
uint256 data,
uint256 startTokenId
) private returns (uint32) {
self.prizePool = 0;
delete self.tokens;
self.tokens[0].offset = self.tokens[1].offset = startTokenId;
return _initGame(self, data.clearRound(), startTokenId);
}
function _getStatus(Game storage self,
uint256 data
) private view returns (GameStatus) {
uint256 pauseTime = pauseEndTime(data);
uint256 resetTime = resetEndTime(data);
if (block.timestamp > pauseTime && (resetTime > 0 && block.timestamp <= resetTime)) {
return GameStatus.WINNERS;
}
if (gameState(data) == GAME_STATE_OFFLINE) {
return GameStatus.MINTING;
}
if (_isPending(data)) {
return GameStatus.PENDING;
}
uint256 roundTime = roundEndTime(data);
if (roundTime != 0 && block.timestamp <= roundTime && self.tokens[data.getLiveIndex()].length() > 1) {
return GameStatus.RUNNING;
}
if (isMultiUser(data)) {
if ((roundTime == 0 || block.timestamp > roundTime) && (pauseTime > 0 && block.timestamp <= pauseTime)) {
return GameStatus.PAUSING;
}
return (pauseTime > 0 && block.timestamp > pauseTime && roundTime < pauseTime) ?
GameStatus.PENDING : GameStatus.RUNNING;
}
return self.tokens[data.getSafeIndex()].length() == 0 ?
GameStatus.FORFEIT : GameStatus.WINNERS;
}
function _liveTokenCount(Game storage self,
uint256 data
) private view returns (uint16) {
return uint16(self.tokens[data.getLiveIndex()].length());
}
function _safeTokenCount(Game storage self,
uint256 data
) private view returns (uint16) {
return uint16(self.tokens[data.getSafeIndex()].length());
}
function _virtualLiveTokenCount(Game storage self,
uint256 data
) private view returns (uint16) {
if ((gameState(data) == GAME_STATE_OFFLINE) ||
(block.timestamp <= roundEndTime(data)) ||
_isPending(data)) {
return _liveTokenCount(self, data);
}
return 0;
}
function _virtualSafeTokenCount(Game storage self,
uint256 data
) private view returns (uint16) {
if (gameState(data) == GAME_STATE_OFFLINE) {
return 0;
}
return _safeTokenCount(self, data);
}
function _virtualBurnTokenCount(Game storage self,
uint256 data
) private view returns (uint16) {
uint16 burnCount = data.getBurnCount();
if ((gameState(data) == GAME_STATE_STARTED) &&
(block.timestamp > roundEndTime(data)) &&
!_isPending(data)) {
burnCount += _liveTokenCount(self, data);
}
return burnCount;
}
function _getTokenStatus(Game storage self,
uint256 tokenId
) private view returns (uint8) {
if (tokenId < self.tokens[0].offset) return TOKEN_STATUS_BANNED;
uint256 data = self.data;
if (gameState(data) == GAME_STATE_OFFLINE) return TOKEN_STATUS_QUEUED;
if (_virtualLiveTokenCount(self, data) > 1 && self.tokens[data.getLiveIndex()].contains(tokenId)) {
return TOKEN_STATUS_ACTIVE;
}
return (self.tokens[data.getSafeIndex()].contains(tokenId)) ? TOKEN_STATUS_SECURE : TOKEN_STATUS_BURNED;
}
function _isGameOver(Game storage self,
uint32 gameRound
) private view returns (uint256) {
GameStatus status = _getStatus(self, self.data);
if (status < GameStatus.FORFEIT) return LibBitSet.NOT_FOUND;
if (status == GameStatus.FORFEIT) return FORFEIT_TOKEN_ID + (gameRound >> OFFSET_GAME_NUMBER);
return self.tokens[gameRound.safeIndex()].findFirst();
}
function _virtualResetEndTime(
uint256 data,
GameStatus status
) private pure returns (uint32) {
uint32 resetTime = resetEndTime(data);
if (status > GameStatus.RUNNING && resetTime == 0) {
return roundEndTime(data) + MIN_RESET_TIME;
}
return resetTime;
}
function _gameInfo(Game storage self
) private view returns (uint256) {
uint256 data = self.data;
GameStatus status = _getStatus(self, data);
uint256 prizePool = self.prizePool / SCALAR_BWEI;
uint32 gameRound = data.getGameRound();
uint256 state = gameState(data);
uint32 roundTime = roundEndTime(data);
uint32 pauseTime = pauseEndTime(data);
uint32 resetTime = resetEndTime(data);
uint16 liveCount = _virtualLiveTokenCount(self, data);
uint16 safeCount = _virtualSafeTokenCount(self, data);
uint16 burnCount = _virtualBurnTokenCount(self, data);
if (status == GameStatus.RUNNING) {
pauseTime = resetTime = 0;
}
if ((status == GameStatus.PAUSING || status == GameStatus.PENDING) && pauseTime != 0) {
unchecked { gameRound++; }
(liveCount, safeCount) = (safeCount, 0);
roundTime = resetTime = 0;
if (block.timestamp > pauseTime) {
pauseTime = 0;
}
}
if (status == GameStatus.PENDING) {
roundTime = pauseTime = resetTime = 0;
}
if (status > GameStatus.RUNNING) {
roundTime = pauseTime = 0;
if (resetTime == 0) {
resetTime = _virtualResetEndTime(data, status);
state = GAME_STATE_VIRTUAL;
}
if (resetTime != 0 && block.timestamp > resetTime) {
status = GameStatus.MINTING;
gameRound = _nextGameRound(data);
prizePool = roundTime = pauseTime = liveCount = safeCount = burnCount = 0;
}
}
uint256 info = (state << INFO_OFFSET_GAME_STATE);
info |=
(uint256(block.timestamp) << INFO_OFFSET_BLOCK_TIME) |
(uint256(resetTime) << INFO_OFFSET_RESET_TIME) |
(uint256(pauseTime) << INFO_OFFSET_PAUSE_TIME) |
(uint256(roundTime) << INFO_OFFSET_ROUND_TIME) |
(prizePool << INFO_OFFSET_PRIZE_POOL) |
(uint256(status) << INFO_OFFSET_GAMESTATUS);
info |=
(uint256(burnCount) << INFO_OFFSET_BURN_COUNT) |
(uint256(safeCount) << INFO_OFFSET_SAFE_COUNT) |
(uint256(liveCount) << INFO_OFFSET_LIVE_COUNT) |
(uint256(gameRound) << INFO_OFFSET_GAME_ROUND);
return info;
}
function gameState(uint256 data) internal pure returns (uint8) {
return uint8((data >> GAME_OFFSET_GAME_STATE) & MASK_STATE);
}
function setMultiUser(uint256 data) internal pure returns (uint256) {
return _clearMultiUser(data) | (1 << GAME_OFFSET_MULTI_USER);
}
function isMultiUser(uint256 data) internal pure returns (bool) {
return uint8((data >> GAME_OFFSET_MULTI_USER) & MASK_MULTI) == 1;
}
function hasPauseExpired(uint256 data) internal view returns (bool) {
uint32 pauseTime = pauseEndTime(data);
return (pauseTime > 0 && block.timestamp > pauseTime && roundEndTime(data) < pauseTime);
}
function roundEndTime(uint256 data) internal pure returns (uint32) {
return uint32((data >> GAME_OFFSET_ROUND_TIME) & MASK_TIMESTAMP);
}
function setRoundEndTime(uint256 data, uint256 time) internal pure returns (uint256) {
return clearRoundEndTime(data) | (time << GAME_OFFSET_ROUND_TIME);
}
function clearRoundEndTime(uint256 data) internal pure returns (uint256) {
return (data & ~(MASK_TIMESTAMP << GAME_OFFSET_ROUND_TIME));
}
function pauseEndTime(uint256 data) internal pure returns (uint32) {
return uint32((data >> GAME_OFFSET_PAUSE_TIME) & MASK_TIMESTAMP);
}
function setPauseEndTime(uint256 data, uint256 time) internal pure returns (uint256) {
return clearPauseEndTime(data) | (time << GAME_OFFSET_PAUSE_TIME);
}
function clearPauseEndTime(uint256 data) internal pure returns (uint256) {
return (data & ~(MASK_TIMESTAMP << GAME_OFFSET_PAUSE_TIME));
}
function resetEndTime(uint256 data) internal pure returns (uint32) {
return uint32((data >> GAME_OFFSET_RESET_TIME) & MASK_TIMESTAMP);
}
function setResetEndTime(uint256 data, uint256 time) internal pure returns (uint256) {
return clearPauseEndTime(data) | (time << GAME_OFFSET_RESET_TIME);
}
function clearResetEndTime(uint256 data) internal pure returns (uint256) {
return (data & ~(MASK_TIMESTAMP << GAME_OFFSET_RESET_TIME));
}
function _clearMultiUser(uint256 data) private pure returns (uint256) {
return (data & ~(MASK_MULTI << GAME_OFFSET_MULTI_USER));
}
function _clearEndTimes(uint256 data) private pure returns (uint256) {
return (data & ~(MASK_PENDING << GAME_OFFSET_RESET_TIME));
}
function _isPending(uint256 data) private pure returns (bool) {
return ((data >> GAME_OFFSET_RESET_TIME) & MASK_PENDING) == 0;
}
}
文件 16 的 28:LibPRNG.sol
pragma solidity ^0.8.4;
library LibPRNG {
struct PRNG {
uint256 state;
}
function seed(PRNG memory prng, uint256 state) internal pure {
assembly {
mstore(prng, state)
}
}
function next(PRNG memory prng) internal pure returns (uint256 result) {
assembly {
result := keccak256(prng, 0x20)
mstore(prng, result)
}
}
function uniform(PRNG memory prng, uint256 upper) internal pure returns (uint256 result) {
assembly {
for {} 1 {} {
result := keccak256(prng, 0x20)
mstore(prng, result)
if iszero(lt(result, mod(sub(0, upper), upper))) { break }
}
result := mod(result, upper)
}
}
function shuffle(PRNG memory prng, uint256[] memory a) internal pure {
assembly {
let n := mload(a)
let w := not(0)
let mask := shr(128, w)
if n {
for { a := add(a, 0x20) } 1 {} {
let r := keccak256(prng, 0x20)
mstore(prng, r)
{
let j := add(a, shl(5, mod(shr(128, r), n)))
n := add(n, w)
if iszero(n) { break }
let i := add(a, shl(5, n))
let t := mload(i)
mstore(i, mload(j))
mstore(j, t)
}
{
let j := add(a, shl(5, mod(and(r, mask), n)))
n := add(n, w)
if iszero(n) { break }
let i := add(a, shl(5, n))
let t := mload(i)
mstore(i, mload(j))
mstore(j, t)
}
}
}
}
}
function shuffle(PRNG memory prng, bytes memory a) internal pure {
assembly {
let n := mload(a)
let w := not(0)
let mask := shr(128, w)
if n {
let b := add(a, 0x01)
for { a := add(a, 0x20) } 1 {} {
let r := keccak256(prng, 0x20)
mstore(prng, r)
{
let o := mod(shr(128, r), n)
n := add(n, w)
if iszero(n) { break }
let t := mload(add(b, n))
mstore8(add(a, n), mload(add(b, o)))
mstore8(add(a, o), t)
}
{
let o := mod(and(r, mask), n)
n := add(n, w)
if iszero(n) { break }
let t := mload(add(b, n))
mstore8(add(a, n), mload(add(b, o)))
mstore8(add(a, o), t)
}
}
}
}
}
}
文件 17 的 28:LibRefundable.sol
pragma solidity ^0.8.23;
library LibRefundable {
struct MintData {
mapping(bytes32 => uint256) _mintAmounts;
uint256[] _cancelledMints;
}
error ErrorMintNonRefundable();
function cancelMint(MintData storage self, uint256 mintId, uint256 total) internal {
self._mintAmounts[_mintKey(mintId, address(0))] = total;
self._cancelledMints.push(mintId);
}
function cancelledMints(MintData storage self) internal view returns (uint256[] memory) {
return self._cancelledMints;
}
function totalCancelledMints(MintData storage self) internal view returns (uint256) {
return self._cancelledMints.length;
}
function cancelledMintAtIndex(MintData storage self, uint256 index) internal view returns (uint256) {
return index < self._cancelledMints.length ? self._cancelledMints[index] : 0;
}
function addRefundableAmount(MintData storage self, uint256 mintId, address owner, uint256 amount) internal {
self._mintAmounts[_mintKey(mintId, owner)] += amount;
}
function getRefundableAmount(MintData storage self, uint256 mintId, address owner) internal view returns (uint256) {
return self._mintAmounts[_mintKey(mintId, owner)];
}
function removeRefundableAmount(MintData storage self, uint256 mintId, address owner) internal returns (uint256) {
bytes32 cancelKey = _mintKey(mintId, address(0));
bytes32 key = _mintKey(mintId, owner);
if (self._mintAmounts[cancelKey] == 0) {
revert ErrorMintNonRefundable();
}
uint256 refund = self._mintAmounts[key];
delete self._mintAmounts[key];
self._mintAmounts[cancelKey] -= refund;
if (self._mintAmounts[cancelKey] == 0) {
delete self._mintAmounts[cancelKey];
}
return refund;
}
function _mintKey(uint256 mintId, address owner) private pure returns (bytes32) {
return keccak256(abi.encodePacked(mintId, owner));
}
}
文件 18 的 28:LibShared.sol
pragma solidity ^0.8.23;
import "./Constants.sol";
library LibShared {
uint256 internal constant MASK_GAME_ROUND = 0xFFFFFFFF;
uint256 internal constant MASK_GAME_NUMBER = 0xFFFFFF;
uint256 internal constant MASK_COUNT = 0xFFFF;
uint256 internal constant MASK_ROUND = 0xFF;
function liveIndex(uint32 n) internal pure returns (uint8) {
return uint8((n + 1) & 1);
}
function safeIndex(uint32 n) internal pure returns (uint8) {
return uint8(n & 1);
}
function getLiveIndex(uint256 data) internal pure returns (uint8) {
return liveIndex(uint32((data >> DATA_OFFSET_ROUND_COUNT) & MASK_ROUND));
}
function getSafeIndex(uint256 data) internal pure returns (uint8) {
return safeIndex(uint32((data >> DATA_OFFSET_ROUND_COUNT) & MASK_ROUND));
}
function getGameNumber(uint256 data) internal pure returns (uint32) {
return uint32((data >> DATA_OFFSET_GAME_NUMBER) & MASK_GAME_NUMBER);
}
function getGameRound(uint256 data) internal pure returns (uint32) {
return uint32((data >> DATA_OFFSET_GAME_ROUND) & MASK_GAME_ROUND);
}
function setGameRound(uint256 data, uint32 gameRound) internal pure returns (uint256) {
return (data & ~(MASK_GAME_ROUND << DATA_OFFSET_GAME_ROUND)) | (uint256(gameRound) << DATA_OFFSET_GAME_ROUND);
}
function clearRound(uint256 data) internal pure returns (uint256) {
return (data & ~(MASK_ROUND << DATA_OFFSET_ROUND_COUNT));
}
function getLiveCount(uint256 data) internal pure returns (uint16) {
return uint16((data >> DATA_OFFSET_LIVE_COUNT) & MASK_COUNT);
}
function addLiveCount(uint256 data, uint16 count) internal pure returns (uint256) {
unchecked {
return data + (uint256(count) << DATA_OFFSET_LIVE_COUNT);
}
}
function subLiveCount(uint256 data, uint16 count) internal pure returns (uint256) {
unchecked {
return data - (uint256(count) << DATA_OFFSET_LIVE_COUNT);
}
}
function setLiveCount(uint256 data, uint16 count) internal pure returns (uint256) {
return clearLiveCount(data) | (uint256(count) << DATA_OFFSET_LIVE_COUNT);
}
function clearLiveCount(uint256 data) internal pure returns (uint256) {
return data & ~(MASK_COUNT << DATA_OFFSET_LIVE_COUNT);
}
function getSafeCount(uint256 data) internal pure returns (uint16) {
return uint16((data >> DATA_OFFSET_SAFE_COUNT) & MASK_COUNT);
}
function addSafeCount(uint256 data, uint16 count) internal pure returns (uint256) {
unchecked {
return data + (uint256(count) << DATA_OFFSET_SAFE_COUNT);
}
}
function subSafeCount(uint256 data, uint16 count) internal pure returns (uint256) {
unchecked {
return data - (uint256(count) << DATA_OFFSET_SAFE_COUNT);
}
}
function setSafeCount(uint256 data, uint16 count) internal pure returns (uint256) {
return clearSafeCount(data) | (uint256(count) << DATA_OFFSET_SAFE_COUNT);
}
function clearSafeCount(uint256 data) internal pure returns (uint256) {
return data & ~(MASK_COUNT << DATA_OFFSET_SAFE_COUNT);
}
function getBurnCount(uint256 data) internal pure returns (uint16) {
return uint16(data);
}
function addBurnCount(uint256 data, uint16 count) internal pure returns (uint256) {
unchecked {
return data + count;
}
}
function setBurnCount(uint256 data, uint16 count) internal pure returns (uint256) {
return clearBurnCount(data) | uint256(count);
}
function clearBurnCount(uint256 data) internal pure returns (uint256) {
return data & ~MASK_COUNT;
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
}
文件 19 的 28:LibString.sol
pragma solidity ^0.8.4;
library LibString {
error HexLengthInsufficient();
uint256 internal constant NOT_FOUND = type(uint256).max;
function toString(uint256 value) internal pure returns (string memory str) {
assembly {
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20))
mstore(str, 0)
let end := str
let w := not(0)
for { let temp := value } 1 {} {
str := add(str, w)
mstore8(str, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
str := sub(str, 0x20)
mstore(str, length)
}
}
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(uint256(-value));
}
assembly {
let length := mload(str)
mstore(str, 0x2d)
str := sub(str, 1)
mstore(str, add(length, 1))
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
assembly {
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
mstore(0x40, add(str, 0x20))
mstore(str, 0)
let end := str
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1)
let temp := value
for {} 1 {} {
str := add(str, w)
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a)
revert(0x1c, 0x04)
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength)
}
}
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30)
let strLength := add(mload(str), 2)
mstore(add(str, o), 0x3078)
str := sub(add(str, o), 2)
mstore(str, sub(strLength, o))
}
}
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30)
let strLength := mload(str)
str := add(str, o)
mstore(str, sub(strLength, o))
}
}
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
assembly {
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20))
mstore(str, 0)
let end := str
mstore(0x0f, 0x30313233343536373839616263646566)
let w := not(1)
for { let temp := value } 1 {} {
str := add(str, w)
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength)
}
}
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
assembly {
let mask := shl(6, div(not(0), 255))
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask))
let t := shl(240, 136)
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
assembly {
str := mload(0x40)
mstore(0x40, add(str, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
value := shl(96, value)
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
assembly {
let strLength := add(mload(str), 2)
mstore(str, 0x3078)
str := sub(str, 2)
mstore(str, strLength)
}
}
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
assembly {
let length := mload(raw)
str := add(mload(0x40), 2)
mstore(str, add(length, length))
mstore(0x0f, 0x30313233343536373839616263646566)
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0)
mstore(0x40, add(o, 0x20))
}
}
function runeCount(string memory s) internal pure returns (uint256 result) {
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
function is7BitASCII(string memory s) internal pure returns (bool result) {
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k)
mstore(last, 0)
mstore(0x40, add(last, 0x20))
mstore(result, k)
}
}
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0)
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
assembly {
for {} 1 {} {
result := not(0)
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w)
if iszero(gt(subject, end)) { break }
}
break
}
}
}
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
assembly {
let searchLength := mload(search)
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
let withinRange := iszero(gt(searchLength, subjectLength))
result := and(
withinRange,
eq(
keccak256(
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0)
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength)
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w)
if iszero(o) { break }
}
mstore(add(add(result, 0x20), resultLength), 0)
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
result := mload(0x40)
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
mstore(0x40, add(resultEnd, 0x20))
}
}
}
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w)
if iszero(o) { break }
}
mstore(add(add(element, 0x20), elementLength), 0)
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w)
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w)
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
mstore(last, 0)
mstore(result, totalLength)
mstore(0x40, and(add(last, 0x1f), w))
}
}
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length)
let last := add(add(result, 0x20), length)
mstore(last, 0)
mstore(0x40, add(last, 0x20))
}
}
}
function fromSmallString(bytes32 smallString) internal pure returns (string memory result) {
if (smallString == bytes32(0)) return result;
assembly {
result := mload(0x40)
let n := 0
for {} 1 {} {
n := add(n, 1)
if iszero(byte(n, smallString)) { break }
}
mstore(result, n)
let o := add(result, 0x20)
mstore(o, smallString)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
function escapeHTML(string memory s) internal pure returns (string memory result) {
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0)
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20)))
mstore(0x40, add(last, 0x20))
}
}
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c)
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
mstore8(0x1d, mload(shr(4, c)))
mstore8(0x1e, mload(and(c, 15)))
mstore(result, mload(0x19))
result := add(result, 6)
continue
}
mstore8(result, 0x5c)
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0)
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20)))
mstore(0x40, add(last, 0x20))
}
}
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
function eq(string memory a, string memory b) internal pure returns (bool result) {
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
assembly {
let x := and(b, add(not(b), 1))
let r := or(shl(8, iszero(b)), shl(7, iszero(iszero(shr(128, x)))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
result := gt(eq(mload(a), sub(32, shr(3, r))), shr(r, xor(b, mload(add(a, 0x20)))))
}
}
function packOne(string memory a) internal pure returns (bytes32 result) {
assembly {
result :=
mul(
mload(add(a, 0x1f)),
lt(sub(mload(a), 1), 0x1f)
)
}
}
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
assembly {
result := mload(0x40)
mstore(0x40, add(result, 0x40))
mstore(result, 0)
mstore(add(result, 0x1f), packed)
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
assembly {
let aLength := mload(a)
result :=
mul(
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
assembly {
resultA := mload(0x40)
resultB := add(resultA, 0x40)
mstore(0x40, add(resultB, 0x40))
mstore(resultA, 0)
mstore(resultB, 0)
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
function directReturn(string memory a) internal pure {
assembly {
let retStart := sub(a, 0x20)
let retSize := add(mload(a), 0x40)
mstore(add(retStart, retSize), 0)
mstore(retStart, 0x20)
return(retStart, retSize)
}
}
}
文件 20 的 28:LibUser.sol
pragma solidity ^0.8.23;
import {LibShared} from "./LibShared.sol";
import {LibGame, GameStatus} from "./LibGame.sol";
import "./Constants.sol";
enum UserStatus {
EXPIRED,
STALE,
VALID
}
library LibUser {
using LibGame for LibGame.Game;
using LibShared for uint256;
using LibGame for uint256;
using LibShared for uint32;
uint256 private constant COMMIT_WINDOW = 120;
uint256 private constant OFFSET_COMMIT = 32;
uint256 private constant USER_OFFSET_OWNER_ADDR = 64;
uint256 private constant INFO_OFFSET_IS_PLAYING = 0;
uint256 private constant INFO_OFFSET_PERMISSION = 1;
uint256 private constant INFO_OFFSET_BURN_COUNT = 8;
uint256 private constant INFO_OFFSET_SAFE_COUNT = 24;
uint256 private constant INFO_OFFSET_LIVE_COUNT = 40;
uint8 private constant PERM_DENIED = 0x0;
uint8 private constant PERM_COMMIT = 0x1;
uint8 private constant PERM_REVEAL = 0x2;
uint8 private constant PERM_CLAIMS = 0x4;
struct User {
uint256 data;
uint256 lastCommit;
}
error ErrorCommitDenied();
error ErrorCommitPrevious();
error ErrorRevealDenied();
error ErrorRevealLength();
error ErrorRevealMismatch();
function isInvalid(User storage self,
uint32 gameRound
) internal view returns (bool) {
uint256 data = self.data;
unchecked {
return (
(data == 0) ||
(gameRound - data.getGameRound() >= 2) ||
(data.getLiveCount() == 0 && data.getSafeCount() == 0)
);
}
}
function initUser(User storage self,
address addy,
uint32 gameRound
) internal returns (uint256) {
return _initUser(self, addy, gameRound);
}
function isExpired(User storage self,
uint32 gameRound
) internal view returns (bool) {
return _isExpired(self, gameRound);
}
function commit(User storage self,
uint32 gameRound,
GameStatus status,
bytes32 hash
) internal {
_commit(self, gameRound, status, hash);
}
function reveal(User storage self,
uint32 gameRound,
GameStatus status,
bytes memory secret
) internal {
_reveal(self, gameRound, status, secret);
}
function getUserInfo(User storage self,
LibGame.Game storage game
) internal view returns (uint256) {
return _getUserInfo(self, game);
}
function _initUser(User storage self,
address addr,
uint32 gameRound
) private returns (uint256) {
uint256 data = self.data;
uint32 prevGameRound = data.getGameRound();
unchecked {
uint32 nextGameRound = prevGameRound + 1;
if (data == 0 || gameRound - prevGameRound >= 2) {
data = uint256(uint160(addr)) << USER_OFFSET_OWNER_ADDR;
self.lastCommit = 0;
} else if (nextGameRound == gameRound) {
data = data.addBurnCount(data.getLiveCount());
data = data.setLiveCount(data.getSafeCount());
data = data.clearSafeCount();
self.lastCommit = 0;
}
}
data = data.setGameRound(gameRound);
self.data = data;
return data;
}
function _isExpired(User storage self, uint32 gameRound) private view returns (bool) {
return gameRound - self.data.getGameRound() >= 2;
}
function _commit(User storage self,
uint32 gameRound,
GameStatus status,
bytes32 hash
) private {
_initUser(self, address(0), gameRound);
if (self.lastCommit != 0) revert ErrorCommitPrevious();
if ((_getPermissions(self, status, gameRound) != PERM_COMMIT)) revert ErrorCommitDenied();
self.lastCommit = (uint256(hash) << OFFSET_COMMIT) | (block.number + COMMIT_WINDOW);
}
function _reveal(User storage self,
uint32 gameRound,
GameStatus status,
bytes memory secret
) private {
if (secret.length != 4) revert ErrorRevealLength();
if (_getPermissions(self, status, gameRound) != PERM_REVEAL) revert ErrorRevealDenied();
bytes32 hash = keccak256(secret);
self.lastCommit = uint256(hash) << OFFSET_COMMIT;
if (self.lastCommit != (self.lastCommit & ~uint256(0xFFFFFFFF))) revert ErrorRevealMismatch();
return;
}
function _getStatus(User storage self,
uint32 gameRound
) private view returns (UserStatus) {
uint256 lastGameRound = self.data.getGameRound();
if (lastGameRound == gameRound) return UserStatus.VALID;
unchecked {
if (lastGameRound + 1 == gameRound) return UserStatus.STALE;
}
return UserStatus.EXPIRED;
}
function _getPermissions(User storage self,
GameStatus status,
uint32 gameRound
) private view returns (uint8) {
uint256 data = self.data;
uint32 lastGameRound = data.getGameRound();
uint16 count = data.getLiveCount();
if ((status == GameStatus.PENDING || status == GameStatus.RUNNING) && (lastGameRound + 1 == gameRound)) {
count = data.getSafeCount();
}
if ((status != GameStatus.PENDING && status != GameStatus.RUNNING) ||
(gameRound - lastGameRound >= 2) || (count == 0)) {
return PERM_DENIED;
}
uint256 lastCommit = self.lastCommit;
if (lastCommit == 0) return PERM_COMMIT;
uint32 blockNumber = uint32(lastCommit);
if ((blockNumber == 0) && (lastGameRound + 1 == gameRound) &&
(lastCommit > REVEAL_THRESHOLD)) {
return PERM_COMMIT;
}
if (lastCommit <= REVEAL_THRESHOLD) return PERM_DENIED;
return (blockNumber > 1 && block.number <= blockNumber) ? PERM_REVEAL : PERM_CLAIMS;
}
function _getUserInfo(User storage self,
LibGame.Game storage game
) private view returns (uint256) {
uint256 userData = self.data;
uint32 lastGameRound = userData.getGameRound();
if (lastGameRound == 0) return 0;
uint256 gameData = game.data;
uint256 resetEndTime = gameData.resetEndTime();
uint32 gameRound = gameData.getGameRound();
uint16 liveCount = userData.getLiveCount();
uint16 safeCount = userData.getSafeCount();
uint16 burnCount = userData.getBurnCount();
GameStatus status = game.getStatus();
if (status > GameStatus.RUNNING) {
if (resetEndTime == 0) {
resetEndTime = game.virtualResetEndTime(status);
}
if (resetEndTime != 0 && block.timestamp > resetEndTime) {
gameRound = game.nextGameRound();
}
}
unchecked {
uint256 isPlayer = (userData.getGameNumber() == gameData.getGameNumber() &&
(liveCount | safeCount | burnCount) != 0) ? 1 : 0;
if (((status == GameStatus.PAUSING || status == GameStatus.PENDING) && (gameData.pauseEndTime() != 0))) {
gameRound++;
}
uint256 perms = _getPermissions(self, status, gameRound);
if (gameRound - lastGameRound >= 2) {
burnCount += (liveCount + safeCount);
liveCount = safeCount = 0;
}
else if ((lastGameRound + 1 == gameRound) || (status > GameStatus.RUNNING)) {
burnCount += liveCount;
liveCount = 0;
if (status == GameStatus.PENDING || status == GameStatus.RUNNING) {
(liveCount, safeCount) = (safeCount, 0);
} else if (status > GameStatus.RUNNING) {
burnCount += safeCount;
safeCount = 0;
}
}
return
(isPlayer << INFO_OFFSET_IS_PLAYING) |
(perms << INFO_OFFSET_PERMISSION) |
(uint256(burnCount) << INFO_OFFSET_BURN_COUNT) |
(uint256(safeCount) << INFO_OFFSET_SAFE_COUNT) |
(uint256(liveCount) << INFO_OFFSET_LIVE_COUNT);
}
}
}
文件 21 的 28:LibWinners.sol
pragma solidity ^0.8.23;
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {LibShared} from "./LibShared.sol";
import "./Constants.sol";
library LibWinners {
using EnumerableMap for EnumerableMap.UintToUintMap;
using LibShared for uint32;
uint256 private constant WINNER_OFFSET_ADDRESS = 96;
uint256 private constant WINNER_OFFSET_ID = 32;
uint32 private constant WINNER_MASK_ID = 0xFFFFFFFF;
struct Winner {
uint256 data;
uint256 tokenId;
uint256 prize;
}
struct Winners {
mapping(uint256 => Winner) _winningData;
EnumerableMap.UintToUintMap _gameWinners;
}
function recordWinner(Winners storage self,
uint256 tokenId,
uint256 prize,
uint32 gameRound,
address winnerAddress
) internal {
_recordWinner(self, tokenId, prize, gameRound, winnerAddress);
}
function hasWinner(Winners storage self,
uint256 tokenId
) internal view returns (bool) {
return self._winningData[tokenId].data != 0;
}
function totalWinners(Winners storage self
) internal view returns (uint256) {
return self._gameWinners.length();
}
function getWinnerAt(Winners storage self,
uint256 index
) internal view returns (Winner memory) {
(, uint256 tokenId) = self._gameWinners.at(index);
return self._winningData[tokenId];
}
function getWinner(Winners storage self,
uint32 gameNumber
) internal view returns (Winner memory) {
return _getWinner(self, gameNumber);
}
function getWinnerId(Winners storage self,
uint256 tokenId
) internal view returns (uint32) {
return uint32((self._winningData[tokenId].data >> WINNER_OFFSET_ID) & WINNER_MASK_ID);
}
function packWinnerData(Winners storage self,
uint32 gameRound,
address winnerAddress
) internal view returns (uint256) {
return
(uint256(uint160(winnerAddress)) << WINNER_OFFSET_ADDRESS) |
(self._gameWinners.length() << WINNER_OFFSET_ID) |
uint256(gameRound);
}
function _recordWinner(Winners storage self,
uint256 tokenId,
uint256 prize,
uint32 gameRound,
address winnerAddress
) private {
self._gameWinners.set(gameRound >> OFFSET_GAME_NUMBER, tokenId);
self._winningData[tokenId] = Winner({
data: packWinnerData(self, gameRound, winnerAddress),
tokenId: tokenId,
prize: prize
});
}
function _getWinner(Winners storage self,
uint32 gameNumber
) private view returns (Winner memory) {
Winner memory winner;
(bool isFound, uint256 tokenId) = self._gameWinners.tryGet(gameNumber);
if (!isFound) return winner;
winner = self._winningData[tokenId];
if (tokenId >= FORFEIT_TOKEN_ID) winner.tokenId = 0;
return winner;
}
}
文件 22 的 28:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 23 的 28:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 24 的 28:PullPayment.sol
pragma solidity ^0.8.0;
import "./Escrow.sol";
abstract contract PullPayment {
Escrow private immutable _escrow;
constructor() {
_escrow = new Escrow();
}
function withdrawPayments(address payable payee) public virtual {
_escrow.withdraw(payee);
}
function payments(address dest) public view returns (uint256) {
return _escrow.depositsOf(dest);
}
function _asyncTransfer(address dest, uint256 amount) internal virtual {
_escrow.deposit{value: amount}(dest);
}
}
文件 25 的 28:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 26 的 28:SignedMath.sol
pragma solidity ^0.8.20;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 27 的 28:Strings.sol
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
error StringsInsufficientHexLength(uint256 value, uint256 length);
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 28 的 28:TimeLock.sol
pragma solidity ^0.8.23;
contract TimeLock {
uint256 internal constant MAX_LOCK = type(uint256).max;
uint256 public startTime;
uint256 public lastTime;
uint256 public startLock;
uint256 public lastLock;
error ErrorTimeLocked(uint256 remaining);
constructor(uint256 _startLock, uint256 _lastLock) {
startLock = _startLock;
lastLock = _lastLock;
}
modifier timeLocked() {
uint256 t = timeLockLeft();
if (t > 0) revert ErrorTimeLocked(t);
_;
}
function timeLock() internal {
if (timeLockExpired()) return;
lastTime = block.timestamp;
if (startTime == 0) startTime = lastTime;
}
function resetTimeLock() internal {
startTime = lastTime = 0;
}
function timeLockLeft() internal view returns (uint256) {
if (startTime == 0) return MAX_LOCK;
uint256 cancelTime = _cancelTime(startLock, lastLock);
return (block.timestamp < cancelTime) ? cancelTime - block.timestamp : 0;
}
function timeLockExpired() internal view returns (bool) {
return startTime > 0 && block.timestamp > (startTime + startLock);
}
function _cancelTime(uint256 _startLock, uint256 _lastLock) private view returns (uint256) {
uint256 result;
assembly {
let s := add(sload(startTime.slot), _startLock)
let l := add(sload(lastTime.slot), _lastLock)
result := or(mul(lt(s, l), s), mul(iszero(lt(s, l)), l))
}
return result;
}
}
{
"compilationTarget": {
"BurnItDAO.sol": "BurnItDAO"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint32","name":"contractId","type":"uint32"},{"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"internalType":"uint16","name":"maxTokens","type":"uint16"},{"internalType":"uint16","name":"maxWallet","type":"uint16"},{"internalType":"uint16","name":"maxAmount","type":"uint16"},{"internalType":"uint8","name":"teamSplit","type":"uint8"},{"internalType":"address payable","name":"teamAddress","type":"address"},{"internalType":"address payable","name":"drawAddress","type":"address"},{"internalType":"string","name":"baseTokenURI","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ErrorClaimInvalidBurn","type":"error"},{"inputs":[],"name":"ErrorClaimInvalidOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ErrorClaimInvalidToken","type":"error"},{"inputs":[],"name":"ErrorClaimInvalidUser","type":"error"},{"inputs":[],"name":"ErrorClaimMaxWallet","type":"error"},{"inputs":[],"name":"ErrorClaimPermissionDenied","type":"error"},{"inputs":[],"name":"ErrorClaimRoundClosed","type":"error"},{"inputs":[],"name":"ErrorClaimUnavailable","type":"error"},{"inputs":[],"name":"ErrorCommitDenied","type":"error"},{"inputs":[],"name":"ErrorCommitInvalidUser","type":"error"},{"inputs":[],"name":"ErrorCommitPrevious","type":"error"},{"inputs":[],"name":"ErrorDoNotSendDirectEth","type":"error"},{"inputs":[],"name":"ErrorDrawAddress","type":"error"},{"inputs":[],"name":"ErrorGameNotRunning","type":"error"},{"inputs":[],"name":"ErrorInvalidTokenURI","type":"error"},{"inputs":[],"name":"ErrorMaxAmount","type":"error"},{"inputs":[],"name":"ErrorMaxTokens","type":"error"},{"inputs":[],"name":"ErrorMaxWallet","type":"error"},{"inputs":[],"name":"ErrorMintComplete","type":"error"},{"inputs":[],"name":"ErrorMintExpired","type":"error"},{"inputs":[],"name":"ErrorMintMaxTokens","type":"error"},{"inputs":[],"name":"ErrorMintMaxWallet","type":"error"},{"inputs":[],"name":"ErrorMintNonRefundable","type":"error"},{"inputs":[],"name":"ErrorMintNotActive","type":"error"},{"inputs":[],"name":"ErrorMintResetting","type":"error"},{"inputs":[],"name":"ErrorMintTxAmount","type":"error"},{"inputs":[],"name":"ErrorMintTxPrice","type":"error"},{"inputs":[],"name":"ErrorNonRefundable","type":"error"},{"inputs":[],"name":"ErrorRevealDenied","type":"error"},{"inputs":[],"name":"ErrorRevealLength","type":"error"},{"inputs":[],"name":"ErrorRevealMismatch","type":"error"},{"inputs":[],"name":"ErrorTeamAddress","type":"error"},{"inputs":[],"name":"ErrorTeamSplit","type":"error"},{"inputs":[{"internalType":"uint256","name":"remaining","type":"uint256"}],"name":"ErrorTimeLocked","type":"error"},{"inputs":[],"name":"ErrorTokenPrice","type":"error"},{"inputs":[],"name":"ErrorTransferDenied","type":"error"},{"inputs":[],"name":"ErrorTransferInvalidBalance","type":"error"},{"inputs":[],"name":"ErrorTransferInvalidUser","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"OperatorQueryForNonexistentToken","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint32","name":"gameRound","type":"uint32"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint32","name":"gameRound","type":"uint32"}],"name":"Commit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"gameRound","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"mintCount","type":"uint256"}],"name":"GameCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"gameRound","type":"uint32"},{"indexed":false,"internalType":"address","name":"winner","type":"address"}],"name":"GameOver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"gameRound","type":"uint32"}],"name":"GameStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"uint32","name":"gameRound","type":"uint32"}],"name":"Reveal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"gameRound","type":"uint32"}],"name":"RoundStart","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canCancelGame","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"cancelledGameAtIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelledGames","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"commit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"config","outputs":[{"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"address","name":"drawAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGameInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"gameNumber","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"getRefundAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getTokenStatus","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getUserInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"gameNumber","type":"uint32"}],"name":"getWinner","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"prize","type":"uint256"}],"internalType":"struct LibWinners.Winner","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getWinnerAtIndex","outputs":[{"components":[{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"prize","type":"uint256"}],"internalType":"struct LibWinners.Winner","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isGameExpired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isGameFinalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"idx","type":"uint256"}],"name":"isTokenOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"liveTokenOfOwner","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"amount","type":"uint16"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dest","type":"address"}],"name":"payments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"gameNumber","type":"uint32"},{"internalType":"address payable","name":"owner","type":"address"}],"name":"refund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"secret","type":"bytes"}],"name":"reveal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"teamAddress","type":"address"},{"internalType":"address payable","name":"drawAddress","type":"address"}],"name":"setWallets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCancelledGames","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalWinners","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"payee","type":"address"}],"name":"withdrawPayments","outputs":[],"stateMutability":"nonpayable","type":"function"}]