Source Code
Latest 25 from a total of 821,957 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw Deposit... | 29826800 | 16 days ago | IN | 0 ETH | 0.00000104 | ||||
| Cancel | 29826771 | 16 days ago | IN | 0 ETH | 0.00000068 | ||||
| Deposit | 29826710 | 16 days ago | IN | 0.001 ETH | 0.00000062 | ||||
| Withdraw Deposit... | 27994316 | 58 days ago | IN | 0 ETH | 0 | ||||
| Cancel | 27994237 | 58 days ago | IN | 0 ETH | 0.00000008 | ||||
| Deposit | 27994175 | 58 days ago | IN | 0.001 ETH | 0 | ||||
| Withdraw Deposit... | 27943582 | 59 days ago | IN | 0 ETH | 0 | ||||
| Cancel | 27943556 | 59 days ago | IN | 0 ETH | 0.00000008 | ||||
| Deposit | 27943494 | 59 days ago | IN | 0.001 ETH | 0 | ||||
| Withdraw Deposit... | 27870401 | 61 days ago | IN | 0 ETH | 0 | ||||
| Cancel | 27870386 | 61 days ago | IN | 0 ETH | 0.00000008 | ||||
| Deposit | 27870324 | 61 days ago | IN | 0.001 ETH | 0 | ||||
| Withdraw Deposit... | 27820561 | 62 days ago | IN | 0 ETH | 0 | ||||
| Cancel | 27820450 | 62 days ago | IN | 0 ETH | 0.00000008 | ||||
| Deposit | 27820388 | 62 days ago | IN | 0.001 ETH | 0 | ||||
| Withdraw Deposit... | 27728736 | 64 days ago | IN | 0 ETH | 0 | ||||
| Cancel | 27728702 | 64 days ago | IN | 0 ETH | 0.00000008 | ||||
| Deposit | 27728639 | 64 days ago | IN | 0.001 ETH | 0 | ||||
| Withdraw Deposit... | 27722402 | 65 days ago | IN | 0 ETH | 0 | ||||
| Cancel | 27722388 | 65 days ago | IN | 0 ETH | 0.00000008 | ||||
| Deposit | 27722326 | 65 days ago | IN | 0.001 ETH | 0 | ||||
| Withdraw Deposit... | 27722118 | 65 days ago | IN | 0 ETH | 0 | ||||
| Cancel | 27722068 | 65 days ago | IN | 0 ETH | 0.00000008 | ||||
| Deposit | 27722006 | 65 days ago | IN | 0.001 ETH | 0 | ||||
| Withdraw Deposit... | 27704654 | 65 days ago | IN | 0 ETH | 0 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 29826800 | 16 days ago | 0.001 ETH | |||||
| 27994316 | 58 days ago | 0.001 ETH | |||||
| 27943582 | 59 days ago | 0.001 ETH | |||||
| 27870401 | 61 days ago | 0.001 ETH | |||||
| 27820561 | 62 days ago | 0.001 ETH | |||||
| 27728736 | 64 days ago | 0.001 ETH | |||||
| 27722402 | 65 days ago | 0.001 ETH | |||||
| 27722118 | 65 days ago | 0.001 ETH | |||||
| 27704654 | 65 days ago | 0.001 ETH | |||||
| 27694056 | 65 days ago | 0.001 ETH | |||||
| 27658916 | 66 days ago | 0.001 ETH | |||||
| 27653191 | 66 days ago | 0.001 ETH | |||||
| 27443139 | 71 days ago | 0.001 ETH | |||||
| 26686088 | 89 days ago | 0 ETH | |||||
| 26686088 | 89 days ago | 0 ETH | |||||
| 26686088 | 89 days ago | 0 ETH | |||||
| 26686088 | 89 days ago | 0 ETH | |||||
| 26664525 | 89 days ago | 0.001 ETH | |||||
| 26660670 | 89 days ago | 0.001 ETH | |||||
| 26427670 | 95 days ago | 0.01 ETH | |||||
| 26306315 | 97 days ago | 0.001 ETH | |||||
| 24452127 | 140 days ago | 0.002 ETH | |||||
| 24451362 | 140 days ago | 0.001 ETH | |||||
| 23775755 | 156 days ago | 0.001 ETH | |||||
| 23425510 | 164 days ago | 0.00098 ETH |
Latest 2 Deposits
| L2 Txn Hash | L1 Deposit Txn | Value | Token | |
|---|---|---|---|---|
| 0x12e0fdb894a7373b6573c010c8089c8bc9f94303a4c313f46f7a16769bc3e13c | 513 days ago | 0.01 | ||
| 0x8056eda977ece199672cf4a7fa5d0e4b15ed7216bdbf14d01b7c67c89e32ab6c | 513 days ago | 0.01 |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
BlastYoloLimit
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 888888 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {YoloLimit} from "./YoloLimit.sol";
import {IBlast, YieldMode as IBlast__YieldMode, GasMode as IBlast__GasMode} from "./interfaces/IBlast.sol";
import {IBlastPoints} from "./interfaces/IBlastPoints.sol";
import {IERC20Rebasing, YieldMode as IERC20Rebasing__YieldMode} from "./interfaces/IERC20Rebasing.sol";
// @@@@@@@@@@@@@ @@@@@@@@@@@@@
// @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@ @@@@%*+++++++++*%@@@@ @@@@@@@@@@@@@@ @@@@%+-:::::::-+%@@@@
// @#:........=@@ @@*.........+@@@@*=================*@@@ @@=........=@@ @@@+.................+@@@
// @@=........:#@@ @@.........:@@%+=====================+%@@ @@=........=@@ @@%-.....................-#@@
// @@%:........=@@ @@=........:%@*=========================+%@@@=........=@@ @@%-.........................=@@@
// @@+........:#@ @@#........:%%============================+#@@=........=@@ @@#:...........................:#@@
// @@:........+@@ @@:.......:#%%@#*=======*%%@@@%%*==========+%@=........=@@ @#:.........:=*%%@%%#=..........:#@@
// @@#........:%@ @@+........+%+==+#@@*==#@@@@ @@@@#=========+@#........=@@ @%-........:*@@@@ @@@@+:........-%@
// @@=........=@@@#:.......-%*=======*@@@@ @@@=========*@:.......+@@ @@*.........@@@ @@%:........+@@
// @%:.......:%@@=........=@+========*@@ @%=========@+.......+@@ @@-........%@@ @@*........=%@
// @@+:.....:.-@#:.:......+@@@@@@@@@@@@ @@%%%%%%%%#@#::....:+@@ @@:.:.....:@@ @@::.:..:.-%@
// @%-::::::::*-:::::::::@@*+++++++*@@ @@+++++++++@*:::::::+@@ @@:::::::::%@ @@%::::::::-%@
// @@%********=:::::::::*@@*+*#%@@@##@@ @@*========+@=:::::::+@@ @@=::::::::=@@ @@=::::::::+@@
// @@@@@@@@@@:::::::::=@@@@@#*++++++*@@ @@@+=========%@::::::::+@@ @%:::::::::=@@@ @@@=:::::::::#@@
// @@=:::::::::%@ @@*++++++++++*@@@@@@@@@*+========+#@+::::::::+%@@@@@@@%+::::::::::+@@@@@@@@@+::::::::::+@@
// @@#:::::::::*@@ @@*+++++++++*@#++++++==========+*@@+::::::::::::::::::::::::::::::::-===-::::::::::::=@@
// @@-::::::::-%@ @@#++++++++%%+===============+#@@@+::::::::::::::::::::::::::::::::::::::::::::::::*@@
// @@+:::::::::*@@ @@@*+++++%%+===============*%@@@@+:::::::::::::::::::::*=:::::::::::::::::::::::=%@@
// @%-::::::::=@@ @@@#++#@*+============+*@@@ @@+:::::::::::::::::::::%@%+-::::::::::::::::::+%@@
// @@%*********%@@ @@@@@*+==========+#@@@@ @@#*********************%@@@@@*-:::::::::::-*@@@@
// @@@@@@@@@@@@@@ @@@@@@@%%%@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@%%%@@@@@@@
// @@@@ @@@@@
/**
* @title BlastYoloLimit
* @notice This contract permissionlessly hosts yolos on Blast on YOLO Games.
* @author YOLO Games protocol team
*/
contract BlastYoloLimit is YoloLimit {
IERC20Rebasing private immutable WETH_YIELD_CONFIGURATION;
IERC20Rebasing private immutable USDB_YIELD_CONFIGURATION;
/**
* @param params The constructor params.
* @param _ethYieldConfiguration The ETH yield configuration.
* @param _usdbYieldConfiguration The USDB yield configuration.
* @param _blastPoints The Blast points configuration.
* @param _blastPointsOperator The Blast points operator.
*/
constructor(
ConstructorCalldata memory params,
address _ethYieldConfiguration,
address _usdbYieldConfiguration,
address _blastPoints,
address _blastPointsOperator
) YoloLimit(params) {
IBlast(_ethYieldConfiguration).configure(IBlast__YieldMode.CLAIMABLE, IBlast__GasMode.CLAIMABLE, params.owner);
IERC20Rebasing(params.weth).configure(IERC20Rebasing__YieldMode.CLAIMABLE);
IERC20Rebasing(_usdbYieldConfiguration).configure(IERC20Rebasing__YieldMode.CLAIMABLE);
IBlastPoints(_blastPoints).configurePointsOperator(_blastPointsOperator);
WETH_YIELD_CONFIGURATION = IERC20Rebasing(params.weth);
USDB_YIELD_CONFIGURATION = IERC20Rebasing(_usdbYieldConfiguration);
}
/**
* @param wethReceiver The receiver of WETH.
* @param usdbReceiver The receiver of USDB.
*/
function claim(address wethReceiver, address usdbReceiver) external {
_validateIsOwner();
uint256 claimableWETH = WETH_YIELD_CONFIGURATION.getClaimableAmount(address(this));
if (claimableWETH != 0) {
WETH_YIELD_CONFIGURATION.claim(wethReceiver, claimableWETH);
}
uint256 claimableUSDB = USDB_YIELD_CONFIGURATION.getClaimableAmount(address(this));
if (claimableUSDB != 0) {
USDB_YIELD_CONFIGURATION.claim(usdbReceiver, claimableUSDB);
}
}
}// 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
pragma solidity ^0.8.17;
/**
* @dev ERC1271's magic value (bytes4(keccak256("isValidSignature(bytes32,bytes)"))
*/
bytes4 constant ERC1271_MAGIC_VALUE = 0x1626ba7e;// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @notice It is emitted if the call recipient is not a contract. */ error NotAContract();
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @notice It is emitted if the ETH transfer fails. */ error ETHTransferFail(); /** * @notice It is emitted if the ERC20 approval fails. */ error ERC20ApprovalFail(); /** * @notice It is emitted if the ERC20 transfer fails. */ error ERC20TransferFail(); /** * @notice It is emitted if the ERC20 transferFrom fails. */ error ERC20TransferFromFail(); /** * @notice It is emitted if the ERC721 transferFrom fails. */ error ERC721TransferFromFail(); /** * @notice It is emitted if the ERC1155 safeTransferFrom fails. */ error ERC1155SafeTransferFromFail(); /** * @notice It is emitted if the ERC1155 safeBatchTransferFrom fails. */ error ERC1155SafeBatchTransferFromFail();
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @notice It is emitted if the signer is null. */ error NullSignerAddress(); /** * @notice It is emitted if the signature is invalid for an EOA (the address recovered is not the expected one). */ error SignatureEOAInvalid(); /** * @notice It is emitted if the signature is invalid for a ERC1271 contract signer. */ error SignatureERC1271Invalid(); /** * @notice It is emitted if the signature's length is neither 64 nor 65 bytes. */ error SignatureLengthInvalid(uint256 length); /** * @notice It is emitted if the signature is invalid due to S parameter. */ error SignatureParameterSInvalid(); /** * @notice It is emitted if the signature is invalid due to V parameter. */ error SignatureParameterVInvalid(uint8 v);
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IERC1271 {
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
interface IERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
interface IWETH {
function deposit() external payable;
function transfer(address dst, uint256 wad) external returns (bool);
function withdraw(uint256 wad) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title IReentrancyGuard
* @author LooksRare protocol team (👀,💎)
*/
interface IReentrancyGuard {
/**
* @notice This is returned when there is a reentrant call.
*/
error ReentrancyFail();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IERC20} from "../interfaces/generic/IERC20.sol";
// Errors
import {ERC20TransferFail, ERC20TransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
/**
* @title LowLevelERC20Transfer
* @notice This contract contains low-level calls to transfer ERC20 tokens.
* @author LooksRare protocol team (👀,💎)
*/
contract LowLevelERC20Transfer {
/**
* @notice Execute ERC20 transferFrom
* @param currency Currency address
* @param from Sender address
* @param to Recipient address
* @param amount Amount to transfer
*/
function _executeERC20TransferFrom(address currency, address from, address to, uint256 amount) internal {
if (currency.code.length == 0) {
revert NotAContract();
}
(bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount)));
if (!status) {
revert ERC20TransferFromFail();
}
if (data.length > 0) {
if (!abi.decode(data, (bool))) {
revert ERC20TransferFromFail();
}
}
}
/**
* @notice Execute ERC20 (direct) transfer
* @param currency Currency address
* @param to Recipient address
* @param amount Amount to transfer
*/
function _executeERC20DirectTransfer(address currency, address to, uint256 amount) internal {
if (currency.code.length == 0) {
revert NotAContract();
}
(bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transfer, (to, amount)));
if (!status) {
revert ERC20TransferFail();
}
if (data.length > 0) {
if (!abi.decode(data, (bool))) {
revert ERC20TransferFail();
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IERC721} from "../interfaces/generic/IERC721.sol";
// Errors
import {ERC721TransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
/**
* @title LowLevelERC721Transfer
* @notice This contract contains low-level calls to transfer ERC721 tokens.
* @author LooksRare protocol team (👀,💎)
*/
contract LowLevelERC721Transfer {
/**
* @notice Execute ERC721 transferFrom
* @param collection Address of the collection
* @param from Address of the sender
* @param to Address of the recipient
* @param tokenId tokenId to transfer
*/
function _executeERC721TransferFrom(address collection, address from, address to, uint256 tokenId) internal {
if (collection.code.length == 0) {
revert NotAContract();
}
(bool status, ) = collection.call(abi.encodeCall(IERC721.transferFrom, (from, to, tokenId)));
if (!status) {
revert ERC721TransferFromFail();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IWETH} from "../interfaces/generic/IWETH.sol";
/**
* @title LowLevelWETH
* @notice This contract contains a function to transfer ETH with an option to wrap to WETH.
* If the ETH transfer fails within a gas limit, the amount in ETH is wrapped to WETH and then transferred.
* @author LooksRare protocol team (👀,💎)
*/
contract LowLevelWETH {
/**
* @notice It transfers ETH to a recipient with a specified gas limit.
* If the original transfers fails, it wraps to WETH and transfers the WETH to recipient.
* @param _WETH WETH address
* @param _to Recipient address
* @param _amount Amount to transfer
* @param _gasLimit Gas limit to perform the ETH transfer
*/
function _transferETHAndWrapIfFailWithGasLimit(
address _WETH,
address _to,
uint256 _amount,
uint256 _gasLimit
) internal {
bool status;
assembly {
status := call(_gasLimit, _to, _amount, 0, 0, 0, 0)
}
if (!status) {
IWETH(_WETH).deposit{value: _amount}();
IWETH(_WETH).transfer(_to, _amount);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title Pausable
* @notice This contract makes it possible to pause the contract.
* It is adjusted from OpenZeppelin.
* @author LooksRare protocol team (👀,💎)
*/
abstract contract Pausable {
/**
* @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);
error IsPaused();
error NotPaused();
bool private _paused;
/**
* @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 {
if (paused()) {
revert IsPaused();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert NotPaused();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(msg.sender);
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(msg.sender);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IReentrancyGuard} from "./interfaces/IReentrancyGuard.sol";
/**
* @title ReentrancyGuard
* @notice This contract protects against reentrancy attacks.
* It is adjusted from OpenZeppelin.
* @author LooksRare protocol team (👀,💎)
*/
abstract contract ReentrancyGuard is IReentrancyGuard {
uint256 private _status;
/**
* @notice Modifier to wrap functions to prevent reentrancy calls.
*/
modifier nonReentrant() {
if (_status == 2) {
revert ReentrancyFail();
}
_status = 2;
_;
_status = 1;
}
constructor() {
_status = 1;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IERC1271} from "./interfaces/generic/IERC1271.sol";
// Constants
import {ERC1271_MAGIC_VALUE} from "./constants/StandardConstants.sol";
// Errors
import {SignatureParameterSInvalid, SignatureParameterVInvalid, SignatureERC1271Invalid, SignatureEOAInvalid, NullSignerAddress, SignatureLengthInvalid} from "./errors/SignatureCheckerErrors.sol";
/**
* @title SignatureCheckerMemory
* @notice This library is used to verify signatures for EOAs (with lengths of both 65 and 64 bytes)
* and contracts (ERC1271).
* @author LooksRare protocol team (👀,💎)
*/
library SignatureCheckerMemory {
/**
* @notice This function verifies whether the signer is valid for a hash and raw signature.
* @param hash Data hash
* @param signer Signer address (to confirm message validity)
* @param signature Signature parameters encoded (v, r, s)
* @dev For EIP-712 signatures, the hash must be the digest (computed with signature hash and domain separator)
*/
function verify(bytes32 hash, address signer, bytes memory signature) internal view {
if (signer.code.length == 0) {
if (_recoverEOASigner(hash, signature) == signer) return;
revert SignatureEOAInvalid();
} else {
if (IERC1271(signer).isValidSignature(hash, signature) == ERC1271_MAGIC_VALUE) return;
revert SignatureERC1271Invalid();
}
}
/**
* @notice This function is internal and splits a signature into r, s, v outputs.
* @param signature A 64 or 65 bytes signature
* @return r The r output of the signature
* @return s The s output of the signature
* @return v The recovery identifier, must be 27 or 28
*/
function splitSignature(bytes memory signature) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
uint256 length = signature.length;
if (length == 65) {
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
} else if (length == 64) {
assembly {
r := mload(add(signature, 0x20))
let vs := mload(add(signature, 0x40))
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
} else {
revert SignatureLengthInvalid(length);
}
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert SignatureParameterSInvalid();
}
if (v != 27 && v != 28) {
revert SignatureParameterVInvalid(v);
}
}
/**
* @notice This function is private and recovers the signer of a signature (for EOA only).
* @param hash Hash of the signed message
* @param signature Bytes containing the signature (64 or 65 bytes)
* @return signer The address that signed the signature
*/
function _recoverEOASigner(bytes32 hash, bytes memory signature) private pure returns (address signer) {
(bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
// If the signature is valid (and not malleable), return the signer's address
signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
revert NullSignerAddress();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
enum TokenType {
ERC20,
ERC721,
ERC1155
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Enums
import {TokenType} from "../enums/TokenType.sol";
/**
* @title ITransferManager
* @author LooksRare protocol team (👀,💎)
*/
interface ITransferManager {
/**
* @notice This struct is only used for transferBatchItemsAcrossCollections.
* @param tokenAddress Token address
* @param tokenType 0 for ERC721, 1 for ERC1155
* @param itemIds Array of item ids to transfer
* @param amounts Array of amounts to transfer
*/
struct BatchTransferItem {
address tokenAddress;
TokenType tokenType;
uint256[] itemIds;
uint256[] amounts;
}
/**
* @notice It is emitted if operators' approvals to transfer NFTs are granted by a user.
* @param user Address of the user
* @param operators Array of operator addresses
*/
event ApprovalsGranted(address user, address[] operators);
/**
* @notice It is emitted if operators' approvals to transfer NFTs are revoked by a user.
* @param user Address of the user
* @param operators Array of operator addresses
*/
event ApprovalsRemoved(address user, address[] operators);
/**
* @notice It is emitted if a new operator is added to the global allowlist.
* @param operator Operator address
*/
event OperatorAllowed(address operator);
/**
* @notice It is emitted if an operator is removed from the global allowlist.
* @param operator Operator address
*/
event OperatorRemoved(address operator);
/**
* @notice It is returned if the operator to approve has already been approved by the user.
*/
error OperatorAlreadyApprovedByUser();
/**
* @notice It is returned if the operator to revoke has not been previously approved by the user.
*/
error OperatorNotApprovedByUser();
/**
* @notice It is returned if the transfer caller is already allowed by the owner.
* @dev This error can only be returned for owner operations.
*/
error OperatorAlreadyAllowed();
/**
* @notice It is returned if the operator to approve is not in the global allowlist defined by the owner.
* @dev This error can be returned if the user tries to grant approval to an operator address not in the
* allowlist or if the owner tries to remove the operator from the global allowlist.
*/
error OperatorNotAllowed();
/**
* @notice It is returned if the transfer caller is invalid.
* For a transfer called to be valid, the operator must be in the global allowlist and
* approved by the 'from' user.
*/
error TransferCallerInvalid();
/**
* @notice This function transfers ERC20 tokens.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param amount amount
*/
function transferERC20(
address tokenAddress,
address from,
address to,
uint256 amount
) external;
/**
* @notice This function transfers a single item for a single ERC721 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemId Item ID
*/
function transferItemERC721(
address tokenAddress,
address from,
address to,
uint256 itemId
) external;
/**
* @notice This function transfers items for a single ERC721 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemIds Array of itemIds
* @param amounts Array of amounts
*/
function transferItemsERC721(
address tokenAddress,
address from,
address to,
uint256[] calldata itemIds,
uint256[] calldata amounts
) external;
/**
* @notice This function transfers a single item for a single ERC1155 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemId Item ID
* @param amount Amount
*/
function transferItemERC1155(
address tokenAddress,
address from,
address to,
uint256 itemId,
uint256 amount
) external;
/**
* @notice This function transfers items for a single ERC1155 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemIds Array of itemIds
* @param amounts Array of amounts
* @dev It does not allow batch transferring if from = msg.sender since native function should be used.
*/
function transferItemsERC1155(
address tokenAddress,
address from,
address to,
uint256[] calldata itemIds,
uint256[] calldata amounts
) external;
/**
* @notice This function transfers items across an array of tokens that can be ERC20, ERC721 and ERC1155.
* @param items Array of BatchTransferItem
* @param from Sender address
* @param to Recipient address
*/
function transferBatchItemsAcrossCollections(
BatchTransferItem[] calldata items,
address from,
address to
) external;
/**
* @notice This function allows a user to grant approvals for an array of operators.
* Users cannot grant approvals if the operator is not allowed by this contract's owner.
* @param operators Array of operator addresses
* @dev Each operator address must be globally allowed to be approved.
*/
function grantApprovals(address[] calldata operators) external;
/**
* @notice This function allows a user to revoke existing approvals for an array of operators.
* @param operators Array of operator addresses
* @dev Each operator address must be approved at the user level to be revoked.
*/
function revokeApprovals(address[] calldata operators) external;
/**
* @notice This function allows an operator to be added for the shared transfer system.
* Once the operator is allowed, users can grant NFT approvals to this operator.
* @param operator Operator address to allow
* @dev Only callable by owner.
*/
function allowOperator(address operator) external;
/**
* @notice This function allows the user to remove an operator for the shared transfer system.
* @param operator Operator address to remove
* @dev Only callable by owner.
*/
function removeOperator(address operator) external;
/**
* @notice This returns whether the user has approved the operator address.
* The first address is the user and the second address is the operator.
*/
function hasUserApprovedOperator(address user, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// 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/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// 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.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT pragma solidity 0.8.23; /* * @dev error AlreadyWithdrawn() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant AlreadyWithdrawn_error_selector = 0x6507689f; /* * @dev error CutoffTimeNotReached() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant CutoffTimeNotReached_error_selector = 0xf9ad93f5; /* * @dev error DrawExpirationTimeNotReached() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant DrawExpirationTimeNotReached_error_selector = 0xf4c0ca6e; /* * @dev error InsufficientParticipants() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InsufficientParticipants_error_selector = 0x7e439aed; /* * @dev error InvalidIndex() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InvalidIndex_error_selector = 0x63df8171; /* * @dev error InvalidLength() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InvalidLength_error_selector = 0x947d5a84; /* * @dev error InvalidSignatureTimestamp() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InvalidSignatureTimestamp_error_selector = 0xc11f5976; /* * @dev error InvalidStatus() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InvalidStatus_error_selector = 0xf525e320; /* * @dev error InvalidToken() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InvalidToken_error_selector = 0xc1ab6dc1; /* * @dev error InvalidTokenType() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InvalidTokenType_error_selector = 0xa1e9dd9d; /* * @dev error InvalidValue() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant InvalidValue_error_selector = 0xaa7feadc; /* * @dev error LooksAlreadySet() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant LooksAlreadySet_error_selector = 0xd6336f0d; /* * @dev error MaximumNumberOfDepositsReached() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant MaximumNumberOfDepositsReached_error_selector = 0x27e6fcc7; /* * @dev error MaximumNumberOfParticipantsReached() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant MaximumNumberOfParticipantsReached_error_selector = 0xb53a57db; /* * @dev error MessageIdInvalid() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant MessageIdInvalid_error_selector = 0x0da5618b; /* * @dev error NotDepositor() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant NotDepositor_error_selector = 0x3cc50b45; /* * @dev error NotOwner() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant NotOwner_error_selector = 0x30cd7471; /* * @dev error NotOperator() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant NotOperator_error_selector = 0x7c214f04; /* * @dev error NotWinner() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant NotWinner_error_selector = 0x618c7242; /* * @dev error OnePlayerCannotFillUpTheWholeRound() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant OnePlayerCannotFillUpTheWholeRound_error_selector = 0xae24220e; /* * @dev error OutflowNotAllowed() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant OutflowNotAllowed_error_selector = 0x010a265a; /* * @dev error ProtocolFeeNotPaid() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant ProtocolFeeNotPaid_error_selector = 0x0134f278; /* * @dev error RandomnessRequestAlreadyExists() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant RandomnessRequestAlreadyExists_error_selector = 0xf9012132; /* * @dev error RoundCannotBeClosed() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant RoundCannotBeClosed_error_selector = 0x7cd9dd6a; /* * @dev error TooFewEntries() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant TooFewEntries_error_selector = 0xf48cb8a0; /* * @dev error ZeroDeposits() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant ZeroDeposits_error_selector = 0xa95231d5; /* * @dev error ZeroEntries() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant ZeroEntries_error_selector = 0xf9121438; /* * @dev error ZeroRounds() * Memory layout: * - 0x00: Left-padded selector (data begins at 0x1c) * Revert buffer is memory[0x1c:0x20] */ uint256 constant ZeroRounds_error_selector = 0xcbc4e060; uint256 constant Error_selector_offset = 0x1c; uint256 constant Error_standard_length = 0x04;
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
enum GasMode {
VOID,
CLAIMABLE
}
interface IBlast {
// configure
function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
function configure(YieldMode _yield, GasMode gasMode, address governor) external;
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;
// claim yield
function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);
// claim gas
function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGasAtMinClaimRate(
address contractAddress,
address recipientOfGas,
uint256 minClaimRateBips
) external returns (uint256);
function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGas(
address contractAddress,
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external returns (uint256);
// read functions
function readClaimableYield(address contractAddress) external view returns (uint256);
function readYieldConfiguration(address contractAddress) external view returns (uint8);
function readGasParams(
address contractAddress
) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
interface IBlastPoints {
function configurePointsOperator(address operator) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
interface IERC20Rebasing {
// changes the yield mode of the caller and update the balance
// to reflect the configuration
function configure(YieldMode) external returns (uint256);
// "claimable" yield mode accounts can call this this claim their yield
// to another address
function claim(address recipient, uint256 amount) external returns (uint256);
// read the claimable amount for an account
function getClaimableAmount(address account) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
interface IPriceOracle {
error PoolNotAllowed();
error PriceIsZero();
event PoolAdded(address token, address pool);
event PoolRemoved(address token);
function getTWAP(address token, uint32 secondsAgo) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
interface IYoloV2 {
/**
* @notice A round's status
* @param None The round does not exist
* @param Open The round is open for deposits
* @param Drawing The round is being drawn
* @param Drawn The round has been drawn
* @param Cancelled The round has been cancelled
*/
enum RoundStatus {
None,
Open,
Drawing,
Drawn,
Cancelled
}
/**
* @dev Giving TokenType a namespace to avoid name conflicts with TransferManager.
*/
enum YoloV2__TokenType {
ETH,
ERC20,
ERC721
}
event TokensStatusUpdated(address[] tokens, YoloV2__TokenType tokenType, bool isAllowed);
event Deposited(address depositor, uint256 roundId, uint256 entriesCount);
event ERC20OracleUpdated(address erc20Oracle);
event MaximumNumberOfParticipantsPerRoundUpdated(uint40 maximumNumberOfParticipantsPerRound);
event MultipleRoundsDeposited(
address depositor,
uint256 startingRoundId,
uint256[] amounts,
uint256[] entriesCounts
);
event PrizesClaimed(address winner, WithdrawalCalldata[] withdrawalCalldata);
event DepositsWithdrawn(address depositor, WithdrawalCalldata[] withdrawalCalldata);
event Rollover(
address depositor,
WithdrawalCalldata[] withdrawalCalldata,
uint256 enteredRoundId,
uint256 entriesCount
);
event ProtocolFeeBpUpdated(uint16 protocolFeeBp);
event DiscountedProtocolFeeBpUpdated(uint16 discountedProtocolFeeBp);
event ProtocolFeePayment(uint256 amount, address token);
event ProtocolFeeRecipientUpdated(address protocolFeeRecipient);
event RandomnessRequested(uint256 roundId, uint256 requestId);
event ReservoirOracleUpdated(address reservoirOracle);
event RoundDurationUpdated(uint40 roundDuration);
event RoundsCancelled(uint256 startingRoundId, uint256 numberOfRounds);
event RoundStatusUpdated(uint256 roundId, RoundStatus status);
event SignatureValidityPeriodUpdated(uint40 signatureValidityPeriod);
event ValuePerEntryUpdated(uint256 valuePerEntry);
event OutflowAllowedUpdated(bool isAllowed);
error AlreadyWithdrawn();
error CutoffTimeNotReached();
error DrawExpirationTimeNotReached();
error InsufficientParticipants();
error InvalidIndex();
error InvalidLength();
error InvalidSignatureTimestamp();
error InvalidStatus();
error InvalidToken();
error InvalidTokenType();
error InvalidValue();
error LooksAlreadySet();
error MaximumNumberOfDepositsReached();
error MaximumNumberOfParticipantsReached();
error MessageIdInvalid();
error NotOperator();
error NotOwner();
error NotWinner();
error NotDepositor();
error OnePlayerCannotFillUpTheWholeRound();
error OutflowNotAllowed();
error ProtocolFeeNotPaid();
error RandomnessRequestAlreadyExists();
error RoundCannotBeClosed();
error TooFewEntries();
error ZeroDeposits();
error ZeroEntries();
error ZeroRounds();
/**
* @param owner The owner of the contract.
* @param operator The operator of the contract.
* @param roundDuration The duration of each round.
* @param valuePerEntry The value of each entry in ETH.
* @param protocolFeeRecipient The protocol fee recipient.
* @param protocolFeeBp The protocol fee basis points.
* @param discountedProtocolFeeBp The discounted protocol fee basis points.
* @param keyHash Chainlink VRF key hash
* @param subscriptionId Chainlink VRF subscription ID
* @param vrfCoordinator Chainlink VRF coordinator address
* @param reservoirOracle Reservoir off-chain oracle address
* @param erc20Oracle ERC20 on-chain oracle address
* @param transferManager Transfer manager
* @param signatureValidityPeriod The validity period of a Reservoir signature.
* @param minimumRequestConfirmations The minimum number of confirmation blocks on VRF requests before oracles respond.
*/
struct ConstructorCalldata {
address owner;
address operator;
uint40 maximumNumberOfParticipantsPerRound;
uint40 roundDuration;
uint96 valuePerEntry;
address protocolFeeRecipient;
uint16 protocolFeeBp;
uint16 discountedProtocolFeeBp;
bytes32 keyHash;
uint64 subscriptionId;
address vrfCoordinator;
address reservoirOracle;
address transferManager;
address erc20Oracle;
address weth;
uint40 signatureValidityPeriod;
uint16 minimumRequestConfirmations;
}
/**
* @param id The id of the response.
* @param payload The payload of the response.
* @param timestamp The timestamp of the response.
* @param signature The signature of the response.
*/
struct ReservoirOracleFloorPrice {
bytes32 id;
bytes payload;
uint256 timestamp;
bytes signature;
}
/**
* @param tokenType The type of the token.
* @param tokenAddress The address of the token.
* @param tokenIdsOrAmounts The ids (ERC-721) or amounts (ERC-20) of the tokens.
* @param minimumEntries The minimum entries to receive if it's an ERC-20 deposit. Unused for ERC-721 deposits.
* @param reservoirOracleFloorPrice The Reservoir oracle floor price. Required for ERC-721 deposits.
*/
struct DepositCalldata {
YoloV2__TokenType tokenType;
address tokenAddress;
uint256[] tokenIdsOrAmounts;
uint256 minimumEntries;
ReservoirOracleFloorPrice reservoirOracleFloorPrice;
}
/*
* @notice A round
* @dev The storage layout of a round is as follows:
* |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
* | empty (72 bits) | numberOfParticipants (40) bits) | drawnAt (40 bits) | cutoffTime (40 bits) | protcoolFeeBp (16 bits) | maximumNumberOfParticipants (40 bits) | status (8 bits) |
* |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
* | valuePerEntry (96 bits) | winner (160 bits) |
* |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
* | protocolFeeOwed (256 bits) |
* |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
* | deposits length (256 bits) |
* |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
*
* @param status The status of the round.
* @param maximumNumberOfParticipants The maximum number of participants.
* @param protocolFeeBp The protocol fee basis points.
* @param cutoffTime The cutoff time of the round.
* @param drawnAt The time the round was drawn.
* @param numberOfParticipants The current number of participants.
* @param winner The winner of the round.
* @param valuePerEntry The value of each entry in ETH.
* @param protocolFeeOwed The protocol fee owed in ETH.
* @param deposits The deposits in the round.
*/
struct Round {
RoundStatus status;
uint40 maximumNumberOfParticipants;
uint16 protocolFeeBp;
uint40 cutoffTime;
uint40 drawnAt;
uint40 numberOfParticipants;
address winner;
uint96 valuePerEntry;
uint256 protocolFeeOwed;
Deposit[] deposits;
}
/**
* @notice A deposit in a round.
* @dev The storage layout of a deposit is as follows:
* |-------------------------------------------------------------------------------------------|
* | empty (88 bits) | tokenAddress (160 bits) | tokenType (8 bits) |
* |-------------------------------------------------------------------------------------------|
* | tokenId (256 bits) |
* |-------------------------------------------------------------------------------------------|
* | tokenAmount (256 bits) |
* |-------------------------------------------------------------------------------------------|
* | empty (48 bits) | currentEntryIndex (40 bits) | withdrawn (8 bits) | depositor (160 bits) |
* |-------------------------------------------------------------------------------------------|
*
* @param tokenType The type of the token.
* @param tokenAddress The address of the token.
* @param tokenId The id of the token.
* @param tokenAmount The amount of the token.
* @param depositor The depositor of the token.
* @param withdrawn Whether the token has been withdrawn.
* @param currentEntryIndex The current entry index.
*/
struct Deposit {
YoloV2__TokenType tokenType;
address tokenAddress;
uint256 tokenId;
uint256 tokenAmount;
address depositor;
bool withdrawn;
uint40 currentEntryIndex;
}
/**
* @param exists Whether the request exists.
* @param roundId The id of the round.
* @param randomWord The random words returned by Chainlink VRF.
* If randomWord == 0, then the request is still pending.
*/
struct RandomnessRequest {
bool exists;
uint40 roundId;
uint256 randomWord;
}
/**
* @param roundId The id of the round.
* @param depositIndices The indices of the deposits to be claimed.
*/
struct WithdrawalCalldata {
uint256 roundId;
uint256[] depositIndices;
}
/**
* @notice This is used to accumulate the amount of tokens to be transferred.
* @param tokenAddress The address of the token.
* @param amount The amount of tokens accumulated.
*/
struct TransferAccumulator {
address tokenAddress;
uint256 amount;
}
/**
* @notice This function cancels an expired round that does not have at least 2 participants.
*/
function cancel() external;
/**
* @notice This function cancels multiple rounds (current and future) without any validations (can be any state).
* Only callable by the contract owner.
* @param numberOfRounds Number of rounds to cancel.
*/
function cancel(uint256 numberOfRounds) external;
/**
* @notice Cancels a round after randomness request if the randomness request
* does not arrive after 25 hours. Chainlink VRF stops trying after 24 hours.
* Giving an extra hour should prevent any frontrun attempts.
*/
function cancelAfterRandomnessRequest() external;
/**
* @notice This function allows the winner of a round to withdraw the prizes.
* @param withdrawalCalldata The rounds and the indices for the rounds for the prizes to claim.
* @param payWithLOOKS Whether to pay for the protocol fee with LOOKS.
*/
function claimPrizes(WithdrawalCalldata[] calldata withdrawalCalldata, bool payWithLOOKS) external payable;
/**
* @notice This function calculates the ETH payment required to claim the prizes for multiple rounds.
* @param withdrawalCalldata The rounds and the indices for the rounds for the prizes to claim.
* @param payWithLOOKS Whether to pay for the protocol fee with LOOKS.
* @return protocolFeeOwed The protocol fee owed in ETH or LOOKS.
*/
function getClaimPrizesPaymentRequired(
WithdrawalCalldata[] calldata withdrawalCalldata,
bool payWithLOOKS
) external view returns (uint256 protocolFeeOwed);
/**
* @notice This function is used by the frontend to estimate the minimumEntries to be set for ERC-20 deposits.
* @dev This function does 0 validations. It is the responsibility of the caller to ensure that the deposit is valid and the round is enterable.
* @param roundId The round ID.
* @param singleDeposit The ERC-20 deposit.
* @return entriesCount The estimated number of entries for the deposit amount.
*/
function estimatedERC20DepositEntriesCount(
uint256 roundId,
DepositCalldata calldata singleDeposit
) external view returns (uint256 entriesCount);
/**
* @notice This function allows withdrawal of deposits from a round if the round is cancelled
* @param withdrawalCalldata The rounds and the indices for the rounds for the prizes to claim.
*/
function withdrawDeposits(WithdrawalCalldata[] calldata withdrawalCalldata) external;
/**
* @notice This function allows players to deposit into a round.
* @param roundId The open round ID.
* @param deposits The ERC-20/ERC-721 deposits to be made.
*/
function deposit(uint256 roundId, DepositCalldata[] calldata deposits) external payable;
/**
* @notice This function allows a player to deposit into multiple rounds at once. ETH only.
* @param startingRoundId The starting round ID to deposit into. Round status must be Open.
* @param amounts The amount of ETH to deposit into each round.
*/
function depositETHIntoMultipleRounds(uint256 startingRoundId, uint256[] calldata amounts) external payable;
/**
* @notice This function draws a round.
*/
function drawWinner() external;
/**
* @notice This function returns the given round's data.
* @param roundId The round ID.
* @return status The status of the round.
* @return maximumNumberOfParticipants The round's maximum number of participants.
* @return roundProtocolFeeBp The round's protocol fee in basis points.
* @return cutoffTime The round's cutoff time.
* @return drawnAt The time the round was drawn.
* @return numberOfParticipants The round's current number of participants.
* @return winner The round's winner.
* @return roundValuePerEntry The round's value per entry.
* @return protocolFeeOwed The round's protocol fee owed in ETH.
* @return deposits The round's deposits.
*/
function getRound(
uint256 roundId
)
external
view
returns (
IYoloV2.RoundStatus status,
uint40 maximumNumberOfParticipants,
uint16 roundProtocolFeeBp,
uint40 cutoffTime,
uint40 drawnAt,
uint40 numberOfParticipants,
address winner,
uint96 roundValuePerEntry,
uint256 protocolFeeOwed,
Deposit[] memory deposits
);
/**
* @notice This function allows a player to rollover prizes or deposits from a cancelled round to the current round.
* @param roundId The round ID to rollover ETH into.
* @param withdrawalCalldata The rounds and the indices for the rounds for the prizes to claim.
* @param payWithLOOKS Whether to pay for the protocol fee with LOOKS.
*/
function rolloverETH(uint256 roundId, WithdrawalCalldata[] calldata withdrawalCalldata, bool payWithLOOKS) external;
/**
* @notice This function allows the owner to pause/unpause the contract.
*/
function togglePaused() external;
/**
* @notice This function allows the owner to allow/forbid token outflow.
*/
function toggleOutflowAllowed() external;
/**
* @notice This function allows the owner to update token statuses (ERC-20 and ERC-721).
* @param tokens Token addresses
* @param tokenType Token type
* @param isAllowed Whether the tokens should be allowed in the yolos
* @dev Only callable by owner.
*/
function updateTokensStatus(address[] calldata tokens, YoloV2__TokenType tokenType, bool isAllowed) external;
/**
* @notice This function allows the owner to update the duration of each round.
* @param _roundDuration The duration of each round.
*/
function updateRoundDuration(uint40 _roundDuration) external;
/**
* @notice This function allows the owner to update the signature validity period.
* @param _signatureValidityPeriod The signature validity period.
*/
function updateSignatureValidityPeriod(uint40 _signatureValidityPeriod) external;
/**
* @notice This function allows the owner to update the value of each entry in ETH.
* @param _valuePerEntry The value of each entry in ETH.
*/
function updateValuePerEntry(uint96 _valuePerEntry) external;
/**
* @notice This function allows the owner to update the discounted protocol fee in basis points if paid in LOOKS.
* @param discountedProtocolFeeBp The discounted protocol fee in basis points.
*/
function updateDiscountedProtocolFeeBp(uint16 discountedProtocolFeeBp) external;
/**
* @notice This function allows the owner to update the protocol fee in basis points.
* @param protocolFeeBp The protocol fee in basis points.
*/
function updateProtocolFeeBp(uint16 protocolFeeBp) external;
/**
* @notice This function allows the owner to update the protocol fee recipient.
* @param protocolFeeRecipient The protocol fee recipient.
*/
function updateProtocolFeeRecipient(address protocolFeeRecipient) external;
/**
* @notice This function allows the owner to update Reservoir oracle's address.
* @param reservoirOracle Reservoir oracle address.
*/
function updateReservoirOracle(address reservoirOracle) external;
/**
* @notice This function allows the owner to update the maximum number of participants per round.
* @param _maximumNumberOfParticipantsPerRound The maximum number of participants per round.
*/
function updateMaximumNumberOfParticipantsPerRound(uint40 _maximumNumberOfParticipantsPerRound) external;
/**
* @notice This function allows the owner to update ERC20 oracle's address.
* @param erc20Oracle ERC20 oracle address.
*/
function updateERC20Oracle(address erc20Oracle) external;
/**
* @notice This function allows the owner to set the LOOKS token address. Only callable once.
* @param looks LOOKS token address.
*/
function setLOOKS(address looks) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev Collection of functions related to array types.
* Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Arrays.sol
*/
library Arrays {
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
function findUpperBound(uint256[] memory array, uint256 element) internal pure returns (uint256) {
if (array.length == 0) {
return 0;
}
uint256 low = 0;
uint256 high = array.length;
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds down (it does integer division with truncation).
if (array[mid] > element) {
high = mid;
} else {
unchecked {
low = mid + 1;
}
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && array[low - 1] == element) {
unchecked {
return low - 1;
}
} else {
return low;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {ITransferManager} from "@looksrare/contracts-transfer-manager/contracts/interfaces/ITransferManager.sol";
import {TokenType as TransferManager__TokenType} from "@looksrare/contracts-transfer-manager/contracts/enums/TokenType.sol";
import {IERC20} from "@looksrare/contracts-libs/contracts/interfaces/generic/IERC20.sol";
import {SignatureCheckerMemory} from "@looksrare/contracts-libs/contracts/SignatureCheckerMemory.sol";
import {ReentrancyGuard} from "@looksrare/contracts-libs/contracts/ReentrancyGuard.sol";
import {Pausable} from "@looksrare/contracts-libs/contracts/Pausable.sol";
import {LowLevelWETH} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelWETH.sol";
import {LowLevelERC20Transfer} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelERC20Transfer.sol";
import {LowLevelERC721Transfer} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelERC721Transfer.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import {IYoloV2} from "./interfaces/IYoloV2.sol";
import {IPriceOracle} from "./interfaces/IPriceOracle.sol";
import {Arrays} from "./libraries/Arrays.sol";
import {AlreadyWithdrawn_error_selector, CutoffTimeNotReached_error_selector, DrawExpirationTimeNotReached_error_selector, InsufficientParticipants_error_selector, InvalidIndex_error_selector, InvalidLength_error_selector, InvalidSignatureTimestamp_error_selector, InvalidStatus_error_selector, InvalidToken_error_selector, InvalidTokenType_error_selector, InvalidValue_error_selector, LooksAlreadySet_error_selector, MaximumNumberOfDepositsReached_error_selector, MaximumNumberOfParticipantsReached_error_selector, MessageIdInvalid_error_selector, NotDepositor_error_selector, NotOperator_error_selector, NotOwner_error_selector, NotWinner_error_selector, OnePlayerCannotFillUpTheWholeRound_error_selector, OutflowNotAllowed_error_selector, ProtocolFeeNotPaid_error_selector, RandomnessRequestAlreadyExists_error_selector, RoundCannotBeClosed_error_selector, TooFewEntries_error_selector, ZeroDeposits_error_selector, ZeroEntries_error_selector, ZeroRounds_error_selector, Error_selector_offset, Error_standard_length} from "./constants/AssemblyConstants.sol";
// @@@@@@@@@@@@@ @@@@@@@@@@@@@
// @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@ @@@@%*+++++++++*%@@@@ @@@@@@@@@@@@@@ @@@@%+-:::::::-+%@@@@
// @#:........=@@ @@*.........+@@@@*=================*@@@ @@=........=@@ @@@+.................+@@@
// @@=........:#@@ @@.........:@@%+=====================+%@@ @@=........=@@ @@%-.....................-#@@
// @@%:........=@@ @@=........:%@*=========================+%@@@=........=@@ @@%-.........................=@@@
// @@+........:#@ @@#........:%%============================+#@@=........=@@ @@#:...........................:#@@
// @@:........+@@ @@:.......:#%%@#*=======*%%@@@%%*==========+%@=........=@@ @#:.........:=*%%@%%#=..........:#@@
// @@#........:%@ @@+........+%+==+#@@*==#@@@@ @@@@#=========+@#........=@@ @%-........:*@@@@ @@@@+:........-%@
// @@=........=@@@#:.......-%*=======*@@@@ @@@=========*@:.......+@@ @@*.........@@@ @@%:........+@@
// @%:.......:%@@=........=@+========*@@ @%=========@+.......+@@ @@-........%@@ @@*........=%@
// @@+:.....:.-@#:.:......+@@@@@@@@@@@@ @@%%%%%%%%#@#::....:+@@ @@:.:.....:@@ @@::.:..:.-%@
// @%-::::::::*-:::::::::@@*+++++++*@@ @@+++++++++@*:::::::+@@ @@:::::::::%@ @@%::::::::-%@
// @@%********=:::::::::*@@*+*#%@@@##@@ @@*========+@=:::::::+@@ @@=::::::::=@@ @@=::::::::+@@
// @@@@@@@@@@:::::::::=@@@@@#*++++++*@@ @@@+=========%@::::::::+@@ @%:::::::::=@@@ @@@=:::::::::#@@
// @@=:::::::::%@ @@*++++++++++*@@@@@@@@@*+========+#@+::::::::+%@@@@@@@%+::::::::::+@@@@@@@@@+::::::::::+@@
// @@#:::::::::*@@ @@*+++++++++*@#++++++==========+*@@+::::::::::::::::::::::::::::::::-===-::::::::::::=@@
// @@-::::::::-%@ @@#++++++++%%+===============+#@@@+::::::::::::::::::::::::::::::::::::::::::::::::*@@
// @@+:::::::::*@@ @@@*+++++%%+===============*%@@@@+:::::::::::::::::::::*=:::::::::::::::::::::::=%@@
// @%-::::::::=@@ @@@#++#@*+============+*@@@ @@+:::::::::::::::::::::%@%+-::::::::::::::::::+%@@
// @@%*********%@@ @@@@@*+==========+#@@@@ @@#*********************%@@@@@*-:::::::::::-*@@@@
// @@@@@@@@@@@@@@ @@@@@@@%%%@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@%%%@@@@@@@
// @@@@ @@@@@
/**
* @title YoloLimit
* @notice This contract permissionlessly hosts yolos with deposit limit on YOLO Games.
* @author YOLO Games protocol team
*/
contract YoloLimit is
IYoloV2,
AccessControl,
VRFConsumerBaseV2,
LowLevelWETH,
LowLevelERC20Transfer,
LowLevelERC721Transfer,
ReentrancyGuard,
Pausable
{
using Arrays for uint256[];
/**
* @notice Operators are allowed to add/remove allowed ERC-20 and ERC-721 tokens.
*/
bytes32 private constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
/**
* @notice The TWAP period in seconds to use.
*/
uint256 private constant TWAP_DURATION = 3_600;
/**
* @notice The maximum protocol fee in basis points, which is 25%.
*/
uint16 private constant MAXIMUM_PROTOCOL_FEE_BP = 2_500;
/**
* @notice The maximum number of deposits per round.
*/
uint256 private constant MAXIMUM_NUMBER_OF_DEPOSITS_PER_ROUND = 1_000;
/**
* @notice Reservoir oracle's message typehash.
* @dev It is used to compute the hash of the message using the (message) id, the payload, and the timestamp.
*/
bytes32 private constant RESERVOIR_ORACLE_MESSAGE_TYPEHASH =
keccak256("Message(bytes32 id,bytes payload,uint256 timestamp,uint256 chainId)");
/**
* @notice Reservoir oracle's ID typehash.
* @dev It is used to compute the hash of the ID using price kind, TWAP seconds, and the contract address.
*/
bytes32 private constant RESERVOIR_ORACLE_ID_TYPEHASH =
keccak256(
"ContractWideCollectionPrice(uint8 kind,uint256 twapSeconds,address contract,bool onlyNonFlaggedTokens)"
);
/**
* @notice The bits offset of the round's maximum number of participants in a round slot.
*/
uint256 private constant ROUND__MAXIMUM_NUMBER_OF_PARTICIPANTS_OFFSET = 8;
/**
* @notice The bits offset of the round's protocol fee basis points in a round slot.
*/
uint256 private constant ROUND__PROTOCOL_FEE_BP_OFFSET = 48;
/**
* @notice The bits offset of the round's cutoff time in a round slot.
*/
uint256 private constant ROUND__CUTOFF_TIME_OFFSET = 64;
/**
* @notice The bits offset of the round's value per entry in a round slot.
*/
uint256 private constant ROUND__VALUE_PER_ENTRY_OFFSET = 160;
/**
* @notice The slot offset of the round's value per entry starting from the round's slot.
*/
uint256 private constant ROUND__VALUE_PER_ENTRY_SLOT_OFFSET = 1;
/**
* @notice The bits offset of the randomness request's round ID in a randomness request slot.
*/
uint256 private constant RANDOMNESS_REQUEST__ROUND_ID_OFFSET = 8;
/**
* @notice The slot offset of the round's deposits length starting from the round's slot.
*/
uint256 private constant ROUND__DEPOSITS_LENGTH_SLOT_OFFSET = 3;
/**
* @notice The number of slots a round struct occupies.
*/
uint256 private constant DEPOSIT__OCCUPIED_SLOTS = 4;
/**
* @notice The slot offset of the deposit's token ID starting from the deposit's slot.
*/
uint256 private constant DEPOSIT__TOKEN_ID_SLOT_OFFSET = 1;
/**
* @notice The slot offset of the deposit's token amount starting from the deposit's slot.
*/
uint256 private constant DEPOSIT__TOKEN_AMOUNT_SLOT_OFFSET = 2;
/**
* @notice The slot offset of the deposit's last slot starting from the deposit's slot.
*/
uint256 private constant DEPOSIT__LAST_SLOT_OFFSET = 3;
/**
* @notice The bits offset of the deposit's token address in the deposit's slot 0.
*/
uint256 private constant DEPOSIT__TOKEN_ADDRESS_OFFSET = 8;
/**
* @notice The bits offset of the deposit's current entry index in the deposit's slot 3.
*/
uint256 private constant DEPOSIT__CURRENT_ENTRY_INDEX_OFFSET = 168;
/**
* @notice Wrapped Ether address.
*/
address private immutable WETH;
/**
* @notice The key hash of the Chainlink VRF.
*/
bytes32 private immutable KEY_HASH;
/**
* @notice The subscription ID of the Chainlink VRF.
*/
uint64 private immutable SUBSCRIPTION_ID;
/**
* @notice The Chainlink VRF coordinator.
*/
VRFCoordinatorV2Interface private immutable VRF_COORDINATOR;
/**
* @notice The minimum number of confirmation blocks on VRF requests before oracles respond.
*/
uint16 private immutable MINIMUM_REQUEST_CONFIRMATIONS;
/**
* @notice Transfer manager faciliates token transfers.
*/
ITransferManager private immutable transferManager;
/**
* @notice LOOKS token address.
*/
address private LOOKS;
/**
* @notice The value of each entry in ETH.
*/
uint96 public valuePerEntry;
/**
* @notice The duration of each round.
*/
uint40 public roundDuration;
/**
* @notice The protocol fee basis points.
*/
uint16 public protocolFeeBp;
/**
* @notice The discounted protocol fee basis points if paid with LOOKS.
*/
uint16 public discountedProtocolFeeBp;
/**
* @notice Number of rounds that have been created.
* @dev In this smart contract, roundId is an uint256 but its
* max value can only be 2^40 - 1. Realistically we will still
* not reach this number.
*/
uint40 public roundsCount;
/**
* @notice The maximum number of participants per round.
*/
uint40 public maximumNumberOfParticipantsPerRound;
/**
* @notice Whether token outflow is allowed.
*/
bool public outflowAllowed = true;
/**
* @notice The maximum deposit amount per round in ETH.
*
* @dev Unlike valuePerEntry, this is not stored in each round's round struct.
* So whenever this is updated, it will be applied to the current round.
* as well.
*/
uint96 public maximumDepositAmountPerRound;
/**
* @notice The address of the protocol fee recipient.
*/
address private protocolFeeRecipient;
/**
* @notice ERC-20 oracle address.
*/
IPriceOracle public erc20Oracle;
/**
* @notice Reservoir oracle address.
*/
address public reservoirOracle;
/**
* @notice Reservoir oracle's signature validity period.
*/
uint40 public signatureValidityPeriod;
/**
* @notice It checks whether the token is allowed.
* @dev 0 is not allowed, 1 is allowed.
* token is the hash of the token address and the token type.
*/
mapping(bytes32 token => uint256 isAllowed) private isTokenAllowed;
mapping(uint256 roundId => Round) private rounds;
/**
* @notice The deposit count of a user in any given round.
*/
mapping(uint256 roundId => mapping(address depositor => uint256 depositCount)) public depositCount;
/**
* @notice A user's deposited amount in ETH in any given round.
*/
mapping(uint256 roundId => mapping(address depositor => uint256 amount)) public depositedAmount;
/**
* @notice Chainlink randomness requests.
*/
mapping(uint256 requestId => RandomnessRequest) public randomnessRequests;
/**
* @notice The price of an ERC-20/ERC-712 token or a collection in any given round.
*/
mapping(address tokenOrCollection => mapping(uint256 roundId => uint256 price)) public prices;
event MaximumDepositAmountPerRoundUpdated(uint256 maximumDepositAmountPerRound);
error ExceededMaximumDepositAmountPerRound();
/**
* @param params The constructor params.
*/
constructor(ConstructorCalldata memory params) VRFConsumerBaseV2(params.vrfCoordinator) {
_grantRole(DEFAULT_ADMIN_ROLE, params.owner);
_grantRole(OPERATOR_ROLE, params.operator);
_updateRoundDuration(params.roundDuration);
_updateProtocolFeeRecipient(params.protocolFeeRecipient);
_updateProtocolFeeBp(params.protocolFeeBp);
_updateDiscountedProtocolFeeBp(params.discountedProtocolFeeBp);
_updateValuePerEntry(params.valuePerEntry);
_updateERC20Oracle(params.erc20Oracle);
_updateMaximumNumberOfParticipantsPerRound(params.maximumNumberOfParticipantsPerRound);
_updateReservoirOracle(params.reservoirOracle);
_updateSignatureValidityPeriod(params.signatureValidityPeriod);
_updateMaximumDepositAmountPerRound(0.1 ether);
WETH = params.weth;
KEY_HASH = params.keyHash;
VRF_COORDINATOR = VRFCoordinatorV2Interface(params.vrfCoordinator);
SUBSCRIPTION_ID = params.subscriptionId;
MINIMUM_REQUEST_CONFIRMATIONS = params.minimumRequestConfirmations;
transferManager = ITransferManager(params.transferManager);
_startRound({_roundsCount: 0});
}
/**
* @param looks The LOOKS token address.
*/
function setLOOKS(address looks) external {
_validateIsOwner();
if (LOOKS != address(0)) {
_revertWith(LooksAlreadySet_error_selector);
}
LOOKS = looks;
}
/**
* @inheritdoc IYoloV2
*/
function deposit(uint256 roundId, DepositCalldata[] calldata deposits) external payable nonReentrant whenNotPaused {
_deposit(roundId, deposits);
}
/**
* @inheritdoc IYoloV2
*/
function depositETHIntoMultipleRounds(
uint256 startingRoundId,
uint256[] calldata amounts
) external payable nonReentrant whenNotPaused {
uint256 numberOfRounds = amounts.length;
if (numberOfRounds == 0) {
_revertWith(ZeroDeposits_error_selector);
}
Round storage startingRound = rounds[startingRoundId];
_validateRoundIsOpen(startingRound);
_setCutoffTimeIfNotSet(startingRound);
uint256 expectedValue;
uint256[] memory entriesCounts = new uint256[](numberOfRounds);
for (uint256 i; i < numberOfRounds; ++i) {
uint256 roundId = _unsafeAdd(startingRoundId, i);
Round storage round = rounds[roundId];
uint256 roundValuePerEntry = round.valuePerEntry;
if (roundValuePerEntry == 0) {
roundValuePerEntry = _writeDataToRound({roundId: roundId, roundValue: 0});
}
_incrementUserDepositCount(roundId, round);
uint256 depositAmount = amounts[i];
if (depositAmount % roundValuePerEntry != 0) {
_revertWithInvalidValue();
}
_incrementUserDepositAmount(roundId, depositAmount);
uint256 entriesCount = _depositETH(round, roundId, roundValuePerEntry, depositAmount);
expectedValue += depositAmount;
entriesCounts[i] = entriesCount;
}
if (expectedValue != msg.value) {
_revertWithInvalidValue();
}
emit MultipleRoundsDeposited(msg.sender, startingRoundId, amounts, entriesCounts);
if (
_shouldDrawWinner(
startingRound.numberOfParticipants,
startingRound.maximumNumberOfParticipants,
startingRound.deposits.length
)
) {
_drawWinner(startingRound, startingRoundId);
}
}
/**
* @inheritdoc IYoloV2
*/
function getRound(
uint256 roundId
)
external
view
returns (
RoundStatus status,
uint40 maximumNumberOfParticipants,
uint16 roundProtocolFeeBp,
uint40 cutoffTime,
uint40 drawnAt,
uint40 numberOfParticipants,
address winner,
uint96 roundValuePerEntry,
uint256 protocolFeeOwed,
Deposit[] memory deposits
)
{
status = rounds[roundId].status;
maximumNumberOfParticipants = rounds[roundId].maximumNumberOfParticipants;
roundProtocolFeeBp = rounds[roundId].protocolFeeBp;
cutoffTime = rounds[roundId].cutoffTime;
drawnAt = rounds[roundId].drawnAt;
numberOfParticipants = rounds[roundId].numberOfParticipants;
winner = rounds[roundId].winner;
roundValuePerEntry = rounds[roundId].valuePerEntry;
protocolFeeOwed = rounds[roundId].protocolFeeOwed;
deposits = rounds[roundId].deposits;
}
/**
* @inheritdoc IYoloV2
*/
function drawWinner() external nonReentrant whenNotPaused {
uint256 roundId = roundsCount;
Round storage round = rounds[roundId];
_validateRoundStatus(round, RoundStatus.Open);
uint256 numberOfParticipants = round.numberOfParticipants;
if (!_shouldDrawWinner(numberOfParticipants, round.maximumNumberOfParticipants, round.deposits.length)) {
if (block.timestamp < round.cutoffTime) {
_revertWith(CutoffTimeNotReached_error_selector);
}
if (numberOfParticipants < 2) {
_revertWith(InsufficientParticipants_error_selector);
}
}
_drawWinner(round, roundId);
}
/**
* @inheritdoc IYoloV2
*/
function cancel() external nonReentrant {
_validateOutflowIsAllowed();
_cancel({roundId: roundsCount});
}
/**
* @inheritdoc IYoloV2
*/
function cancel(uint256 numberOfRounds) external {
_validateIsOwner();
if (numberOfRounds == 0) {
_revertWith(ZeroRounds_error_selector);
}
_cancelMultipleRounds(numberOfRounds);
}
/**
* @inheritdoc IYoloV2
*/
function cancelAfterRandomnessRequest() external nonReentrant {
_validateOutflowIsAllowed();
uint256 roundId = roundsCount;
Round storage round = rounds[roundId];
_validateRoundStatus(round, RoundStatus.Drawing);
if (block.timestamp < round.drawnAt + 25 hours) {
_revertWith(DrawExpirationTimeNotReached_error_selector);
}
_setRoundStatus(round, roundId, RoundStatus.Cancelled);
_startRound({_roundsCount: roundId});
}
/**
* @inheritdoc IYoloV2
*/
function claimPrizes(
WithdrawalCalldata[] calldata withdrawalCalldata,
bool payWithLOOKS
) external payable nonReentrant {
_validateOutflowIsAllowed();
TransferAccumulator memory transferAccumulator;
uint256 ethAmount;
uint256 protocolFeeOwed;
_validateArrayLengthIsNotEmpty(withdrawalCalldata.length);
if (payWithLOOKS) {
if (msg.value != 0) {
_revertWithInvalidValue();
}
}
for (uint256 i; i < withdrawalCalldata.length; ++i) {
WithdrawalCalldata calldata perRoundWithdrawalCalldata = withdrawalCalldata[i];
Round storage round = rounds[perRoundWithdrawalCalldata.roundId];
_validateRoundStatus(round, RoundStatus.Drawn);
_validateMsgSenderIsWinner(round);
uint256[] calldata depositIndices = perRoundWithdrawalCalldata.depositIndices;
_validateArrayLengthIsNotEmpty(depositIndices.length);
for (uint256 j; j < depositIndices.length; ++j) {
uint256 index = depositIndices[j];
_validateDepositsArrayIndex(index, round.deposits.length);
ethAmount = _transferTokenOut(round.deposits[index], transferAccumulator, ethAmount);
}
protocolFeeOwed += round.protocolFeeOwed;
round.protocolFeeOwed = 0;
}
if (protocolFeeOwed != 0) {
if (payWithLOOKS) {
_payForProtocolFeesInLOOKS(protocolFeeOwed);
} else {
_payForProtocolFeesInETH(protocolFeeOwed);
protocolFeeOwed -= msg.value;
if (protocolFeeOwed <= ethAmount) {
unchecked {
ethAmount -= protocolFeeOwed;
}
} else {
_revertWith(ProtocolFeeNotPaid_error_selector);
}
}
}
if (transferAccumulator.amount != 0) {
_executeERC20DirectTransfer(transferAccumulator.tokenAddress, msg.sender, transferAccumulator.amount);
}
if (ethAmount != 0) {
_transferETHAndWrapIfFailWithGasLimit(WETH, msg.sender, ethAmount, gasleft());
}
emit PrizesClaimed(msg.sender, withdrawalCalldata);
}
/**
* @inheritdoc IYoloV2
* @dev This function does not validate withdrawalCalldata to not contain duplicate round IDs and prize indices.
* It is the responsibility of the caller to ensure that. Otherwise, the returned protocol fee owed will be incorrect.
*/
function getClaimPrizesPaymentRequired(
WithdrawalCalldata[] calldata withdrawalCalldata,
bool payWithLOOKS
) external view returns (uint256 protocolFeeOwed) {
uint256 ethAmount;
for (uint256 i; i < withdrawalCalldata.length; ++i) {
WithdrawalCalldata calldata perRoundWithdrawalCalldata = withdrawalCalldata[i];
Round storage round = rounds[perRoundWithdrawalCalldata.roundId];
_validateRoundStatus(round, RoundStatus.Drawn);
uint256[] calldata depositIndices = perRoundWithdrawalCalldata.depositIndices;
uint256 numberOfPrizes = depositIndices.length;
uint256 prizesCount = round.deposits.length;
for (uint256 j; j < numberOfPrizes; ++j) {
uint256 index = depositIndices[j];
if (index >= prizesCount) {
_revertWith(InvalidIndex_error_selector);
}
Deposit storage prize = round.deposits[index];
if (prize.tokenType == YoloV2__TokenType.ETH) {
ethAmount += prize.tokenAmount;
}
}
protocolFeeOwed += round.protocolFeeOwed;
}
if (payWithLOOKS) {
protocolFeeOwed = _protocolFeeOwedInLOOKS(protocolFeeOwed);
} else {
if (protocolFeeOwed < ethAmount) {
protocolFeeOwed = 0;
} else {
unchecked {
protocolFeeOwed -= ethAmount;
}
}
}
}
/**
* @inheritdoc IYoloV2
*/
function estimatedERC20DepositEntriesCount(
uint256 roundId,
DepositCalldata calldata singleDeposit
) external view returns (uint256 entriesCount) {
address tokenAddress = singleDeposit.tokenAddress;
uint256 price = prices[tokenAddress][roundId];
if (price == 0) {
price = _getTWAP(tokenAddress);
}
entriesCount =
((price * singleDeposit.tokenIdsOrAmounts[0]) / (10 ** IERC20(tokenAddress).decimals())) /
rounds[roundId].valuePerEntry;
}
/**
* @inheritdoc IYoloV2
*/
function withdrawDeposits(WithdrawalCalldata[] calldata withdrawalCalldata) external nonReentrant {
_validateOutflowIsAllowed();
TransferAccumulator memory transferAccumulator;
uint256 ethAmount;
_validateArrayLengthIsNotEmpty(withdrawalCalldata.length);
for (uint256 i; i < withdrawalCalldata.length; ++i) {
WithdrawalCalldata calldata perRoundWithdrawalCalldata = withdrawalCalldata[i];
Round storage round = rounds[perRoundWithdrawalCalldata.roundId];
_validateRoundStatus(round, RoundStatus.Cancelled);
uint256[] calldata depositIndices = perRoundWithdrawalCalldata.depositIndices;
uint256 depositIndicesLength = depositIndices.length;
_validateArrayLengthIsNotEmpty(depositIndicesLength);
for (uint256 j; j < depositIndicesLength; ++j) {
uint256 index = depositIndices[j];
_validateDepositsArrayIndex(index, round.deposits.length);
Deposit storage singleDeposit = round.deposits[index];
_validateMsgSenderIsDepositor(singleDeposit);
ethAmount = _transferTokenOut(singleDeposit, transferAccumulator, ethAmount);
}
}
if (transferAccumulator.amount != 0) {
_executeERC20DirectTransfer(transferAccumulator.tokenAddress, msg.sender, transferAccumulator.amount);
}
if (ethAmount != 0) {
_transferETHAndWrapIfFailWithGasLimit(WETH, msg.sender, ethAmount, gasleft());
}
emit DepositsWithdrawn(msg.sender, withdrawalCalldata);
}
/**
* @inheritdoc IYoloV2
*/
function rolloverETH(
uint256 roundId,
WithdrawalCalldata[] calldata withdrawalCalldata,
bool payWithLOOKS
) external nonReentrant whenNotPaused {
uint256 rolloverAmount;
uint256 protocolFeeOwed;
uint256 withdrawalCalldataLength = withdrawalCalldata.length;
_validateArrayLengthIsNotEmpty(withdrawalCalldataLength);
for (uint256 i; i < withdrawalCalldataLength; ++i) {
WithdrawalCalldata calldata perRoundWithdrawalCalldata = withdrawalCalldata[i];
Round storage cancelledOrDrawnRound = rounds[perRoundWithdrawalCalldata.roundId];
RoundStatus status = cancelledOrDrawnRound.status;
if (status < RoundStatus.Drawn) {
_revertWithInvalidStatus();
}
if (status == RoundStatus.Drawn) {
_validateMsgSenderIsWinner(cancelledOrDrawnRound);
protocolFeeOwed += cancelledOrDrawnRound.protocolFeeOwed;
cancelledOrDrawnRound.protocolFeeOwed = 0;
}
uint256[] calldata depositIndices = perRoundWithdrawalCalldata.depositIndices;
uint256 depositIndicesLength = depositIndices.length;
_validateArrayLengthIsNotEmpty(depositIndicesLength);
for (uint256 j; j < depositIndicesLength; ++j) {
uint256 index = depositIndices[j];
_validateDepositsArrayIndex(index, cancelledOrDrawnRound.deposits.length);
Deposit storage singleDeposit = cancelledOrDrawnRound.deposits[index];
_validateDepositNotWithdrawn(singleDeposit);
if (singleDeposit.tokenType != YoloV2__TokenType.ETH) {
_revertWith(InvalidTokenType_error_selector);
}
if (status == RoundStatus.Cancelled) {
_validateMsgSenderIsDepositor(singleDeposit);
}
singleDeposit.withdrawn = true;
rolloverAmount += singleDeposit.tokenAmount;
}
}
if (protocolFeeOwed != 0) {
if (payWithLOOKS) {
_payForProtocolFeesInLOOKS(protocolFeeOwed);
} else {
if (rolloverAmount < protocolFeeOwed) {
_revertWith(ProtocolFeeNotPaid_error_selector);
} else {
unchecked {
rolloverAmount -= protocolFeeOwed;
}
}
_payForProtocolFeesInETH(protocolFeeOwed);
}
}
Round storage round = rounds[roundId];
_validateRoundIsOpen(round);
_incrementUserDepositCount(roundId, round);
_setCutoffTimeIfNotSet(round);
uint256 roundValuePerEntry = round.valuePerEntry;
uint256 dust = rolloverAmount % roundValuePerEntry;
if (dust != 0) {
_validateOutflowIsAllowed();
unchecked {
rolloverAmount -= dust;
}
_transferETHAndWrapIfFailWithGasLimit(WETH, msg.sender, dust, gasleft());
}
if (rolloverAmount < roundValuePerEntry) {
_revertWithInvalidValue();
}
_incrementUserDepositAmount(roundId, rolloverAmount);
uint256 entriesCount = _depositETH(round, roundId, roundValuePerEntry, rolloverAmount);
if (_shouldDrawWinner(round.numberOfParticipants, round.maximumNumberOfParticipants, round.deposits.length)) {
_drawWinner(round, roundId);
}
emit Rollover(msg.sender, withdrawalCalldata, roundId, entriesCount);
}
/**
* @inheritdoc IYoloV2
*/
function togglePaused() external {
_validateIsOwner();
if (paused()) {
_unpause();
_cancelMultipleRounds({numberOfRounds: 1});
} else {
_pause();
}
}
/**
* @inheritdoc IYoloV2
*/
function toggleOutflowAllowed() external {
_validateIsOwner();
bool _outflowAllowed = outflowAllowed;
outflowAllowed = !_outflowAllowed;
emit OutflowAllowedUpdated(!_outflowAllowed);
}
/**
* @inheritdoc IYoloV2
*/
function updateTokensStatus(address[] calldata tokens, YoloV2__TokenType tokenType, bool isAllowed) external {
_validateIsOperator();
if (tokenType == YoloV2__TokenType.ETH) {
_revertWithInvalidToken();
}
uint256 count = tokens.length;
for (uint256 i; i < count; ++i) {
isTokenAllowed[keccak256(abi.encodePacked(tokens[i], tokenType))] = (isAllowed ? 1 : 0);
}
emit TokensStatusUpdated(tokens, tokenType, isAllowed);
}
/**
* @inheritdoc IYoloV2
*/
function updateRoundDuration(uint40 _roundDuration) external {
_validateIsOwner();
_updateRoundDuration(_roundDuration);
}
/**
* @inheritdoc IYoloV2
*/
function updateSignatureValidityPeriod(uint40 _signatureValidityPeriod) external {
_validateIsOwner();
_updateSignatureValidityPeriod(_signatureValidityPeriod);
}
/**
* @inheritdoc IYoloV2
*/
function updateValuePerEntry(uint96 _valuePerEntry) external {
_validateIsOwner();
_updateValuePerEntry(_valuePerEntry);
}
/**
* @notice Updates the maximum deposit amount per round. Only callable by the owner.
*/
function updateMaximumDepositAmountPerRound(uint96 _maximumDepositAmountPerRound) external {
_validateIsOwner();
_updateMaximumDepositAmountPerRound(_maximumDepositAmountPerRound);
}
/**
* @inheritdoc IYoloV2
*/
function updateProtocolFeeRecipient(address _protocolFeeRecipient) external {
_validateIsOwner();
_updateProtocolFeeRecipient(_protocolFeeRecipient);
}
/**
* @inheritdoc IYoloV2
*/
function updateProtocolFeeBp(uint16 _protocolFeeBp) external {
_validateIsOwner();
_updateProtocolFeeBp(_protocolFeeBp);
}
/**
* @inheritdoc IYoloV2
*/
function updateDiscountedProtocolFeeBp(uint16 _discountedProtocolFeeBp) external {
_validateIsOwner();
_updateDiscountedProtocolFeeBp(_discountedProtocolFeeBp);
}
/**
* @inheritdoc IYoloV2
*/
function updateMaximumNumberOfParticipantsPerRound(uint40 _maximumNumberOfParticipantsPerRound) external {
_validateIsOwner();
_updateMaximumNumberOfParticipantsPerRound(_maximumNumberOfParticipantsPerRound);
}
/**
* @inheritdoc IYoloV2
*/
function updateReservoirOracle(address _reservoirOracle) external {
_validateIsOwner();
_updateReservoirOracle(_reservoirOracle);
}
/**
* @inheritdoc IYoloV2
*/
function updateERC20Oracle(address _erc20Oracle) external {
_validateIsOwner();
_updateERC20Oracle(_erc20Oracle);
}
/**
* @param round The round to update.
* @param roundId The round's ID.
* @param status The round's status.
*/
function _setRoundStatus(Round storage round, uint256 roundId, RoundStatus status) private {
round.status = status;
emit RoundStatusUpdated(roundId, status);
}
/**
* @param _roundDuration The duration of each round.
*/
function _updateRoundDuration(uint40 _roundDuration) private {
if (_roundDuration > 1 hours) {
_revertWithInvalidValue();
}
roundDuration = _roundDuration;
emit RoundDurationUpdated(_roundDuration);
}
/**
* @param _signatureValidityPeriod The validity period of a Reservoir signature.
*/
function _updateSignatureValidityPeriod(uint40 _signatureValidityPeriod) private {
signatureValidityPeriod = _signatureValidityPeriod;
emit SignatureValidityPeriodUpdated(_signatureValidityPeriod);
}
/**
* @param _valuePerEntry The value of each entry in ETH.
*/
function _updateValuePerEntry(uint96 _valuePerEntry) private {
if (_valuePerEntry == 0) {
_revertWithInvalidValue();
}
valuePerEntry = _valuePerEntry;
emit ValuePerEntryUpdated(_valuePerEntry);
}
/**
* @param _maximumDepositAmountPerRound The maximum deposit amount per round in ETH.
*/
function _updateMaximumDepositAmountPerRound(uint96 _maximumDepositAmountPerRound) private {
if (_maximumDepositAmountPerRound == 0) {
_revertWithInvalidValue();
}
maximumDepositAmountPerRound = _maximumDepositAmountPerRound;
emit MaximumDepositAmountPerRoundUpdated(_maximumDepositAmountPerRound);
}
/**
* @param _protocolFeeRecipient The new protocol fee recipient address
*/
function _updateProtocolFeeRecipient(address _protocolFeeRecipient) private {
if (_protocolFeeRecipient == address(0)) {
_revertWithInvalidValue();
}
protocolFeeRecipient = _protocolFeeRecipient;
emit ProtocolFeeRecipientUpdated(_protocolFeeRecipient);
}
/**
* @param _protocolFeeBp The new protocol fee in basis points
*/
function _updateProtocolFeeBp(uint16 _protocolFeeBp) private {
if (_protocolFeeBp > MAXIMUM_PROTOCOL_FEE_BP) {
_revertWithInvalidValue();
}
protocolFeeBp = _protocolFeeBp;
emit ProtocolFeeBpUpdated(_protocolFeeBp);
}
/**
* @param _discountedProtocolFeeBp The new discounted protocol fee in basis points
*/
function _updateDiscountedProtocolFeeBp(uint16 _discountedProtocolFeeBp) private {
if (_discountedProtocolFeeBp > 10_000) {
_revertWithInvalidValue();
}
discountedProtocolFeeBp = _discountedProtocolFeeBp;
emit DiscountedProtocolFeeBpUpdated(_discountedProtocolFeeBp);
}
/**
* @param _maximumNumberOfParticipantsPerRound The new maximum number of participants per round
*/
function _updateMaximumNumberOfParticipantsPerRound(uint40 _maximumNumberOfParticipantsPerRound) private {
if (_maximumNumberOfParticipantsPerRound < 2) {
_revertWithInvalidValue();
}
maximumNumberOfParticipantsPerRound = _maximumNumberOfParticipantsPerRound;
emit MaximumNumberOfParticipantsPerRoundUpdated(_maximumNumberOfParticipantsPerRound);
}
/**
* @param _reservoirOracle The new Reservoir oracle address
*/
function _updateReservoirOracle(address _reservoirOracle) private {
if (_reservoirOracle == address(0)) {
_revertWithInvalidValue();
}
reservoirOracle = _reservoirOracle;
emit ReservoirOracleUpdated(_reservoirOracle);
}
/**
* @param _erc20Oracle The new ERC-20 oracle address
*/
function _updateERC20Oracle(address _erc20Oracle) private {
if (_erc20Oracle == address(0)) {
_revertWithInvalidValue();
}
erc20Oracle = IPriceOracle(_erc20Oracle);
emit ERC20OracleUpdated(_erc20Oracle);
}
/**
* @param _roundsCount The current rounds count
* @return roundId The started round ID
*/
function _startRound(uint256 _roundsCount) private returns (uint256 roundId) {
unchecked {
roundId = _roundsCount + 1;
}
roundsCount = uint40(roundId);
Round storage round = rounds[roundId];
if (round.valuePerEntry == 0) {
// On top of the 4 values covered by _writeDataToRound, this also writes the round's status to Open (1).
_writeDataToRound({roundId: roundId, roundValue: 1});
} else {
uint256 numberOfParticipants = round.numberOfParticipants;
uint40 _roundDuration = roundDuration;
// This is equivalent to
// round.status = RoundStatus.Open;
// if (round.numberOfParticipants > 0) {
// round.cutoffTime = uint40(block.timestamp) + _roundDuration;
// }
uint256 roundSlot = _getRoundSlot(roundId);
assembly {
// RoundStatus.Open is equal to 1.
let roundValue := or(sload(roundSlot), 1)
if gt(numberOfParticipants, 0) {
roundValue := or(roundValue, shl(ROUND__CUTOFF_TIME_OFFSET, add(timestamp(), _roundDuration)))
}
sstore(roundSlot, roundValue)
}
}
emit RoundStatusUpdated(roundId, RoundStatus.Open);
}
/**
* @param round The open round.
* @param roundId The open round ID.
*/
function _drawWinner(Round storage round, uint256 roundId) private {
_setRoundStatus(round, roundId, RoundStatus.Drawing);
round.drawnAt = uint40(block.timestamp);
uint256 requestId = VRF_COORDINATOR.requestRandomWords({
keyHash: KEY_HASH,
subId: SUBSCRIPTION_ID,
minimumRequestConfirmations: MINIMUM_REQUEST_CONFIRMATIONS,
callbackGasLimit: uint32(2_500_000),
numWords: uint32(1)
});
if (randomnessRequests[requestId].exists) {
_revertWith(RandomnessRequestAlreadyExists_error_selector);
}
// This is equivalent to
// randomnessRequests[requestId].exists = true;
// randomnessRequests[requestId].roundId = uint40(roundId);
assembly {
mstore(0x00, requestId)
mstore(0x20, randomnessRequests.slot)
let randomnessRequestSlot := keccak256(0x00, 0x40)
// 1 is true
sstore(randomnessRequestSlot, or(1, shl(RANDOMNESS_REQUEST__ROUND_ID_OFFSET, roundId)))
}
emit RandomnessRequested(roundId, requestId);
}
/**
* @param roundId The open round ID.
* @param deposits The ERC-20/ERC-721 deposits to be made.
*/
function _deposit(uint256 roundId, DepositCalldata[] calldata deposits) private {
Round storage round = rounds[roundId];
_validateRoundIsOpen(round);
_incrementUserDepositCount(roundId, round);
_setCutoffTimeIfNotSet(round);
uint256 roundDepositCount = round.deposits.length;
uint40 currentEntryIndex;
uint256 totalEntriesCount;
uint256 roundDepositsLengthSlot = _getRoundSlot(roundId) + ROUND__DEPOSITS_LENGTH_SLOT_OFFSET;
if (msg.value == 0) {
if (deposits.length == 0) {
_revertWith(ZeroDeposits_error_selector);
}
} else {
uint256 roundValuePerEntry = round.valuePerEntry;
if (msg.value % roundValuePerEntry != 0) {
_revertWithInvalidValue();
}
uint256 entriesCount = msg.value / roundValuePerEntry;
totalEntriesCount += entriesCount;
currentEntryIndex = _getCurrentEntryIndexWithoutAccrual(round, roundDepositCount, entriesCount);
// This is equivalent to
// round.deposits.push(
// Deposit({
// tokenType: YoloV2__TokenType.ETH,
// tokenAddress: address(0),
// tokenId: 0,
// tokenAmount: msg.value,
// depositor: msg.sender,
// withdrawn: false,
// currentEntryIndex: currentEntryIndex
// })
// );
uint256 depositDataSlotWithCountOffset = _getDepositDataSlotWithCountOffset(
roundDepositsLengthSlot,
roundDepositCount
);
// We don't have to write tokenType, tokenAddress, tokenId, and withdrawn because they are 0.
_writeDepositorAndCurrentEntryIndexToDeposit(depositDataSlotWithCountOffset, currentEntryIndex);
_writeDepositAmountToDeposit(depositDataSlotWithCountOffset, msg.value);
unchecked {
++roundDepositCount;
}
}
if (deposits.length != 0) {
ITransferManager.BatchTransferItem[] memory batchTransferItems = new ITransferManager.BatchTransferItem[](
deposits.length
);
for (uint256 i; i < deposits.length; ++i) {
DepositCalldata calldata singleDeposit = deposits[i];
address tokenAddress = singleDeposit.tokenAddress;
if (isTokenAllowed[keccak256(abi.encodePacked(tokenAddress, singleDeposit.tokenType))] != 1) {
_revertWithInvalidToken();
}
uint256 price = prices[tokenAddress][roundId];
if (singleDeposit.tokenType == YoloV2__TokenType.ERC721) {
if (price == 0) {
price = _getReservoirPrice(singleDeposit);
prices[tokenAddress][roundId] = price;
}
uint256 entriesCount = price / round.valuePerEntry;
_validateNonZeroEntries(entriesCount);
uint256[] memory amounts = new uint256[](singleDeposit.tokenIdsOrAmounts.length);
for (uint256 j; j < singleDeposit.tokenIdsOrAmounts.length; ++j) {
totalEntriesCount += entriesCount;
currentEntryIndex = _incrementCurrentEntryIndex(
currentEntryIndex,
entriesCount,
round,
roundDepositCount
);
uint256 tokenId = singleDeposit.tokenIdsOrAmounts[j];
// tokenAmount is in reality 1, but we never use it and it is cheaper to set it as 0.
// This is equivalent to
// round.deposits.push(
// Deposit({
// tokenType: YoloV2__TokenType.ERC721,
// tokenAddress: tokenAddress,
// tokenId: tokenId,
// tokenAmount: 0,
// depositor: msg.sender,
// withdrawn: false,
// currentEntryIndex: currentEntryIndex
// })
// );
// unchecked {
// roundDepositCount += 1;
// }
uint256 depositDataSlotWithCountOffset = _getDepositDataSlotWithCountOffset(
roundDepositsLengthSlot,
roundDepositCount
);
_writeDepositorAndCurrentEntryIndexToDeposit(depositDataSlotWithCountOffset, currentEntryIndex);
_writeTokenAddressToDeposit(
depositDataSlotWithCountOffset,
YoloV2__TokenType.ERC721,
tokenAddress
);
assembly {
sstore(add(depositDataSlotWithCountOffset, DEPOSIT__TOKEN_ID_SLOT_OFFSET), tokenId)
roundDepositCount := add(roundDepositCount, 1)
}
amounts[j] = 1;
}
batchTransferItems[i].tokenAddress = tokenAddress;
batchTransferItems[i].tokenType = TransferManager__TokenType.ERC721;
batchTransferItems[i].itemIds = singleDeposit.tokenIdsOrAmounts;
batchTransferItems[i].amounts = amounts;
} else if (singleDeposit.tokenType == YoloV2__TokenType.ERC20) {
if (price == 0) {
price = _getTWAP(tokenAddress);
prices[tokenAddress][roundId] = price;
}
uint256[] memory amounts = singleDeposit.tokenIdsOrAmounts;
if (amounts.length != 1) {
_revertWith(InvalidLength_error_selector);
}
uint256 amount = amounts[0];
uint256 entriesCount = ((price * amount) / (10 ** IERC20(tokenAddress).decimals())) /
round.valuePerEntry;
_validateNonZeroEntries(entriesCount);
if (entriesCount < singleDeposit.minimumEntries) {
_revertWith(TooFewEntries_error_selector);
}
batchTransferItems[i].tokenAddress = tokenAddress;
batchTransferItems[i].tokenType = TransferManager__TokenType.ERC20;
batchTransferItems[i].amounts = singleDeposit.tokenIdsOrAmounts;
totalEntriesCount += entriesCount;
currentEntryIndex = _incrementCurrentEntryIndex(
currentEntryIndex,
entriesCount,
round,
roundDepositCount
);
// round.deposits.push(
// Deposit({
// tokenType: YoloV2__TokenType.ERC20,
// tokenAddress: tokenAddress,
// tokenId: 0,
// tokenAmount: amount,
// depositor: msg.sender,
// withdrawn: false,
// currentEntryIndex: currentEntryIndex
// })
// );
uint256 depositDataSlotWithCountOffset = _getDepositDataSlotWithCountOffset(
roundDepositsLengthSlot,
roundDepositCount
);
_writeDepositorAndCurrentEntryIndexToDeposit(depositDataSlotWithCountOffset, currentEntryIndex);
_writeDepositAmountToDeposit(depositDataSlotWithCountOffset, amount);
_writeTokenAddressToDeposit(depositDataSlotWithCountOffset, YoloV2__TokenType.ERC20, tokenAddress);
unchecked {
++roundDepositCount;
}
}
}
transferManager.transferBatchItemsAcrossCollections(batchTransferItems, msg.sender, address(this));
}
_incrementUserDepositAmount(roundId, totalEntriesCount * round.valuePerEntry);
assembly {
sstore(roundDepositsLengthSlot, roundDepositCount)
}
{
uint256 numberOfParticipants = round.numberOfParticipants;
_validateRoundDepositsAndPlayers(roundDepositCount, numberOfParticipants);
if (_shouldDrawWinner(numberOfParticipants, round.maximumNumberOfParticipants, roundDepositCount)) {
_drawWinner(round, roundId);
}
}
emit Deposited(msg.sender, roundId, totalEntriesCount);
}
/**
* @param roundId The ID of the round to be cancelled.
*/
function _cancel(uint256 roundId) private {
Round storage round = rounds[roundId];
_validateRoundStatus(round, RoundStatus.Open);
uint256 cutoffTime = round.cutoffTime;
if (cutoffTime == 0 || block.timestamp < cutoffTime) {
_revertWith(CutoffTimeNotReached_error_selector);
}
if (round.numberOfParticipants > 1) {
_revertWith(RoundCannotBeClosed_error_selector);
}
_setRoundStatus(round, roundId, RoundStatus.Cancelled);
_startRound({_roundsCount: roundId});
}
/**
* @param numberOfRounds Number of rounds to cancel.
*/
function _cancelMultipleRounds(uint256 numberOfRounds) private {
uint256 startingRoundId = roundsCount;
for (uint256 i; i < numberOfRounds; ++i) {
rounds[_unsafeAdd(startingRoundId, i)].status = RoundStatus.Cancelled;
}
emit RoundsCancelled(startingRoundId, numberOfRounds);
_startRound({_roundsCount: _unsafeSubtract(_unsafeAdd(startingRoundId, numberOfRounds), 1)});
}
/**
* @param requestId The ID of the request
* @param randomWords The random words returned by Chainlink
*/
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
if (randomnessRequests[requestId].exists) {
uint256 roundId = randomnessRequests[requestId].roundId;
Round storage round = rounds[roundId];
if (round.status == RoundStatus.Drawing) {
_setRoundStatus(round, roundId, RoundStatus.Drawn);
uint256 randomWord = randomWords[0];
randomnessRequests[requestId].randomWord = randomWord;
uint256 count = round.deposits.length;
uint256[] memory currentEntryIndexArray = new uint256[](count);
for (uint256 i; i < count; ++i) {
currentEntryIndexArray[i] = uint256(round.deposits[i].currentEntryIndex);
}
uint256 currentEntryIndex = currentEntryIndexArray[_unsafeSubtract(count, 1)];
uint256 winningEntry = _unsafeAdd(randomWord % currentEntryIndex, 1);
round.winner = round.deposits[currentEntryIndexArray.findUpperBound(winningEntry)].depositor;
round.protocolFeeOwed = (round.valuePerEntry * currentEntryIndex * round.protocolFeeBp) / 10_000;
_startRound({_roundsCount: roundId});
}
}
}
/**
* @param roundId The round ID.
* @param round The round.
*/
function _incrementUserDepositCount(uint256 roundId, Round storage round) private {
uint256 userDepositCount = depositCount[roundId][msg.sender];
if (userDepositCount == 0) {
uint256 numberOfParticipants = round.numberOfParticipants;
if (numberOfParticipants == round.maximumNumberOfParticipants) {
_revertWith(MaximumNumberOfParticipantsReached_error_selector);
}
unchecked {
round.numberOfParticipants = uint40(numberOfParticipants + 1);
}
}
unchecked {
depositCount[roundId][msg.sender] = userDepositCount + 1;
}
}
/**
* @param roundId The round ID.
* @param depositAmount The deposit amount in ETH.
*/
function _incrementUserDepositAmount(uint256 roundId, uint256 depositAmount) private {
if (depositedAmount[roundId][msg.sender] + depositAmount > maximumDepositAmountPerRound) {
revert ExceededMaximumDepositAmountPerRound();
}
depositedAmount[roundId][msg.sender] += depositAmount;
}
/**
* @param round The round to check.
*/
function _setCutoffTimeIfNotSet(Round storage round) private {
if (round.cutoffTime == 0) {
round.cutoffTime = uint40(block.timestamp + roundDuration);
}
}
/**
* @dev This function is used to write the following values to the round:
* - maximumNumberOfParticipants
* - valuePerEntry
* - protocolFeeBp
*
* roundValue can be provided to write other to other fields in the round.
* @param roundId The round ID.
* @param roundValue The starting round slot value to write to the round.
* @return _valuePerEntry The round's value per entry in ETH.
*/
function _writeDataToRound(uint256 roundId, uint256 roundValue) private returns (uint256 _valuePerEntry) {
// This is equivalent to
// round.maximumNumberOfParticipants = maximumNumberOfParticipantsPerRound;
// round.valuePerEntry = valuePerEntry;
// round.protocolFeeBp = protocolFeeBp;
uint256 _maximumNumberOfParticipantsPerRound = maximumNumberOfParticipantsPerRound;
uint256 _protocolFeeBp = protocolFeeBp;
_valuePerEntry = valuePerEntry;
uint256 roundSlot = _getRoundSlot(roundId);
assembly {
roundValue := or(
roundValue,
shl(ROUND__MAXIMUM_NUMBER_OF_PARTICIPANTS_OFFSET, _maximumNumberOfParticipantsPerRound)
)
roundValue := or(roundValue, shl(ROUND__PROTOCOL_FEE_BP_OFFSET, _protocolFeeBp))
sstore(roundSlot, roundValue)
sstore(
add(roundSlot, ROUND__VALUE_PER_ENTRY_SLOT_OFFSET),
shl(ROUND__VALUE_PER_ENTRY_OFFSET, _valuePerEntry)
)
}
}
/**
* @param depositDataSlotWithCountOffset The deposit data slot with count offset.
* @param currentEntryIndex The current entry index at the current deposit.
*/
function _writeDepositorAndCurrentEntryIndexToDeposit(
uint256 depositDataSlotWithCountOffset,
uint256 currentEntryIndex
) private {
assembly {
sstore(
add(depositDataSlotWithCountOffset, DEPOSIT__LAST_SLOT_OFFSET),
or(caller(), shl(DEPOSIT__CURRENT_ENTRY_INDEX_OFFSET, currentEntryIndex))
)
}
}
/**
* @param depositDataSlotWithCountOffset The deposit data slot with count offset.
* @param depositAmount The token amount to write to the deposit.
*/
function _writeDepositAmountToDeposit(uint256 depositDataSlotWithCountOffset, uint256 depositAmount) private {
assembly {
sstore(add(depositDataSlotWithCountOffset, DEPOSIT__TOKEN_AMOUNT_SLOT_OFFSET), depositAmount)
}
}
/**
* @param depositDataSlotWithCountOffset The deposit data slot with count offset.
* @param tokenType The token type to write to the deposit.
* @param tokenAddress The token address to write to the deposit.
*/
function _writeTokenAddressToDeposit(
uint256 depositDataSlotWithCountOffset,
YoloV2__TokenType tokenType,
address tokenAddress
) private {
assembly {
sstore(depositDataSlotWithCountOffset, or(tokenType, shl(DEPOSIT__TOKEN_ADDRESS_OFFSET, tokenAddress)))
}
}
/**
* @param round The round to deposit ETH into.
* @param roundId The round ID.
* @param roundValuePerEntry The value of each entry in ETH.
* @param depositAmount The amount of ETH to deposit.
* @return entriesCount The number of entries for the deposit amount.
*/
function _depositETH(
Round storage round,
uint256 roundId,
uint256 roundValuePerEntry,
uint256 depositAmount
) private returns (uint256 entriesCount) {
entriesCount = depositAmount / roundValuePerEntry;
uint256 roundDepositCount = round.deposits.length;
uint256 roundDepositCountAfterDeposit = _unsafeAdd(roundDepositCount, 1);
_validateRoundDepositsAndPlayers(roundDepositCountAfterDeposit, round.numberOfParticipants);
uint40 currentEntryIndex = _getCurrentEntryIndexWithoutAccrual(round, roundDepositCount, entriesCount);
// This is equivalent to
// round.deposits.push(
// Deposit({
// tokenType: YoloV2__TokenType.ETH,
// tokenAddress: address(0),
// tokenId: 0,
// tokenAmount: msg.value,
// depositor: msg.sender,
// withdrawn: false,
// currentEntryIndex: currentEntryIndex
// })
// );
// unchecked {
// roundDepositCount += 1;
// }
uint256 roundDepositsLengthSlot = _getRoundSlot(roundId) + ROUND__DEPOSITS_LENGTH_SLOT_OFFSET;
uint256 depositDataSlotWithCountOffset = _getDepositDataSlotWithCountOffset(
roundDepositsLengthSlot,
roundDepositCount
);
// We don't have to write tokenType, tokenAddress, tokenId, and withdrawn because they are 0.
_writeDepositorAndCurrentEntryIndexToDeposit(depositDataSlotWithCountOffset, currentEntryIndex);
_writeDepositAmountToDeposit(depositDataSlotWithCountOffset, depositAmount);
assembly {
sstore(roundDepositsLengthSlot, roundDepositCountAfterDeposit)
}
}
/**
* @param singleDeposit The deposit to withdraw from.
* @param transferAccumulator The ERC-20 transfer accumulator so far.
* @param ethAmount The ETH amount so far.
* @return The new ETH amount.
*/
function _transferTokenOut(
Deposit storage singleDeposit,
TransferAccumulator memory transferAccumulator,
uint256 ethAmount
) private returns (uint256) {
_validateDepositNotWithdrawn(singleDeposit);
singleDeposit.withdrawn = true;
YoloV2__TokenType tokenType = singleDeposit.tokenType;
if (tokenType == YoloV2__TokenType.ETH) {
ethAmount += singleDeposit.tokenAmount;
} else if (tokenType == YoloV2__TokenType.ERC721) {
_executeERC721TransferFrom(singleDeposit.tokenAddress, address(this), msg.sender, singleDeposit.tokenId);
} else if (tokenType == YoloV2__TokenType.ERC20) {
address tokenAddress = singleDeposit.tokenAddress;
if (tokenAddress == transferAccumulator.tokenAddress) {
transferAccumulator.amount += singleDeposit.tokenAmount;
} else {
if (transferAccumulator.amount != 0) {
_executeERC20DirectTransfer(
transferAccumulator.tokenAddress,
msg.sender,
transferAccumulator.amount
);
}
transferAccumulator.tokenAddress = tokenAddress;
transferAccumulator.amount = singleDeposit.tokenAmount;
}
}
return ethAmount;
}
/**
* @param protocolFeeOwed Protocol fee owed in ETH.
*/
function _payForProtocolFeesInLOOKS(uint256 protocolFeeOwed) private {
protocolFeeOwed = _protocolFeeOwedInLOOKS(protocolFeeOwed);
transferManager.transferERC20(LOOKS, msg.sender, protocolFeeRecipient, protocolFeeOwed);
emit ProtocolFeePayment(protocolFeeOwed, LOOKS);
}
/**
* @param protocolFeeOwed Protocol fee owed in ETH.
*/
function _payForProtocolFeesInETH(uint256 protocolFeeOwed) private {
_transferETHAndWrapIfFailWithGasLimit(WETH, protocolFeeRecipient, protocolFeeOwed, gasleft());
emit ProtocolFeePayment(protocolFeeOwed, address(0));
}
function _validateIsOwner() internal view {
if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
_revertWith(NotOwner_error_selector);
}
}
function _validateIsOperator() private view {
if (!hasRole(OPERATOR_ROLE, msg.sender)) {
_revertWith(NotOperator_error_selector);
}
}
/**
* @param round The round to check the status of.
* @param status The expected status of the round
*/
function _validateRoundStatus(Round storage round, RoundStatus status) private view {
if (round.status != status) {
_revertWithInvalidStatus();
}
}
/**
* @param round The round to check the status and cutoffTime of.
*/
function _validateRoundIsOpen(Round storage round) private view {
if (round.status != RoundStatus.Open || (round.cutoffTime != 0 && block.timestamp >= round.cutoffTime)) {
_revertWithInvalidStatus();
}
}
/**
* @param singleDeposit The deposit to withdraw from.
*/
function _validateDepositNotWithdrawn(Deposit storage singleDeposit) private view {
if (singleDeposit.withdrawn) {
_revertWith(AlreadyWithdrawn_error_selector);
}
}
/**
* @param length The length of the array.
*/
function _validateArrayLengthIsNotEmpty(uint256 length) private pure {
if (length == 0) {
_revertWith(InvalidLength_error_selector);
}
}
function _validateOutflowIsAllowed() private view {
if (!outflowAllowed) {
_revertWith(OutflowNotAllowed_error_selector);
}
}
/**
* @param index The array index.
* @param roundDepositsLength The round's number of deposits.
*/
function _validateDepositsArrayIndex(uint256 index, uint256 roundDepositsLength) private pure {
if (index >= roundDepositsLength) {
_revertWith(InvalidIndex_error_selector);
}
}
/**
* @param singleDeposit The deposit to check the depositor of.
*/
function _validateMsgSenderIsDepositor(Deposit storage singleDeposit) private view {
if (msg.sender != singleDeposit.depositor) {
_revertWith(NotDepositor_error_selector);
}
}
/**
* @param round The round to check the winner of.
*/
function _validateMsgSenderIsWinner(Round storage round) private view {
if (msg.sender != round.winner) {
_revertWith(NotWinner_error_selector);
}
}
/**
* @param entriesCount The number of entries to be added.
*/
function _validateNonZeroEntries(uint256 entriesCount) private pure {
if (entriesCount == 0) {
_revertWith(ZeroEntries_error_selector);
}
}
/**
* @param errorSelector The uint256 representation of the error's 4 bytes selector.
*/
function _revertWith(uint256 errorSelector) private pure {
assembly {
mstore(0x00, errorSelector)
revert(Error_selector_offset, Error_standard_length)
}
}
function _revertWithInvalidStatus() private pure {
_revertWith(InvalidStatus_error_selector);
}
function _revertWithInvalidToken() private pure {
_revertWith(InvalidToken_error_selector);
}
function _revertWithInvalidValue() private pure {
_revertWith(InvalidValue_error_selector);
}
/**
* @param roundDepositCount The number of deposits in the round.
* @param numberOfParticipants The number of participants in the round.
*/
function _validateRoundDepositsAndPlayers(uint256 roundDepositCount, uint256 numberOfParticipants) private pure {
if (roundDepositCount > MAXIMUM_NUMBER_OF_DEPOSITS_PER_ROUND) {
_revertWith(MaximumNumberOfDepositsReached_error_selector);
}
if (roundDepositCount == MAXIMUM_NUMBER_OF_DEPOSITS_PER_ROUND) {
if (numberOfParticipants == 1) {
_revertWith(OnePlayerCannotFillUpTheWholeRound_error_selector);
}
}
}
/**
* @param collection The collection address.
* @param floorPrice The floor price response from Reservoir oracle.
*/
function _verifyReservoirSignature(address collection, ReservoirOracleFloorPrice calldata floorPrice) private view {
if (
floorPrice.timestamp > block.timestamp ||
block.timestamp > floorPrice.timestamp + uint256(signatureValidityPeriod)
) {
_revertWith(InvalidSignatureTimestamp_error_selector);
}
bytes32 expectedMessageId = keccak256(
abi.encode(RESERVOIR_ORACLE_ID_TYPEHASH, uint8(1), TWAP_DURATION, collection, false)
);
if (expectedMessageId != floorPrice.id) {
_revertWith(MessageIdInvalid_error_selector);
}
bytes32 messageHash = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
keccak256(
abi.encode(
RESERVOIR_ORACLE_MESSAGE_TYPEHASH,
expectedMessageId,
keccak256(floorPrice.payload),
floorPrice.timestamp,
block.chainid
)
)
)
);
SignatureCheckerMemory.verify(messageHash, reservoirOracle, floorPrice.signature);
}
/**
* @param singleDeposit The ERC-721 deposit to get the price of.
* @return price The price decoded from the Reservoir oracle payload.
*/
function _getReservoirPrice(DepositCalldata calldata singleDeposit) private view returns (uint256 price) {
address token;
ReservoirOracleFloorPrice calldata reservoirOracleFloorPrice = singleDeposit.reservoirOracleFloorPrice;
_verifyReservoirSignature(singleDeposit.tokenAddress, reservoirOracleFloorPrice);
(token, price) = abi.decode(reservoirOracleFloorPrice.payload, (address, uint256));
if (token != address(0)) {
_revertWithInvalidToken();
}
}
/**
* @param currentEntryIndex The round's current entry index.
* @param entriesCount The number of entries to be added.
* @param round The open round.
* @param roundDepositCount The number of deposits in the round.
* @return The new current entry index.
*/
function _incrementCurrentEntryIndex(
uint40 currentEntryIndex,
uint256 entriesCount,
Round storage round,
uint256 roundDepositCount
) private view returns (uint40) {
if (currentEntryIndex != 0) {
return currentEntryIndex + uint40(entriesCount);
} else {
return _getCurrentEntryIndexWithoutAccrual(round, roundDepositCount, entriesCount);
}
}
/**
* @param round The open round.
* @param roundDepositCount The number of deposits in the round.
* @param entriesCount The number of entries to be added.
* @return currentEntryIndex The current entry index after adding entries count.
*/
function _getCurrentEntryIndexWithoutAccrual(
Round storage round,
uint256 roundDepositCount,
uint256 entriesCount
) private view returns (uint40 currentEntryIndex) {
_validateNonZeroEntries(entriesCount);
if (roundDepositCount == 0) {
currentEntryIndex = uint40(entriesCount);
} else {
currentEntryIndex = uint40(
round.deposits[_unsafeSubtract(roundDepositCount, 1)].currentEntryIndex + entriesCount
);
}
}
/**
* @param protocolFeeOwedInETH The protocol fee owed in ETH.
* @return protocolFeeOwedInLOOKS The protocol fee owed in LOOKS.
*/
function _protocolFeeOwedInLOOKS(
uint256 protocolFeeOwedInETH
) private view returns (uint256 protocolFeeOwedInLOOKS) {
protocolFeeOwedInLOOKS = (1e18 * protocolFeeOwedInETH * discountedProtocolFeeBp) / _getTWAP(LOOKS) / 10_000;
}
/**
* @param tokenAddress The token address to get the TWAP price in.
*/
function _getTWAP(address tokenAddress) private view returns (uint256 price) {
price = erc20Oracle.getTWAP(tokenAddress, uint32(TWAP_DURATION));
}
/**
* @param roundId The round ID.
* @return roundSlot The round's starting storage slot.
*/
function _getRoundSlot(uint256 roundId) private pure returns (uint256 roundSlot) {
assembly {
mstore(0x00, roundId)
mstore(0x20, rounds.slot)
roundSlot := keccak256(0x00, 0x40)
}
}
/**
* @param roundDepositsLengthSlot The round's deposits length slot.
* @param roundDepositCount The number of deposits in the round.
* @return depositDataSlotWithCountOffset The round's next deposit's starting storage slot.
*/
function _getDepositDataSlotWithCountOffset(
uint256 roundDepositsLengthSlot,
uint256 roundDepositCount
) private pure returns (uint256 depositDataSlotWithCountOffset) {
assembly {
mstore(0x00, roundDepositsLengthSlot)
let depositsDataSlot := keccak256(0x00, 0x20)
depositDataSlotWithCountOffset := add(depositsDataSlot, mul(DEPOSIT__OCCUPIED_SLOTS, roundDepositCount))
}
}
/**
* @param numberOfParticipants The number of participants in the round.
* @param maximumNumberOfParticipants The maximum number of participants in the round.
* @param roundDepositCount The number of deposits in the round.
*/
function _shouldDrawWinner(
uint256 numberOfParticipants,
uint256 maximumNumberOfParticipants,
uint256 roundDepositCount
) private pure returns (bool shouldDraw) {
shouldDraw =
numberOfParticipants >= maximumNumberOfParticipants ||
(numberOfParticipants > 1 && roundDepositCount >= MAXIMUM_NUMBER_OF_DEPOSITS_PER_ROUND);
}
/**
* Unsafe math functions.
*/
function _unsafeAdd(uint256 a, uint256 b) private pure returns (uint256) {
unchecked {
return a + b;
}
}
function _unsafeSubtract(uint256 a, uint256 b) private pure returns (uint256) {
unchecked {
return a - b;
}
}
}{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 888888
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint40","name":"maximumNumberOfParticipantsPerRound","type":"uint40"},{"internalType":"uint40","name":"roundDuration","type":"uint40"},{"internalType":"uint96","name":"valuePerEntry","type":"uint96"},{"internalType":"address","name":"protocolFeeRecipient","type":"address"},{"internalType":"uint16","name":"protocolFeeBp","type":"uint16"},{"internalType":"uint16","name":"discountedProtocolFeeBp","type":"uint16"},{"internalType":"bytes32","name":"keyHash","type":"bytes32"},{"internalType":"uint64","name":"subscriptionId","type":"uint64"},{"internalType":"address","name":"vrfCoordinator","type":"address"},{"internalType":"address","name":"reservoirOracle","type":"address"},{"internalType":"address","name":"transferManager","type":"address"},{"internalType":"address","name":"erc20Oracle","type":"address"},{"internalType":"address","name":"weth","type":"address"},{"internalType":"uint40","name":"signatureValidityPeriod","type":"uint40"},{"internalType":"uint16","name":"minimumRequestConfirmations","type":"uint16"}],"internalType":"struct IYoloV2.ConstructorCalldata","name":"params","type":"tuple"},{"internalType":"address","name":"_ethYieldConfiguration","type":"address"},{"internalType":"address","name":"_usdbYieldConfiguration","type":"address"},{"internalType":"address","name":"_blastPoints","type":"address"},{"internalType":"address","name":"_blastPointsOperator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyWithdrawn","type":"error"},{"inputs":[],"name":"CutoffTimeNotReached","type":"error"},{"inputs":[],"name":"DrawExpirationTimeNotReached","type":"error"},{"inputs":[],"name":"ERC20TransferFail","type":"error"},{"inputs":[],"name":"ERC721TransferFromFail","type":"error"},{"inputs":[],"name":"ExceededMaximumDepositAmountPerRound","type":"error"},{"inputs":[],"name":"InsufficientParticipants","type":"error"},{"inputs":[],"name":"InvalidIndex","type":"error"},{"inputs":[],"name":"InvalidLength","type":"error"},{"inputs":[],"name":"InvalidSignatureTimestamp","type":"error"},{"inputs":[],"name":"InvalidStatus","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidTokenType","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"IsPaused","type":"error"},{"inputs":[],"name":"LooksAlreadySet","type":"error"},{"inputs":[],"name":"MaximumNumberOfDepositsReached","type":"error"},{"inputs":[],"name":"MaximumNumberOfParticipantsReached","type":"error"},{"inputs":[],"name":"MessageIdInvalid","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"NotDepositor","type":"error"},{"inputs":[],"name":"NotOperator","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotPaused","type":"error"},{"inputs":[],"name":"NotWinner","type":"error"},{"inputs":[],"name":"NullSignerAddress","type":"error"},{"inputs":[],"name":"OnePlayerCannotFillUpTheWholeRound","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"want","type":"address"}],"name":"OnlyCoordinatorCanFulfill","type":"error"},{"inputs":[],"name":"OutflowNotAllowed","type":"error"},{"inputs":[],"name":"ProtocolFeeNotPaid","type":"error"},{"inputs":[],"name":"RandomnessRequestAlreadyExists","type":"error"},{"inputs":[],"name":"ReentrancyFail","type":"error"},{"inputs":[],"name":"RoundCannotBeClosed","type":"error"},{"inputs":[],"name":"SignatureEOAInvalid","type":"error"},{"inputs":[],"name":"SignatureERC1271Invalid","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"SignatureLengthInvalid","type":"error"},{"inputs":[],"name":"SignatureParameterSInvalid","type":"error"},{"inputs":[{"internalType":"uint8","name":"v","type":"uint8"}],"name":"SignatureParameterVInvalid","type":"error"},{"inputs":[],"name":"TooFewEntries","type":"error"},{"inputs":[],"name":"ZeroDeposits","type":"error"},{"inputs":[],"name":"ZeroEntries","type":"error"},{"inputs":[],"name":"ZeroRounds","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"entriesCount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"components":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256[]","name":"depositIndices","type":"uint256[]"}],"indexed":false,"internalType":"struct IYoloV2.WithdrawalCalldata[]","name":"withdrawalCalldata","type":"tuple[]"}],"name":"DepositsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"discountedProtocolFeeBp","type":"uint16"}],"name":"DiscountedProtocolFeeBpUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"erc20Oracle","type":"address"}],"name":"ERC20OracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maximumDepositAmountPerRound","type":"uint256"}],"name":"MaximumDepositAmountPerRoundUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint40","name":"maximumNumberOfParticipantsPerRound","type":"uint40"}],"name":"MaximumNumberOfParticipantsPerRoundUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"startingRoundId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"entriesCounts","type":"uint256[]"}],"name":"MultipleRoundsDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"OutflowAllowedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"winner","type":"address"},{"components":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256[]","name":"depositIndices","type":"uint256[]"}],"indexed":false,"internalType":"struct IYoloV2.WithdrawalCalldata[]","name":"withdrawalCalldata","type":"tuple[]"}],"name":"PrizesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"protocolFeeBp","type":"uint16"}],"name":"ProtocolFeeBpUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"ProtocolFeePayment","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"protocolFeeRecipient","type":"address"}],"name":"ProtocolFeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"RandomnessRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"reservoirOracle","type":"address"}],"name":"ReservoirOracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"components":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256[]","name":"depositIndices","type":"uint256[]"}],"indexed":false,"internalType":"struct IYoloV2.WithdrawalCalldata[]","name":"withdrawalCalldata","type":"tuple[]"},{"indexed":false,"internalType":"uint256","name":"enteredRoundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"entriesCount","type":"uint256"}],"name":"Rollover","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint40","name":"roundDuration","type":"uint40"}],"name":"RoundDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"enum IYoloV2.RoundStatus","name":"status","type":"uint8"}],"name":"RoundStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startingRoundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numberOfRounds","type":"uint256"}],"name":"RoundsCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint40","name":"signatureValidityPeriod","type":"uint40"}],"name":"SignatureValidityPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"enum IYoloV2.YoloV2__TokenType","name":"tokenType","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"TokensStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"valuePerEntry","type":"uint256"}],"name":"ValuePerEntryUpdated","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numberOfRounds","type":"uint256"}],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelAfterRandomnessRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"wethReceiver","type":"address"},{"internalType":"address","name":"usdbReceiver","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256[]","name":"depositIndices","type":"uint256[]"}],"internalType":"struct IYoloV2.WithdrawalCalldata[]","name":"withdrawalCalldata","type":"tuple[]"},{"internalType":"bool","name":"payWithLOOKS","type":"bool"}],"name":"claimPrizes","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"components":[{"internalType":"enum IYoloV2.YoloV2__TokenType","name":"tokenType","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256[]","name":"tokenIdsOrAmounts","type":"uint256[]"},{"internalType":"uint256","name":"minimumEntries","type":"uint256"},{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IYoloV2.ReservoirOracleFloorPrice","name":"reservoirOracleFloorPrice","type":"tuple"}],"internalType":"struct IYoloV2.DepositCalldata[]","name":"deposits","type":"tuple[]"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"address","name":"depositor","type":"address"}],"name":"depositCount","outputs":[{"internalType":"uint256","name":"depositCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startingRoundId","type":"uint256"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"depositETHIntoMultipleRounds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"address","name":"depositor","type":"address"}],"name":"depositedAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"discountedProtocolFeeBp","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"drawWinner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"erc20Oracle","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"components":[{"internalType":"enum IYoloV2.YoloV2__TokenType","name":"tokenType","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256[]","name":"tokenIdsOrAmounts","type":"uint256[]"},{"internalType":"uint256","name":"minimumEntries","type":"uint256"},{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct IYoloV2.ReservoirOracleFloorPrice","name":"reservoirOracleFloorPrice","type":"tuple"}],"internalType":"struct IYoloV2.DepositCalldata","name":"singleDeposit","type":"tuple"}],"name":"estimatedERC20DepositEntriesCount","outputs":[{"internalType":"uint256","name":"entriesCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256[]","name":"depositIndices","type":"uint256[]"}],"internalType":"struct IYoloV2.WithdrawalCalldata[]","name":"withdrawalCalldata","type":"tuple[]"},{"internalType":"bool","name":"payWithLOOKS","type":"bool"}],"name":"getClaimPrizesPaymentRequired","outputs":[{"internalType":"uint256","name":"protocolFeeOwed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getRound","outputs":[{"internalType":"enum IYoloV2.RoundStatus","name":"status","type":"uint8"},{"internalType":"uint40","name":"maximumNumberOfParticipants","type":"uint40"},{"internalType":"uint16","name":"roundProtocolFeeBp","type":"uint16"},{"internalType":"uint40","name":"cutoffTime","type":"uint40"},{"internalType":"uint40","name":"drawnAt","type":"uint40"},{"internalType":"uint40","name":"numberOfParticipants","type":"uint40"},{"internalType":"address","name":"winner","type":"address"},{"internalType":"uint96","name":"roundValuePerEntry","type":"uint96"},{"internalType":"uint256","name":"protocolFeeOwed","type":"uint256"},{"components":[{"internalType":"enum IYoloV2.YoloV2__TokenType","name":"tokenType","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"address","name":"depositor","type":"address"},{"internalType":"bool","name":"withdrawn","type":"bool"},{"internalType":"uint40","name":"currentEntryIndex","type":"uint40"}],"internalType":"struct IYoloV2.Deposit[]","name":"deposits","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumDepositAmountPerRound","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumNumberOfParticipantsPerRound","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"outflowAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenOrCollection","type":"address"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"prices","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeBp","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"randomnessRequests","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint40","name":"roundId","type":"uint40"},{"internalType":"uint256","name":"randomWord","type":"uint256"}],"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":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reservoirOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"components":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256[]","name":"depositIndices","type":"uint256[]"}],"internalType":"struct IYoloV2.WithdrawalCalldata[]","name":"withdrawalCalldata","type":"tuple[]"},{"internalType":"bool","name":"payWithLOOKS","type":"bool"}],"name":"rolloverETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"roundDuration","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundsCount","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"looks","type":"address"}],"name":"setLOOKS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signatureValidityPeriod","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleOutflowAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"togglePaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_discountedProtocolFeeBp","type":"uint16"}],"name":"updateDiscountedProtocolFeeBp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20Oracle","type":"address"}],"name":"updateERC20Oracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"_maximumDepositAmountPerRound","type":"uint96"}],"name":"updateMaximumDepositAmountPerRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"_maximumNumberOfParticipantsPerRound","type":"uint40"}],"name":"updateMaximumNumberOfParticipantsPerRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_protocolFeeBp","type":"uint16"}],"name":"updateProtocolFeeBp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolFeeRecipient","type":"address"}],"name":"updateProtocolFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_reservoirOracle","type":"address"}],"name":"updateReservoirOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"_roundDuration","type":"uint40"}],"name":"updateRoundDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint40","name":"_signatureValidityPeriod","type":"uint40"}],"name":"updateSignatureValidityPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"enum IYoloV2.YoloV2__TokenType","name":"tokenType","type":"uint8"},{"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"updateTokensStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"_valuePerEntry","type":"uint96"}],"name":"updateValuePerEntry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"valuePerEntry","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"internalType":"uint256[]","name":"depositIndices","type":"uint256[]"}],"internalType":"struct IYoloV2.WithdrawalCalldata[]","name":"withdrawalCalldata","type":"tuple[]"}],"name":"withdrawDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101a0604052346200009e57620000236200001962000171565b9392909262000359565b604051615f90908162000d3a8239608051816106fe015260a051818181610e00015281816121280152615047015260c05181614bd9015260e05181614bff01526101005181614c7001526101205181614c2f015261014051818181612ce8015261509b01526101605181610abe01526101805181610b100152f35b5f80fd5b6001600160401b038111620000b657604052565b634e487b7160e01b5f52604160045260245ffd5b601f909101601f19168101906001600160401b03821190821017620000b657604052565b6040519061022082016001600160401b03811183821017620000b657604052565b51906001600160a01b03821682036200009e57565b519064ffffffffff821682036200009e57565b51906001600160601b03821682036200009e57565b519061ffff821682036200009e57565b51906001600160401b03821682036200009e57565b62006d2a9081380391826040519384926200018d8285620000ca565b8339810103916102a083126200009e576102208093126200009e57620002fc620001b6620000ee565b93620001c2836200010f565b8552620001d2602084016200010f565b6020860152620001e56040840162000124565b6040860152620001f86060840162000124565b60608601526200020b6080840162000137565b60808601526200021e60a084016200010f565b60a08601526200023160c084016200014c565b60c08601526200024460e084016200014c565b60e08601526101008084015190860152610120620002648185016200015c565b90860152610140620002788185016200010f565b908601526101606200028c8185016200010f565b90860152610180620002a08185016200010f565b908601526101a0620002b48185016200010f565b908601526101c0620002c88185016200010f565b908601526101e0620002dc81850162000124565b90860152610200620002f08185016200014c565b9086015282016200010f565b926200030c61024083016200010f565b926200032b6102806200032361026086016200010f565b94016200010f565b919493929190565b5f9103126200009e57565b6040513d5f823e3d90fd5b908160209103126200009e575190565b9392919362000368816200054a565b80516001600160a01b039283169190831691803b156200009e5760405163c8992e6160e01b815260026004820152600160248201526001600160a01b039390931660448401525f908390606490829084905af1918215620004d6576101c09262000533575b50018051909290620003f690620003ea906001600160a01b031681565b6001600160a01b031690565b604051631a33757d60e01b80825260026004830152966020928390839060249082905f905af1918215620004d657859262000511575b5060405197885260026004890152169581816024815f8b5af18015620004d657620004dc575b505016803b156200009e576040516336b91f2b60e01b81526001600160a01b039390931660048401525f908390602490829084905af1908115620004d657620004ae92620003ea92620004b8575b50516001600160a01b031690565b6101605261018052565b80620004c8620004cf92620000a2565b8062000333565b5f620004a0565b6200033e565b816200050092903d1062000509575b620004f78183620000ca565b81019062000349565b505f8062000452565b503d620004eb565b6200052b90843d86116200050957620004f78183620000ca565b505f6200042c565b80620004c86200054392620000a2565b5f620003cd565b6200071b620003ea610180610140938481016200057a62000571825160018060a01b031690565b60805260018055565b600380546001600160f81b0316600160f81b1790558151620005a5906001600160a01b03166200072a565b6020820151620005be906001600160a01b0316620007a0565b620005db620005d5606084015164ffffffffff1690565b62000850565b60a0820151620005f4906001600160a01b0316620008d1565b6200060e6200060860c084015161ffff1690565b62000934565b620006286200062260e084015161ffff1690565b62000991565b608082015162000641906001600160601b0316620009ee565b6101a08201516200065b906001600160a01b031662000a3e565b6200067862000672604084015164ffffffffff1690565b62000a8e565b61016082015162000692906001600160a01b031662000af3565b620006b0620006aa6101e084015164ffffffffff1690565b62000b43565b620006ba62000b9a565b6101c08201516001600160a01b031660a0526101008281015160c0529051620006ec906001600160a01b0316620003ea565b9052610120818101516001600160401b031660e05261020082015161ffff16905201516001600160a01b031690565b90526200072762000be0565b50565b6001600160a01b0381165f9081525f8051602062006d0a833981519152602052604090205460ff16156200075b5750565b6001600160a01b03165f8181525f8051602062006d0a83398151915260205260408120805460ff191660011790553391905f8051602062006cca8339815191528180a4565b6001600160a01b0381165f9081527fee57cd81e84075558e8fcc182a1f4393f91fc97f963a136e66b7f949a62f319f60205260409020547f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9299060ff161562000806575050565b5f818152602081815260408083206001600160a01b03861684529091529020805460ff1916600117905533916001600160a01b0316905f8051602062006cca8339815191525f80a4565b64ffffffffff811690610e108211620008c4577fbdeccc4cf4c928bfff2005eee2fd4b91bb3d22035af62a93e35b1eedf853e3a39160209170ffffffffff0000000000000000000000006003549160601b169070ffffffffff000000000000000000000000191617600355604051908152a1565b63aa7feadc5f526004601cfd5b6001600160a01b038116908115620008c457600480546001600160601b031660609290921b6001600160601b0319169190911790556040519081527fc1b5345cce283376356748dc57f2dfa7120431d016fc7ca9ba641bc65f91411d90602090a1565b61ffff8116906109c48211620008c4576003805461ffff60881b191660889290921b61ffff60881b169190911790556040519081527fede4aee4284b8033b84c1aadcc51b229a4e46e6b42ab40092e237f07508b462690602090a1565b61ffff8116906127108211620008c4576003805461ffff60981b191660989290921b61ffff60981b169190911790556040519081527f2e38c85b973ddf3d7c0cbd813bddb9b63db96d86ff7b6ccce361777d674d813c90602090a1565b6001600160601b03168015620008c457600380546001600160601b031916821790556040519081527f988fd919afedb9b211cf201222b0091d485871857d14d2a87946e1ee390a17f490602090a1565b6001600160a01b03168015620008c457600580546001600160a01b031916821790556040519081527f2d5eba1cf3f9252b2aa813087328fc416c6c2ad0fb1417f9f5f78f08cb24cfdb90602090a1565b64ffffffffff81169060028210620008c4576003805464ffffffffff60d01b191660d09290921b64ffffffffff60d01b169190911790556040519081527f32e1033127c5a25fa4a87832fb767338db2bca3d4eba8f34be6d5a8f71e9227590602090a1565b6001600160a01b03168015620008c457600680546001600160a01b031916821790556040519081527fa75f06c1fcb711f11e4fe2a1e8b84e798cce8ea1c6d6db252abe31ebd836e21f90602090a1565b6006805464ffffffffff60a01b191660a083901b64ffffffffff60a01b1617905560405164ffffffffff90911681527ff020ff23e722cd62b0e67a05c051cc3dc4b4d0c48e01de57a0571ad6a180c35d90602090a1565b7fab9b0becdb6bda45e0cb0aedf05c920174daf5b035652783df65736347ec8b98602067016345785d8a00008060018060601b03196004541617600455604051908152a1565b6003805464ffffffffff60a81b1916600160a81b179081905560015f819052600860205291905f8051602062006cea83398151915260018101546001600160601b039060a01c1662000cdd57505060035460015f819052600860205260c882901c65ffffffffff0016605883901c67ffff0000000000001617175f8051602062006cea8339815191525560a081901b6001600160a01b0319167fad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac56055505b7ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c6040518062000cd881906001602060408401938281520152565b0390a1565b5460901c64ffffffffff1660015f526008602052905f8051602062006cea83398151915290815490600182179362000d19575b50505562000c9d565b600192935064ffffffffff9060601c16420160401b1717905f8062000d1056fe60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a714610344578063197a32431461033f5780631df47f801461033a5780631fe543e31461033557806320e672321461033057806321c0b3421461032b578063248a9ca31461032657806327ab6b68146103215780632f2ff15d1461031c578063311b8d5c1461031757806336566f061461031257806336568abe1461030d57806340e58ee5146103085780634245d5f014610303578063472f5b06146102fe5780635410d098146102f957806354854797146102f4578063553be400146102ef57806358fc096f146102ea5780635c975abb146102e55780635cb6dfff146102e057806361510d31146102db578063624ab3ac146102d657806365294500146102d15780636df1d48a146102cc578063709563e2146102c75780637c8b287c146102c25780637d33ca3f146102bd5780637d7c3c74146102b85780638f1327c0146102b3578063911c66b8146102ae57806391d14854146102a957806398753c46146102a45780639f5cfe031461029f578063a217fddf1461029a578063a5cbbd2314610295578063aa73b5ac14610290578063b12957f61461028b578063b2185bb114610286578063bbc492c014610281578063bcfdf2191461027c578063bfbf228414610277578063c66944ba14610272578063ca232b091461026d578063cb5305f314610268578063d0a2c33614610263578063d547741f1461025e578063ea8a1af014610259578063f4c8770e14610254578063f7cb789a1461024f5763fa2f0a8d1461024a575f80fd5b6137ac565b613767565b6136f7565b613641565b6135e4565b61348d565b613346565b612b89565b612af1565b612a5f565b612a16565b6129a3565b6128e3565b61286a565b612821565b6127a0565b612768565b612723565b6126e1565b612664565b6125c0565b612473565b611f99565b611ddd565b611cb7565b611c0e565b611b58565b611aa2565b6119fa565b6119a9565b611941565b611901565b6118bf565b61173b565b611631565b6115e0565b61155f565b611483565b61132e565b611246565b611119565b611083565b610f1c565b610d1b565b610ca0565b610a21565b61096a565b610684565b6104a3565b610440565b610377565b7fffffffff0000000000000000000000000000000000000000000000000000000081160361037357565b5f80fd5b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760207fffffffff000000000000000000000000000000000000000000000000000000006004356103d581610349565b167f7965db0b00000000000000000000000000000000000000000000000000000000811490811561040c575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150145f610401565b5f91031261037357565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60035460d01c16604051908152f35b73ffffffffffffffffffffffffffffffffffffffff81160361037357565b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004356104de81610485565b6104e6613ebb565b73ffffffffffffffffffffffffffffffffffffffff811690811561056e577fc1b5345cce283376356748dc57f2dfa7120431d016fc7ca9ba641bc65f91411d916020916bffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006004549260601b16911617600455604051908152a1005b63aa7feadc5f526004601cfd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116105bc57604052565b61057b565b60e0810190811067ffffffffffffffff8211176105bc57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105bc57604052565b67ffffffffffffffff81116105bc5760051b60200190565b92916106418261061e565b9161064f60405193846105dd565b829481845260208094019160051b810192831161037357905b8282106106755750505050565b81358152908301908301610668565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043560243567ffffffffffffffff81116103735736602382011215610373576106e7903690602481600401359101610636565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803303610933575061074261073b835f52600b60205260405f2090565b5460ff1690565b61074857005b61077861076e610760845f52600b60205260405f2090565b5460081c64ffffffffff1690565b64ffffffffff1690565b9061078b825f52600860205260405f2090565b906002610799835460ff1690565b6107a281612324565b146107aa575b005b6107bd906107b88484614d4b565b613b78565b51928360016107d581935f52600b60205260405f2090565b01556003808301918254906107e982613aa0565b925f5b8381106108ff575050506108e06107a8966108db6108ca61087a600361085e6002996108586108f79a60016108516108497fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6108ef9e0184613b85565b51809b613b1c565b01906154ca565b9061390e565b50015473ffffffffffffffffffffffffffffffffffffffff1690565b6108c360018a0191829073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b5460a01c90565b6bffffffffffffffffffffffff1690565b613e74565b845460301c61ffff1690613e74565b612710900490565b910155614e67565b8061092261076e8461091287958b61390e565b50015460a81c64ffffffffff1690565b61092c8288613b85565b52016107ec565b604490604051907f1cf993f40000000000000000000000000000000000000000000000000000000082523360048301526024820152fd5b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576109a0613ebb565b7f67aa0e8084cf2c79566eed90861fb37a9bf4106afb0fd2ee5b3428b4a5767c7b60206003548060f81c15907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fff000000000000000000000000000000000000000000000000000000000000008360f81b16911617600355604051908152a1005b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600435610a5c81610485565b60243590610a6982610485565b610a71613ebb565b6040517fe12f3a61000000000000000000000000000000000000000000000000000000008082523060048301526020939092909173ffffffffffffffffffffffffffffffffffffffff91907f000000000000000000000000000000000000000000000000000000000000000083168682602481845afa908115610bd85787925f92610c81575b5081610bfa575b505060405194855250503060048401527f000000000000000000000000000000000000000000000000000000000000000016918381602481865afa908115610bd8575f91610bdd575b5080610b4f57005b6040517faad3ec9600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024820152908290829060449082905f905af18015610bd857610bb257005b816107a892903d10610bd1575b610bc981836105dd565b8101906137ec565b503d610bbf565b6137fb565b610bf49150843d8611610bd157610bc981836105dd565b5f610b47565b6040517faad3ec9600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9490941660048501526024840191909152829060449082905f905af18015610bd857610c64575b848180610afe565b610c7a90853d8711610bd157610bc981836105dd565b505f610c5c565b610c99919250833d8511610bd157610bc981836105dd565b905f610af7565b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004355f525f6020526020600160405f200154604051908152f35b9181601f840112156103735782359167ffffffffffffffff8311610373576020808501948460051b01011161037357565b34610373576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043567ffffffffffffffff811161037357610d6b903690600401610cea565b90600192600260015414610ef2576002600155610d86613f00565b610d8e613806565b5f91610d9985613f1a565b5f5b858110610e595750507f573056d40bdb0e131a339ac518c08135633a29c4a83b7efb171c4f931fbf40d59450602081015180610e2a575b505080610df7575b50610deb6040519283923384613a73565b0390a16107a860018055565b610e24905a90337f00000000000000000000000000000000000000000000000000000000000000006142d1565b5f610dda565b610e4b610e52925173ffffffffffffffffffffffffffffffffffffffff1690565b33906141d8565b5f80610dd2565b80610e668892888861385c565b610e8f610e7c82355f52600860205260405f2090565b91610e8683613f2e565b858101906138a1565b610e9881613f1a565b60035f93015b818410610eaf575050505001610d9b565b90919297610ee8869189610ed9610ec78d888a6138fe565b35610ed3875482613f93565b8661390e565b50610ee381613fa8565b613fd7565b9801929190610e9e565b60046040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602435600435610f5a82610485565b805f525f602052610f71600160405f2001546143f4565b805f525f60205260ff610fa58360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541615610fae57005b805f525f602052610fe08260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126103735760043561ffff811681036103735790565b346103735761109136611048565b611099613ebb565b61ffff8116906109c4821161056e577fede4aee4284b8033b84c1aadcc51b229a4e46e6b42ab40092e237f07508b4626916020917fffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffff72ffff00000000000000000000000000000000006003549260881b16911617600355604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735761114f613ebb565b60025460ff8116156111e9575060025460ff8116156111bf577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a16107a861456d565b60046040517f6cd60201000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060019161121561474d565b16176002557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760243561128181610485565b3373ffffffffffffffffffffffffffffffffffffffff8216036112aa576107a890600435614603565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152fd5b34610373576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004359061136a613ebb565b81156114385764ffffffffff60035460a81c16905f5b8381106113f4576107a87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85857f0c4874e1a947afdb9c813baf258eec6a442b25531183f82046efc9760f640773604051806113ea85858360209093929193604081019481520152565b0390a10101614e67565b8060019184015f526008835261143260405f2060047fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b01611380565b63cbc4e0605f526004601cfd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126103735760043564ffffffffff811681036103735790565b346103735761149136611445565b611499613ebb565b64ffffffffff811690610e10821161056e577fbdeccc4cf4c928bfff2005eee2fd4b91bb3d22035af62a93e35b1eedf853e3a3916020917fffffffffffffffffffffffffffffff0000000000ffffffffffffffffffffffff70ffffffffff0000000000000000000000006003549260601b16911617600355604051908152a1005b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020910112610373576004356bffffffffffffffffffffffff811681036103735790565b34610373576bffffffffffffffffffffffff61157a3661151a565b611582613ebb565b16801561056e576020817f988fd919afedb9b211cf201222b0091d485871857d14d2a87946e1ee390a17f4927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006003541617600355604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043561166c81610485565b611674613ebb565b6002549073ffffffffffffffffffffffffffffffffffffffff8260081c166116dc5774ffffffffffffffffffffffffffffffffffffffff007fffffffffffffffffffffff0000000000000000000000000000000000000000ff9160081b169116176002555f80f35b63d6336f0d5f526004601cfd5b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261037357600435916024359067ffffffffffffffff82116103735761173791600401610cea565b9091565b611744366116e9565b600192919290600260015414610ef257600260015561176161474d565b80156118ba57611779835f52600860205260405f2090565b9361178385614783565b61178c856147cb565b5f61179683613aa0565b935f5b848110611823575050340361181e576117db7fa315167fc4200676e8597c5df065fafa8cecfeac15a8e2aded299a649e4a517593604051938493873386613b99565b0390a1611800825464ffffffffff90600385015491808260081c169160901c16614b39565b61180e575b6107a860018055565b61181791614b62565b5f80611805565b61056e565b80870190611839825f52600860205260405f2090565b9161184a6108ca8585015460a01c90565b9081156118a9575b61185c848261490e565b6118678389896138fe565b35906118738383613b1c565b61181e57816118979382889761188c84611891966149f7565b614ab5565b95613b6b565b936118a28289613b85565b5201611799565b90506118b48161482e565b90611852565b6146d8565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602061ffff60035460981c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602060ff600254166040519015158152f35b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004355f52600b602052606060405f206001815491015464ffffffffff6040519260ff81161515845260081c1660208301526040820152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602073ffffffffffffffffffffffffffffffffffffffff60055416604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600260015414610ef2576002600155611a3f613f00565b64ffffffffff8060035460a81c16805f52600860205260405f2091611a6383613f51565b611a7281845460681c16613c0c565b164210611a9557611a8681611a8b93614da8565b614e67565b506107a860018055565b63f4c0ca6e5f526004601cfd5b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735773ffffffffffffffffffffffffffffffffffffffff600435611af281610485565b611afa613ebb565b16801561056e576020817f2d5eba1cf3f9252b2aa813087328fc416c6c2ad0fb1417f9f5f78f08cb24cfdb927fffffffffffffffffffffffff00000000000000000000000000000000000000006005541617600555604051908152a1005b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735773ffffffffffffffffffffffffffffffffffffffff600435611ba881610485565b611bb0613ebb565b16801561056e576020817fa75f06c1fcb711f11e4fe2a1e8b84e798cce8ea1c6d6db252abe31ebd836e21f927fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60065460a01c16604051908152f35b8015150361037357565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610373576004359067ffffffffffffffff821161037357611ca691600401610cea565b9091602435611cb481611c53565b90565b3461037357611cc536611c5d565b5f92915f925f915b808310611d1b575050505f14611cfe5750611cea611cfa91614f8a565b6040519081529081906020820190565b0390f35b80821015611d11575050611cfa5f611cea565b611cfa9103611cea565b909194611d2986838561385c565b611d53611d3f82355f52600860205260405f2090565b91611d4983613f67565b60208101906138a1565b60038301908154915f5b828110611d835750505050506001916002611d7a92015490613b6b565b95019190611ccd565b611d8e8184876138fe565b3584811015611dd857611da1908361390e565b50805460ff16611db081612340565b15611dbf575b50600101611d5d565b6001919b6002611dd192015490613b6b565b9a90611db6565b613f9b565b611de636611c5d565b91600292600193600260015414610ef2576002600155611e04613f00565b611e0c613806565b915f955f9282611e1b88613f1a565b611f8e575b5f5b878110611efb5750505081611e79575b5050928360207ffbb3d671e318a7ebc6333ba95648c22dc746b01f141b5e81939d55d4664be29595015180610e2a57505080610df75750610deb6040519283923384613a73565b15611eb25790611eaa7ffbb3d671e318a7ebc6333ba95648c22dc746b01f141b5e81939d55d4664be2959592615079565b90935f611e32565b80611ebf611ec692615015565b3490613c51565b848111611ef6577ffbb3d671e318a7ebc6333ba95648c22dc746b01f141b5e81939d55d4664be295940390611eaa565b6146e5565b611f0681898961385c565b611f2f611f1c82355f52600860205260405f2090565b91611f2683613f67565b611d4983614fe6565b90611f3982613f1a565b5f91600384015b818410611f655750505050905f611f5d8486940197885490613b6b565b965501611e22565b9091929c611f8488918f611f7e610ec78f92888a6138fe565b50613fd7565b9d01929190611f40565b3415611e205761056e565b346103735760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600480359060243567ffffffffffffffff811161037357611fed903690600401610cea565b9160443590611ffb82611c53565b6002916001600260015414610ef257600260015561201761474d565b5f935f9361202488613f1a565b5f5b888110612182575050505081612152575b505061204b845f52600860205260405f2090565b61205481614783565b61205e818661490e565b612067816147cb565b6120786108ca600183015460a01c90565b946120838684613b1c565b80612113575b5085831061181e576120cb837fecd5c5544dcd4a5344389c360ff979c215f25290177e786ca2c407e58a95b88a976120c4610deb96856149f7565b8385614ab5565b91816120ef825464ffffffffff90600385015491808260081c169160901c16614b39565b612103575b50506040519485943386613c5e565b61210c91614b62565b5f816120f4565b928361214c91612121613f00565b03935a90337f00000000000000000000000000000000000000000000000000000000000000006142d1565b5f612089565b156121675761216090615079565b5f80612037565b908181105f036146e5578161217d910391615015565b612160565b61218d818a8a61385c565b6121a081355f52600860205260405f2090565b906121ac825460ff1690565b916121b683612324565b6003918284106122f2576121e2906121cd85612324565b8385146122d2575b60208195939501906138a1565b90926121ed82613f1a565b805f950192878114935b83871061220c57505050505050508301612026565b9091929394959c6122336122218f878a6138fe565b3561222d845482613f93565b8361390e565b5061223d816151a3565b805460ff1661224b81612340565b6122cd578b918b6122b2928961226088612324565b6122bf575b6122aa888201740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055565b015490613b6b565b9d019594939291906121f7565b6122c881613fa8565b612265565b6146f2565b6122db83614fe6565b5f6122eb8985019c8d5490613b6b565b9b556121d5565b613f44565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6005111561232e57565b6122f7565b90600582101561232e5752565b6003111561232e57565b9698939590979492919260206101606101409961236a818c81019d612333565b829a64ffffffffff8098168483015261ffff6040971660408301528760609b166060830152876080951660808301528760a0961660a083015260c09873ffffffffffffffffffffffffffffffffffffffff9e8f1660c08401526bffffffffffffffffffffffff60e09b1660e08401526101008301526101208201528d51809c52019b01995f975b8a89106124075750505050505050505050505090565b909192939495969798999a9b8a896001928f8b8b9151805161242881612340565b8452808601518916868501528b8101518c85015287810151888501528981015189168a8501528a81015115158b8501520151168b820152019d019b9a990197969594939291906123f1565b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004355f8181526008602052604090208054603081901c61ffff16929091611cfa91906124d7905460401c64ffffffffff1690565b906124fb6124ed825f52600860205260405f2090565b5460681c64ffffffffff1690565b61251e612510835f52600860205260405f2090565b5460901c64ffffffffff1690565b6125506001612535855f52600860205260405f2090565b015473ffffffffffffffffffffffffffffffffffffffff1690565b906125706001612568865f52600860205260405f2090565b015460a01c90565b926125a4600361259e600261258d895f52600860205260405f2090565b0154975f52600860205260405f2090565b01613c9d565b95604051998960ff64ffffffffff8d9c60081c1691168b61234a565b34610373576125ce36611445565b6125d6613ebb565b64ffffffffff8116906002821061056e577f32e1033127c5a25fa4a87832fb767338db2bca3d4eba8f34be6d5a8f71e92275916020917fff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff7effffffffff00000000000000000000000000000000000000000000000000006003549260d01b16911617600355604051908152a1005b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602060ff6126d56024356126a681610485565b6004355f525f845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602061ffff60035460881c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60035460a81c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206040515f8152f35b34610373576bffffffffffffffffffffffff6127bb3661151a565b6127c3613ebb565b16801561056e576020817fab9b0becdb6bda45e0cb0aedf05c920174daf5b035652783df65736347ec8b98927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006004541617600455604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206bffffffffffffffffffffffff60035416604051908152f35b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206128da6024356128aa81610485565b6004355f526009835260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600260015414610ef257600260015561292861474d565b64ffffffffff8060035460a81c16805f52600860205260405f209161294c83613f7d565b825490808260901c169161297161296d6003870154848460081c1686614b39565b1590565b612980575b6118058486614b62565b60401c16421061299e57600211612999575f8080612976565b6146ff565b6136e0565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735773ffffffffffffffffffffffffffffffffffffffff6004356129f381610485565b165f52600c60205260405f206024355f52602052602060405f2054604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206bffffffffffffffffffffffff60045416604051908152f35b34610373577ff020ff23e722cd62b0e67a05c051cc3dc4b4d0c48e01de57a0571ad6a180c35d6020612a9036611445565b612a98613ebb565b6006547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff78ffffffffff00000000000000000000000000000000000000008360a01b1691161760065564ffffffffff60405191168152a1005b3461037357612aff36611048565b612b07613ebb565b61ffff811690612710821161056e577f2e38c85b973ddf3d7c0cbd813bddb9b63db96d86ff7b6ccce361777d674d813c916020917fffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffff74ffff000000000000000000000000000000000000006003549260981b16911617600355604051908152a1005b612b92366116e9565b91600260015414610ef2576002600155612baa61474d565b612bbc815f52600860205260405f2090565b612bc581614783565b612bcf818361490e565b612bd8816147cb565b6003810154935f905f94612bfc612bf7865f52600860205260405f2090565b613b58565b92346132b45782156118ba575b82612cb7575b505050612c81857f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca968593612c5d612c57612c516108ca60018a015460a01c90565b8a613e74565b866149f7565b55835464ffffffffff808260901c1691612c7783856156b4565b60081c1690614b39565b612ca7575b50506040805133815260208101929092528101919091528060608101610deb565b612cb091614b62565b5f81612c86565b9190612cc2826151c0565b925f905b838210612dab575050505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b1561037357612d505f929183926040519485809481937f0ca8e8a800000000000000000000000000000000000000000000000000000000835230903390600485016152d4565b03925af1958615610bd857808593612c81937f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca99612d92575b50935096612c0f565b80612d9f612da5926105a8565b80610436565b5f612d89565b93979298612dbe828b859a949995615257565b94612dcb60208701613db7565b946001612e2d612dda89615297565b604051612e1c81612df060208201948d86613dc1565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826105dd565b5190205f52600760205260405f2090565b54036132af57612e6c84612e5f8873ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b905f5260205260405f2090565b546002612e7889615297565b612e8181612340565b0361306057801561301e575b612eae90612ea86108ca6001899e959798969e015460a01c90565b90613eb1565b93612eb885615756565b612ecf612ec860408a018a6138a1565b9050613aa0565b9a5f945b612ee060408b018b6138a1565b9050861015612f8a578c83808d898b98968f9a988e612f008c859a613b6b565b9b612f0a9361576a565b9960408101612f18916138a1565b612f2292916138fe565b3591612f36915f5260205f209060021b0190565b3379ffffffffff00000000000000000000000000000000000000000060a88b901b16176003820155600260088e901b1781556001015560010196612f7991613b85565b600190526001019491939092612ed3565b9c98613003929e965060019550612fca612feb92989c959d9499612fae878d613b85565b519073ffffffffffffffffffffffffffffffffffffffff169052565b612fe16020612fd9878d613b85565b510160019052565b60408101906138a1565b91906040612ff9868c613b85565b5101923691610636565b905260606130118389613b85565b5101525b01909291612cc6565b50612eae61302b88615808565b8061305887612e5f8b73ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b559050612e8d565b60016130768994979b959e969c98939d99615297565b61307f81612340565b14613090575b505050600190613015565b919a829c939c95919515613270575b6130b66130af60408e018e6138a1565b3691610636565b600181510361326b576130cb6130d391613b78565b518094613e74565b6040517f313ce56700000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff8c165afa8015610bd8576108ca60016131418f9495612ea8612ea89561314a985f9161323c575b50613ea0565b93015460a01c90565b9b6131548d615756565b6060918d8383013511613237578d8f8b88958f958c6131738585613b85565b5173ffffffffffffffffffffffffffffffffffffffff90911690526131988484613b85565b516020015f9052604081016131ac916138a1565b936131b79193613b85565b51019136906131c592610636565b90526131d091613b6b565b9c6131da9361576a565b936131ee83895f5260205f209060021b0190565b3379ffffffffff00000000000000000000000000000000000000000060a888901b1617600382015591600283015561322b919060081b6001179055565b600101985f8080613085565b61470c565b61325e915060203d602011613264575b61325681836105dd565b810190613e87565b5f61313b565b503d61324c565b613f21565b915061327b85615446565b91826132a98c612e5f8973ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b5561309f565b615439565b5094506132c86108ca600185015460a01c90565b6132d28134613b1c565b61181e576132e09034613eb1565b9460016132ee8789876156ea565b9761333561330582875f5260205f209060021b0190565b3379ffffffffff00000000000000000000000000000000000000000060a88d901b16176003820155349060020155565b0196612c09565b6003111561037357565b346103735760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043567ffffffffffffffff811161037357613395903690600401610cea565b602435916133a28361333c565b604435926133af84611c53565b6133b76153f4565b6133c081612340565b80156132af575f5b83811061340757507f14f4f9b9a2d50cb168899c4825e24b7ffdc472b1dca88ddad74a834f7b62470b936134029160405194859485613e02565b0390a1005b600190851561348757815b6134806134286134238489896138fe565b613db7565b60ff6040519361346d856134418a602083019687613dc1565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018752866105dd565b16925190205f52600760205260405f2090565b55016133c8565b5f613412565b34610373577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60408136011261037357600435906024359067ffffffffffffffff82116103735760a09082360301126103735773ffffffffffffffffffffffffffffffffffffffff91602061355e61350760248501613db7565b9361353484612e5f8773ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b549081156135ca575b6135518160446135579301906004016138a1565b906138f5565b3590613e74565b926004604051809681937f313ce567000000000000000000000000000000000000000000000000000000008352165afa928315610bd8576108ca60016125686135bb611cea96612ea8611cfa99612ea8975f9161323c5750613ea0565b945f52600860205260405f2090565b90506135576135516135db87615446565b9291505061353d565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576107a860243560043561362582610485565b805f525f60205261363c600160405f2001546143f4565b614603565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600260015414610ef2576002600155613686613f00565b64ffffffffff8060035460a81c16805f52600860205260405f20916136aa83613f7d565b825490808260401c1680159081156136ed575b506136e05760019160901c16116136db57611a8681611a8b93614da8565b614719565b63f9ad93f55f526004601cfd5b905042105f6136bd565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206128da60243561373781610485565b6004355f52600a835260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60035460601c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602060035460f81c6040519015158152f35b90816020910312610373575190565b6040513d5f823e3d90fd5b604051906040820182811067ffffffffffffffff8211176105bc576040525f6020838281520152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919081101561389c5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc181360301821215610373570190565b61382f565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610373570180359067ffffffffffffffff821161037357602001918160051b3603831361037357565b901561389c5790565b919081101561389c5760051b0190565b805482101561389c575f5260205f209060021b01905f90565b90918281527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116103735760209260051b809284830137010190565b908290808452602080940193600592818360051b82010195855f925b85841061399257505050505050505090565b90919293949596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820301845288357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18436030181121561037357830160409080358352878101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561037357019087823592019267ffffffffffffffff831161037357828b1b360384136103735760019389938383869586613a619601520191613927565b9a019401940192969594939190613980565b60409073ffffffffffffffffffffffffffffffffffffffff611cb495931681528160208201520191613964565b90613aaa8261061e565b613ab760405191826105dd565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613ae5829461061e565b0190602036910137565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115613b26570690565b613aef565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9060038201809211613b6657565b613b2b565b91908201809211613b6657565b80511561389c5760200190565b805182101561389c5760209160051b010190565b92909373ffffffffffffffffffffffffffffffffffffffff613bd293951684526020946020850152608060408501526080840191613927565b906060818303910152602080845192838152019301915f5b828110613bf8575050505090565b835185529381019392810192600101613bea565b9062015f9064ffffffffff80931601918211613b6657565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211613b6657565b91908203918211613b6657565b9160609373ffffffffffffffffffffffffffffffffffffffff613c939398979698168452608060208501526080840191613964565b9460408201520152565b908154613ca98161061e565b92604093613cba60405191826105dd565b82815280946020809201925f5260205f20905f935b858510613cde57505050505050565b6004846001928451613cef816105c1565b613da964ffffffffff8854613d9560ff808316613d0b81612340565b8652613d4973ffffffffffffffffffffffffffffffffffffffff809460081c168988019073ffffffffffffffffffffffffffffffffffffffff169052565b898c01548b87015260028c01546060870152613d8560038d0154938416608088019073ffffffffffffffffffffffffffffffffffffffff169052565b60a083811c909116151590860152565b60a81c1660c083019064ffffffffff169052565b815201930194019391613ccf565b35611cb481610485565b90917fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060159360601b168252613df681612340565b60f81b60148201520190565b92949391806060850160608652526080840191905f5b818110613e3c575050509060409194613e3081612340565b60208401521515910152565b90919260019073ffffffffffffffffffffffffffffffffffffffff8535613e6281610485565b16815260209081019401929101613e18565b81810292918115918404141715613b6657565b90816020910312610373575160ff811681036103735790565b60ff16604d8111613b6657600a0a90565b8115613b26570490565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff1615613ef357565b6330cd74715f526004601cfd5b60035460f81c15613f0d57565b63010a265a5f526004601cfd5b15613f2157565b63947d5a845f526004601cfd5b5460ff16600581101561232e57600403613f4457565b63f525e3205f526004601cfd5b5460ff16600581101561232e57600203613f4457565b5460ff16600581101561232e57600303613f4457565b5460ff16600581101561232e57600103613f4457565b1015613f9b57565b6363df81715f526004601cfd5b6003015473ffffffffffffffffffffffffffffffffffffffff163303613fca57565b633cc50b455f526004601cfd5b90613fe1826151a3565b61402760038301740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055565b815460ff811661403681612340565b8061404b5750505060020154611cb491613b6b565b61405781949394612340565b600281036140915750508054611cb4925060019060081c73ffffffffffffffffffffffffffffffffffffffff169101549033903090615567565b8061409d600192612340565b146140a9575b50505090565b73ffffffffffffffffffffffffffffffffffffffff809160081c166140e2845173ffffffffffffffffffffffffffffffffffffffff1690565b918216810361410c575050602060026141019201549201918251613b6b565b90525b5f80806140a3565b61413f9084602060029594960195865180614148575b50509073ffffffffffffffffffffffffffffffffffffffff169052565b01549052614104565b6141539133906141d8565b5f80614122565b67ffffffffffffffff81116105bc57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b3d156141be573d906141a58261415a565b916141b360405193846105dd565b82523d5f602084013e565b606090565b908160209103126103735751611cb481611c53565b919091803b156142a7576040517fa9059cbb000000000000000000000000000000000000000000000000000000006020820190815273ffffffffffffffffffffffffffffffffffffffff909416602482015260448101929092525f92839283906142458160648101612df0565b51925af1614251614194565b901561427d57805180614262575050565b8160208061296d9361427795010191016141c3565b61427d57565b60046040517ff1568f95000000000000000000000000000000000000000000000000000000008152fd5b60046040517f09ee12d5000000000000000000000000000000000000000000000000000000008152fd5b9091925f80808087876142e396f11590565b6142ec57505050565b73ffffffffffffffffffffffffffffffffffffffff1691823b1561037357604051927fd0e30db00000000000000000000000000000000000000000000000000000000084525f8460048185855af1928315610bd8576143a7946020946143e1575b505f6040518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015610bd8576143b9575b50565b6143b69060203d6020116143da575b6143d281836105dd565b8101906141c3565b503d6143c8565b80612d9f6143ee926105a8565b5f61434d565b805f525f60205260ff6144283360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416156144325750565b339061443c615a86565b91603061444884613b78565b53607861445484615ab3565b5360295b6001811161451f5761451b6144c06144e986612df06144808861447b8915615aff565b615b64565b6144ba6040519586946144ba602087016017907f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081520190565b90615649565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000815260110190565b6040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352600483016156a3565b0390fd5b90600f811690601082101561389c577f3031323334353637383961626364656600000000000000000000000000000000614568921a61455e8487615ac3565b5360041c91615ad4565b614458565b64ffffffffff60035460a81c165f5b600181106145be57506143b6907f0c4874e1a947afdb9c813baf258eec6a442b25531183f82046efc9760f6407736040805183815260016020820152a1614e67565b8060019183015f5260086020526145fd60405f2060047fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b0161457c565b805f525f60205260ff6146378360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416614641575050565b805f525f6020526146738260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4565b63a95231d55f526004601cfd5b630134f2785f526004601cfd5b63a1e9dd9d5f526004601cfd5b637e439aed5f526004601cfd5b63f48cb8a05f526004601cfd5b637cd9dd6a5f526004601cfd5b63f90121325f526004601cfd5b63c11f59765f526004601cfd5b630da5618b5f526004601cfd5b60ff6002541661475957565b60046040517f1309a563000000000000000000000000000000000000000000000000000000008152fd5b5460ff8116600581101561232e57600114908115916147a4575b50613f4457565b60401c64ffffffffff168015159150816147c0575b505f61479d565b90504210155f6147b9565b805464ffffffffff808260401c16156147e357505050565b60035460601c164201804211613b66576cffffffffff00000000000000007fffffffffffffffffffffffffffffffffffffff0000000000ffffffffffffffff9160401b169116179055565b9060035460017fffffffffffffffffffffffff00000000000000000000000000000000000000006148766bffffffffffffffffffffffff8416955f52600860205260405f2090565b9267ffff0000000000008160581c1665ffffffffff008260c81c1617845560a01b16910155565b9060035460017fffffffffffffffffffffffff00000000000000000000000000000000000000006148e56bffffffffffffffffffffffff8416955f52600860205260405f2090565b928267ffff0000000000008260581c1665ffffffffff008360c81c161717845560a01b16910155565b805f52600960205261494060405f203373ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54908115614983575b600192505f526009602052016149803360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b55565b82549264ffffffffff93848160901c16948160081c1685146149ea577fffffffffffffffffff0000000000ffffffffffffffffffffffffffffffffffff16600194850160901b76ffffffffff00000000000000000000000000000000000016179055614949565b63b53a57db5f526004601cfd5b805f52600a602052614a2960405f203373ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54828101809111613b66576bffffffffffffffffffffffff6004541610614a8b575f52600a602052614a7c3360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b8054918201809211613b665755565b60046040517f8369f344000000000000000000000000000000000000000000000000000000008152fd5b90919392938015613b26578404936003820154916001830193614b01614af2888664ffffffffff95614aed87825460901c168b6156b4565b6156ea565b915f52600860205260405f2090565b9360038501809511613b6657600292614b26614b3492875f5260205f209060021b0190565b921660a81b33176003830155565b015555565b9190821091821592614b4a57505090565b600110915081614b58575090565b6103e89150101590565b614bb190614b708382614e0a565b80547fffffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffff164260681b71ffffffffff0000000000000000000000000016179055565b6040517f5d3b1d300000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1660248201527f000000000000000000000000000000000000000000000000000000000000000061ffff166044820152622625a060648201526001608482015260208160a4815f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1908115610bd8575f91614d2c575b50614ccb61073b825f52600b60205260405f2090565b614d27577f3d94fecedaa4f90b8bd459797adb95f5bb11426025c5541390d9ccc1ad1b60a191815f52600b60205260405f208160081b6001179055614d226040519283928360209093929193604081019481520152565b0390a1565b614726565b614d45915060203d602011610bd157610bc981836105dd565b5f614cb5565b7ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c9160409160037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055815190815260036020820152a1565b7ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c91614dfb60409260047fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b815190815260046020820152a1565b7ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c9160409160027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055815190815260026020820152a1565b600101906003547fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff79ffffffffff0000000000000000000000000000000000000000008460a81b1691161780600355614ec8835f52600860205260405f2090565b6bffffffffffffffffffffffff614ee3600183015460a01c90565b16614f2e575050614ef38261489d565b505b60408051838152600160208201527ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c9181908101614d22565b54614f429060901c64ffffffffff1661076e565b90614f55845f52600860205260405f2090565b908154906001821793614f6b575b505055614ef5565b600192935064ffffffffff9060601c16420160401b1717905f80614f63565b670de0b6b3a76400009080820291820403613b6657614fb39061ffff60035460981c1690613e74565b614fd773ffffffffffffffffffffffffffffffffffffffff60025460081c16615446565b8015613b265761271091040490565b6001015473ffffffffffffffffffffffffffffffffffffffff16330361500857565b63618c72425f526004601cfd5b60407fa0c67dacab0c86ad0a872c2ed3fa9deb08f80f4f0543d481f2fac9ee41d026919161506b60045460601c825a917f00000000000000000000000000000000000000000000000000000000000000006142d1565b81519081525f6020820152a1565b61508290614f8a565b73ffffffffffffffffffffffffffffffffffffffff90817f0000000000000000000000000000000000000000000000000000000000000000169160025460081c1660045460601c833b156103735760845f928360405196879485937fda3e8ce4000000000000000000000000000000000000000000000000000000008552600485015233602485015260448401528660648401525af1918215610bd8577fa0c67dacab0c86ad0a872c2ed3fa9deb08f80f4f0543d481f2fac9ee41d0269192615190575b5060025460081c73ffffffffffffffffffffffffffffffffffffffff166040805192835273ffffffffffffffffffffffffffffffffffffffff909116602083015281908101614d22565b80612d9f61519d926105a8565b5f615146565b6003015460a01c60ff166151b357565b636507689f5f526004601cfd5b906151ca8261061e565b60406151d960405192836105dd565b8382527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0615207839561061e565b01905f5b8281106152185750505050565b815190608082019180831067ffffffffffffffff8411176105bc5760209284525f8152825f81830152606080868401528083015282870101520161520b565b919081101561389c5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6181360301821215610373570190565b35611cb48161333c565b9081518082526020808093019301915f5b8281106152c0575050505090565b8351855293810193928101926001016152b2565b93919392909260609460608201606083528551809152608090608084019760808260051b860101986020809901935f925b848410615359575050505050509061533b604092615357959683019073ffffffffffffffffffffffffffffffffffffffff169052565b019073ffffffffffffffffffffffffffffffffffffffff169052565b565b90919293949a8a806153e4838f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808d60019703018952519073ffffffffffffffffffffffffffffffffffffffff8251168152838201516153b881612340565b84820152866153d460408a81860151918501528a8401906152a1565b92015190878184039101526152a1565b9d01940194019294939190615305565b335f9081527fee57cd81e84075558e8fcc182a1f4393f91fc97f963a136e66b7f949a62f319f602052604090205460ff161561542c57565b637c214f045f526004601cfd5b63c1ab6dc15f526004601cfd5b602073ffffffffffffffffffffffffffffffffffffffff604481600554169360405194859384927fe3cae776000000000000000000000000000000000000000000000000000000008452166004830152610e1060248301525afa908115610bd8575f916154b1575090565b611cb4915060203d602011610bd157610bc981836105dd565b8051918215615560575f925b80841061553257508215159182615514575b505015611cb4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b61552991925061552384613c24565b90613b85565b51145f806154e8565b61553c8185615a71565b90826155488386613b85565b51111561555557506154d6565b9350600101926154d6565b5050505f90565b909192813b156142a7576040519260208401947f23b872dd00000000000000000000000000000000000000000000000000000000865273ffffffffffffffffffffffffffffffffffffffff809216602486015216604484015260648301526064825260a082019282841067ffffffffffffffff8511176105bc575f809493819460405251925af16155f6614194565b50156155fe57565b60046040517fe0f5c508000000000000000000000000000000000000000000000000000000008152fd5b5f5b8381106156395750505f910152565b818101518382015260200161562a565b9061565c60209282815194859201615628565b0190565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361569c81518092818752878088019101615628565b0116010190565b906020611cb4928181520190615660565b6103e8908181116156dd57146156c75750565b6001146156d057565b63ae24220e5f526004601cfd5b6327e6fcc75f526004601cfd5b91906156f582615756565b80615707575064ffffffffff91501690565b600392837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6157389301910161390e565b5064ffffffffff928391015460a81c16908101809111613b66571690565b1561575d57565b63f91214385f526004601cfd5b64ffffffffff9390841692831561578a575050821601908111613b665790565b90919250611cb493506156ea565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610373570180359067ffffffffffffffff82116103735760200191813603831361037357565b9190826040910312610373576020823561580281610485565b92013590565b9060808201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112156103735782019160208091013561584c81610485565b6040840135934285118015615a49575b615a445760405191826158ca858201928360805f9193929373ffffffffffffffffffffffffffffffffffffffff60a08201957f60d14eec5d309c7bcbba8f8779f8070820b882731ce8afd4d9282a327dfe6ede835260016020840152610e1060408401521660608201520152565b03926158fc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0948581018352826105dd565b5190209080358203615a3f57615a2a846159ec6159f8615a2f9673ffffffffffffffffffffffffffffffffffffffff9a615a37998701976159af61594a6159438b8b615798565b3691615c01565b878151910120926159a36040519384928a8401964692889094939260809260a08301967f8c01f2233cc36f533c3fbfd8037c653a0326c46e0dc25bff9aea020c1a72a05484526020840152604083015260608201520152565b038481018352826105dd565b5190209360405193849182019586603c917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008252601c8201520190565b039081018352826105dd565b51902060065473ffffffffffffffffffffffffffffffffffffffff16615a246159436060860186615798565b91615c63565b615798565b8101906157e9565b92166132af57565b614740565b614733565b50615a6a615a6461076e60065464ffffffffff9060a01c1690565b86613b6b565b421161585c565b90808216911860011c8101809111613b665790565b604051906060820182811067ffffffffffffffff8211176105bc57604052602a8252604082602036910137565b80516001101561389c5760210190565b90815181101561389c570160200190565b8015613b66577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b15615b0657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b604051906080820182811067ffffffffffffffff8211176105bc576040526042825260603660208401376030615b9983613b78565b536078615ba583615ab3565b536041905b60018211615bbd57611cb4915015615aff565b600f811690601082101561389c577f3031323334353637383961626364656600000000000000000000000000000000615bfb921a61455e8486615ac3565b90615baa565b929192615c0d8261415a565b91615c1b60405193846105dd565b829481845281830111610373578281602093845f960137010152565b908160209103126103735751611cb481610349565b604090611cb4939281528160208201520190615660565b91813b615cbd57615c749192615da4565b73ffffffffffffffffffffffffffffffffffffffff9081169116146153575760046040517fd1085d1b000000000000000000000000000000000000000000000000000000008152fd5b615d1073ffffffffffffffffffffffffffffffffffffffff926020926040519485809481937f1626ba7e00000000000000000000000000000000000000000000000000000000998a845260048401615c4c565b0392165afa8015610bd8577fffffffff00000000000000000000000000000000000000000000000000000000915f91615d75575b5016146153575760046040517ff6cd0e2f000000000000000000000000000000000000000000000000000000008152fd5b615d97915060203d602011615d9d575b615d8f81836105dd565b810190615c37565b5f615d44565b503d615d85565b815160418103615ee45750602082015191606060408201519101515f1a925b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211615eba5760ff8416601b8114159081615eae575b50615e7a576040805193845260ff949094166020808501919091529383015260608201525f8080529060809060015afa15610bd8575f519073ffffffffffffffffffffffffffffffffffffffff821615615e5057565b60046040517ff05a20c7000000000000000000000000000000000000000000000000000000008152fd5b6040517f417893a400000000000000000000000000000000000000000000000000000000815260ff85166004820152602490fd5b601c915014155f615dfa565b60046040517fc1851252000000000000000000000000000000000000000000000000000000008152fd5b60408103615f27575060406020830151920151601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82169160ff1c0192615dc3565b6040517fd42b4bbd0000000000000000000000000000000000000000000000000000000081526004810191909152602490fdfea2646970667358221220c87ac4716d5663a44d1771fe149e28db2dbd3ac53e5cb91c2bc779d5375c852564736f6c634300081700332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0dad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac55fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5000000000000000000000000fb03340fdb69024c6579b35170f1b3c0d7d672420000000000000000000000002c64e6ee1dd9fc2a0db6a6b1aa2c3f163c7a2c7800000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000007800000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000006b86ff7863e27d1c8ccf05df9cb03b8efaa52125000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000095c68c52bb12a43069973fdcd88e4e93d2142f10000000000000000000000000aeb1d03929bf87f69888f381e73fbf75753d75af00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab0000000000000000000000009c9d8c8ee91f21004a40c4664d49cc1b9dfa69f90000000000000000000000004300000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000430000000000000000000000000000000000000200000000000000000000000043000000000000000000000000000000000000030000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd8000000000000000000000000004066b9bd584b5fa88897194dabe3a37883ac35f7
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a714610344578063197a32431461033f5780631df47f801461033a5780631fe543e31461033557806320e672321461033057806321c0b3421461032b578063248a9ca31461032657806327ab6b68146103215780632f2ff15d1461031c578063311b8d5c1461031757806336566f061461031257806336568abe1461030d57806340e58ee5146103085780634245d5f014610303578063472f5b06146102fe5780635410d098146102f957806354854797146102f4578063553be400146102ef57806358fc096f146102ea5780635c975abb146102e55780635cb6dfff146102e057806361510d31146102db578063624ab3ac146102d657806365294500146102d15780636df1d48a146102cc578063709563e2146102c75780637c8b287c146102c25780637d33ca3f146102bd5780637d7c3c74146102b85780638f1327c0146102b3578063911c66b8146102ae57806391d14854146102a957806398753c46146102a45780639f5cfe031461029f578063a217fddf1461029a578063a5cbbd2314610295578063aa73b5ac14610290578063b12957f61461028b578063b2185bb114610286578063bbc492c014610281578063bcfdf2191461027c578063bfbf228414610277578063c66944ba14610272578063ca232b091461026d578063cb5305f314610268578063d0a2c33614610263578063d547741f1461025e578063ea8a1af014610259578063f4c8770e14610254578063f7cb789a1461024f5763fa2f0a8d1461024a575f80fd5b6137ac565b613767565b6136f7565b613641565b6135e4565b61348d565b613346565b612b89565b612af1565b612a5f565b612a16565b6129a3565b6128e3565b61286a565b612821565b6127a0565b612768565b612723565b6126e1565b612664565b6125c0565b612473565b611f99565b611ddd565b611cb7565b611c0e565b611b58565b611aa2565b6119fa565b6119a9565b611941565b611901565b6118bf565b61173b565b611631565b6115e0565b61155f565b611483565b61132e565b611246565b611119565b611083565b610f1c565b610d1b565b610ca0565b610a21565b61096a565b610684565b6104a3565b610440565b610377565b7fffffffff0000000000000000000000000000000000000000000000000000000081160361037357565b5f80fd5b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760207fffffffff000000000000000000000000000000000000000000000000000000006004356103d581610349565b167f7965db0b00000000000000000000000000000000000000000000000000000000811490811561040c575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150145f610401565b5f91031261037357565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60035460d01c16604051908152f35b73ffffffffffffffffffffffffffffffffffffffff81160361037357565b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004356104de81610485565b6104e6613ebb565b73ffffffffffffffffffffffffffffffffffffffff811690811561056e577fc1b5345cce283376356748dc57f2dfa7120431d016fc7ca9ba641bc65f91411d916020916bffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006004549260601b16911617600455604051908152a1005b63aa7feadc5f526004601cfd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116105bc57604052565b61057b565b60e0810190811067ffffffffffffffff8211176105bc57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176105bc57604052565b67ffffffffffffffff81116105bc5760051b60200190565b92916106418261061e565b9161064f60405193846105dd565b829481845260208094019160051b810192831161037357905b8282106106755750505050565b81358152908301908301610668565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043560243567ffffffffffffffff81116103735736602382011215610373576106e7903690602481600401359101610636565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000095c68c52bb12a43069973fdcd88e4e93d2142f1016803303610933575061074261073b835f52600b60205260405f2090565b5460ff1690565b61074857005b61077861076e610760845f52600b60205260405f2090565b5460081c64ffffffffff1690565b64ffffffffff1690565b9061078b825f52600860205260405f2090565b906002610799835460ff1690565b6107a281612324565b146107aa575b005b6107bd906107b88484614d4b565b613b78565b51928360016107d581935f52600b60205260405f2090565b01556003808301918254906107e982613aa0565b925f5b8381106108ff575050506108e06107a8966108db6108ca61087a600361085e6002996108586108f79a60016108516108497fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6108ef9e0184613b85565b51809b613b1c565b01906154ca565b9061390e565b50015473ffffffffffffffffffffffffffffffffffffffff1690565b6108c360018a0191829073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b5460a01c90565b6bffffffffffffffffffffffff1690565b613e74565b845460301c61ffff1690613e74565b612710900490565b910155614e67565b8061092261076e8461091287958b61390e565b50015460a81c64ffffffffff1690565b61092c8288613b85565b52016107ec565b604490604051907f1cf993f40000000000000000000000000000000000000000000000000000000082523360048301526024820152fd5b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576109a0613ebb565b7f67aa0e8084cf2c79566eed90861fb37a9bf4106afb0fd2ee5b3428b4a5767c7b60206003548060f81c15907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fff000000000000000000000000000000000000000000000000000000000000008360f81b16911617600355604051908152a1005b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600435610a5c81610485565b60243590610a6982610485565b610a71613ebb565b6040517fe12f3a61000000000000000000000000000000000000000000000000000000008082523060048301526020939092909173ffffffffffffffffffffffffffffffffffffffff91907f000000000000000000000000430000000000000000000000000000000000000483168682602481845afa908115610bd85787925f92610c81575b5081610bfa575b505060405194855250503060048401527f000000000000000000000000430000000000000000000000000000000000000316918381602481865afa908115610bd8575f91610bdd575b5080610b4f57005b6040517faad3ec9600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301526024820152908290829060449082905f905af18015610bd857610bb257005b816107a892903d10610bd1575b610bc981836105dd565b8101906137ec565b503d610bbf565b6137fb565b610bf49150843d8611610bd157610bc981836105dd565b5f610b47565b6040517faad3ec9600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9490941660048501526024840191909152829060449082905f905af18015610bd857610c64575b848180610afe565b610c7a90853d8711610bd157610bc981836105dd565b505f610c5c565b610c99919250833d8511610bd157610bc981836105dd565b905f610af7565b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004355f525f6020526020600160405f200154604051908152f35b9181601f840112156103735782359167ffffffffffffffff8311610373576020808501948460051b01011161037357565b34610373576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043567ffffffffffffffff811161037357610d6b903690600401610cea565b90600192600260015414610ef2576002600155610d86613f00565b610d8e613806565b5f91610d9985613f1a565b5f5b858110610e595750507f573056d40bdb0e131a339ac518c08135633a29c4a83b7efb171c4f931fbf40d59450602081015180610e2a575b505080610df7575b50610deb6040519283923384613a73565b0390a16107a860018055565b610e24905a90337f00000000000000000000000043000000000000000000000000000000000000046142d1565b5f610dda565b610e4b610e52925173ffffffffffffffffffffffffffffffffffffffff1690565b33906141d8565b5f80610dd2565b80610e668892888861385c565b610e8f610e7c82355f52600860205260405f2090565b91610e8683613f2e565b858101906138a1565b610e9881613f1a565b60035f93015b818410610eaf575050505001610d9b565b90919297610ee8869189610ed9610ec78d888a6138fe565b35610ed3875482613f93565b8661390e565b50610ee381613fa8565b613fd7565b9801929190610e9e565b60046040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602435600435610f5a82610485565b805f525f602052610f71600160405f2001546143f4565b805f525f60205260ff610fa58360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b541615610fae57005b805f525f602052610fe08260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4005b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126103735760043561ffff811681036103735790565b346103735761109136611048565b611099613ebb565b61ffff8116906109c4821161056e577fede4aee4284b8033b84c1aadcc51b229a4e46e6b42ab40092e237f07508b4626916020917fffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffff72ffff00000000000000000000000000000000006003549260881b16911617600355604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735761114f613ebb565b60025460ff8116156111e9575060025460ff8116156111bf577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a16107a861456d565b60046040517f6cd60201000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060019161121561474d565b16176002557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760243561128181610485565b3373ffffffffffffffffffffffffffffffffffffffff8216036112aa576107a890600435614603565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152fd5b34610373576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004359061136a613ebb565b81156114385764ffffffffff60035460a81c16905f5b8381106113f4576107a87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85857f0c4874e1a947afdb9c813baf258eec6a442b25531183f82046efc9760f640773604051806113ea85858360209093929193604081019481520152565b0390a10101614e67565b8060019184015f526008835261143260405f2060047fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b01611380565b63cbc4e0605f526004601cfd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60209101126103735760043564ffffffffff811681036103735790565b346103735761149136611445565b611499613ebb565b64ffffffffff811690610e10821161056e577fbdeccc4cf4c928bfff2005eee2fd4b91bb3d22035af62a93e35b1eedf853e3a3916020917fffffffffffffffffffffffffffffff0000000000ffffffffffffffffffffffff70ffffffffff0000000000000000000000006003549260601b16911617600355604051908152a1005b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020910112610373576004356bffffffffffffffffffffffff811681036103735790565b34610373576bffffffffffffffffffffffff61157a3661151a565b611582613ebb565b16801561056e576020817f988fd919afedb9b211cf201222b0091d485871857d14d2a87946e1ee390a17f4927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006003541617600355604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043561166c81610485565b611674613ebb565b6002549073ffffffffffffffffffffffffffffffffffffffff8260081c166116dc5774ffffffffffffffffffffffffffffffffffffffff007fffffffffffffffffffffff0000000000000000000000000000000000000000ff9160081b169116176002555f80f35b63d6336f0d5f526004601cfd5b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261037357600435916024359067ffffffffffffffff82116103735761173791600401610cea565b9091565b611744366116e9565b600192919290600260015414610ef257600260015561176161474d565b80156118ba57611779835f52600860205260405f2090565b9361178385614783565b61178c856147cb565b5f61179683613aa0565b935f5b848110611823575050340361181e576117db7fa315167fc4200676e8597c5df065fafa8cecfeac15a8e2aded299a649e4a517593604051938493873386613b99565b0390a1611800825464ffffffffff90600385015491808260081c169160901c16614b39565b61180e575b6107a860018055565b61181791614b62565b5f80611805565b61056e565b80870190611839825f52600860205260405f2090565b9161184a6108ca8585015460a01c90565b9081156118a9575b61185c848261490e565b6118678389896138fe565b35906118738383613b1c565b61181e57816118979382889761188c84611891966149f7565b614ab5565b95613b6b565b936118a28289613b85565b5201611799565b90506118b48161482e565b90611852565b6146d8565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602061ffff60035460981c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602060ff600254166040519015158152f35b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004355f52600b602052606060405f206001815491015464ffffffffff6040519260ff81161515845260081c1660208301526040820152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602073ffffffffffffffffffffffffffffffffffffffff60055416604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600260015414610ef2576002600155611a3f613f00565b64ffffffffff8060035460a81c16805f52600860205260405f2091611a6383613f51565b611a7281845460681c16613c0c565b164210611a9557611a8681611a8b93614da8565b614e67565b506107a860018055565b63f4c0ca6e5f526004601cfd5b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735773ffffffffffffffffffffffffffffffffffffffff600435611af281610485565b611afa613ebb565b16801561056e576020817f2d5eba1cf3f9252b2aa813087328fc416c6c2ad0fb1417f9f5f78f08cb24cfdb927fffffffffffffffffffffffff00000000000000000000000000000000000000006005541617600555604051908152a1005b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735773ffffffffffffffffffffffffffffffffffffffff600435611ba881610485565b611bb0613ebb565b16801561056e576020817fa75f06c1fcb711f11e4fe2a1e8b84e798cce8ea1c6d6db252abe31ebd836e21f927fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60065460a01c16604051908152f35b8015150361037357565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610373576004359067ffffffffffffffff821161037357611ca691600401610cea565b9091602435611cb481611c53565b90565b3461037357611cc536611c5d565b5f92915f925f915b808310611d1b575050505f14611cfe5750611cea611cfa91614f8a565b6040519081529081906020820190565b0390f35b80821015611d11575050611cfa5f611cea565b611cfa9103611cea565b909194611d2986838561385c565b611d53611d3f82355f52600860205260405f2090565b91611d4983613f67565b60208101906138a1565b60038301908154915f5b828110611d835750505050506001916002611d7a92015490613b6b565b95019190611ccd565b611d8e8184876138fe565b3584811015611dd857611da1908361390e565b50805460ff16611db081612340565b15611dbf575b50600101611d5d565b6001919b6002611dd192015490613b6b565b9a90611db6565b613f9b565b611de636611c5d565b91600292600193600260015414610ef2576002600155611e04613f00565b611e0c613806565b915f955f9282611e1b88613f1a565b611f8e575b5f5b878110611efb5750505081611e79575b5050928360207ffbb3d671e318a7ebc6333ba95648c22dc746b01f141b5e81939d55d4664be29595015180610e2a57505080610df75750610deb6040519283923384613a73565b15611eb25790611eaa7ffbb3d671e318a7ebc6333ba95648c22dc746b01f141b5e81939d55d4664be2959592615079565b90935f611e32565b80611ebf611ec692615015565b3490613c51565b848111611ef6577ffbb3d671e318a7ebc6333ba95648c22dc746b01f141b5e81939d55d4664be295940390611eaa565b6146e5565b611f0681898961385c565b611f2f611f1c82355f52600860205260405f2090565b91611f2683613f67565b611d4983614fe6565b90611f3982613f1a565b5f91600384015b818410611f655750505050905f611f5d8486940197885490613b6b565b965501611e22565b9091929c611f8488918f611f7e610ec78f92888a6138fe565b50613fd7565b9d01929190611f40565b3415611e205761056e565b346103735760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600480359060243567ffffffffffffffff811161037357611fed903690600401610cea565b9160443590611ffb82611c53565b6002916001600260015414610ef257600260015561201761474d565b5f935f9361202488613f1a565b5f5b888110612182575050505081612152575b505061204b845f52600860205260405f2090565b61205481614783565b61205e818661490e565b612067816147cb565b6120786108ca600183015460a01c90565b946120838684613b1c565b80612113575b5085831061181e576120cb837fecd5c5544dcd4a5344389c360ff979c215f25290177e786ca2c407e58a95b88a976120c4610deb96856149f7565b8385614ab5565b91816120ef825464ffffffffff90600385015491808260081c169160901c16614b39565b612103575b50506040519485943386613c5e565b61210c91614b62565b5f816120f4565b928361214c91612121613f00565b03935a90337f00000000000000000000000043000000000000000000000000000000000000046142d1565b5f612089565b156121675761216090615079565b5f80612037565b908181105f036146e5578161217d910391615015565b612160565b61218d818a8a61385c565b6121a081355f52600860205260405f2090565b906121ac825460ff1690565b916121b683612324565b6003918284106122f2576121e2906121cd85612324565b8385146122d2575b60208195939501906138a1565b90926121ed82613f1a565b805f950192878114935b83871061220c57505050505050508301612026565b9091929394959c6122336122218f878a6138fe565b3561222d845482613f93565b8361390e565b5061223d816151a3565b805460ff1661224b81612340565b6122cd578b918b6122b2928961226088612324565b6122bf575b6122aa888201740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055565b015490613b6b565b9d019594939291906121f7565b6122c881613fa8565b612265565b6146f2565b6122db83614fe6565b5f6122eb8985019c8d5490613b6b565b9b556121d5565b613f44565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6005111561232e57565b6122f7565b90600582101561232e5752565b6003111561232e57565b9698939590979492919260206101606101409961236a818c81019d612333565b829a64ffffffffff8098168483015261ffff6040971660408301528760609b166060830152876080951660808301528760a0961660a083015260c09873ffffffffffffffffffffffffffffffffffffffff9e8f1660c08401526bffffffffffffffffffffffff60e09b1660e08401526101008301526101208201528d51809c52019b01995f975b8a89106124075750505050505050505050505090565b909192939495969798999a9b8a896001928f8b8b9151805161242881612340565b8452808601518916868501528b8101518c85015287810151888501528981015189168a8501528a81015115158b8501520151168b820152019d019b9a990197969594939291906123f1565b346103735760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576004355f8181526008602052604090208054603081901c61ffff16929091611cfa91906124d7905460401c64ffffffffff1690565b906124fb6124ed825f52600860205260405f2090565b5460681c64ffffffffff1690565b61251e612510835f52600860205260405f2090565b5460901c64ffffffffff1690565b6125506001612535855f52600860205260405f2090565b015473ffffffffffffffffffffffffffffffffffffffff1690565b906125706001612568865f52600860205260405f2090565b015460a01c90565b926125a4600361259e600261258d895f52600860205260405f2090565b0154975f52600860205260405f2090565b01613c9d565b95604051998960ff64ffffffffff8d9c60081c1691168b61234a565b34610373576125ce36611445565b6125d6613ebb565b64ffffffffff8116906002821061056e577f32e1033127c5a25fa4a87832fb767338db2bca3d4eba8f34be6d5a8f71e92275916020917fff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff7effffffffff00000000000000000000000000000000000000000000000000006003549260d01b16911617600355604051908152a1005b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602060ff6126d56024356126a681610485565b6004355f525f845260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54166040519015158152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602061ffff60035460881c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60035460a81c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206040515f8152f35b34610373576bffffffffffffffffffffffff6127bb3661151a565b6127c3613ebb565b16801561056e576020817fab9b0becdb6bda45e0cb0aedf05c920174daf5b035652783df65736347ec8b98927fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006004541617600455604051908152a1005b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206bffffffffffffffffffffffff60035416604051908152f35b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206128da6024356128aa81610485565b6004355f526009835260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600260015414610ef257600260015561292861474d565b64ffffffffff8060035460a81c16805f52600860205260405f209161294c83613f7d565b825490808260901c169161297161296d6003870154848460081c1686614b39565b1590565b612980575b6118058486614b62565b60401c16421061299e57600211612999575f8080612976565b6146ff565b6136e0565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735773ffffffffffffffffffffffffffffffffffffffff6004356129f381610485565b165f52600c60205260405f206024355f52602052602060405f2054604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206bffffffffffffffffffffffff60045416604051908152f35b34610373577ff020ff23e722cd62b0e67a05c051cc3dc4b4d0c48e01de57a0571ad6a180c35d6020612a9036611445565b612a98613ebb565b6006547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff78ffffffffff00000000000000000000000000000000000000008360a01b1691161760065564ffffffffff60405191168152a1005b3461037357612aff36611048565b612b07613ebb565b61ffff811690612710821161056e577f2e38c85b973ddf3d7c0cbd813bddb9b63db96d86ff7b6ccce361777d674d813c916020917fffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffffff74ffff000000000000000000000000000000000000006003549260981b16911617600355604051908152a1005b612b92366116e9565b91600260015414610ef2576002600155612baa61474d565b612bbc815f52600860205260405f2090565b612bc581614783565b612bcf818361490e565b612bd8816147cb565b6003810154935f905f94612bfc612bf7865f52600860205260405f2090565b613b58565b92346132b45782156118ba575b82612cb7575b505050612c81857f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca968593612c5d612c57612c516108ca60018a015460a01c90565b8a613e74565b866149f7565b55835464ffffffffff808260901c1691612c7783856156b4565b60081c1690614b39565b612ca7575b50506040805133815260208101929092528101919091528060608101610deb565b612cb091614b62565b5f81612c86565b9190612cc2826151c0565b925f905b838210612dab575050505073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab16803b1561037357612d505f929183926040519485809481937f0ca8e8a800000000000000000000000000000000000000000000000000000000835230903390600485016152d4565b03925af1958615610bd857808593612c81937f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca99612d92575b50935096612c0f565b80612d9f612da5926105a8565b80610436565b5f612d89565b93979298612dbe828b859a949995615257565b94612dcb60208701613db7565b946001612e2d612dda89615297565b604051612e1c81612df060208201948d86613dc1565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826105dd565b5190205f52600760205260405f2090565b54036132af57612e6c84612e5f8873ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b905f5260205260405f2090565b546002612e7889615297565b612e8181612340565b0361306057801561301e575b612eae90612ea86108ca6001899e959798969e015460a01c90565b90613eb1565b93612eb885615756565b612ecf612ec860408a018a6138a1565b9050613aa0565b9a5f945b612ee060408b018b6138a1565b9050861015612f8a578c83808d898b98968f9a988e612f008c859a613b6b565b9b612f0a9361576a565b9960408101612f18916138a1565b612f2292916138fe565b3591612f36915f5260205f209060021b0190565b3379ffffffffff00000000000000000000000000000000000000000060a88b901b16176003820155600260088e901b1781556001015560010196612f7991613b85565b600190526001019491939092612ed3565b9c98613003929e965060019550612fca612feb92989c959d9499612fae878d613b85565b519073ffffffffffffffffffffffffffffffffffffffff169052565b612fe16020612fd9878d613b85565b510160019052565b60408101906138a1565b91906040612ff9868c613b85565b5101923691610636565b905260606130118389613b85565b5101525b01909291612cc6565b50612eae61302b88615808565b8061305887612e5f8b73ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b559050612e8d565b60016130768994979b959e969c98939d99615297565b61307f81612340565b14613090575b505050600190613015565b919a829c939c95919515613270575b6130b66130af60408e018e6138a1565b3691610636565b600181510361326b576130cb6130d391613b78565b518094613e74565b6040517f313ce56700000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff8c165afa8015610bd8576108ca60016131418f9495612ea8612ea89561314a985f9161323c575b50613ea0565b93015460a01c90565b9b6131548d615756565b6060918d8383013511613237578d8f8b88958f958c6131738585613b85565b5173ffffffffffffffffffffffffffffffffffffffff90911690526131988484613b85565b516020015f9052604081016131ac916138a1565b936131b79193613b85565b51019136906131c592610636565b90526131d091613b6b565b9c6131da9361576a565b936131ee83895f5260205f209060021b0190565b3379ffffffffff00000000000000000000000000000000000000000060a888901b1617600382015591600283015561322b919060081b6001179055565b600101985f8080613085565b61470c565b61325e915060203d602011613264575b61325681836105dd565b810190613e87565b5f61313b565b503d61324c565b613f21565b915061327b85615446565b91826132a98c612e5f8973ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b5561309f565b615439565b5094506132c86108ca600185015460a01c90565b6132d28134613b1c565b61181e576132e09034613eb1565b9460016132ee8789876156ea565b9761333561330582875f5260205f209060021b0190565b3379ffffffffff00000000000000000000000000000000000000000060a88d901b16176003820155349060020155565b0196612c09565b6003111561037357565b346103735760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760043567ffffffffffffffff811161037357613395903690600401610cea565b602435916133a28361333c565b604435926133af84611c53565b6133b76153f4565b6133c081612340565b80156132af575f5b83811061340757507f14f4f9b9a2d50cb168899c4825e24b7ffdc472b1dca88ddad74a834f7b62470b936134029160405194859485613e02565b0390a1005b600190851561348757815b6134806134286134238489896138fe565b613db7565b60ff6040519361346d856134418a602083019687613dc1565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018752866105dd565b16925190205f52600760205260405f2090565b55016133c8565b5f613412565b34610373577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60408136011261037357600435906024359067ffffffffffffffff82116103735760a09082360301126103735773ffffffffffffffffffffffffffffffffffffffff91602061355e61350760248501613db7565b9361353484612e5f8773ffffffffffffffffffffffffffffffffffffffff165f52600c60205260405f2090565b549081156135ca575b6135518160446135579301906004016138a1565b906138f5565b3590613e74565b926004604051809681937f313ce567000000000000000000000000000000000000000000000000000000008352165afa928315610bd8576108ca60016125686135bb611cea96612ea8611cfa99612ea8975f9161323c5750613ea0565b945f52600860205260405f2090565b90506135576135516135db87615446565b9291505061353d565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610373576107a860243560043561362582610485565b805f525f60205261363c600160405f2001546143f4565b614603565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357600260015414610ef2576002600155613686613f00565b64ffffffffff8060035460a81c16805f52600860205260405f20916136aa83613f7d565b825490808260401c1680159081156136ed575b506136e05760019160901c16116136db57611a8681611a8b93614da8565b614719565b63f9ad93f55f526004601cfd5b905042105f6136bd565b346103735760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103735760206128da60243561373781610485565b6004355f52600a835260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602064ffffffffff60035460601c16604051908152f35b34610373575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261037357602060035460f81c6040519015158152f35b90816020910312610373575190565b6040513d5f823e3d90fd5b604051906040820182811067ffffffffffffffff8211176105bc576040525f6020838281520152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919081101561389c5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc181360301821215610373570190565b61382f565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610373570180359067ffffffffffffffff821161037357602001918160051b3603831361037357565b901561389c5790565b919081101561389c5760051b0190565b805482101561389c575f5260205f209060021b01905f90565b90918281527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116103735760209260051b809284830137010190565b908290808452602080940193600592818360051b82010195855f925b85841061399257505050505050505090565b90919293949596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820301845288357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18436030181121561037357830160409080358352878101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561037357019087823592019267ffffffffffffffff831161037357828b1b360384136103735760019389938383869586613a619601520191613927565b9a019401940192969594939190613980565b60409073ffffffffffffffffffffffffffffffffffffffff611cb495931681528160208201520191613964565b90613aaa8261061e565b613ab760405191826105dd565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613ae5829461061e565b0190602036910137565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115613b26570690565b613aef565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9060038201809211613b6657565b613b2b565b91908201809211613b6657565b80511561389c5760200190565b805182101561389c5760209160051b010190565b92909373ffffffffffffffffffffffffffffffffffffffff613bd293951684526020946020850152608060408501526080840191613927565b906060818303910152602080845192838152019301915f5b828110613bf8575050505090565b835185529381019392810192600101613bea565b9062015f9064ffffffffff80931601918211613b6657565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211613b6657565b91908203918211613b6657565b9160609373ffffffffffffffffffffffffffffffffffffffff613c939398979698168452608060208501526080840191613964565b9460408201520152565b908154613ca98161061e565b92604093613cba60405191826105dd565b82815280946020809201925f5260205f20905f935b858510613cde57505050505050565b6004846001928451613cef816105c1565b613da964ffffffffff8854613d9560ff808316613d0b81612340565b8652613d4973ffffffffffffffffffffffffffffffffffffffff809460081c168988019073ffffffffffffffffffffffffffffffffffffffff169052565b898c01548b87015260028c01546060870152613d8560038d0154938416608088019073ffffffffffffffffffffffffffffffffffffffff169052565b60a083811c909116151590860152565b60a81c1660c083019064ffffffffff169052565b815201930194019391613ccf565b35611cb481610485565b90917fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060159360601b168252613df681612340565b60f81b60148201520190565b92949391806060850160608652526080840191905f5b818110613e3c575050509060409194613e3081612340565b60208401521515910152565b90919260019073ffffffffffffffffffffffffffffffffffffffff8535613e6281610485565b16815260209081019401929101613e18565b81810292918115918404141715613b6657565b90816020910312610373575160ff811681036103735790565b60ff16604d8111613b6657600a0a90565b8115613b26570490565b335f9081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff1615613ef357565b6330cd74715f526004601cfd5b60035460f81c15613f0d57565b63010a265a5f526004601cfd5b15613f2157565b63947d5a845f526004601cfd5b5460ff16600581101561232e57600403613f4457565b63f525e3205f526004601cfd5b5460ff16600581101561232e57600203613f4457565b5460ff16600581101561232e57600303613f4457565b5460ff16600581101561232e57600103613f4457565b1015613f9b57565b6363df81715f526004601cfd5b6003015473ffffffffffffffffffffffffffffffffffffffff163303613fca57565b633cc50b455f526004601cfd5b90613fe1826151a3565b61402760038301740100000000000000000000000000000000000000007fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff825416179055565b815460ff811661403681612340565b8061404b5750505060020154611cb491613b6b565b61405781949394612340565b600281036140915750508054611cb4925060019060081c73ffffffffffffffffffffffffffffffffffffffff169101549033903090615567565b8061409d600192612340565b146140a9575b50505090565b73ffffffffffffffffffffffffffffffffffffffff809160081c166140e2845173ffffffffffffffffffffffffffffffffffffffff1690565b918216810361410c575050602060026141019201549201918251613b6b565b90525b5f80806140a3565b61413f9084602060029594960195865180614148575b50509073ffffffffffffffffffffffffffffffffffffffff169052565b01549052614104565b6141539133906141d8565b5f80614122565b67ffffffffffffffff81116105bc57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b3d156141be573d906141a58261415a565b916141b360405193846105dd565b82523d5f602084013e565b606090565b908160209103126103735751611cb481611c53565b919091803b156142a7576040517fa9059cbb000000000000000000000000000000000000000000000000000000006020820190815273ffffffffffffffffffffffffffffffffffffffff909416602482015260448101929092525f92839283906142458160648101612df0565b51925af1614251614194565b901561427d57805180614262575050565b8160208061296d9361427795010191016141c3565b61427d57565b60046040517ff1568f95000000000000000000000000000000000000000000000000000000008152fd5b60046040517f09ee12d5000000000000000000000000000000000000000000000000000000008152fd5b9091925f80808087876142e396f11590565b6142ec57505050565b73ffffffffffffffffffffffffffffffffffffffff1691823b1561037357604051927fd0e30db00000000000000000000000000000000000000000000000000000000084525f8460048185855af1928315610bd8576143a7946020946143e1575b505f6040518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015610bd8576143b9575b50565b6143b69060203d6020116143da575b6143d281836105dd565b8101906141c3565b503d6143c8565b80612d9f6143ee926105a8565b5f61434d565b805f525f60205260ff6144283360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416156144325750565b339061443c615a86565b91603061444884613b78565b53607861445484615ab3565b5360295b6001811161451f5761451b6144c06144e986612df06144808861447b8915615aff565b615b64565b6144ba6040519586946144ba602087016017907f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081520190565b90615649565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000815260110190565b6040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352600483016156a3565b0390fd5b90600f811690601082101561389c577f3031323334353637383961626364656600000000000000000000000000000000614568921a61455e8487615ac3565b5360041c91615ad4565b614458565b64ffffffffff60035460a81c165f5b600181106145be57506143b6907f0c4874e1a947afdb9c813baf258eec6a442b25531183f82046efc9760f6407736040805183815260016020820152a1614e67565b8060019183015f5260086020526145fd60405f2060047fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b0161457c565b805f525f60205260ff6146378360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5416614641575050565b805f525f6020526146738260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4565b63a95231d55f526004601cfd5b630134f2785f526004601cfd5b63a1e9dd9d5f526004601cfd5b637e439aed5f526004601cfd5b63f48cb8a05f526004601cfd5b637cd9dd6a5f526004601cfd5b63f90121325f526004601cfd5b63c11f59765f526004601cfd5b630da5618b5f526004601cfd5b60ff6002541661475957565b60046040517f1309a563000000000000000000000000000000000000000000000000000000008152fd5b5460ff8116600581101561232e57600114908115916147a4575b50613f4457565b60401c64ffffffffff168015159150816147c0575b505f61479d565b90504210155f6147b9565b805464ffffffffff808260401c16156147e357505050565b60035460601c164201804211613b66576cffffffffff00000000000000007fffffffffffffffffffffffffffffffffffffff0000000000ffffffffffffffff9160401b169116179055565b9060035460017fffffffffffffffffffffffff00000000000000000000000000000000000000006148766bffffffffffffffffffffffff8416955f52600860205260405f2090565b9267ffff0000000000008160581c1665ffffffffff008260c81c1617845560a01b16910155565b9060035460017fffffffffffffffffffffffff00000000000000000000000000000000000000006148e56bffffffffffffffffffffffff8416955f52600860205260405f2090565b928267ffff0000000000008260581c1665ffffffffff008360c81c161717845560a01b16910155565b805f52600960205261494060405f203373ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54908115614983575b600192505f526009602052016149803360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b55565b82549264ffffffffff93848160901c16948160081c1685146149ea577fffffffffffffffffff0000000000ffffffffffffffffffffffffffffffffffff16600194850160901b76ffffffffff00000000000000000000000000000000000016179055614949565b63b53a57db5f526004601cfd5b805f52600a602052614a2960405f203373ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54828101809111613b66576bffffffffffffffffffffffff6004541610614a8b575f52600a602052614a7c3360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b8054918201809211613b665755565b60046040517f8369f344000000000000000000000000000000000000000000000000000000008152fd5b90919392938015613b26578404936003820154916001830193614b01614af2888664ffffffffff95614aed87825460901c168b6156b4565b6156ea565b915f52600860205260405f2090565b9360038501809511613b6657600292614b26614b3492875f5260205f209060021b0190565b921660a81b33176003830155565b015555565b9190821091821592614b4a57505090565b600110915081614b58575090565b6103e89150101590565b614bb190614b708382614e0a565b80547fffffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffff164260681b71ffffffffff0000000000000000000000000016179055565b6040517f5d3b1d300000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1660248201527f000000000000000000000000000000000000000000000000000000000000000361ffff166044820152622625a060648201526001608482015260208160a4815f7f00000000000000000000000095c68c52bb12a43069973fdcd88e4e93d2142f1073ffffffffffffffffffffffffffffffffffffffff165af1908115610bd8575f91614d2c575b50614ccb61073b825f52600b60205260405f2090565b614d27577f3d94fecedaa4f90b8bd459797adb95f5bb11426025c5541390d9ccc1ad1b60a191815f52600b60205260405f208160081b6001179055614d226040519283928360209093929193604081019481520152565b0390a1565b614726565b614d45915060203d602011610bd157610bc981836105dd565b5f614cb5565b7ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c9160409160037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055815190815260036020820152a1565b7ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c91614dfb60409260047fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b815190815260046020820152a1565b7ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c9160409160027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055815190815260026020820152a1565b600101906003547fffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffffff79ffffffffff0000000000000000000000000000000000000000008460a81b1691161780600355614ec8835f52600860205260405f2090565b6bffffffffffffffffffffffff614ee3600183015460a01c90565b16614f2e575050614ef38261489d565b505b60408051838152600160208201527ffbe046f0ca401af45e57af7a8efd3840294bfc077bf062f4b9919e2c028e161c9181908101614d22565b54614f429060901c64ffffffffff1661076e565b90614f55845f52600860205260405f2090565b908154906001821793614f6b575b505055614ef5565b600192935064ffffffffff9060601c16420160401b1717905f80614f63565b670de0b6b3a76400009080820291820403613b6657614fb39061ffff60035460981c1690613e74565b614fd773ffffffffffffffffffffffffffffffffffffffff60025460081c16615446565b8015613b265761271091040490565b6001015473ffffffffffffffffffffffffffffffffffffffff16330361500857565b63618c72425f526004601cfd5b60407fa0c67dacab0c86ad0a872c2ed3fa9deb08f80f4f0543d481f2fac9ee41d026919161506b60045460601c825a917f00000000000000000000000043000000000000000000000000000000000000046142d1565b81519081525f6020820152a1565b61508290614f8a565b73ffffffffffffffffffffffffffffffffffffffff90817f00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab169160025460081c1660045460601c833b156103735760845f928360405196879485937fda3e8ce4000000000000000000000000000000000000000000000000000000008552600485015233602485015260448401528660648401525af1918215610bd8577fa0c67dacab0c86ad0a872c2ed3fa9deb08f80f4f0543d481f2fac9ee41d0269192615190575b5060025460081c73ffffffffffffffffffffffffffffffffffffffff166040805192835273ffffffffffffffffffffffffffffffffffffffff909116602083015281908101614d22565b80612d9f61519d926105a8565b5f615146565b6003015460a01c60ff166151b357565b636507689f5f526004601cfd5b906151ca8261061e565b60406151d960405192836105dd565b8382527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0615207839561061e565b01905f5b8281106152185750505050565b815190608082019180831067ffffffffffffffff8411176105bc5760209284525f8152825f81830152606080868401528083015282870101520161520b565b919081101561389c5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6181360301821215610373570190565b35611cb48161333c565b9081518082526020808093019301915f5b8281106152c0575050505090565b8351855293810193928101926001016152b2565b93919392909260609460608201606083528551809152608090608084019760808260051b860101986020809901935f925b848410615359575050505050509061533b604092615357959683019073ffffffffffffffffffffffffffffffffffffffff169052565b019073ffffffffffffffffffffffffffffffffffffffff169052565b565b90919293949a8a806153e4838f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808d60019703018952519073ffffffffffffffffffffffffffffffffffffffff8251168152838201516153b881612340565b84820152866153d460408a81860151918501528a8401906152a1565b92015190878184039101526152a1565b9d01940194019294939190615305565b335f9081527fee57cd81e84075558e8fcc182a1f4393f91fc97f963a136e66b7f949a62f319f602052604090205460ff161561542c57565b637c214f045f526004601cfd5b63c1ab6dc15f526004601cfd5b602073ffffffffffffffffffffffffffffffffffffffff604481600554169360405194859384927fe3cae776000000000000000000000000000000000000000000000000000000008452166004830152610e1060248301525afa908115610bd8575f916154b1575090565b611cb4915060203d602011610bd157610bc981836105dd565b8051918215615560575f925b80841061553257508215159182615514575b505015611cb4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b61552991925061552384613c24565b90613b85565b51145f806154e8565b61553c8185615a71565b90826155488386613b85565b51111561555557506154d6565b9350600101926154d6565b5050505f90565b909192813b156142a7576040519260208401947f23b872dd00000000000000000000000000000000000000000000000000000000865273ffffffffffffffffffffffffffffffffffffffff809216602486015216604484015260648301526064825260a082019282841067ffffffffffffffff8511176105bc575f809493819460405251925af16155f6614194565b50156155fe57565b60046040517fe0f5c508000000000000000000000000000000000000000000000000000000008152fd5b5f5b8381106156395750505f910152565b818101518382015260200161562a565b9061565c60209282815194859201615628565b0190565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361569c81518092818752878088019101615628565b0116010190565b906020611cb4928181520190615660565b6103e8908181116156dd57146156c75750565b6001146156d057565b63ae24220e5f526004601cfd5b6327e6fcc75f526004601cfd5b91906156f582615756565b80615707575064ffffffffff91501690565b600392837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6157389301910161390e565b5064ffffffffff928391015460a81c16908101809111613b66571690565b1561575d57565b63f91214385f526004601cfd5b64ffffffffff9390841692831561578a575050821601908111613b665790565b90919250611cb493506156ea565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610373570180359067ffffffffffffffff82116103735760200191813603831361037357565b9190826040910312610373576020823561580281610485565b92013590565b9060808201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112156103735782019160208091013561584c81610485565b6040840135934285118015615a49575b615a445760405191826158ca858201928360805f9193929373ffffffffffffffffffffffffffffffffffffffff60a08201957f60d14eec5d309c7bcbba8f8779f8070820b882731ce8afd4d9282a327dfe6ede835260016020840152610e1060408401521660608201520152565b03926158fc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0948581018352826105dd565b5190209080358203615a3f57615a2a846159ec6159f8615a2f9673ffffffffffffffffffffffffffffffffffffffff9a615a37998701976159af61594a6159438b8b615798565b3691615c01565b878151910120926159a36040519384928a8401964692889094939260809260a08301967f8c01f2233cc36f533c3fbfd8037c653a0326c46e0dc25bff9aea020c1a72a05484526020840152604083015260608201520152565b038481018352826105dd565b5190209360405193849182019586603c917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008252601c8201520190565b039081018352826105dd565b51902060065473ffffffffffffffffffffffffffffffffffffffff16615a246159436060860186615798565b91615c63565b615798565b8101906157e9565b92166132af57565b614740565b614733565b50615a6a615a6461076e60065464ffffffffff9060a01c1690565b86613b6b565b421161585c565b90808216911860011c8101809111613b665790565b604051906060820182811067ffffffffffffffff8211176105bc57604052602a8252604082602036910137565b80516001101561389c5760210190565b90815181101561389c570160200190565b8015613b66577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b15615b0657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b604051906080820182811067ffffffffffffffff8211176105bc576040526042825260603660208401376030615b9983613b78565b536078615ba583615ab3565b536041905b60018211615bbd57611cb4915015615aff565b600f811690601082101561389c577f3031323334353637383961626364656600000000000000000000000000000000615bfb921a61455e8486615ac3565b90615baa565b929192615c0d8261415a565b91615c1b60405193846105dd565b829481845281830111610373578281602093845f960137010152565b908160209103126103735751611cb481610349565b604090611cb4939281528160208201520190615660565b91813b615cbd57615c749192615da4565b73ffffffffffffffffffffffffffffffffffffffff9081169116146153575760046040517fd1085d1b000000000000000000000000000000000000000000000000000000008152fd5b615d1073ffffffffffffffffffffffffffffffffffffffff926020926040519485809481937f1626ba7e00000000000000000000000000000000000000000000000000000000998a845260048401615c4c565b0392165afa8015610bd8577fffffffff00000000000000000000000000000000000000000000000000000000915f91615d75575b5016146153575760046040517ff6cd0e2f000000000000000000000000000000000000000000000000000000008152fd5b615d97915060203d602011615d9d575b615d8f81836105dd565b810190615c37565b5f615d44565b503d615d85565b815160418103615ee45750602082015191606060408201519101515f1a925b7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211615eba5760ff8416601b8114159081615eae575b50615e7a576040805193845260ff949094166020808501919091529383015260608201525f8080529060809060015afa15610bd8575f519073ffffffffffffffffffffffffffffffffffffffff821615615e5057565b60046040517ff05a20c7000000000000000000000000000000000000000000000000000000008152fd5b6040517f417893a400000000000000000000000000000000000000000000000000000000815260ff85166004820152602490fd5b601c915014155f615dfa565b60046040517fc1851252000000000000000000000000000000000000000000000000000000008152fd5b60408103615f27575060406020830151920151601b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82169160ff1c0192615dc3565b6040517fd42b4bbd0000000000000000000000000000000000000000000000000000000081526004810191909152602490fdfea2646970667358221220c87ac4716d5663a44d1771fe149e28db2dbd3ac53e5cb91c2bc779d5375c852564736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000fb03340fdb69024c6579b35170f1b3c0d7d672420000000000000000000000002c64e6ee1dd9fc2a0db6a6b1aa2c3f163c7a2c7800000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000007800000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000006b86ff7863e27d1c8ccf05df9cb03b8efaa52125000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000013880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000095c68c52bb12a43069973fdcd88e4e93d2142f10000000000000000000000000aeb1d03929bf87f69888f381e73fbf75753d75af00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab0000000000000000000000009c9d8c8ee91f21004a40c4664d49cc1b9dfa69f90000000000000000000000004300000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000430000000000000000000000000000000000000200000000000000000000000043000000000000000000000000000000000000030000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd8000000000000000000000000004066b9bd584b5fa88897194dabe3a37883ac35f7
-----Decoded View---------------
Arg [0] : params (tuple):
Arg [1] : owner (address): 0xFB03340fdB69024C6579b35170F1B3c0D7D67242
Arg [2] : operator (address): 0x2C64e6Ee1Dd9Fc2a0Db6a6B1aa2c3f163C7A2C78
Arg [3] : maximumNumberOfParticipantsPerRound (uint40): 1000
Arg [4] : roundDuration (uint40): 120
Arg [5] : valuePerEntry (uint96): 1000000000000000
Arg [6] : protocolFeeRecipient (address): 0x6b86fF7863e27d1C8CCf05dF9cB03b8eFaA52125
Arg [7] : protocolFeeBp (uint16): 100
Arg [8] : discountedProtocolFeeBp (uint16): 5000
Arg [9] : keyHash (bytes32): 0x0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : subscriptionId (uint64): 0
Arg [11] : vrfCoordinator (address): 0x95c68c52bb12a43069973FDCD88e4e93d2142f10
Arg [12] : reservoirOracle (address): 0xAeB1D03929bF87F69888f381e73FBf75753d75AF
Arg [13] : transferManager (address): 0x00000000007FE8d7666BB0da2A5D13f72b8dABaB
Arg [14] : erc20Oracle (address): 0x9C9D8C8ee91f21004A40c4664D49CC1b9DFa69f9
Arg [15] : weth (address): 0x4300000000000000000000000000000000000004
Arg [16] : signatureValidityPeriod (uint40): 90
Arg [17] : minimumRequestConfirmations (uint16): 3
Arg [1] : _ethYieldConfiguration (address): 0x4300000000000000000000000000000000000002
Arg [2] : _usdbYieldConfiguration (address): 0x4300000000000000000000000000000000000003
Arg [3] : _blastPoints (address): 0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800
Arg [4] : _blastPointsOperator (address): 0x4066b9BD584b5FA88897194dAbE3a37883AC35F7
-----Encoded View---------------
21 Constructor Arguments found :
Arg [0] : 000000000000000000000000fb03340fdb69024c6579b35170f1b3c0d7d67242
Arg [1] : 0000000000000000000000002c64e6ee1dd9fc2a0db6a6b1aa2c3f163c7a2c78
Arg [2] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000078
Arg [4] : 00000000000000000000000000000000000000000000000000038d7ea4c68000
Arg [5] : 0000000000000000000000006b86ff7863e27d1c8ccf05df9cb03b8efaa52125
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000064
Arg [7] : 0000000000000000000000000000000000000000000000000000000000001388
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 00000000000000000000000095c68c52bb12a43069973fdcd88e4e93d2142f10
Arg [11] : 000000000000000000000000aeb1d03929bf87f69888f381e73fbf75753d75af
Arg [12] : 00000000000000000000000000000000007fe8d7666bb0da2a5d13f72b8dabab
Arg [13] : 0000000000000000000000009c9d8c8ee91f21004a40c4664d49cc1b9dfa69f9
Arg [14] : 0000000000000000000000004300000000000000000000000000000000000004
Arg [15] : 000000000000000000000000000000000000000000000000000000000000005a
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [17] : 0000000000000000000000004300000000000000000000000000000000000002
Arg [18] : 0000000000000000000000004300000000000000000000000000000000000003
Arg [19] : 0000000000000000000000002536fe9ab3f511540f2f9e2ec2a805005c3dd800
Arg [20] : 0000000000000000000000004066b9bd584b5fa88897194dabe3a37883ac35f7
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$67,520.86
Net Worth in ETH
27.931548
Token Allocations
ETH
99.94%
USDB
0.04%
YOLO
0.02%
Multichain Portfolio | 35 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.