Polygon Sponsored slots available. Book your slot here!
Contract Overview
My Name Tag:
Not Available, login to update
[ Download CSV Export ]
Latest 2 internal transactions
Parent Txn Hash | Block | From | To | Value | |||
---|---|---|---|---|---|---|---|
0xc4ab96c1498182136206ea91e3cbb0d30e081e54e69a9582964c222d8b04fc2c | 43206903 | 7 days 8 hrs ago | 0x58645a4488b9e7a05a3ec5dc874bfa5abbd62cfc | 0xdbb55ed3b9ef6cc79a79872f68b0cc7cb5ef7e51 | 5.58 MATIC | ||
0x2df7eb29e4fb94807e001728da116ed4ce15e914c1038bd7fc1cbf0eceebc4c0 | 41465875 | 51 days 17 hrs ago | 0x58645a4488b9e7a05a3ec5dc874bfa5abbd62cfc | 0xd9683a0aaf63af132e93f9d46656e63b0a5f76bf | 9.65 MATIC |
[ Download CSV Export ]
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
RussianRoulette
Compiler Version
v0.8.17+commit.8df45f5f
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface VRFCoordinatorV2Interface { /** * @notice Get configuration relevant for making requests * @return minimumRequestConfirmations global min for request confirmations * @return maxGasLimit global max for request gas limit * @return s_provingKeyHashes list of registered key hashes */ function getRequestConfig() external view returns ( uint16, uint32, bytes32[] memory ); /** * @notice Request a set of random words. * @param keyHash - Corresponds to a particular oracle job which uses * that key for generating the VRF proof. Different keyHash's have different gas price * ceilings, so you can select a specific one to bound your maximum per request cost. * @param subId - The ID of the VRF subscription. Must be funded * with the minimum subscription balance required for the selected keyHash. * @param minimumRequestConfirmations - How many blocks you'd like the * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS * for why you may want to request more. The acceptable range is * [minimumRequestBlockConfirmations, 200]. * @param callbackGasLimit - How much gas you'd like to receive in your * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords * may be slightly less than this amount because of gas used calling the function * (argument decoding etc.), so you may need to request slightly more than you expect * to have inside fulfillRandomWords. The acceptable range is * [0, maxGasLimit] * @param numWords - The number of uint256 random values you'd like to receive * in your fulfillRandomWords callback. Note these numbers are expanded in a * secure way by the VRFCoordinator from a single random value supplied by the oracle. * @return requestId - A unique identifier of the request. Can be used to match * a request to a response in fulfillRandomWords. */ function requestRandomWords( bytes32 keyHash, uint64 subId, uint16 minimumRequestConfirmations, uint32 callbackGasLimit, uint32 numWords ) external returns (uint256 requestId); /** * @notice Create a VRF subscription. * @return subId - A unique subscription id. * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer. * @dev Note to fund the subscription, use transferAndCall. For example * @dev LINKTOKEN.transferAndCall( * @dev address(COORDINATOR), * @dev amount, * @dev abi.encode(subId)); */ function createSubscription() external returns (uint64 subId); /** * @notice Get a VRF subscription. * @param subId - ID of the subscription * @return balance - LINK balance of the subscription in juels. * @return reqCount - number of requests for this subscription, determines fee tier. * @return owner - owner of the subscription. * @return consumers - list of consumer address which are able to use this subscription. */ function getSubscription(uint64 subId) external view returns ( uint96 balance, uint64 reqCount, address owner, address[] memory consumers ); /** * @notice Request subscription owner transfer. * @param subId - ID of the subscription * @param newOwner - proposed new owner of the subscription */ function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external; /** * @notice Request subscription owner transfer. * @param subId - ID of the subscription * @dev will revert if original owner of subId has * not requested that msg.sender become the new owner. */ function acceptSubscriptionOwnerTransfer(uint64 subId) external; /** * @notice Add a consumer to a VRF subscription. * @param subId - ID of the subscription * @param consumer - New consumer which can use the subscription */ function addConsumer(uint64 subId, address consumer) external; /** * @notice Remove a consumer from a VRF subscription. * @param subId - ID of the subscription * @param consumer - Consumer to remove from the subscription */ function removeConsumer(uint64 subId, address consumer) external; /** * @notice Cancel a subscription * @param subId - ID of the subscription * @param to - Where to send the remaining LINK to */ function cancelSubscription(uint64 subId, address to) external; /* * @notice Check to see if there exists a request commitment consumers * for all consumers and keyhashes for a given sub. * @param subId - ID of the subscription * @return true if there exists at least one unfulfilled request for the subscription, false * otherwise. */ function pendingRequestExists(uint64 subId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** **************************************************************************** * @notice Interface for contracts using VRF randomness * ***************************************************************************** * @dev PURPOSE * * @dev Reggie the Random Oracle (not his real job) wants to provide randomness * @dev to Vera the verifier in such a way that Vera can be sure he's not * @dev making his output up to suit himself. Reggie provides Vera a public key * @dev to which he knows the secret key. Each time Vera provides a seed to * @dev Reggie, he gives back a value which is computed completely * @dev deterministically from the seed and the secret key. * * @dev Reggie provides a proof by which Vera can verify that the output was * @dev correctly computed once Reggie tells it to her, but without that proof, * @dev the output is indistinguishable to her from a uniform random sample * @dev from the output space. * * @dev The purpose of this contract is to make it easy for unrelated contracts * @dev to talk to Vera the verifier about the work Reggie is doing, to provide * @dev simple access to a verifiable source of randomness. It ensures 2 things: * @dev 1. The fulfillment came from the VRFCoordinator * @dev 2. The consumer contract implements fulfillRandomWords. * ***************************************************************************** * @dev USAGE * * @dev Calling contracts must inherit from VRFConsumerBase, and can * @dev initialize VRFConsumerBase's attributes in their constructor as * @dev shown: * * @dev contract VRFConsumer { * @dev constructor(<other arguments>, address _vrfCoordinator, address _link) * @dev VRFConsumerBase(_vrfCoordinator) public { * @dev <initialization with other arguments goes here> * @dev } * @dev } * * @dev The oracle will have given you an ID for the VRF keypair they have * @dev committed to (let's call it keyHash). Create subscription, fund it * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface * @dev subscription management functions). * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations, * @dev callbackGasLimit, numWords), * @dev see (VRFCoordinatorInterface for a description of the arguments). * * @dev Once the VRFCoordinator has received and validated the oracle's response * @dev to your request, it will call your contract's fulfillRandomWords method. * * @dev The randomness argument to fulfillRandomWords is a set of random words * @dev generated from your requestId and the blockHash of the request. * * @dev If your contract could have concurrent requests open, you can use the * @dev requestId returned from requestRandomWords to track which response is associated * @dev with which randomness request. * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind, * @dev if your contract could have multiple requests in flight simultaneously. * * @dev Colliding `requestId`s are cryptographically impossible as long as seeds * @dev differ. * * ***************************************************************************** * @dev SECURITY CONSIDERATIONS * * @dev A method with the ability to call your fulfillRandomness method directly * @dev could spoof a VRF response with any random value, so it's critical that * @dev it cannot be directly called by anything other than this base contract * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). * * @dev For your users to trust that your contract's random behavior is free * @dev from malicious interference, it's best if you can write it so that all * @dev behaviors implied by a VRF response are executed *during* your * @dev fulfillRandomness method. If your contract must store the response (or * @dev anything derived from it) and use it later, you must ensure that any * @dev user-significant behavior which depends on that stored value cannot be * @dev manipulated by a subsequent VRF request. * * @dev Similarly, both miners and the VRF oracle itself have some influence * @dev over the order in which VRF responses appear on the blockchain, so if * @dev your contract could have multiple VRF requests in flight simultaneously, * @dev you must ensure that the order in which the VRF responses arrive cannot * @dev be used to manipulate your contract's user-significant behavior. * * @dev Since the block hash of the block which contains the requestRandomness * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful * @dev miner could, in principle, fork the blockchain to evict the block * @dev containing the request, forcing the request to be included in a * @dev different block with a different hash, and therefore a different input * @dev to the VRF. However, such an attack would incur a substantial economic * @dev cost. This cost scales with the number of blocks the VRF oracle waits * @dev until it calls responds to a request. It is for this reason that * @dev that you can signal to an oracle you'd like them to wait longer before * @dev responding to the request (however this is not enforced in the contract * @dev and so remains effective only in the case of unmodified oracle software). */ abstract contract VRFConsumerBaseV2 { error OnlyCoordinatorCanFulfill(address have, address want); address private immutable vrfCoordinator; /** * @param _vrfCoordinator address of VRFCoordinator contract */ constructor(address _vrfCoordinator) { vrfCoordinator = _vrfCoordinator; } /** * @notice fulfillRandomness handles the VRF response. Your contract must * @notice implement it. See "SECURITY CONSIDERATIONS" above for important * @notice principles to keep in mind when implementing your fulfillRandomness * @notice method. * * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this * @dev signature, and will call it once it has verified the proof * @dev associated with the randomness. (It is triggered via a call to * @dev rawFulfillRandomness, below.) * * @param requestId The Id initially returned by requestRandomness * @param randomWords the VRF output expanded to the requested number of words */ function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual; // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF // proof. rawFulfillRandomness then calls fulfillRandomness, after validating // the origin of the call function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external { if (msg.sender != vrfCoordinator) { revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator); } fulfillRandomWords(requestId, randomWords); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol) pragma solidity ^0.8.0; import "./Address.sol"; /** * @dev Provides a function to batch together multiple calls in a single external call. * * _Available since v4.1._ */ abstract contract Multicall { /** * @dev Receives and executes a batch of function calls on this contract. */ function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { results[i] = Address.functionDelegateCall(address(this), data[i]); } return results; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface IPvPGamesStore { /// @notice Token's house edge allocations struct. /// The games house edge is split into several allocations. /// The allocated amounts stays in the controct until authorized parties withdraw. They are subtracted from the balance. /// @param dividend Rate to be allocated as staking rewards, on bet payout. /// @param treasury Rate to be allocated to the treasury, on bet payout. /// @param team Rate to be allocated to the team, on bet payout. /// @param dividendAmount The number of tokens to be sent as staking rewards. /// @param treasuryAmount The number of tokens to be sent to the treasury. /// @param teamAmount The number of tokens to be sent to the team. struct HouseEdgeSplit { uint16 dividend; uint16 treasury; uint16 team; uint16 initiator; } /// @notice Token struct. /// @param houseEdge House edge rate. /// @param pendingCount Number of pending bets. /// @param VRFFees Chainlink's VRF collected fees amount. struct Token { uint64 vrfSubId; HouseEdgeSplit houseEdgeSplit; } /// @notice Token's metadata struct. It contains additional information from the ERC20 token. /// @dev Only used on the `getTokens` getter for the front-end. /// @param decimals Number of token's decimals. /// @param tokenAddress Contract address of the token. /// @param name Name of the token. /// @param symbol Symbol of the token. /// @param token Token data. struct TokenMetadata { uint8 decimals; address tokenAddress; string name; string symbol; Token token; } function setHouseEdgeSplit( address token, uint16 dividend, uint16 treasury, uint16 team, uint16 initiator ) external; function addToken(address token) external; function setVRFSubId(address token, uint64 vrfSubId) external; function setTeamWallet(address teamWallet) external; function getTokenConfig(address token) external view returns (Token memory config); function getTreasuryAndTeamAddresses() external view returns (address, address); function getTokensAddresses() external view returns (address[] memory); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol"; import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; import {IPvPGamesStore} from "./IPvPGamesStore.sol"; // import "hardhat/console.sol"; /// @title PvPGame base contract /// @author BetSwirl.eth /// @notice This should be parent contract of each games. /// It defines all the games common functions and state variables. /// @dev All rates are in basis point. Chainlink VRF v2 is used. abstract contract PvPGame is Pausable, Multicall, VRFConsumerBaseV2, ReentrancyGuard, Ownable { using SafeERC20 for IERC20; /// @notice Bet information struct. /// @param token Address of the token. /// @param resolved Whether the bet has been resolved. /// @param canceled Whether the bet has been canceled. /// @param id Bet ID. /// @param vrfRequestTimestamp Block timestamp of the VRF request used to refund in case. /// @param houseEdge House edge that'll be charged. /// @param opponents Addresses of the opponents. /// @param seats Players addresses of each seat. /// @param vrfRequestId Request ID generated by Chainlink VRF. /// @param amount The buy-in amount. /// @param payout The total paid amount, minus fees if applied. /// @param pot The current prize pool is the sum of all buy-ins from players. struct Bet { address token; bool resolved; bool canceled; uint24 id; uint32 vrfRequestTimestamp; uint16 houseEdge; address[] opponents; address[] seats; uint256 vrfRequestId; uint256 amount; uint256 payout; uint256 pot; } /// @notice stores the NFTs params struct NFTs { IERC721 nftContract; uint256[] tokenIds; address[] to; } /// @notice Maps bet ID -> NFTs struct. mapping(uint24 => NFTs[]) public betNFTs; /// @notice Maps bet ID -> NFT contract -> token ID for claimed NFTs mapping(uint24 => mapping(IERC721 => mapping(uint256 => bool))) public claimedNFTs; /// @notice Token's house edge allocations struct. /// The games house edge is split into several allocations. /// The allocated amounts stays in the contract until authorized parties withdraw. /// NB: The initiator allocation is stored on the `payouts` mapping. /// @param dividendAmount The number of tokens to be sent as staking rewards. /// @param treasuryAmount The number of tokens to be sent to the treasury. /// @param teamAmount The number of tokens to be sent to the team. struct HouseEdgeSplit { uint256 dividendAmount; uint256 treasuryAmount; uint256 teamAmount; } /// @notice Token struct. /// @param houseEdge House edge rate. /// @param VRFCallbackGasLimit How much gas is needed in the Chainlink VRF callback. /// @param houseEdgeSplit House edge allocations. struct Token { uint16 houseEdge; uint32 VRFCallbackGasLimit; HouseEdgeSplit houseEdgeSplit; } /// @notice Maps tokens addresses to token configuration. mapping(address => Token) public tokens; /// @notice Maximum number of NFTs per game. uint16 public maxNFTs; /// @notice Chainlink VRF configuration struct. /// @param requestConfirmations How many confirmations the Chainlink node should wait before responding. /// @param keyHash Hash of the public key used to verify the VRF proof. /// @param chainlinkCoordinator Reference to the VRFCoordinatorV2 deployed contract. struct ChainlinkConfig { uint16 requestConfirmations; bytes32 keyHash; VRFCoordinatorV2Interface chainlinkCoordinator; } /// @notice Chainlink VRF configuration state. ChainlinkConfig private _chainlinkConfig; /// @notice The PvPGamesStore contract that contains the tokens configuration. IPvPGamesStore public pvpGamesStore; /// @notice Address allowed to harvest dividends. address public harvester; /// @notice Maps bets IDs to Bet information. mapping(uint24 => Bet) public bets; /// @notice Bet ID nonce. uint24 public betId = 1; /// @notice Maps VRF request IDs to bet ID. mapping(uint256 => uint24) internal _betsByVrfRequestId; /// @notice Maps user -> token -> amount for due payouts mapping(address => mapping(address => uint256)) public payouts; /// @notice maps bet id -> player address -> played mapping(uint24 => mapping(address => bool)) private _opponentPlayed; /// @notice Emitted after the max seats is set. event SetMaxNFTs(uint16 maxNFTs); /// @notice Emitted after the Chainlink config is set. /// @param requestConfirmations How many confirmations the Chainlink node should wait before responding. /// @param keyHash Hash of the public key used to verify the VRF proof. event SetChainlinkConfig(uint16 requestConfirmations, bytes32 keyHash); /// @notice Emitted after the Chainlink callback gas limit is set for a token. /// @param token Address of the token. /// @param callbackGasLimit New Chainlink VRF callback gas limit. event SetVRFCallbackGasLimit(address token, uint32 callbackGasLimit); event AddNFTsPrize( uint24 indexed id, IERC721 nftContract, uint256[] tokenIds ); event WonNFTs(uint24 indexed id, IERC721 nftContract, address[] winners); event ClaimedNFT(uint24 indexed id, IERC721 nftContract, uint256 tokenId); /// @notice Emitted after the bet amount is transfered to the user. /// @param id The bet ID. /// @param seats Address of the gamers. /// @param amount Number of tokens refunded. event BetRefunded(uint24 indexed id, address[] seats, uint256 amount); /// @notice Emitted after the bet is canceled. /// @param id The bet ID. /// @param user Address of the gamer. /// @param amount Number of tokens refunded. event BetCanceled(uint24 id, address user, uint256 amount); /// @notice Emitted after the bet is started. /// @param id The bet ID. event GameStarted(uint24 indexed id); /// @notice Emitted after a player joined seat(s) /// @param id The bet ID. /// @param player Address of the player. /// @param pot total played /// @param received Amount received /// @param seatsNumber Number of seats. event Joined( uint24 indexed id, address player, uint256 pot, uint256 received, uint16 seatsNumber ); /// @notice Emitted after the house edge is set for a token. /// @param token Address of the token. /// @param houseEdge House edge rate. event SetHouseEdge(address token, uint16 houseEdge); /// @notice Emitted when a new harvester is set. event HarvesterSet(address newHarvester); /// @notice Emitted after the token's treasury and team allocations are distributed. /// @param token Address of the token. /// @param treasuryAmount The number of tokens sent to the treasury. /// @param teamAmount The number of tokens sent to the team. event HouseEdgeDistribution( address token, uint256 treasuryAmount, uint256 teamAmount ); /// @notice Emitted after the token's dividend allocation is distributed. /// @param token Address of the token. /// @param amount The number of tokens sent to the Harvester. event HarvestDividend(address token, uint256 amount); /// @notice Emitted after the token's house edge is allocated. /// @param token Address of the token. /// @param dividend The number of tokens allocated as staking rewards. /// @param treasury The number of tokens allocated to the treasury. /// @param team The number of tokens allocated to the team. event AllocateHouseEdgeAmount( address token, uint256 dividend, uint256 treasury, uint256 team, uint256 initiator ); /// @notice Emitted after a player claimed his payouts. /// @param user Address of the token. /// @param token The number of tokens allocated as staking rewards. /// @param amount The number of tokens allocated to the treasury. event PayoutsClaimed( address indexed user, address indexed token, uint256 amount ); /// @notice Bet provided doesn't exist or was already resolved. error NotPendingBet(); /// @notice Bet isn't resolved yet. error NotFulfilled(); /// @notice Token is not allowed. error ForbiddenToken(); /// @notice Reverting error when sender isn't allowed. error AccessDenied(); /// @notice Reverting error when provided address isn't valid. error InvalidAddress(); /// @notice Bet amount isn't enough to accept bet. /// @param betAmount Bet amount. error WrongBetAmount(uint256 betAmount); /// @notice User isn't one of the defined bet opponents. /// @param user The unallowed opponent address. error InvalidOpponent(address user); /// @notice Wrong number of seat to launch the game. error WrongSeatsNumber(); /// @notice The maximum of seats is reached error TooManySeats(); /// @notice The maximum of NFTs is reached error TooManyNFTs(); /// @notice Initialize contract's state variables and VRF Consumer. /// @param chainlinkCoordinatorAddress Address of the Chainlink VRF Coordinator. /// @param pvpGamesStoreAddress The PvPGamesStore address. constructor( address chainlinkCoordinatorAddress, address pvpGamesStoreAddress ) VRFConsumerBaseV2(chainlinkCoordinatorAddress) { if ( chainlinkCoordinatorAddress == address(0) || pvpGamesStoreAddress == address(0) ) { revert InvalidAddress(); } pvpGamesStore = IPvPGamesStore(pvpGamesStoreAddress); _chainlinkConfig.chainlinkCoordinator = VRFCoordinatorV2Interface( chainlinkCoordinatorAddress ); } function setMaxNFTs(uint16 _maxNFTs) external onlyOwner { maxNFTs = _maxNFTs; emit SetMaxNFTs(_maxNFTs); } function _transferNFTs(uint24 id, bytes memory nfts) private { (IERC721[] memory nftContracts, uint256[][] memory tokenIds) = abi .decode(nfts, (IERC721[], uint256[][])); uint256 NFTsCount; for (uint256 i = 0; i < nftContracts.length; i++) { IERC721 nftContract = nftContracts[i]; uint256[] memory nftContractTokenIds = tokenIds[i]; betNFTs[id].push(); betNFTs[id][i].nftContract = nftContract; betNFTs[id][i].tokenIds = nftContractTokenIds; for (uint256 j = 0; j < nftContractTokenIds.length; j++) { nftContract.transferFrom( msg.sender, address(this), nftContractTokenIds[j] ); NFTsCount++; if (NFTsCount > maxNFTs) { revert TooManyNFTs(); } } emit AddNFTsPrize(id, nftContract, nftContractTokenIds); } } function getBetNFTs(uint24 id) external view returns (NFTs[] memory) { return betNFTs[id]; } /// @notice Creates a new bet, transfer the ERC20 tokens to the contract. /// @param tokenAddress Address of the token. /// @param tokenAmount The number of tokens bet. /// @param opponents The defined opponents. /// @return A new Bet struct information. function _newBet( address tokenAddress, uint256 tokenAmount, address[] memory opponents, bytes memory nfts ) internal whenNotPaused nonReentrant returns (Bet memory) { uint16 houseEdge = tokens[tokenAddress].houseEdge; if (houseEdge == 0) { revert ForbiddenToken(); } bool isGasToken = tokenAddress == address(0); uint256 betAmount = isGasToken ? msg.value : tokenAmount; uint256 received = betAmount; if (!isGasToken) { uint256 balanceBefore = IERC20(tokenAddress).balanceOf( address(this) ); IERC20(tokenAddress).safeTransferFrom( msg.sender, address(this), betAmount ); uint256 balanceAfter = IERC20(tokenAddress).balanceOf( address(this) ); received = balanceAfter - balanceBefore; } // Create bet uint24 id = betId++; Bet memory newBet = Bet({ resolved: false, canceled: false, opponents: opponents, seats: new address[](1), token: tokenAddress, id: id, vrfRequestId: 0, amount: betAmount, vrfRequestTimestamp: 0, payout: 0, pot: received, houseEdge: houseEdge }); newBet.seats[0] = msg.sender; bets[id] = newBet; _transferNFTs(id, nfts); return newBet; } function betMinSeats(uint24 betId) public view virtual returns (uint256); function betMaxSeats(uint24 betId) public view virtual returns (uint256); function gameCanStart(uint24) public view virtual returns (bool) { return true; } function _joinGame(uint24 id, uint16 seatsNumber) internal nonReentrant { Bet storage bet = bets[id]; uint256 _maxSeats = betMaxSeats(id); if (bet.resolved || bet.vrfRequestId != 0) { revert NotPendingBet(); } uint256 seatsLength = bet.seats.length; if (seatsLength + seatsNumber > _maxSeats) { revert TooManySeats(); } address user = msg.sender; address[] memory opponents = bet.opponents; uint256 opponentsLength = opponents.length; // Only check if player is in the opponent list if there is one. if (opponentsLength > 0) { bool included = false; for (uint256 i = 0; i < opponentsLength; i++) { if (opponents[i] == user) { included = true; break; } } if (!included) { revert InvalidOpponent(user); } if (!_opponentPlayed[id][user]) { _opponentPlayed[id][user] = true; } } address tokenAddress = bet.token; uint256 received = 0; if (tokenAddress == address(0)) { received = msg.value; if (received != bet.amount * seatsNumber) { revert WrongBetAmount(msg.value); } } else { uint256 balanceBefore = IERC20(tokenAddress).balanceOf( address(this) ); IERC20(tokenAddress).safeTransferFrom( user, address(this), bet.amount * seatsNumber ); uint256 balanceAfter = IERC20(tokenAddress).balanceOf( address(this) ); received = balanceAfter - balanceBefore; } for (uint16 i = 0; i < seatsNumber; i++) { bet.seats.push(user); } seatsLength = bet.seats.length; bet.pot += received; if ( seatsLength == _maxSeats || (opponentsLength > 0 && _allOpponentsHavePlayed(id, opponents)) ) { _launchGame(id); } emit Joined(id, user, bet.pot, received, seatsNumber); } function _allOpponentsHavePlayed( uint24 id, address[] memory opponents ) private view returns (bool) { for (uint256 i = 0; i < opponents.length; i++) { if (!_opponentPlayed[id][opponents[i]]) { return false; } } return true; } function _cleanOpponentsList( uint24 id, address[] memory opponents ) private { for (uint256 i = 0; i < opponents.length; i++) { delete _opponentPlayed[id][opponents[i]]; } } function launchGame(uint24 id) external { Bet storage bet = bets[id]; if (bet.seats.length < betMinSeats(id)) { revert WrongSeatsNumber(); } if (bet.resolved || bet.vrfRequestId != 0) { revert NotPendingBet(); } if (!gameCanStart(id)) { revert NotPendingBet(); } _launchGame(id); } function _launchGame(uint24 id) private { Bet storage bet = bets[id]; address tokenAddress = bet.token; IPvPGamesStore.Token memory token = pvpGamesStore.getTokenConfig( tokenAddress ); uint256 requestId = _chainlinkConfig .chainlinkCoordinator .requestRandomWords( _chainlinkConfig.keyHash, token.vrfSubId, _chainlinkConfig.requestConfirmations, tokens[tokenAddress].VRFCallbackGasLimit, 1 ); bet.vrfRequestId = requestId; bet.vrfRequestTimestamp = uint32(block.timestamp); _betsByVrfRequestId[requestId] = id; emit GameStarted(id); } function cancelBet(uint24 id) external { Bet storage bet = bets[id]; if (bet.resolved || bet.id == 0) { revert NotPendingBet(); } else if (bet.seats.length > 1) { revert NotFulfilled(); } else if (bet.seats[0] != msg.sender && owner() != msg.sender) { revert AccessDenied(); } bet.canceled = true; bet.resolved = true; bet.payout = bet.pot; if (bet.opponents.length > 0) _cleanOpponentsList(id, bet.opponents); address host = bet.seats[0]; payouts[host][bet.token] += bet.payout; NFTs[] storage nfts = betNFTs[bet.id]; for (uint256 i = 0; i < nfts.length; i++) { NFTs storage NFT = nfts[i]; for (uint256 j = 0; j < NFT.tokenIds.length; j++) { NFT.to.push(host); } } emit BetCanceled(id, host, bet.payout); } function claimNFTs(uint24 _betId) external { NFTs[] memory nfts = betNFTs[_betId]; for (uint256 i = 0; i < nfts.length; i++) { for (uint256 j = 0; j < nfts[i].tokenIds.length; j++) { claimNFT(_betId, i, j); } } } function claimNFT(uint24 _betId, uint256 nftIndex, uint256 tokenId) public { NFTs memory nft = betNFTs[_betId][nftIndex]; if (!claimedNFTs[_betId][nft.nftContract][tokenId]) { claimedNFTs[_betId][nft.nftContract][tokenId] = true; nft.nftContract.transferFrom( address(this), nft.to[tokenId], nft.tokenIds[tokenId] ); emit ClaimedNFT(_betId, nft.nftContract, tokenId); } } function claimAll(address user) external { address[] memory tokensList = pvpGamesStore.getTokensAddresses(); for (uint256 i = 0; i < tokensList.length; i++) { claim(user, tokensList[i]); } } function claim(address user, address token) public { uint256 amount = payouts[user][token]; if (amount > 0) { delete payouts[user][token]; _safeTransfer(payable(user), token, amount); emit PayoutsClaimed(user, token, amount); } } /// @notice Refunds the bet to the user if the Chainlink VRF callback failed. /// @param id The Bet ID. function refundBet(uint24 id) external { Bet storage bet = bets[id]; if ( bet.resolved || bet.vrfRequestTimestamp == 0 || bet.seats.length < 2 ) { revert NotPendingBet(); } else if (block.timestamp < bet.vrfRequestTimestamp + 60 * 60 * 24) { revert NotFulfilled(); } else if (bet.seats[0] != msg.sender && owner() != msg.sender) { revert AccessDenied(); } bet.resolved = true; bet.payout = bet.pot; if (bet.opponents.length > 0) _cleanOpponentsList(id, bet.opponents); // Refund players uint256 refundAmount = bet.pot / bet.seats.length; for (uint256 i = 0; i < bet.seats.length; i++) { payouts[bet.seats[i]][bet.token] += refundAmount; } address host = bet.seats[0]; NFTs[] storage nfts = betNFTs[bet.id]; for (uint256 i = 0; i < nfts.length; i++) { NFTs storage NFT = nfts[i]; for (uint256 j = 0; j < NFT.tokenIds.length; j++) { NFT.to.push(host); } } emit BetRefunded(id, bet.seats, bet.payout); } /// @notice Resolves the bet based on the game child contract result. /// @param bet The Bet struct information. /// @param winners List of winning addresses /// @return The payout amount per winner. function _resolveBet( Bet storage bet, address[] memory winners, uint256 randomWord ) internal nonReentrant returns (uint256) { if (bet.resolved == true || bet.id == 0) { revert NotPendingBet(); } bet.resolved = true; address token = bet.token; uint256 payout = bet.pot; uint256 fee = (bet.houseEdge * payout) / 10000; payout -= fee; bet.payout = payout; _allocateHouseEdge(token, fee, payable(bet.seats[0])); if (bet.opponents.length > 0) { _cleanOpponentsList(bet.id, bet.opponents); } uint256 payoutPerWinner = payout / winners.length; for (uint256 i = 0; i < winners.length; i++) { payouts[winners[i]][token] += payoutPerWinner; } // Distribute NFTs NFTs[] storage nfts = betNFTs[bet.id]; for (uint256 i = 0; i < nfts.length; i++) { NFTs storage NFT = nfts[i]; for (uint256 j = 0; j < NFT.tokenIds.length; j++) { uint256 winnerIndex = uint256( keccak256(abi.encode(randomWord, i, j)) ) % bet.seats.length; NFT.to.push(bet.seats[winnerIndex]); } if (NFT.to.length != 0) { emit WonNFTs(bet.id, NFT.nftContract, NFT.to); } } return payout; } /// @notice Sets the game house edge rate for a specific token. /// @param token Address of the token. /// @param houseEdge House edge rate. /// @dev The house edge rate couldn't exceed 4%. function setHouseEdge(address token, uint16 houseEdge) external onlyOwner { tokens[token].houseEdge = houseEdge; emit SetHouseEdge(token, houseEdge); } /// @notice Sets the Chainlink VRF V2 configuration. /// @param callbackGasLimit How much gas is needed in the Chainlink VRF callback. function setVRFCallbackGasLimit( address token, uint32 callbackGasLimit ) external onlyOwner { tokens[token].VRFCallbackGasLimit = callbackGasLimit; emit SetVRFCallbackGasLimit(token, callbackGasLimit); } /// @notice Pauses the contract to disable new bets. function pause() external onlyOwner { if (paused()) { _unpause(); } else { _pause(); } } /// @notice Sets the Chainlink VRF V2 configuration. /// @param requestConfirmations How many confirmations the Chainlink node should wait before responding. /// @param keyHash Hash of the public key used to verify the VRF proof. function setChainlinkConfig( uint16 requestConfirmations, bytes32 keyHash ) external onlyOwner { _chainlinkConfig.requestConfirmations = requestConfirmations; _chainlinkConfig.keyHash = keyHash; emit SetChainlinkConfig(requestConfirmations, keyHash); } /// @notice Returns the Chainlink VRF config. /// @param requestConfirmations How many confirmations the Chainlink node should wait before responding. /// @param keyHash Hash of the public key used to verify the VRF proof. /// @param chainlinkCoordinator Reference to the VRFCoordinatorV2 deployed contract. function getChainlinkConfig() external view returns ( uint16 requestConfirmations, bytes32 keyHash, VRFCoordinatorV2Interface chainlinkCoordinator ) { return ( _chainlinkConfig.requestConfirmations, _chainlinkConfig.keyHash, _chainlinkConfig.chainlinkCoordinator ); } /// @notice Returns the bet with the seats list included /// @return bet The required bet function readBet(uint24 id) external view returns (Bet memory bet) { return bets[id]; } /// @notice Allows to change the harvester address. /// @param newHarvester provides the new address to use. function setHarvester(address newHarvester) external onlyOwner { harvester = newHarvester; emit HarvesterSet(newHarvester); } /// @notice Harvests tokens dividends. function harvestDividends(address tokenAddress) external { if (msg.sender != harvester) revert AccessDenied(); HouseEdgeSplit storage split = tokens[tokenAddress].houseEdgeSplit; uint256 dividendAmount = split.dividendAmount; if (dividendAmount != 0) { delete split.dividendAmount; _safeTransfer(harvester, tokenAddress, dividendAmount); emit HarvestDividend(tokenAddress, dividendAmount); } } /// @notice Splits the house edge fees and allocates them as dividends, the treasury, and team. /// @param token Address of the token. /// @param fees Bet amount and bet profit fees amount. function _allocateHouseEdge( address token, uint256 fees, address payable initiator ) private { IPvPGamesStore.HouseEdgeSplit memory tokenHouseEdgeConfig = pvpGamesStore .getTokenConfig(token) .houseEdgeSplit; HouseEdgeSplit storage tokenHouseEdge = tokens[token].houseEdgeSplit; uint256 treasuryAmount = (fees * tokenHouseEdgeConfig.treasury) / 10000; uint256 teamAmount = (fees * tokenHouseEdgeConfig.team) / 10000; uint256 initiatorAmount = (fees * tokenHouseEdgeConfig.initiator) / 10000; uint256 dividendAmount = fees - initiatorAmount - teamAmount - treasuryAmount; if (teamAmount > 0) tokenHouseEdge.teamAmount += teamAmount; if (treasuryAmount > 0) tokenHouseEdge.treasuryAmount += treasuryAmount; if (dividendAmount > 0) tokenHouseEdge.dividendAmount += dividendAmount; if (initiatorAmount > 0) { payouts[initiator][token] += initiatorAmount; } emit AllocateHouseEdgeAmount( token, dividendAmount, treasuryAmount, teamAmount, initiatorAmount ); } /// @notice Distributes the token's treasury and team allocations amounts. /// @param tokenAddress Address of the token. function withdrawHouseEdgeAmount(address tokenAddress) public { (address treasury, address teamWallet) = pvpGamesStore .getTreasuryAndTeamAddresses(); HouseEdgeSplit storage tokenHouseEdge = tokens[tokenAddress] .houseEdgeSplit; uint256 treasuryAmount = tokenHouseEdge.treasuryAmount; uint256 teamAmount = tokenHouseEdge.teamAmount; if (treasuryAmount != 0) { delete tokenHouseEdge.treasuryAmount; _safeTransfer(treasury, tokenAddress, treasuryAmount); } if (teamAmount != 0) { delete tokenHouseEdge.teamAmount; _safeTransfer(teamWallet, tokenAddress, teamAmount); } if (treasuryAmount != 0 || teamAmount != 0) { emit HouseEdgeDistribution( tokenAddress, treasuryAmount, teamAmount ); } } /// @notice Transfers a specific amount of token to an address. /// Uses native transfer or ERC20 transfer depending on the token. /// @dev The 0x address is considered the gas token. /// @param user Address of destination. /// @param token Address of the token. /// @param amount Number of tokens. function _safeTransfer( address user, address token, uint256 amount ) private { if (token == address(0)) { Address.sendValue(payable(user), amount); } else { IERC20(token).safeTransfer(user, amount); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {PvPGame} from "./PvPGame.sol"; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // import "hardhat/console.sol"; contract RussianRoulette is PvPGame { /// @notice Stores the Russian Roulette params struct RussianRouletteBet { uint16 maxSeats; uint32 startsAt; uint8 deathRatio; uint256 randomWord; } /// @notice Maps bets id with Russian Roulette data mapping(uint24 => RussianRouletteBet) private russianRouletteBets; /// @notice Maximum number of seats per game. uint16 public maxSeats; /// @notice Emitted after the max seats is set. event SetMaxSeats(uint16 maxSeats); // / @notice Emitted after a bet is placed. // / @param id The bet ID. // / @param player Address of the gamer. // / @param opponents Address of the opponents. // / @param token Address of the token. // / @param amount The bet amount. // / @param maxSeats Maximum of seats allowed. // / @param startsAt When the game can start even if not all seats has been filled. // / @param deathRatio Percentage of seats to kill. // / @param pot The prize pool. event PlaceBet( uint24 id, address indexed player, address[] opponents, address indexed token, uint256 amount, uint16 maxSeats, uint32 startsAt, uint8 deathRatio, uint256 pot ); /// @notice Emitted after a bet is rolled. /// @param id The bet ID. /// @param seats Players addresses of seats. /// @param winners who won. /// @param killedSeats the bets won. /// @param token The token used. /// @param betAmount The bet amount. /// @param payout The payout amount. event Roll( uint24 indexed id, address[] seats, address[] winners, address[] killedSeats, address indexed token, uint256 betAmount, uint256 payout ); /// @notice Emitted when chainlink's resolution appens /// @param id The bet ID. /// @param killedSeatsNumber Killed seats. event GameResolved(uint24 indexed id, uint256 killedSeatsNumber); error InvalidDeathRatio(); error InvalidDate(); constructor( address chainlinkCoordinatorAddress, address pvpGamesStoreAddress ) PvPGame(chainlinkCoordinatorAddress, pvpGamesStoreAddress) {} function setMaxSeats(uint16 _maxSeats) external onlyOwner { maxSeats = _maxSeats; emit SetMaxSeats(_maxSeats); } /// @notice Creates a new bet and stores the chosen coin face. /// @param token Address of the token. /// @param tokenAmount The number of tokens bet. /// @param opponents Selected players to play with. /// @param _maxSeats Maximum number of seats. The game will start when its reached. /// @param startsAt Timestamp when the game can start. /// @param deathRatio Players kill rate. function wager( address token, uint256 tokenAmount, address[] calldata opponents, uint16 _maxSeats, uint32 startsAt, uint8 deathRatio, bytes calldata nfts ) external payable whenNotPaused { if (_maxSeats < 2 || _maxSeats > maxSeats) revert WrongSeatsNumber(); if (deathRatio == 0 || deathRatio > 99) revert InvalidDeathRatio(); if (startsAt < block.timestamp) revert InvalidDate(); Bet memory bet = _newBet(token, tokenAmount, opponents, nfts); russianRouletteBets[bet.id].maxSeats = _maxSeats; russianRouletteBets[bet.id].startsAt = startsAt; russianRouletteBets[bet.id].deathRatio = deathRatio; emit PlaceBet( bet.id, msg.sender, opponents, token, bet.amount, _maxSeats, startsAt, deathRatio, bet.pot ); } function joinGame(uint24 id, uint16 seatsNumber) external payable { _joinGame(id, seatsNumber); } /// @notice Fullfills the chainlink request /// @param requestId id of the bet /// @param randomWords result function fulfillRandomWords( uint256 requestId, uint256[] memory randomWords ) internal override { uint24 id = _betsByVrfRequestId[requestId]; Bet storage bet = bets[id]; // Check the bet state in case it has been refunded or canceled if (bet.resolved) { revert NotPendingBet(); } uint256 killedSeatsNumber = (bet.seats.length * russianRouletteBets[bet.id].deathRatio) / 100; if (killedSeatsNumber == 0) killedSeatsNumber = 1; russianRouletteBets[bet.id].randomWord = randomWords[0]; emit GameResolved(id, killedSeatsNumber); } /// @notice Pull the triggers killing seats. /// @param id id of the bet function pullTriggers(uint24 id) external { // Check if random result has arrived uint256 randomWord = russianRouletteBets[id].randomWord; if (randomWord == 0) revert NotPendingBet(); Bet storage bet = bets[id]; uint256 seatsLength = bet.seats.length; uint256 killedSeatsNumber = (seatsLength * russianRouletteBets[bet.id].deathRatio) / 100; if (killedSeatsNumber == 0) killedSeatsNumber = 1; // Copy the list of players' seats address[] memory killedSeats = new address[](killedSeatsNumber); address[] memory winners = bet.seats; // Kill the seats for (uint i = 0; i < killedSeatsNumber; i++) { // Pickup a new random index uint256 deadIndex = uint256(keccak256(abi.encode(randomWord, i))) % (seatsLength - i); killedSeats[i] = winners[deadIndex]; winners[deadIndex] = winners[seatsLength - i - 1]; delete winners[seatsLength - i - 1]; } uint256 remainingSeats = seatsLength - killedSeatsNumber; assembly { mstore(winners, remainingSeats) } uint256 payOut = _resolveBet(bet, winners, randomWord); emit Roll( id, bet.seats, winners, killedSeats, bet.token, bet.amount, payOut ); } function betMaxSeats(uint24 id) public view override returns (uint256) { return russianRouletteBets[id].maxSeats; } function betMinSeats(uint24) public pure override returns (uint256) { return 2; } function gameCanStart(uint24 id) public view override returns (bool) { return russianRouletteBets[id].startsAt <= block.timestamp; } function getRussianRouletteBet( uint24 id ) external view returns (RussianRouletteBet memory russianRouletteBet, Bet memory bet) { return (russianRouletteBets[id], bets[id]); } }
{ "optimizer": { "enabled": true, "runs": 9999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"chainlinkCoordinatorAddress","type":"address"},{"internalType":"address","name":"pvpGamesStoreAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"ForbiddenToken","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidDate","type":"error"},{"inputs":[],"name":"InvalidDeathRatio","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"InvalidOpponent","type":"error"},{"inputs":[],"name":"NotFulfilled","type":"error"},{"inputs":[],"name":"NotPendingBet","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"want","type":"address"}],"name":"OnlyCoordinatorCanFulfill","type":"error"},{"inputs":[],"name":"TooManyNFTs","type":"error"},{"inputs":[],"name":"TooManySeats","type":"error"},{"inputs":[{"internalType":"uint256","name":"betAmount","type":"uint256"}],"name":"WrongBetAmount","type":"error"},{"inputs":[],"name":"WrongSeatsNumber","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"contract IERC721","name":"nftContract","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"AddNFTsPrize","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"dividend","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasury","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"team","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initiator","type":"uint256"}],"name":"AllocateHouseEdgeAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BetCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"address[]","name":"seats","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BetRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"contract IERC721","name":"nftContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ClaimedNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"uint256","name":"killedSeatsNumber","type":"uint256"}],"name":"GameResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"}],"name":"GameStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"HarvestDividend","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newHarvester","type":"address"}],"name":"HarvesterSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"treasuryAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"teamAmount","type":"uint256"}],"name":"HouseEdgeDistribution","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"uint256","name":"pot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"received","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"seatsNumber","type":"uint16"}],"name":"Joined","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":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PayoutsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":true,"internalType":"address","name":"player","type":"address"},{"indexed":false,"internalType":"address[]","name":"opponents","type":"address[]"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"maxSeats","type":"uint16"},{"indexed":false,"internalType":"uint32","name":"startsAt","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"deathRatio","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"pot","type":"uint256"}],"name":"PlaceBet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"address[]","name":"seats","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"winners","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"killedSeats","type":"address[]"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"betAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"}],"name":"Roll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"requestConfirmations","type":"uint16"},{"indexed":false,"internalType":"bytes32","name":"keyHash","type":"bytes32"}],"name":"SetChainlinkConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint16","name":"houseEdge","type":"uint16"}],"name":"SetHouseEdge","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"maxNFTs","type":"uint16"}],"name":"SetMaxNFTs","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"maxSeats","type":"uint16"}],"name":"SetMaxSeats","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint32","name":"callbackGasLimit","type":"uint32"}],"name":"SetVRFCallbackGasLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"id","type":"uint24"},{"indexed":false,"internalType":"contract IERC721","name":"nftContract","type":"address"},{"indexed":false,"internalType":"address[]","name":"winners","type":"address[]"}],"name":"WonNFTs","type":"event"},{"inputs":[],"name":"betId","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"betMaxSeats","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"}],"name":"betMinSeats","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"betNFTs","outputs":[{"internalType":"contract IERC721","name":"nftContract","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"}],"name":"bets","outputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"resolved","type":"bool"},{"internalType":"bool","name":"canceled","type":"bool"},{"internalType":"uint24","name":"id","type":"uint24"},{"internalType":"uint32","name":"vrfRequestTimestamp","type":"uint32"},{"internalType":"uint16","name":"houseEdge","type":"uint16"},{"internalType":"uint256","name":"vrfRequestId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"payout","type":"uint256"},{"internalType":"uint256","name":"pot","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"cancelBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"claimAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"_betId","type":"uint24"},{"internalType":"uint256","name":"nftIndex","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claimNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"_betId","type":"uint24"}],"name":"claimNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"","type":"uint24"},{"internalType":"contract IERC721","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimedNFTs","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"gameCanStart","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getBetNFTs","outputs":[{"components":[{"internalType":"contract IERC721","name":"nftContract","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address[]","name":"to","type":"address[]"}],"internalType":"struct PvPGame.NFTs[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChainlinkConfig","outputs":[{"internalType":"uint16","name":"requestConfirmations","type":"uint16"},{"internalType":"bytes32","name":"keyHash","type":"bytes32"},{"internalType":"contract VRFCoordinatorV2Interface","name":"chainlinkCoordinator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"getRussianRouletteBet","outputs":[{"components":[{"internalType":"uint16","name":"maxSeats","type":"uint16"},{"internalType":"uint32","name":"startsAt","type":"uint32"},{"internalType":"uint8","name":"deathRatio","type":"uint8"},{"internalType":"uint256","name":"randomWord","type":"uint256"}],"internalType":"struct RussianRoulette.RussianRouletteBet","name":"russianRouletteBet","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"resolved","type":"bool"},{"internalType":"bool","name":"canceled","type":"bool"},{"internalType":"uint24","name":"id","type":"uint24"},{"internalType":"uint32","name":"vrfRequestTimestamp","type":"uint32"},{"internalType":"uint16","name":"houseEdge","type":"uint16"},{"internalType":"address[]","name":"opponents","type":"address[]"},{"internalType":"address[]","name":"seats","type":"address[]"},{"internalType":"uint256","name":"vrfRequestId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"payout","type":"uint256"},{"internalType":"uint256","name":"pot","type":"uint256"}],"internalType":"struct PvPGame.Bet","name":"bet","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"harvestDividends","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"harvester","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"},{"internalType":"uint16","name":"seatsNumber","type":"uint16"}],"name":"joinGame","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"launchGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxNFTs","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSeats","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"payouts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"pullTriggers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pvpGamesStore","outputs":[{"internalType":"contract IPvPGamesStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"uint256[]","name":"randomWords","type":"uint256[]"}],"name":"rawFulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"readBet","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"resolved","type":"bool"},{"internalType":"bool","name":"canceled","type":"bool"},{"internalType":"uint24","name":"id","type":"uint24"},{"internalType":"uint32","name":"vrfRequestTimestamp","type":"uint32"},{"internalType":"uint16","name":"houseEdge","type":"uint16"},{"internalType":"address[]","name":"opponents","type":"address[]"},{"internalType":"address[]","name":"seats","type":"address[]"},{"internalType":"uint256","name":"vrfRequestId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"payout","type":"uint256"},{"internalType":"uint256","name":"pot","type":"uint256"}],"internalType":"struct PvPGame.Bet","name":"bet","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint24","name":"id","type":"uint24"}],"name":"refundBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"requestConfirmations","type":"uint16"},{"internalType":"bytes32","name":"keyHash","type":"bytes32"}],"name":"setChainlinkConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newHarvester","type":"address"}],"name":"setHarvester","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint16","name":"houseEdge","type":"uint16"}],"name":"setHouseEdge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_maxNFTs","type":"uint16"}],"name":"setMaxNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_maxSeats","type":"uint16"}],"name":"setMaxSeats","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"callbackGasLimit","type":"uint32"}],"name":"setVRFCallbackGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokens","outputs":[{"internalType":"uint16","name":"houseEdge","type":"uint16"},{"internalType":"uint32","name":"VRFCallbackGasLimit","type":"uint32"},{"components":[{"internalType":"uint256","name":"dividendAmount","type":"uint256"},{"internalType":"uint256","name":"treasuryAmount","type":"uint256"},{"internalType":"uint256","name":"teamAmount","type":"uint256"}],"internalType":"struct PvPGame.HouseEdgeSplit","name":"houseEdgeSplit","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"address[]","name":"opponents","type":"address[]"},{"internalType":"uint16","name":"_maxSeats","type":"uint16"},{"internalType":"uint32","name":"startsAt","type":"uint32"},{"internalType":"uint8","name":"deathRatio","type":"uint8"},{"internalType":"bytes","name":"nfts","type":"bytes"}],"name":"wager","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"withdrawHouseEdgeAmount","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a0604052600d805462ffffff191660011790553480156200002057600080fd5b506040516200607a3803806200607a83398101604081905262000043916200014f565b6000805460ff191690556001600160a01b0382166080526001805581816200006b33620000e0565b6001600160a01b03821615806200008957506001600160a01b038116155b15620000a85760405163e6c4247b60e01b815260040160405180910390fd5b600a80546001600160a01b039283166001600160a01b0319918216179091556009805493909216921691909117905550620001879050565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b80516001600160a01b03811681146200014a57600080fd5b919050565b600080604083850312156200016357600080fd5b6200016e8362000132565b91506200017e6020840162000132565b90509250929050565b608051615ed0620001aa60003960008181610c830152610cde0152615ed06000f3fe6080604052600436106102d15760003560e01c80638b0e96ed11610179578063bddb0583116100d6578063e48603391161008a578063f2fde38b11610064578063f2fde38b14610a4d578063f692387f14610a6d578063ff55779914610a8d57600080fd5b8063e48603391461092f578063eb233d39146109d6578063ebebf84614610a0c57600080fd5b8063d192181b116100bb578063d192181b146108cf578063d39fad51146108ef578063e32fe5041461090f57600080fd5b8063bddb05831461088f578063c2c2e03b146108a257600080fd5b8063ac9650d81161012d578063b8f50dc611610112578063b8f50dc614610822578063b989185a14610842578063bb16f87b1461086257600080fd5b8063ac9650d8146107d5578063b380491e1461080257600080fd5b8063905d3f801161015e578063905d3f801461075857806394e1cd5a14610773578063a23cd7df146107b557600080fd5b80638b0e96ed1461071a5780638da5cb5b1461073a57600080fd5b80634e9e1ec611610232578063715018a6116101e65780637d0807d7116101c05780637d0807d7146106a65780638456cb59146106e5578063893d5483146106fa57600080fd5b8063715018a61461065157806377329f351461066657806378bcde221461068657600080fd5b80635c975abb116102175780635c975abb146105ed5780636401f1ca146106115780636a7ab9ad1461063157600080fd5b80634e9e1ec61461045f578063515987fe1461048d57600080fd5b806328c31b1a116102895780632fbb35f71161026e5780632fbb35f7146103dc5780633778c63a146103ef5780634bdaeac11461042757600080fd5b806328c31b1a1461038d5780632f2ad98d146103bc57600080fd5b80631fe543e3116102ba5780631fe543e31461031857806321c0b3421461033857806327bce91b1461035857600080fd5b80630ae30cb0146102d657806315de1daa146102f8575b600080fd5b3480156102e257600080fd5b506102f66102f1366004614f42565b610abb565b005b34801561030457600080fd5b506102f6610313366004614f42565b610c03565b34801561032457600080fd5b506102f661033336600461502f565b610c78565b34801561034457600080fd5b506102f66103533660046150d1565b610d1e565b34801561036457600080fd5b50600d546103749062ffffff1681565b60405162ffffff90911681526020015b60405180910390f35b34801561039957600080fd5b506103ae6103a8366004615122565b50600290565b604051908152602001610384565b3480156103c857600080fd5b506102f66103d736600461514d565b610dd0565b6102f66103ea36600461516a565b610e39565b3480156103fb57600080fd5b506103ae61040a3660046150d1565b600f60209081526000928352604080842090915290825290205481565b34801561043357600080fd5b50600b54610447906001600160a01b031681565b6040516001600160a01b039091168152602001610384565b34801561046b57600080fd5b5060065461047a9061ffff1681565b60405161ffff9091168152602001610384565b34801561049957600080fd5b506105866104a8366004615122565b600c60205260009081526040902080546003820154600483015460058401546006909401546001600160a01b0384169474010000000000000000000000000000000000000000850460ff908116957501000000000000000000000000000000000000000000810490911694760100000000000000000000000000000000000000000000820462ffffff1694790100000000000000000000000000000000000000000000000000830463ffffffff16947d01000000000000000000000000000000000000000000000000000000000090930461ffff169391929091908a565b604080516001600160a01b03909b168b5298151560208b01529615159789019790975262ffffff909416606088015263ffffffff909216608087015261ffff1660a086015260c085015260e084015261010083019190915261012082015261014001610384565b3480156105f957600080fd5b5060005460ff165b6040519015158152602001610384565b34801561061d57600080fd5b506102f661062c36600461514d565b610e43565b34801561063d57600080fd5b5061044761064c366004615196565b610eac565b34801561065d57600080fd5b506102f6610ee9565b34801561067257600080fd5b506102f6610681366004614f42565b610efd565b34801561069257600080fd5b506102f66106a13660046151c0565b610fca565b3480156106b257600080fd5b506007546008546009546040805161ffff909416845260208401929092526001600160a01b031690820152606001610384565b3480156106f157600080fd5b506102f6611056565b34801561070657600080fd5b506102f6610715366004615122565b611079565b34801561072657600080fd5b506102f6610735366004615122565b611433565b34801561074657600080fd5b506002546001600160a01b0316610447565b34801561076457600080fd5b5060125461047a9061ffff1681565b34801561077f57600080fd5b5061060161078e366004615122565b62ffffff16600090815260116020526040902054426201000090910463ffffffff16111590565b3480156107c157600080fd5b506102f66107d03660046151de565b6115c8565b3480156107e157600080fd5b506107f56107f0366004615248565b61163c565b60405161038491906152da565b34801561080e57600080fd5b50600a54610447906001600160a01b031681565b34801561082e57600080fd5b506102f661083d366004615122565b611732565b34801561084e57600080fd5b506102f661085d366004614f42565b611a64565b34801561086e57600080fd5b5061088261087d366004615122565b611b2d565b6040516103849190615486565b6102f661089d3660046154ef565b611d67565b3480156108ae57600080fd5b506108c26108bd366004615122565b611fef565b60405161038491906155ee565b3480156108db57600080fd5b506102f66108ea366004615122565b612126565b3480156108fb57600080fd5b506102f661090a366004615122565b61223e565b34801561091b57600080fd5b506102f661092a3660046156a6565b612696565b34801561093b57600080fd5b5061099c61094a366004614f42565b60056020908152600091825260409182902080548351606081018552600183015481526002830154938101939093526003909101549282019290925261ffff82169162010000900463ffffffff169083565b6040805161ffff909416845263ffffffff90921660208085019190915281518484015281015160608401520151608082015260a001610384565b3480156109e257600080fd5b506103ae6109f1366004615122565b62ffffff1660009081526011602052604090205461ffff1690565b348015610a1857600080fd5b50610601610a273660046156db565b600460209081526000938452604080852082529284528284209052825290205460ff1681565b348015610a5957600080fd5b506102f6610a68366004614f42565b612724565b348015610a7957600080fd5b506102f6610a8836600461571a565b6127ce565b348015610a9957600080fd5b50610aad610aa8366004615122565b612a74565b60405161038492919061574d565b600a54604080517f271b1ed5000000000000000000000000000000000000000000000000000000008152815160009384936001600160a01b039091169263271b1ed592600480830193928290030181865afa158015610b1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b429190615792565b6001600160a01b038516600090815260056020526040902060028101546003820154939550919350600101918115610b865760006001840155610b86858784612d1f565b8015610b9e5760006002840155610b9e848783612d1f565b81151580610bab57508015155b15610bfb57604080516001600160a01b0388168152602081018490529081018290527f0146f1701c23c89f761280798d36d6c4e3acb349438456f5da8f83a2f5dd8cdc9060600160405180910390a15b505050505050565b610c0b612d4b565b600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f17b2c8a80f7d8a58fb89c33c7ca97a2e3107030497101ef140622060dd87d1d9906020015b60405180910390a150565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610d10576040517f1cf993f40000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660248201526044015b60405180910390fd5b610d1a8282612dbf565b5050565b6001600160a01b038083166000908152600f60209081526040808320938516835292905220548015610dcb576001600160a01b038084166000908152600f60209081526040808320938616835292905290812055610d7d838383612d1f565b816001600160a01b0316836001600160a01b03167fea6e6d7adb2529fc4f4142e7e8869777da3a133fe4cd754694f5cc17f1ca62af83604051610dc291815260200190565b60405180910390a35b505050565b610dd8612d4b565b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff83169081179091556040519081527f491fb09ba3c79e12dbba6689c04a67e96cf9be3dc60f403fa0b338de2942d21e90602001610c6d565b610d1a8282612f26565b610e4b612d4b565b600680547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff83169081179091556040519081527febc77f4b340f6fec5ab079ab1d89e848b8882e85854e7378b3d3b51bb5dc4adc90602001610c6d565b60036020528160005260406000208181548110610ec857600080fd5b60009182526020909120600390910201546001600160a01b03169150829050565b610ef1612d4b565b610efb6000613452565b565b600a54604080517f9c103bea00000000000000000000000000000000000000000000000000000000815290516000926001600160a01b031691639c103bea91600480830192869291908290030181865afa158015610f5f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f8791908101906157c1565b905060005b8151811015610dcb57610fb883838381518110610fab57610fab615850565b6020026020010151610d1e565b80610fc2816158ae565b915050610f8c565b610fd2612d4b565b6001600160a01b03821660008181526005602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff86169081179091558251938452908301527f7dfd3ecb8ac83242184c019f85ca408558f2ac0a5a19af6be2a4aee697eac81291015b60405180910390a15050565b61105e612d4b565b60005460ff161561107157610efb6134bc565b610efb61350e565b62ffffff81166000908152600c60205260409020805474010000000000000000000000000000000000000000900460ff16806110d357508054760100000000000000000000000000000000000000000000900462ffffff16155b1561110a576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281015460011015611149576040517f07bc6c3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03168160020160008154811061116957611169615850565b6000918252602090912001546001600160a01b0316148015906111a657503361119a6002546001600160a01b031690565b6001600160a01b031614155b156111dd576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffffff1675010100000000000000000000000000000000000000001781556006810154600582015560018101541561129557611295828260010180548060200260200160405190810160405280929190818152602001828054801561128b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161126d575b505050505061354b565b6000816002016000815481106112ad576112ad615850565b600091825260208083209091015460058501546001600160a01b03918216808552600f8452604080862088549094168652929093529083208054929450909290916112f99084906158e6565b90915550508154760100000000000000000000000000000000000000000000900462ffffff166000908152600360205260408120905b81548110156113da57600082828154811061134c5761134c615850565b9060005260206000209060030201905060005b60018201548110156113c557600282018054600181018255600091825260209091200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038716179055806113bd816158ae565b91505061135f565b505080806113d2906158ae565b91505061132f565b5060058301546040805162ffffff871681526001600160a01b038516602082015280820192909252517fec0cd8712e7564c1830478f6a127e5e25e0974bca653f51b9a0684583f01c3779181900360600190a150505050565b62ffffff8116600090815260036020908152604080832080548251818502810185019093528083529192909190849084015b8282101561155d576000848152602090819020604080516060810182526003860290920180546001600160a01b031683526001810180548351818702810187019094528084529394919385830193928301828280156114e357602002820191906000526020600020905b8154815260200190600101908083116114cf575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561154557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611527575b50505050508152505081526020019060010190611465565b50505050905060005b8151811015610dcb5760005b82828151811061158457611584615850565b602002602001015160200151518110156115b5576115a38483836127ce565b806115ad816158ae565b915050611572565b50806115c0816158ae565b915050611566565b6115d0612d4b565b600780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff8416908117909155600882905560408051918252602082018390527f312eab8f28128692f3404f828c413db540bc15a246c46d0b40e3c004b947c437910161104a565b60608167ffffffffffffffff81111561165757611657614f5f565b60405190808252806020026020018201604052801561168a57816020015b60608152602001906001900390816116755790505b50905060005b8281101561172a576116fa308585848181106116ae576116ae615850565b90506020028101906116c091906158f9565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506135bf92505050565b82828151811061170c5761170c615850565b60200260200101819052508080611722906158ae565b915050611690565b505b92915050565b62ffffff811660009081526011602052604081206001015490819003611784576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62ffffff8281166000908152600c6020908152604080832060028101548154760100000000000000000000000000000000000000000000900490951684526011909252822054909291906064906117e8906601000000000000900460ff168461595e565b6117f291906159a4565b905080600003611800575060015b60008167ffffffffffffffff81111561181b5761181b614f5f565b604051908082528060200260200182016040528015611844578160200160208202803683370190505b5090506000846002018054806020026020016040519081016040528092919081815260200182805480156118a157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611883575b5050505050905060005b838110156119dc5760006118bf82876159b8565b60408051602081018b90529081018490526060016040516020818303038152906040528051906020012060001c6118f691906159cb565b905082818151811061190a5761190a615850565b602002602001015184838151811061192457611924615850565b6001600160a01b039092166020928302919091019091015282600161194984896159b8565b61195391906159b8565b8151811061196357611963615850565b602002602001015183828151811061197d5761197d615850565b6001600160a01b03909216602092830291909101909101528260016119a284896159b8565b6119ac91906159b8565b815181106119bc576119bc615850565b6000602091820292909201015250806119d4816158ae565b9150506118ab565b5060006119e984866159b8565b905080825260006119fb87848a6135eb565b875460048901546040519293506001600160a01b039091169162ffffff8c16917f45de65c03cd38b47c6c96f1e713a3b4b9fa72026ad0b1893f139846f0f27838a91611a519160028d019189918b918990615a1d565b60405180910390a3505050505050505050565b600b546001600160a01b03163314611aa8576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116600090815260056020526040902060010180548015610dcb5760008255600b54611ae6906001600160a01b03168483612d1f565b604080516001600160a01b0385168152602081018390527fca64dbcaf91abfb066e7a5163f1d135f8f48f2cbdb0395e3b35cc278ebbd340b910160405180910390a1505050565b604080516101808101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820181905260e082015261010081018290526101208101829052610140810182905261016081019190915262ffffff8281166000908152600c602090815260409182902082516101808101845281546001600160a01b038116825274010000000000000000000000000000000000000000810460ff908116151583860152750100000000000000000000000000000000000000000082041615158286015276010000000000000000000000000000000000000000000081049095166060820152790100000000000000000000000000000000000000000000000000850463ffffffff1660808201527d01000000000000000000000000000000000000000000000000000000000090940461ffff1660a0850152600181018054845181850281018501909552808552919360c0860193909290830182828015611ccd57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611caf575b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015611d2f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611d11575b505050505081526020016003820154815260200160048201548152602001600582015481526020016006820154815250509050919050565b611d6f613a5b565b60028561ffff161080611d8b575060125461ffff908116908616115b15611dc2576040517fe77e1de400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60ff83161580611dd5575060638360ff16115b15611e0c576040517f1109255300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b428463ffffffff161015611e4c576040517f81bf7f6700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ec08a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020601f8b018190048102820181019092528981529250899150889081908401838280828437600092019190915250613ac892505050565b60608101805162ffffff90811660009081526011602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001661ffff8d1617905583518316825280822080547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff166201000063ffffffff8d16021790558351909216815281902080547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff16660100000000000060ff8a1602179055905161012083015161016084015192519394506001600160a01b038e169333937ffba5ae7eeb5441738d743e968b023ee46416237245fcfe7fd083e5b88a19b81d93611fdb9390928f928f92918f918f918f9190615a6b565b60405180910390a350505050505050505050565b62ffffff81166000908152600360209081526040808320805482518185028101850190935280835260609492939192909184015b8282101561211b576000848152602090819020604080516060810182526003860290920180546001600160a01b031683526001810180548351818702810187019094528084529394919385830193928301828280156120a157602002820191906000526020600020905b81548152602001906001019080831161208d575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561210357602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116120e5575b50505050508152505081526020019060010190612023565b505050509050919050565b62ffffff81166000908152600c602052604090206002808201541015612178576040517fe77e1de400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805474010000000000000000000000000000000000000000900460ff16806121a35750600381015415155b156121da576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62ffffff8216600090815260116020526040902054426201000090910463ffffffff161115612235576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d1a8261402a565b62ffffff81166000908152600c60205260409020805474010000000000000000000000000000000000000000900460ff168061229c57508054790100000000000000000000000000000000000000000000000000900463ffffffff16155b806122aa5750600281810154105b156122e1576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805461231390790100000000000000000000000000000000000000000000000000900463ffffffff1662015180615afe565b63ffffffff16421015612352576040517f07bc6c3e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b03168160020160008154811061237257612372615850565b6000918252602090912001546001600160a01b0316148015906123af5750336123a36002546001600160a01b031690565b6001600160a01b031614155b156123e6576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001781556006810154600582015560018101541561249b5761249b828260010180548060200260200160405190810160405280929190818152602001828054801561128b576020028201919060005260206000209081546001600160a01b0316815260019091019060200180831161126d57505050505061354b565b600281015460068201546000916124b1916159a4565b905060005b600283015481101561253a5781600f60008560020184815481106124dc576124dc615850565b60009182526020808320909101546001600160a01b03908116845283820194909452604092830182208854909416825292909252812080549091906125229084906158e6565b90915550819050612532816158ae565b9150506124b6565b5060008260020160008154811061255357612553615850565b6000918252602080832090910154855462ffffff7601000000000000000000000000000000000000000000009091041683526003909152604082206001600160a01b039091169250905b81548110156126485760008282815481106125ba576125ba615850565b9060005260206000209060030201905060005b600182015481101561263357600282018054600181018255600091825260209091200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0387161790558061262b816158ae565b9150506125cd565b50508080612640906158ae565b91505061259d565b508462ffffff167f87ed5dc427019271ec1dcdebd6624b43cdcaf2a020b089af440ade7f5908dea5856002018660050154604051612687929190615b1b565b60405180910390a25050505050565b61269e612d4b565b6001600160a01b03821660008181526005602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff166201000063ffffffff8716908102919091179091558251938452908301527f9edb52532d26512950c91a14b132f6df3cd06bfb49e795e03e56736121cd24a8910161104a565b61272c612d4b565b6001600160a01b0381166127c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610d07565b6127cb81613452565b50565b62ffffff831660009081526003602052604081208054849081106127f4576127f4615850565b600091825260209182902060408051606081018252600390930290910180546001600160a01b0316835260018101805483518187028101870190945280845293949193858301939283018282801561286b57602002820191906000526020600020905b815481526020019060010190808311612857575b50505050508152602001600282018054806020026020016040519081016040528092919081815260200182805480156128cd57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116128af575b5050509190925250505062ffffff8516600090815260046020908152604080832084516001600160a01b03168452825280832086845290915290205490915060ff16612a6e5762ffffff8416600090815260046020908152604080832084516001600160a01b03908116855290835281842086855290925291829020805460ff19166001179055825191830151805192909116916323b872dd9130918690811061297957612979615850565b60200260200101518460200151868151811061299757612997615850565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b158015612a0957600080fd5b505af1158015612a1d573d6000803e3d6000fd5b50508251604080516001600160a01b0390921682526020820186905262ffffff881693507f93610bffc4f316244f20cec71efadf319bb598a37ec45b65a966758d970a187992500160405180910390a25b50505050565b604080516080810182526000808252602082018190529181018290526060810191909152604080516101808101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820181905260e082015261010081018290526101208101829052610140810182905261016081019190915262ffffff8084166000908152601160209081526040808320600c835292819020815160808082018452855461ffff808216845263ffffffff62010000830481168589015260ff660100000000000090930483168588015260019889015460608087019190915287516101808101895287546001600160a01b038116825274010000000000000000000000000000000000000000810486161515828c0152750100000000000000000000000000000000000000000081049095161515818a01527601000000000000000000000000000000000000000000008504909b16908b0152790100000000000000000000000000000000000000000000000000830416928901929092527d01000000000000000000000000000000000000000000000000000000000090041660a08701529381018054835181860281018601909452808452949591949193859360c0860193909291830182828015612c8157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612c63575b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015612ce357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612cc5575b50505050508152602001600382015481526020016004820154815260200160058201548152602001600682015481525050905091509150915091565b6001600160a01b038216612d3757610dcb838261426a565b610dcb6001600160a01b03831684836143b7565b6002546001600160a01b03163314610efb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610d07565b6000828152600e602090815260408083205462ffffff16808452600c909252909120805474010000000000000000000000000000000000000000900460ff1615612e35576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054760100000000000000000000000000000000000000000000900462ffffff166000908152601160205260408120546002830154606491612e8591660100000000000090910460ff169061595e565b612e8f91906159a4565b905080600003612e9d575060015b83600081518110612eb057612eb0615850565b6020908102919091018101518354760100000000000000000000000000000000000000000000900462ffffff908116600090815260118452604090819020600101929092559051838152908516917fc95c42e39dd1254fae1c8a39b5ab539fb60bd1a3e15904c6f01f26299acc1cd59101612687565b612f2e614460565b62ffffff82166000908152600c60209081526040808320601190925290912054815461ffff9091169074010000000000000000000000000000000000000000900460ff1680612f805750600382015415155b15612fb7576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600282015481612fcb61ffff8616836158e6565b1115613003576040517f2fcac10700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600033905060008460010180548060200260200160405190810160405280929190818152602001828054801561306257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613044575b5050835193945050821591506131769050576000805b828110156130cd57846001600160a01b031684828151811061309c5761309c615850565b60200260200101516001600160a01b0316036130bb57600191506130cd565b806130c5816158ae565b915050613078565b5080613110576040517fa68c23b70000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610d07565b62ffffff891660009081526010602090815260408083206001600160a01b038816845290915290205460ff166131745762ffffff891660009081526010602090815260408083206001600160a01b03881684529091529020805460ff191660011790555b505b85546001600160a01b03166000816131de573490508861ffff16886004015461319f919061595e565b81146131d9576040517f834393ce000000000000000000000000000000000000000000000000000000008152346004820152602401610d07565b613324565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561323e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132629190615b3d565b905061328f86308c61ffff168c6004015461327d919061595e565b6001600160a01b0387169291906144d3565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa1580156132ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133139190615b3d565b905061331f82826159b8565b925050505b60005b8961ffff168161ffff16101561339157600289018054600181018255600091825260209091200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0388161790558061338981615b56565b915050613327565b5087600201805490509550808860060160008282546133b091906158e6565b9091555050858714806133d357506000831180156133d357506133d38a85614524565b156133e1576133e18a61402a565b6006880154604080516001600160a01b03881681526020810192909252810182905261ffff8a16606082015262ffffff8b16907faafeb359322d2509e3bb2d2550827f92f9f8e9e6567ff89446c3f9416db64dae9060800160405180910390a25050505050505050610d1a60018055565b600280546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6134c46145ad565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b613516613a5b565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586134f13390565b60005b8151811015610dcb5762ffffff83166000908152601060205260408120835190919084908490811061358257613582615850565b6020908102919091018101516001600160a01b03168252810191909152604001600020805460ff19169055806135b7816158ae565b91505061354e565b60606135e48383604051806060016040528060278152602001615e7460279139614619565b9392505050565b60006135f5614460565b835474010000000000000000000000000000000000000000900460ff1615156001148061364057508354760100000000000000000000000000000000000000000000900462ffffff16155b15613677576040517f21abe57a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8354740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff82161780865560068601546001600160a01b039092169190600090612710906136ff9084907d010000000000000000000000000000000000000000000000000000000000900461ffff1661595e565b61370991906159a4565b905061371581836159b8565b915081876005018190555061375583828960020160008154811061373b5761373b615850565b6000918252602090912001546001600160a01b0316614691565b6001870154156137e3578654600188018054604080516020808402820181019092528281526137e394760100000000000000000000000000000000000000000000900462ffffff16939092909183018282801561128b576020028201919060005260206000209081546001600160a01b0316815260019091019060200180831161126d57505050505061354b565b60008651836137f291906159a4565b905060005b87518110156138895781600f60008a848151811061381757613817615850565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000876001600160a01b03166001600160a01b03168152602001908152602001600020600082825461387191906158e6565b90915550819050613881816158ae565b9150506137f7565b508754760100000000000000000000000000000000000000000000900462ffffff166000908152600360205260408120905b8154811015613a4a5760008282815481106138d8576138d8615850565b9060005260206000209060030201905060005b60018201548110156139bf5760028c015460408051602081018d905290810185905260608101839052600091906080016040516020818303038152906040528051906020012060001c61393e91906159cb565b9050826002018d600201828154811061395957613959615850565b60009182526020808320909101548354600181018555938352912090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0390921691909117905550806139b7816158ae565b9150506138eb565b50600281015415613a37578a54815460405176010000000000000000000000000000000000000000000090920462ffffff16917f439993180d02099038a5fef6f5fc198783460eda3022d58b1642605a9ba4f8dc91613a2e916001600160a01b03909116906002860190615b77565b60405180910390a25b5080613a42816158ae565b9150506138bb565b50929450505050506135e460018055565b60005460ff1615610efb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610d07565b604080516101808101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820181905260e0820152610100810182905261012081018290526101408101829052610160810191909152613b30613a5b565b613b38614460565b6001600160a01b03851660009081526005602052604081205461ffff1690819003613b8f576040517fc455905500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03861615600081613ba75786613ba9565b345b90508082613ce1576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038b16906370a0823190602401602060405180830381865afa158015613c11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c359190615b3d565b9050613c4c6001600160a01b038b163330866144d3565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038c16906370a0823190602401602060405180830381865afa158015613cac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cd09190615b3d565b9050613cdc82826159b8565b925050505b600d805460009162ffffff9091169082613cfa83615b99565b91906101000a81548162ffffff021916908362ffffff160217905550905060006040518061018001604052808c6001600160a01b031681526020016000151581526020016000151581526020018362ffffff168152602001600063ffffffff1681526020018761ffff1681526020018a8152602001600167ffffffffffffffff811115613d8957613d89614f5f565b604051908082528060200260200182016040528015613db2578160200160208202803683370190505b5081526020016000815260200185815260200160008152602001848152509050338160e00151600081518110613dea57613dea615850565b6001600160a01b0392831660209182029290920181019190915262ffffff8085166000908152600c8352604090819020855181548786015193880151606089015160808a015160a08b015161ffff167d010000000000000000000000000000000000000000000000000000000000027fff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff90921679010000000000000000000000000000000000000000000000000002919091167fff000000000000ffffffffffffffffffffffffffffffffffffffffffffffffff92909816760100000000000000000000000000000000000000000000027fffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffff931515750100000000000000000000000000000000000000000002939093167fffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffff97151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090951695909a169490941792909217949094169690961792909217949094169190911717825560c08301518051849392613fbd926001850192910190614e60565b5060e08201518051613fd9916002840191602090910190614e60565b506101008201516003820155610120820151600482015561014082015160058201556101609091015160069091015561401282896148d6565b9550505050505061402260018055565b949350505050565b62ffffff81166000908152600c60205260408082208054600a5492517fcb67e3b10000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820181905292949293919091169063cb67e3b19060240160a060405180830381865afa1580156140aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140ce9190615bb1565b60095460085482516007546001600160a01b038781166000908152600560205260408082205490517f5d3b1d30000000000000000000000000000000000000000000000000000000008152600481019690965267ffffffffffffffff909416602486015261ffff90921660448501526201000090920463ffffffff1660648401526001608484015293945090911690635d3b1d309060a4016020604051808303816000875af1158015614185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141a99190615b3d565b6003850181905584547fffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffff167901000000000000000000000000000000000000000000000000004263ffffffff16021785556000818152600e602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000001662ffffff8a169081179091559051929350917f43db47d6f3933a61d162c969b857ded1c13697e85f25d8484be452b081b49eac9190a25050505050565b804710156142d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610d07565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614321576040519150601f19603f3d011682016040523d82523d6000602084013e614326565b606091505b5050905080610dcb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610d07565b6040516001600160a01b038316602482015260448101829052610dcb9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152614b6f565b6002600154036144cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d07565b6002600155565b6040516001600160a01b0380851660248301528316604482015260648101829052612a6e9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016143fc565b6000805b82518110156145a35762ffffff84166000908152601060205260408120845190919085908490811061455c5761455c615850565b6020908102919091018101516001600160a01b031682528101919091526040016000205460ff1661459157600091505061172c565b8061459b816158ae565b915050614528565b5060019392505050565b60005460ff16610efb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610d07565b6060600080856001600160a01b0316856040516146369190615c5a565b600060405180830381855af49150503d8060008114614671576040519150601f19603f3d011682016040523d82523d6000602084013e614676565b606091505b509150915061468786838387614c6e565b9695505050505050565b600a546040517fcb67e3b10000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152600092169063cb67e3b19060240160a060405180830381865afa1580156146f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147199190615bb1565b6020908101516001600160a01b038616600090815260058352604081209282015191935060019092019190612710906147569061ffff168761595e565b61476091906159a4565b90506000612710846040015161ffff168761477b919061595e565b61478591906159a4565b90506000612710856060015161ffff16886147a0919061595e565b6147aa91906159a4565b9050600083836147ba848b6159b8565b6147c491906159b8565b6147ce91906159b8565b905082156147f057828560020160008282546147ea91906158e6565b90915550505b8315614810578385600101600082825461480a91906158e6565b90915550505b8015614830578085600001600082825461482a91906158e6565b90915550505b8115614873576001600160a01b038088166000908152600f60209081526040808320938d168352929052908120805484929061486d9084906158e6565b90915550505b604080516001600160a01b038b1681526020810183905290810185905260608101849052608081018390527f0b5a4c9121a83120fc7b30f195ddd2c279ec4510cfc8f051380851e8f0686f499060a00160405180910390a1505050505050505050565b600080828060200190518101906148ed9190615d57565b90925090506000805b8351811015610bfb57600084828151811061491357614913615850565b60200260200101519050600084838151811061493157614931615850565b60209081029190910181015162ffffff8a16600090815260039092526040909120805460010180825591925083918590811061496f5761496f615850565b6000918252602080832060039283020180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03959095169490941790935562ffffff8b168252909152604090208054829190859081106149da576149da615850565b906000526020600020906003020160010190805190602001906149fe929190614edd565b5060005b8151811015614b1a57826001600160a01b03166323b872dd3330858581518110614a2e57614a2e615850565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b158015614aa057600080fd5b505af1158015614ab4573d6000803e3d6000fd5b505050508480614ac3906158ae565b60065490965061ffff168611159050614b08576040517f6e58b71500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80614b12816158ae565b915050614a02565b508762ffffff167f9541b344d8d04b87356e20e96a873b7ec31254de0210581cead65e0de816bab98383604051614b52929190615e1c565b60405180910390a250508080614b67906158ae565b9150506148f6565b6000614bc4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614d019092919063ffffffff16565b805190915015610dcb5780806020019051810190614be29190615e3e565b610dcb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610d07565b60608315614cf7578251600003614cf0576001600160a01b0385163b614cf0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d07565b5081614022565b6140228383614d10565b60606140228484600085614d54565b815115614d205781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d079190615e60565b606082471015614de6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610d07565b600080866001600160a01b03168587604051614e029190615c5a565b60006040518083038185875af1925050503d8060008114614e3f576040519150601f19603f3d011682016040523d82523d6000602084013e614e44565b606091505b5091509150614e5587838387614c6e565b979650505050505050565b828054828255906000526020600020908101928215614ecd579160200282015b82811115614ecd57825182547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909116178255602090920191600190910190614e80565b50614ed9929150614f18565b5090565b828054828255906000526020600020908101928215614ecd579160200282015b82811115614ecd578251825591602001919060010190614efd565b5b80821115614ed95760008155600101614f19565b6001600160a01b03811681146127cb57600080fd5b600060208284031215614f5457600080fd5b81356135e481614f2d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715614fb157614fb1614f5f565b60405290565b6040516080810167ffffffffffffffff81118282101715614fb157614fb1614f5f565b604051601f8201601f1916810167ffffffffffffffff8111828210171561500357615003614f5f565b604052919050565b600067ffffffffffffffff82111561502557615025614f5f565b5060051b60200190565b6000806040838503121561504257600080fd5b8235915060208084013567ffffffffffffffff81111561506157600080fd5b8401601f8101861361507257600080fd5b80356150856150808261500b565b614fda565b81815260059190911b820183019083810190888311156150a457600080fd5b928401925b828410156150c2578335825292840192908401906150a9565b80955050505050509250929050565b600080604083850312156150e457600080fd5b82356150ef81614f2d565b915060208301356150ff81614f2d565b809150509250929050565b803562ffffff8116811461511d57600080fd5b919050565b60006020828403121561513457600080fd5b6135e48261510a565b61ffff811681146127cb57600080fd5b60006020828403121561515f57600080fd5b81356135e48161513d565b6000806040838503121561517d57600080fd5b6151868361510a565b915060208301356150ff8161513d565b600080604083850312156151a957600080fd5b6151b28361510a565b946020939093013593505050565b600080604083850312156151d357600080fd5b823561518681614f2d565b600080604083850312156151f157600080fd5b82356151b28161513d565b60008083601f84011261520e57600080fd5b50813567ffffffffffffffff81111561522657600080fd5b6020830191508360208260051b850101111561524157600080fd5b9250929050565b6000806020838503121561525b57600080fd5b823567ffffffffffffffff81111561527257600080fd5b61527e858286016151fc565b90969095509350505050565b60005b838110156152a557818101518382015260200161528d565b50506000910152565b600081518084526152c681602086016020860161528a565b601f01601f19169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561534d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261533b8583516152ae565b94509285019290850190600101615301565b5092979650505050505050565b600081518084526020808501945080840160005b838110156153935781516001600160a01b03168752958201959082019060010161536e565b509495945050505050565b80516001600160a01b03168252600061018060208301516153c3602086018215159052565b5060408301516153d7604086018215159052565b5060608301516153ee606086018262ffffff169052565b506080830151615406608086018263ffffffff169052565b5060a083015161541c60a086018261ffff169052565b5060c08301518160c08601526154348286018261535a565b91505060e083015184820360e086015261544e828261535a565b610100858101519087015261012080860151908701526101408086015190870152610160948501519490950193909352509192915050565b6020815260006135e4602083018461539e565b803563ffffffff8116811461511d57600080fd5b60008083601f8401126154bf57600080fd5b50813567ffffffffffffffff8111156154d757600080fd5b60208301915083602082850101111561524157600080fd5b600080600080600080600080600060e08a8c03121561550d57600080fd5b893561551881614f2d565b985060208a0135975060408a013567ffffffffffffffff8082111561553c57600080fd5b6155488d838e016151fc565b909950975060608c0135915061555d8261513d565b81965061556c60808d01615499565b955060a08c0135915060ff8216821461558457600080fd5b90935060c08b0135908082111561559a57600080fd5b506155a78c828d016154ad565b915080935050809150509295985092959850929598565b600081518084526020808501945080840160005b83811015615393578151875295820195908201906001016155d2565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015615698577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0898403018552815160606001600160a01b03825116855288820151818a87015261566a828701826155be565b91505087820151915084810388860152615684818361535a565b968901969450505090860190600101615615565b509098975050505050505050565b600080604083850312156156b957600080fd5b82356156c481614f2d565b91506156d260208401615499565b90509250929050565b6000806000606084860312156156f057600080fd5b6156f98461510a565b9250602084013561570981614f2d565b929592945050506040919091013590565b60008060006060848603121561572f57600080fd5b6157388461510a565b95602085013595506040909401359392505050565b61ffff835116815263ffffffff602084015116602082015260ff60408401511660408201526060830151606082015260a06080820152600061402260a083018461539e565b600080604083850312156157a557600080fd5b82516157b081614f2d565b60208401519092506150ff81614f2d565b600060208083850312156157d457600080fd5b825167ffffffffffffffff8111156157eb57600080fd5b8301601f810185136157fc57600080fd5b805161580a6150808261500b565b81815260059190911b8201830190838101908783111561582957600080fd5b928401925b82841015614e5557835161584181614f2d565b8252928401929084019061582e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036158df576158df61587f565b5060010190565b8082018082111561172c5761172c61587f565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261592e57600080fd5b83018035915067ffffffffffffffff82111561594957600080fd5b60200191503681900382131561524157600080fd5b808202811582820484141761172c5761172c61587f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826159b3576159b3615975565b500490565b8181038181111561172c5761172c61587f565b6000826159da576159da615975565b500690565b6000815480845260208085019450836000528060002060005b838110156153935781546001600160a01b0316875295820195600191820191016159f8565b60a081526000615a3060a08301886159df565b8281036020840152615a42818861535a565b90508281036040840152615a56818761535a565b60608401959095525050608001529392505050565b62ffffff8916815260e0602080830182905290820188905260009089906101008401835b8b811015615abd578335615aa281614f2d565b6001600160a01b031682529282019290820190600101615a8f565b50604085018a905261ffff8916606086015263ffffffff881660808601529250615ae5915050565b60ff9390931660a082015260c001529695505050505050565b63ffffffff81811683821601908082111561172a5761172a61587f565b604081526000615b2e60408301856159df565b90508260208301529392505050565b600060208284031215615b4f57600080fd5b5051919050565b600061ffff808316818103615b6d57615b6d61587f565b6001019392505050565b6001600160a01b038316815260406020820152600061402260408301846159df565b600062ffffff808316818103615b6d57615b6d61587f565b600081830360a0811215615bc457600080fd5b615bcc614f8e565b835167ffffffffffffffff81168114615be457600080fd5b81526080601f1983011215615bf857600080fd5b615c00614fb7565b91506020840151615c108161513d565b82526040840151615c208161513d565b60208301526060840151615c338161513d565b60408301526080840151615c468161513d565b606083015260208101919091529392505050565b60008251615c6c81846020870161528a565b9190910192915050565b600082601f830112615c8757600080fd5b81516020615c976150808361500b565b828152600592831b8501820192828201919087851115615cb657600080fd5b8387015b85811015615d4a57805167ffffffffffffffff811115615cda5760008081fd5b8801603f81018a13615cec5760008081fd5b858101516040615cfe6150808361500b565b82815291851b8301810191888101908d841115615d1b5760008081fd5b938201935b83851015615d3957845182529389019390890190615d20565b885250505093850193508401615cba565b5090979650505050505050565b60008060408385031215615d6a57600080fd5b825167ffffffffffffffff80821115615d8257600080fd5b818501915085601f830112615d9657600080fd5b81516020615da66150808361500b565b82815260059290921b84018101918181019089841115615dc557600080fd5b948201945b83861015615dec578551615ddd81614f2d565b82529482019490820190615dca565b91880151919650909350505080821115615e0557600080fd5b50615e1285828601615c76565b9150509250929050565b6001600160a01b038316815260406020820152600061402260408301846155be565b600060208284031215615e5057600080fd5b815180151581146135e457600080fd5b6020815260006135e460208301846152ae56fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212207df054e5008403f163230b801dc019404cbe8da456e24fb2d4939cab238a58ad64736f6c63430008110033000000000000000000000000ae975071be8f8ee67addbc1a82488f1c24858067000000000000000000000000793143c66dca69e03f210265212b7b603b280978
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ae975071be8f8ee67addbc1a82488f1c24858067000000000000000000000000793143c66dca69e03f210265212b7b603b280978
-----Decoded View---------------
Arg [0] : chainlinkCoordinatorAddress (address): 0xae975071be8f8ee67addbc1a82488f1c24858067
Arg [1] : pvpGamesStoreAddress (address): 0x793143c66dca69e03f210265212b7b603b280978
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000ae975071be8f8ee67addbc1a82488f1c24858067
Arg [1] : 000000000000000000000000793143c66dca69e03f210265212b7b603b280978
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.