Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- ErinaceusVRFWrapper
- Optimization enabled
- false
- Compiler version
- v0.8.20+commit.a1b79de6
- EVM Version
- paris
- Verified at
- 2024-05-22T05:52:28.950838Z
Constructor Arguments
0000000000000000000000007fdbf4fe2dbbdf956c010b3dd83177cb86eb1b14
Arg [0] (address) : 0x7fdbf4fe2dbbdf956c010b3dd83177cb86eb1b14
contracts/vrf/ErinaceusVRFWrapper.sol
// SPDX-License-Identifier: MIT // solhint-disable-next-line one-contract-per-file pragma solidity ^0.8.6; import {ErinaceusVRFWrapperInterface} from "./interfaces/ErinaceusVRFWrapperInterface.sol"; import {ErinaceusVRFInterface} from "./interfaces/ErinaceusVRFInterface.sol"; import {VRFV2WrapperConsumerBase} from "./VRFV2WrapperConsumerBase.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {VRFConsumerBaseV2} from "./VRFConsumerBaseV2.sol"; /** * @notice A wrapper for VRFCoordinatorV2 that provides an interface better suited to one-off * @notice requests for randomness. */ contract ErinaceusVRFWrapper is Ownable, VRFConsumerBaseV2, ErinaceusVRFWrapperInterface { event WrapperFulfillmentFailed(uint256 indexed requestId, address indexed consumer); ExtendedErinaceusVRFInterface public immutable COORDINATOR; uint64 public immutable SUBSCRIPTION_ID; /// @dev this is the size of a VRF v2 fulfillment's calldata abi-encoded in bytes. /// @dev proofSize = 13 words = 13 * 256 = 3328 bits /// @dev commitmentSize = 5 words = 5 * 256 = 1280 bits /// @dev dataSize = proofSize + commitmentSize = 4608 bits /// @dev selector = 32 bits /// @dev total data size = 4608 bits + 32 bits = 4640 bits = 580 bytes uint32 public s_fulfillmentTxSizeBytes = 580; // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) // and some arithmetic operations. uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000; // lastRequestId is the request ID of the most recent VRF V2 request made by this wrapper. This // should only be relied on within the same transaction the request was made. uint256 public override lastRequestId; // Configuration fetched from VRFCoordinatorV2 // s_configured tracks whether this contract has been configured. If not configured, randomness // requests cannot be made. bool public s_configured; // s_disabled disables the contract when true. When disabled, new VRF requests cannot be made // but existing ones can still be fulfilled. bool public s_disabled; // s_fulfillmentFlatFeeFTNPPM is the flat fee in millionths of FTN that VRFCoordinatorV2 // charges. uint32 private s_fulfillmentFlatFeeFTNPPM; // Other configuration // s_wrapperGasOverhead reflects the gas overhead of the wrapper's fulfillRandomWords // function. The cost for this gas is passed to the user. uint32 private s_wrapperGasOverhead; // s_coordinatorGasOverhead reflects the gas overhead of the coordinator's fulfillRandomWords // function. The cost for this gas is billed to the subscription, and must therefor be included // in the pricing for wrapped requests. This includes the gas costs of proof verification and // payment calculation in the coordinator. uint32 private s_coordinatorGasOverhead; // s_wrapperPremiumPercentage is the premium ratio in percentage. For example, a value of 0 // indicates no premium. A value of 15 indicates a 15 percent premium. uint8 private s_wrapperPremiumPercentage; // s_keyHash is the key hash to use when requesting randomness. Fees are paid based on current gas // fees, so this should be set to the highest gas lane on the network. bytes32 internal s_keyHash; // s_maxNumWords is the max number of words that can be requested in a single wrapped VRF request. uint8 internal s_maxNumWords; struct Callback { address callbackAddress; uint32 callbackGasLimit; uint256 requestGasPrice; uint256 juelsPaid; } mapping(uint256 => Callback) /* requestID */ /* callback */ public s_callbacks; constructor( address _coordinator ) VRFConsumerBaseV2(_coordinator) { COORDINATOR = ExtendedErinaceusVRFInterface(_coordinator); // Create this wrapper's subscription and add itself as a consumer. uint64 subId = ExtendedErinaceusVRFInterface(_coordinator).createSubscription(); SUBSCRIPTION_ID = subId; ExtendedErinaceusVRFInterface(_coordinator).addConsumer(subId, address(this)); _transferOwnership(msg.sender); } /** * @notice setFulfillmentTxSize sets the size of the fulfillment transaction in bytes. * @param size is the size of the fulfillment transaction in bytes. */ function setFulfillmentTxSize(uint32 size) external onlyOwner { s_fulfillmentTxSizeBytes = size; } /** * @notice setConfig configures VRFV2Wrapper. * * @dev Sets wrapper-specific configuration based on the given parameters, and fetches any needed * @dev VRFCoordinatorV2 configuration from the coordinator. * * @param _wrapperGasOverhead reflects the gas overhead of the wrapper's fulfillRandomWords * function. * * @param _coordinatorGasOverhead reflects the gas overhead of the coordinator's * fulfillRandomWords function. * * @param _wrapperPremiumPercentage is the premium ratio in percentage for wrapper requests. * * @param _keyHash to use for requesting randomness. */ function setConfig( uint32 _wrapperGasOverhead, uint32 _coordinatorGasOverhead, uint8 _wrapperPremiumPercentage, bytes32 _keyHash, uint8 _maxNumWords ) external onlyOwner { s_wrapperGasOverhead = _wrapperGasOverhead; s_coordinatorGasOverhead = _coordinatorGasOverhead; s_wrapperPremiumPercentage = _wrapperPremiumPercentage; s_keyHash = _keyHash; s_maxNumWords = _maxNumWords; s_configured = true; (s_fulfillmentFlatFeeFTNPPM, , , , , , , , ) = COORDINATOR.getFeeConfig(); } /** * @notice getConfig returns the current VRFV2Wrapper configuration. * * @return fulfillmentFlatFeeFTNPPM is the flat fee in millionths of FTN that VRFCoordinatorV2 * charges. * * @return wrapperGasOverhead reflects the gas overhead of the wrapper's fulfillRandomWords * function. The cost for this gas is passed to the user. * * @return coordinatorGasOverhead reflects the gas overhead of the coordinator's * fulfillRandomWords function. * * @return wrapperPremiumPercentage is the premium ratio in percentage. For example, a value of 0 * indicates no premium. A value of 15 indicates a 15 percent premium. * * @return keyHash is the key hash to use when requesting randomness. Fees are paid based on * current gas fees, so this should be set to the highest gas lane on the network. * * @return maxNumWords is the max number of words that can be requested in a single wrapped VRF * request. */ function getConfig() external view returns ( uint32 fulfillmentFlatFeeFTNPPM, uint32 wrapperGasOverhead, uint32 coordinatorGasOverhead, uint8 wrapperPremiumPercentage, bytes32 keyHash, uint8 maxNumWords ) { return ( s_fulfillmentFlatFeeFTNPPM, s_wrapperGasOverhead, s_coordinatorGasOverhead, s_wrapperPremiumPercentage, s_keyHash, s_maxNumWords ); } /** * @notice Calculates the price of a VRF request with the given callbackGasLimit at the current * @notice block. * * @dev This function relies on the transaction gas price which is not automatically set during * @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function. * * @param _callbackGasLimit is the gas limit used to estimate the price. */ function calculateRequestPrice( uint32 _callbackGasLimit ) external view override onlyConfiguredNotDisabled returns (uint256) { return _calculateRequestPrice(_callbackGasLimit, tx.gasprice); } /** * @notice Estimates the price of a VRF request with a specific gas limit and gas price. * * @dev This is a convenience function that can be called in simulation to better understand * @dev pricing. * * @param _callbackGasLimit is the gas limit used to estimate the price. * @param _requestGasPriceWei is the gas price in wei used for the estimation. */ function estimateRequestPrice( uint32 _callbackGasLimit, uint256 _requestGasPriceWei ) external view override onlyConfiguredNotDisabled returns (uint256) { return _calculateRequestPrice(_callbackGasLimit, _requestGasPriceWei); } function _calculateRequestPrice( uint256 _gas, uint256 _requestGasPrice ) internal view returns (uint256) { // costWei is the base fee denominated in wei (native) uint256 costWei = _requestGasPrice * (_gas + s_wrapperGasOverhead + s_coordinatorGasOverhead); // feeWithPremium is the fee after the percentage premium is applied uint256 feeWithPremium = (costWei * (s_wrapperPremiumPercentage + 100)) / 100; // feeWithFlatFee is the fee after the flat fee is applied on top of the premium uint256 feeWithFlatFee = feeWithPremium + (1e12 * uint256(s_fulfillmentFlatFeeFTNPPM)); return feeWithFlatFee; } /** * @dev Requests randomness . * * @param callbackGasLimit is the gas limit that should be used when calling the consumer's * fulfillRandomWords function. * @param requestConfirmations is the number of confirmations to wait before fulfilling the * request. A higher number of confirmations increases security by reducing the likelihood * that a chain re-org changes a published randomness outcome. * @param numWords is the number of random words to request. */ function requestRandomWords(uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords) external payable onlyConfiguredNotDisabled { uint32 eip150Overhead = _getEIP150Overhead(callbackGasLimit); uint256 price = _calculateRequestPrice(callbackGasLimit, tx.gasprice); // solhint-disable-next-line custom-errors require(numWords <= s_maxNumWords, "numWords too high"); require(price <= msg.value, "Insufficient payment amount"); uint256 requestId = COORDINATOR.requestRandomWords( s_keyHash, SUBSCRIPTION_ID, requestConfirmations, callbackGasLimit + eip150Overhead + s_wrapperGasOverhead, numWords ); s_callbacks[requestId] = Callback({ callbackAddress: msg.sender, callbackGasLimit: callbackGasLimit, requestGasPrice: tx.gasprice, juelsPaid: price }); lastRequestId = requestId; } /** * @notice withdraw is used by the VRFV2Wrapper's owner to withdraw FTN revenue. * * @param _recipient is the address that should receive the FTN funds. * * @param _amount is the amount of FTN in Juels that should be withdrawn. */ function withdraw(address _recipient, uint256 _amount) external onlyOwner { _sendViaCall(payable(_recipient), _amount); } /** * @notice enable this contract so that new requests can be accepted. */ function enable() external onlyOwner { s_disabled = false; } /** * @notice disable this contract so that new requests will be rejected. When disabled, new requests * @notice will revert but existing requests can still be fulfilled. */ function disable() external onlyOwner { s_disabled = true; } function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override { Callback memory callback = s_callbacks[_requestId]; delete s_callbacks[_requestId]; // solhint-disable-next-line custom-errors require(callback.callbackAddress != address(0), "request not found"); // This should never happen VRFV2WrapperConsumerBase c; bytes memory resp = abi.encodeWithSelector(c.rawFulfillRandomWords.selector, _requestId, _randomWords); bool success = _callWithExactGas(callback.callbackGasLimit, callback.callbackAddress, resp); if (!success) { emit WrapperFulfillmentFailed(_requestId, callback.callbackAddress); } } /** * @dev Calculates extra amount of gas required for running an assembly call() post-EIP150. */ function _getEIP150Overhead(uint32 gas) private pure returns (uint32) { return gas / 63 + 1; } /** * @dev calls target address with exactly gasAmount gas and data as calldata * or reverts if at least gasAmount gas is not available. */ function _callWithExactGas(uint256 gasAmount, address target, bytes memory data) private returns (bool success) { assembly { let g := gas() // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). // We want to ensure that we revert if gasAmount > 63//64*gas available // as we do not want to provide them with less, however that check itself costs // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able // to revert if gasAmount > 63//64*gas available. if lt(g, GAS_FOR_CALL_EXACT_CHECK) { revert(0, 0) } g := sub(g, GAS_FOR_CALL_EXACT_CHECK) // if g - g//64 <= gasAmount, revert // (we subtract g//64 because of EIP-150) if iszero(gt(sub(g, div(g, 64)), gasAmount)) { revert(0, 0) } // solidity calls check that a contract actually exists at the destination, so we do the same if iszero(extcodesize(target)) { revert(0, 0) } // call and return whether we succeeded. ignore return data // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) } return success; } /** * @notice Internal function to safely send FTN. * @param to Recipient address. * @param amount Amount of ETH to send. */ function _sendViaCall( address payable to, uint256 amount ) internal { (bool sent, ) = to.call{value: amount} (""); if (!sent) { revert(); } } // function typeAndVersion() external pure virtual override returns (string memory) { // return "VRFV2Wrapper 1.0.0"; // } modifier onlyConfiguredNotDisabled() { // solhint-disable-next-line custom-errors require(s_configured, "wrapper is not configured"); // solhint-disable-next-line custom-errors require(!s_disabled, "wrapper is disabled"); _; } } interface ExtendedErinaceusVRFInterface is ErinaceusVRFInterface { function getConfig() external view returns ( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 gasAfterPaymentCalculation ); function getFeeConfig() external view returns ( uint32 fulfillmentFlatFeeFTNPPMTier1, uint32 fulfillmentFlatFeeFTNPPMTier2, uint32 fulfillmentFlatFeeFTNPPMTier3, uint32 fulfillmentFlatFeeFTNPPMTier4, uint32 fulfillmentFlatFeeFTNPPMTier5, uint24 reqsForTier2, uint24 reqsForTier3, uint24 reqsForTier4, uint24 reqsForTier5 ); }
@openzeppelin/contracts/access/Ownable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
@openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (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; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
contracts/vrf/VRFConsumerBaseV2.sol
// 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 ErinaceusVRF * @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 _erinaceusVRF, address _link) * @dev VRFConsumerBase(_erinaceusVRF) 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 ErinaceusVRFInterface * @dev subscription management functions). * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations, * @dev callbackGasLimit, numWords), * @dev see (ErinaceusVRFInterface for a description of the arguments). * * @dev Once the ErinaceusVRF 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 OnlyErinaceusCanFulfill(address have, address want); // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i address private immutable erinaceusVRF; /** * @param _erinaceusVRF address of ErinaceusVRF contract */ constructor(address _erinaceusVRF) { erinaceusVRF = _erinaceusVRF; } /** * @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 */ // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual; // rawFulfillRandomness is called by ErinaceusVRF 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 != erinaceusVRF) { revert OnlyErinaceusCanFulfill(msg.sender, erinaceusVRF); } fulfillRandomWords(requestId, randomWords); } }
contracts/vrf/VRFV2WrapperConsumerBase.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // import {LinkTokenInterface} from "../shared/interfaces/LinkTokenInterface.sol"; import {ErinaceusVRFWrapperInterface} from "./interfaces/ErinaceusVRFWrapperInterface.sol"; /** ******************************************************************************* * @notice Interface for contracts using VRF randomness through the VRF V2 wrapper * ******************************************************************************** * @dev PURPOSE * * @dev Create VRF V2 requests without the need for subscription management. Rather than creating * @dev and funding a VRF V2 subscription, a user can use this wrapper to create one off requests, * @dev paying up front rather than at fulfillment. * * @dev Since the price is determined using the gas price of the request transaction rather than * @dev the fulfillment transaction, the wrapper charges an additional premium on callback gas * @dev usage, in addition to some extra overhead costs associated with the VRFV2Wrapper contract. * ***************************************************************************** * @dev USAGE * * @dev Calling contracts must inherit from VRFV2WrapperConsumerBase. The consumer must be funded * @dev with enough LINK to make the request, otherwise requests will revert. To request randomness, * @dev call the 'requestRandomness' function with the desired VRF parameters. This function handles * @dev paying for the request based on the current pricing. * * @dev Consumers must implement the fullfillRandomWords function, which will be called during * @dev fulfillment with the randomness result. */ abstract contract VRFV2WrapperConsumerBase { ErinaceusVRFWrapperInterface internal immutable ERINACEUS_VRF_WRAPPER; /** * @param _vrfV2Wrapper is the address of the VRFV2Wrapper contract */ constructor(address _vrfV2Wrapper) { ERINACEUS_VRF_WRAPPER = ErinaceusVRFWrapperInterface(_vrfV2Wrapper); } /** * @dev Requests randomness from the VRF V2 wrapper. * * @param _callbackGasLimit is the gas limit that should be used when calling the consumer's * fulfillRandomWords function. * @param _requestConfirmations is the number of confirmations to wait before fulfilling the * request. A higher number of confirmations increases security by reducing the likelihood * that a chain re-org changes a published randomness outcome. * @param _numWords is the number of random words to request. * * @return requestId is the VRF V2 request ID of the newly created randomness request. */ function requestRandomness( uint32 _callbackGasLimit, uint16 _requestConfirmations, uint32 _numWords, uint256 paymentAmount ) internal returns (uint256 requestId) { require(paymentAmount >= ERINACEUS_VRF_WRAPPER.calculateRequestPrice(_callbackGasLimit), "Insufficient payment amount"); ERINACEUS_VRF_WRAPPER.requestRandomWords{value: ERINACEUS_VRF_WRAPPER.calculateRequestPrice(_callbackGasLimit)}(_callbackGasLimit, _requestConfirmations, _numWords); if(paymentAmount > ERINACEUS_VRF_WRAPPER.calculateRequestPrice(_callbackGasLimit)){ _sendViaCall(payable(msg.sender), paymentAmount - ERINACEUS_VRF_WRAPPER.calculateRequestPrice(_callbackGasLimit)); } return ERINACEUS_VRF_WRAPPER.lastRequestId(); } /** * @notice fulfillRandomWords handles the VRF V2 wrapper response. The consuming contract must * @notice implement it. * * @param _requestId is the VRF V2 request ID. * @param _randomWords is the randomness result. */ function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal virtual; function rawFulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) external { // solhint-disable-next-line custom-errors require(msg.sender == address(ERINACEUS_VRF_WRAPPER), "only VRF V2 wrapper can fulfill"); fulfillRandomWords(_requestId, _randomWords); } /** * @notice Internal function to safely send FTN. * @param to Recipient address. * @param amount Amount of ETH to send. */ function _sendViaCall( address payable to, uint256 amount ) internal { (bool sent, ) = to.call{value: amount} (""); if (!sent) { revert(); } } }
contracts/vrf/interfaces/ErinaceusVRFInterface.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ErinaceusVRFInterface { /** * @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 ErinaceusVRF 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(ERINACEUS), * @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 (uint256 balance, uint256 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); }
contracts/vrf/interfaces/ErinaceusVRFWrapperInterface.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; interface ErinaceusVRFWrapperInterface { /** * @return the request ID of the most recent VRF V2 request made by this wrapper. This should only * be relied option within the same transaction that the request was made. */ function lastRequestId() external view returns (uint256); function requestRandomWords(uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords) external payable; /** * @notice Calculates the price of a VRF request with the given callbackGasLimit at the current * @notice block. * * @dev This function relies on the transaction gas price which is not automatically set during * @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function. * * @param _callbackGasLimit is the gas limit used to estimate the price. */ function calculateRequestPrice(uint32 _callbackGasLimit) external view returns (uint256); /** * @notice Estimates the price of a VRF request with a specific gas limit and gas price. * * @dev This is a convenience function that can be called in simulation to better understand * @dev pricing. * * @param _callbackGasLimit is the gas limit used to estimate the price. * @param _requestGasPriceWei is the gas price in wei used for the estimation. */ function estimateRequestPrice(uint32 _callbackGasLimit, uint256 _requestGasPriceWei) external view returns (uint256); }
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":false},"metadata":{"useLiteralContent":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_coordinator","internalType":"address"}]},{"type":"error","name":"OnlyErinaceusCanFulfill","inputs":[{"type":"address","name":"have","internalType":"address"},{"type":"address","name":"want","internalType":"address"}]},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"WrapperFulfillmentFailed","inputs":[{"type":"uint256","name":"requestId","internalType":"uint256","indexed":true},{"type":"address","name":"consumer","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ExtendedErinaceusVRFInterface"}],"name":"COORDINATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"SUBSCRIPTION_ID","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"calculateRequestPrice","inputs":[{"type":"uint32","name":"_callbackGasLimit","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"disable","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"enable","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"estimateRequestPrice","inputs":[{"type":"uint32","name":"_callbackGasLimit","internalType":"uint32"},{"type":"uint256","name":"_requestGasPriceWei","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"fulfillmentFlatFeeFTNPPM","internalType":"uint32"},{"type":"uint32","name":"wrapperGasOverhead","internalType":"uint32"},{"type":"uint32","name":"coordinatorGasOverhead","internalType":"uint32"},{"type":"uint8","name":"wrapperPremiumPercentage","internalType":"uint8"},{"type":"bytes32","name":"keyHash","internalType":"bytes32"},{"type":"uint8","name":"maxNumWords","internalType":"uint8"}],"name":"getConfig","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastRequestId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rawFulfillRandomWords","inputs":[{"type":"uint256","name":"requestId","internalType":"uint256"},{"type":"uint256[]","name":"randomWords","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"requestRandomWords","inputs":[{"type":"uint32","name":"callbackGasLimit","internalType":"uint32"},{"type":"uint16","name":"requestConfirmations","internalType":"uint16"},{"type":"uint32","name":"numWords","internalType":"uint32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"callbackAddress","internalType":"address"},{"type":"uint32","name":"callbackGasLimit","internalType":"uint32"},{"type":"uint256","name":"requestGasPrice","internalType":"uint256"},{"type":"uint256","name":"juelsPaid","internalType":"uint256"}],"name":"s_callbacks","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"s_configured","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"s_disabled","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"s_fulfillmentTxSizeBytes","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setConfig","inputs":[{"type":"uint32","name":"_wrapperGasOverhead","internalType":"uint32"},{"type":"uint32","name":"_coordinatorGasOverhead","internalType":"uint32"},{"type":"uint8","name":"_wrapperPremiumPercentage","internalType":"uint8"},{"type":"bytes32","name":"_keyHash","internalType":"bytes32"},{"type":"uint8","name":"_maxNumWords","internalType":"uint8"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFulfillmentTxSize","inputs":[{"type":"uint32","name":"size","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[{"type":"address","name":"_recipient","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]}]
Contract Creation Code
0x60e0604052610244600060146101000a81548163ffffffff021916908363ffffffff1602179055503480156200003457600080fd5b506040516200275c3803806200275c83398181016040528101906200005a919062000338565b806200007b6200006f6200020260201b60201c565b6200020a60201b60201c565b8073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1681525050508073ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff168152505060008173ffffffffffffffffffffffffffffffffffffffff1663a21a23e46040518163ffffffff1660e01b81526004016020604051808303816000875af115801562000134573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200015a9190620003af565b90508067ffffffffffffffff1660c08167ffffffffffffffff16815250508173ffffffffffffffffffffffffffffffffffffffff16637341c10c82306040518363ffffffff1660e01b8152600401620001b592919062000403565b600060405180830381600087803b158015620001d057600080fd5b505af1158015620001e5573d6000803e3d6000fd5b50505050620001fa336200020a60201b60201c565b505062000430565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200030082620002d3565b9050919050565b6200031281620002f3565b81146200031e57600080fd5b50565b600081519050620003328162000307565b92915050565b600060208284031215620003515762000350620002ce565b5b6000620003618482850162000321565b91505092915050565b600067ffffffffffffffff82169050919050565b62000389816200036a565b81146200039557600080fd5b50565b600081519050620003a9816200037e565b92915050565b600060208284031215620003c857620003c7620002ce565b5b6000620003d88482850162000398565b91505092915050565b620003ec816200036a565b82525050565b620003fd81620002f3565b82525050565b60006040820190506200041a6000830185620003e1565b620004296020830184620003f2565b9392505050565b60805160a05160c0516122e06200047c6000396000818161046b015261084f015260008181610574015281816108100152610bf301526000818161048f01526104e301526122e06000f3fe60806040526004361061011f5760003560e01c80638da5cb5b116100a0578063c3f909d411610064578063c3f909d414610391578063cdd8d885146103c1578063f2fde38b146103ec578063f3fef3a314610415578063fc2a88c31461043e5761011f565b80638da5cb5b146102d2578063a3907d71146102fd578063a608a1e114610314578063bf17e5591461033f578063c15ce4d7146103685761011f565b806348baa1c5116100e757806348baa1c5146101f75780634b494dc01461023757806357a8070a14610253578063715018a61461027e5780637fb5d19d146102955761011f565b8063030932bb146101245780631fe543e31461014f5780632f2770db146101785780633b2bcbf11461018f5780634306d354146101ba575b600080fd5b34801561013057600080fd5b50610139610469565b604051610146919061139d565b60405180910390f35b34801561015b57600080fd5b506101766004803603810190610171919061155b565b61048d565b005b34801561018457600080fd5b5061018d61054d565b005b34801561019b57600080fd5b506101a4610572565b6040516101b19190611636565b60405180910390f35b3480156101c657600080fd5b506101e160048036038101906101dc919061168d565b610596565b6040516101ee91906116c9565b60405180910390f35b34801561020357600080fd5b5061021e600480360381019061021991906116e4565b61064e565b60405161022e9493929190611741565b60405180910390f35b610251600480360381019061024c91906117c0565b6106ae565b005b34801561025f57600080fd5b506102686109e2565b604051610275919061182e565b60405180910390f35b34801561028a57600080fd5b506102936109f5565b005b3480156102a157600080fd5b506102bc60048036038101906102b79190611849565b610a09565b6040516102c991906116c9565b60405180910390f35b3480156102de57600080fd5b506102e7610ac2565b6040516102f49190611889565b60405180910390f35b34801561030957600080fd5b50610312610aeb565b005b34801561032057600080fd5b50610329610b10565b604051610336919061182e565b60405180910390f35b34801561034b57600080fd5b506103666004803603810190610361919061168d565b610b23565b005b34801561037457600080fd5b5061038f600480360381019061038a9190611913565b610b4f565b005b34801561039d57600080fd5b506103a6610ccf565b6040516103b8969594939291906119ac565b60405180910390f35b3480156103cd57600080fd5b506103d6610d46565b6040516103e39190611a0d565b60405180910390f35b3480156103f857600080fd5b50610413600480360381019061040e9190611a54565b610d5c565b005b34801561042157600080fd5b5061043c60048036038101906104379190611a81565b610ddf565b005b34801561044a57600080fd5b50610453610df5565b60405161046091906116c9565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461053f57337f00000000000000000000000000000000000000000000000000000000000000006040517f1cdc5ebb000000000000000000000000000000000000000000000000000000008152600401610536929190611ac1565b60405180910390fd5b6105498282610dfb565b5050565b610555611074565b6001600260016101000a81548160ff021916908315150217905550565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600260009054906101000a900460ff166105e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105de90611b47565b60405180910390fd5b600260019054906101000a900460ff1615610637576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161062e90611bb3565b60405180910390fd5b6106478263ffffffff163a6110f2565b9050919050565b60056020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060000160149054906101000a900463ffffffff16908060010154908060020154905084565b600260009054906101000a900460ff166106fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106f490611b47565b60405180910390fd5b600260019054906101000a900460ff161561074d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161074490611bb3565b60405180910390fd5b6000610758846111c4565b9050600061076c8563ffffffff163a6110f2565b9050600460009054906101000a900460ff1660ff168363ffffffff1611156107c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107c090611c1f565b60405180910390fd5b3481111561080c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161080390611c8b565b60405180910390fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16635d3b1d306003547f000000000000000000000000000000000000000000000000000000000000000088600260069054906101000a900463ffffffff16888c61088e9190611cda565b6108989190611cda565b896040518663ffffffff1660e01b81526004016108b9959493929190611d21565b6020604051808303816000875af11580156108d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108fc9190611d89565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018763ffffffff1681526020013a8152602001838152506005600083815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff160217905550604082015181600101556060820151816002015590505080600181905550505050505050565b600260009054906101000a900460ff1681565b6109fd611074565b610a0760006111e6565b565b6000600260009054906101000a900460ff16610a5a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a5190611b47565b60405180910390fd5b600260019054906101000a900460ff1615610aaa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aa190611bb3565b60405180910390fd5b610aba8363ffffffff16836110f2565b905092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b610af3611074565b6000600260016101000a81548160ff021916908315150217905550565b600260019054906101000a900460ff1681565b610b2b611074565b80600060146101000a81548163ffffffff021916908363ffffffff16021790555050565b610b57611074565b84600260066101000a81548163ffffffff021916908363ffffffff160217905550836002600a6101000a81548163ffffffff021916908363ffffffff160217905550826002600e6101000a81548160ff021916908360ff1602179055508160038190555080600460006101000a81548160ff021916908360ff1602179055506001600260006101000a81548160ff0219169083151502179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16635fbbc0d26040518163ffffffff1660e01b815260040161012060405180830381865afa158015610c5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c819190611e06565b9091929394959650909192939495509091929394509091929350909192509091509050506002808291906101000a81548163ffffffff021916908363ffffffff160217905550505050505050565b60008060008060008060028054906101000a900463ffffffff16600260069054906101000a900463ffffffff166002600a9054906101000a900463ffffffff166002600e9054906101000a900460ff16600354600460009054906101000a900460ff16955095509550955095509550909192939495565b600060149054906101000a900463ffffffff1681565b610d64611074565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610dd3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dca90611f42565b60405180910390fd5b610ddc816111e6565b50565b610de7611074565b610df182826112aa565b5050565b60015481565b6000600560008481526020019081526020016000206040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481525050905060056000848152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556000820160146101000a81549063ffffffff0219169055600182016000905560028201600090555050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff1603610f84576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7b90611fae565b60405180910390fd5b600080631fe543e360e01b8585604051602401610fa292919061208c565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600061101c846020015163ffffffff16856000015184611325565b90508061106c57836000015173ffffffffffffffffffffffffffffffffffffffff16867fc551b83c151f2d1c7eeb938ac59008e0409f1c1dc1e2f112449d4d79b458902260405160405180910390a35b505050505050565b61107c611372565b73ffffffffffffffffffffffffffffffffffffffff1661109a610ac2565b73ffffffffffffffffffffffffffffffffffffffff16146110f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110e790612108565b60405180910390fd5b565b6000806002600a9054906101000a900463ffffffff1663ffffffff16600260069054906101000a900463ffffffff1663ffffffff16856111329190612128565b61113c9190612128565b83611147919061215c565b905060006064806002600e9054906101000a900460ff16611168919061219e565b60ff1683611176919061215c565b6111809190612202565b9050600060028054906101000a900463ffffffff1663ffffffff1664e8d4a510006111ab919061215c565b826111b69190612128565b905080935050505092915050565b60006001603f836111d59190612233565b6111df9190611cda565b9050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b60008273ffffffffffffffffffffffffffffffffffffffff16826040516112d090612295565b60006040518083038185875af1925050503d806000811461130d576040519150601f19603f3d011682016040523d82523d6000602084013e611312565b606091505b505090508061132057600080fd5b505050565b60005a61138881101561133757600080fd5b61138881039050846040820482031161134f57600080fd5b833b61135a57600080fd5b6000808451602086016000888af19150509392505050565b600033905090565b600067ffffffffffffffff82169050919050565b6113978161137a565b82525050565b60006020820190506113b2600083018461138e565b92915050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6113df816113cc565b81146113ea57600080fd5b50565b6000813590506113fc816113d6565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61145082611407565b810181811067ffffffffffffffff8211171561146f5761146e611418565b5b80604052505050565b60006114826113b8565b905061148e8282611447565b919050565b600067ffffffffffffffff8211156114ae576114ad611418565b5b602082029050602081019050919050565b600080fd5b60006114d76114d284611493565b611478565b905080838252602082019050602084028301858111156114fa576114f96114bf565b5b835b81811015611523578061150f88826113ed565b8452602084019350506020810190506114fc565b5050509392505050565b600082601f83011261154257611541611402565b5b81356115528482602086016114c4565b91505092915050565b60008060408385031215611572576115716113c2565b5b6000611580858286016113ed565b925050602083013567ffffffffffffffff8111156115a1576115a06113c7565b5b6115ad8582860161152d565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006115fc6115f76115f2846115b7565b6115d7565b6115b7565b9050919050565b600061160e826115e1565b9050919050565b600061162082611603565b9050919050565b61163081611615565b82525050565b600060208201905061164b6000830184611627565b92915050565b600063ffffffff82169050919050565b61166a81611651565b811461167557600080fd5b50565b60008135905061168781611661565b92915050565b6000602082840312156116a3576116a26113c2565b5b60006116b184828501611678565b91505092915050565b6116c3816113cc565b82525050565b60006020820190506116de60008301846116ba565b92915050565b6000602082840312156116fa576116f96113c2565b5b6000611708848285016113ed565b91505092915050565b600061171c826115b7565b9050919050565b61172c81611711565b82525050565b61173b81611651565b82525050565b60006080820190506117566000830187611723565b6117636020830186611732565b61177060408301856116ba565b61177d60608301846116ba565b95945050505050565b600061ffff82169050919050565b61179d81611786565b81146117a857600080fd5b50565b6000813590506117ba81611794565b92915050565b6000806000606084860312156117d9576117d86113c2565b5b60006117e786828701611678565b93505060206117f8868287016117ab565b925050604061180986828701611678565b9150509250925092565b60008115159050919050565b61182881611813565b82525050565b6000602082019050611843600083018461181f565b92915050565b600080604083850312156118605761185f6113c2565b5b600061186e85828601611678565b925050602061187f858286016113ed565b9150509250929050565b600060208201905061189e6000830184611723565b92915050565b600060ff82169050919050565b6118ba816118a4565b81146118c557600080fd5b50565b6000813590506118d7816118b1565b92915050565b6000819050919050565b6118f0816118dd565b81146118fb57600080fd5b50565b60008135905061190d816118e7565b92915050565b600080600080600060a0868803121561192f5761192e6113c2565b5b600061193d88828901611678565b955050602061194e88828901611678565b945050604061195f888289016118c8565b9350506060611970888289016118fe565b9250506080611981888289016118c8565b9150509295509295909350565b611997816118a4565b82525050565b6119a6816118dd565b82525050565b600060c0820190506119c16000830189611732565b6119ce6020830188611732565b6119db6040830187611732565b6119e8606083018661198e565b6119f5608083018561199d565b611a0260a083018461198e565b979650505050505050565b6000602082019050611a226000830184611732565b92915050565b611a3181611711565b8114611a3c57600080fd5b50565b600081359050611a4e81611a28565b92915050565b600060208284031215611a6a57611a696113c2565b5b6000611a7884828501611a3f565b91505092915050565b60008060408385031215611a9857611a976113c2565b5b6000611aa685828601611a3f565b9250506020611ab7858286016113ed565b9150509250929050565b6000604082019050611ad66000830185611723565b611ae36020830184611723565b9392505050565b600082825260208201905092915050565b7f77726170706572206973206e6f7420636f6e6669677572656400000000000000600082015250565b6000611b31601983611aea565b9150611b3c82611afb565b602082019050919050565b60006020820190508181036000830152611b6081611b24565b9050919050565b7f777261707065722069732064697361626c656400000000000000000000000000600082015250565b6000611b9d601383611aea565b9150611ba882611b67565b602082019050919050565b60006020820190508181036000830152611bcc81611b90565b9050919050565b7f6e756d576f72647320746f6f2068696768000000000000000000000000000000600082015250565b6000611c09601183611aea565b9150611c1482611bd3565b602082019050919050565b60006020820190508181036000830152611c3881611bfc565b9050919050565b7f496e73756666696369656e74207061796d656e7420616d6f756e740000000000600082015250565b6000611c75601b83611aea565b9150611c8082611c3f565b602082019050919050565b60006020820190508181036000830152611ca481611c68565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611ce582611651565b9150611cf083611651565b9250828201905063ffffffff811115611d0c57611d0b611cab565b5b92915050565b611d1b81611786565b82525050565b600060a082019050611d36600083018861199d565b611d43602083018761138e565b611d506040830186611d12565b611d5d6060830185611732565b611d6a6080830184611732565b9695505050505050565b600081519050611d83816113d6565b92915050565b600060208284031215611d9f57611d9e6113c2565b5b6000611dad84828501611d74565b91505092915050565b600081519050611dc581611661565b92915050565b600062ffffff82169050919050565b611de381611dcb565b8114611dee57600080fd5b50565b600081519050611e0081611dda565b92915050565b60008060008060008060008060006101208a8c031215611e2957611e286113c2565b5b6000611e378c828d01611db6565b9950506020611e488c828d01611db6565b9850506040611e598c828d01611db6565b9750506060611e6a8c828d01611db6565b9650506080611e7b8c828d01611db6565b95505060a0611e8c8c828d01611df1565b94505060c0611e9d8c828d01611df1565b93505060e0611eae8c828d01611df1565b925050610100611ec08c828d01611df1565b9150509295985092959850929598565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000611f2c602683611aea565b9150611f3782611ed0565b604082019050919050565b60006020820190508181036000830152611f5b81611f1f565b9050919050565b7f72657175657374206e6f7420666f756e64000000000000000000000000000000600082015250565b6000611f98601183611aea565b9150611fa382611f62565b602082019050919050565b60006020820190508181036000830152611fc781611f8b565b9050919050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b612003816113cc565b82525050565b60006120158383611ffa565b60208301905092915050565b6000602082019050919050565b600061203982611fce565b6120438185611fd9565b935061204e83611fea565b8060005b8381101561207f5781516120668882612009565b975061207183612021565b925050600181019050612052565b5085935050505092915050565b60006040820190506120a160008301856116ba565b81810360208301526120b3818461202e565b90509392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006120f2602083611aea565b91506120fd826120bc565b602082019050919050565b60006020820190508181036000830152612121816120e5565b9050919050565b6000612133826113cc565b915061213e836113cc565b925082820190508082111561215657612155611cab565b5b92915050565b6000612167826113cc565b9150612172836113cc565b9250828202612180816113cc565b9150828204841483151761219757612196611cab565b5b5092915050565b60006121a9826118a4565b91506121b4836118a4565b9250828201905060ff8111156121cd576121cc611cab565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061220d826113cc565b9150612218836113cc565b925082612228576122276121d3565b5b828204905092915050565b600061223e82611651565b915061224983611651565b925082612259576122586121d3565b5b828204905092915050565b600081905092915050565b50565b600061227f600083612264565b915061228a8261226f565b600082019050919050565b60006122a082612272565b915081905091905056fea2646970667358221220030cf753ab60649245258de0393a3010c042590af7aa5c51ecee056d684ffc2564736f6c634300081400330000000000000000000000007fdbf4fe2dbbdf956c010b3dd83177cb86eb1b14
Deployed ByteCode
0x60806040526004361061011f5760003560e01c80638da5cb5b116100a0578063c3f909d411610064578063c3f909d414610391578063cdd8d885146103c1578063f2fde38b146103ec578063f3fef3a314610415578063fc2a88c31461043e5761011f565b80638da5cb5b146102d2578063a3907d71146102fd578063a608a1e114610314578063bf17e5591461033f578063c15ce4d7146103685761011f565b806348baa1c5116100e757806348baa1c5146101f75780634b494dc01461023757806357a8070a14610253578063715018a61461027e5780637fb5d19d146102955761011f565b8063030932bb146101245780631fe543e31461014f5780632f2770db146101785780633b2bcbf11461018f5780634306d354146101ba575b600080fd5b34801561013057600080fd5b50610139610469565b604051610146919061139d565b60405180910390f35b34801561015b57600080fd5b506101766004803603810190610171919061155b565b61048d565b005b34801561018457600080fd5b5061018d61054d565b005b34801561019b57600080fd5b506101a4610572565b6040516101b19190611636565b60405180910390f35b3480156101c657600080fd5b506101e160048036038101906101dc919061168d565b610596565b6040516101ee91906116c9565b60405180910390f35b34801561020357600080fd5b5061021e600480360381019061021991906116e4565b61064e565b60405161022e9493929190611741565b60405180910390f35b610251600480360381019061024c91906117c0565b6106ae565b005b34801561025f57600080fd5b506102686109e2565b604051610275919061182e565b60405180910390f35b34801561028a57600080fd5b506102936109f5565b005b3480156102a157600080fd5b506102bc60048036038101906102b79190611849565b610a09565b6040516102c991906116c9565b60405180910390f35b3480156102de57600080fd5b506102e7610ac2565b6040516102f49190611889565b60405180910390f35b34801561030957600080fd5b50610312610aeb565b005b34801561032057600080fd5b50610329610b10565b604051610336919061182e565b60405180910390f35b34801561034b57600080fd5b506103666004803603810190610361919061168d565b610b23565b005b34801561037457600080fd5b5061038f600480360381019061038a9190611913565b610b4f565b005b34801561039d57600080fd5b506103a6610ccf565b6040516103b8969594939291906119ac565b60405180910390f35b3480156103cd57600080fd5b506103d6610d46565b6040516103e39190611a0d565b60405180910390f35b3480156103f857600080fd5b50610413600480360381019061040e9190611a54565b610d5c565b005b34801561042157600080fd5b5061043c60048036038101906104379190611a81565b610ddf565b005b34801561044a57600080fd5b50610453610df5565b60405161046091906116c9565b60405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000181565b7f0000000000000000000000007fdbf4fe2dbbdf956c010b3dd83177cb86eb1b1473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461053f57337f0000000000000000000000007fdbf4fe2dbbdf956c010b3dd83177cb86eb1b146040517f1cdc5ebb000000000000000000000000000000000000000000000000000000008152600401610536929190611ac1565b60405180910390fd5b6105498282610dfb565b5050565b610555611074565b6001600260016101000a81548160ff021916908315150217905550565b7f0000000000000000000000007fdbf4fe2dbbdf956c010b3dd83177cb86eb1b1481565b6000600260009054906101000a900460ff166105e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105de90611b47565b60405180910390fd5b600260019054906101000a900460ff1615610637576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161062e90611bb3565b60405180910390fd5b6106478263ffffffff163a6110f2565b9050919050565b60056020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060000160149054906101000a900463ffffffff16908060010154908060020154905084565b600260009054906101000a900460ff166106fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106f490611b47565b60405180910390fd5b600260019054906101000a900460ff161561074d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161074490611bb3565b60405180910390fd5b6000610758846111c4565b9050600061076c8563ffffffff163a6110f2565b9050600460009054906101000a900460ff1660ff168363ffffffff1611156107c9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107c090611c1f565b60405180910390fd5b3481111561080c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161080390611c8b565b60405180910390fd5b60007f0000000000000000000000007fdbf4fe2dbbdf956c010b3dd83177cb86eb1b1473ffffffffffffffffffffffffffffffffffffffff16635d3b1d306003547f000000000000000000000000000000000000000000000000000000000000000188600260069054906101000a900463ffffffff16888c61088e9190611cda565b6108989190611cda565b896040518663ffffffff1660e01b81526004016108b9959493929190611d21565b6020604051808303816000875af11580156108d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108fc9190611d89565b905060405180608001604052803373ffffffffffffffffffffffffffffffffffffffff1681526020018763ffffffff1681526020013a8152602001838152506005600083815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff160217905550604082015181600101556060820151816002015590505080600181905550505050505050565b600260009054906101000a900460ff1681565b6109fd611074565b610a0760006111e6565b565b6000600260009054906101000a900460ff16610a5a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a5190611b47565b60405180910390fd5b600260019054906101000a900460ff1615610aaa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aa190611bb3565b60405180910390fd5b610aba8363ffffffff16836110f2565b905092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b610af3611074565b6000600260016101000a81548160ff021916908315150217905550565b600260019054906101000a900460ff1681565b610b2b611074565b80600060146101000a81548163ffffffff021916908363ffffffff16021790555050565b610b57611074565b84600260066101000a81548163ffffffff021916908363ffffffff160217905550836002600a6101000a81548163ffffffff021916908363ffffffff160217905550826002600e6101000a81548160ff021916908360ff1602179055508160038190555080600460006101000a81548160ff021916908360ff1602179055506001600260006101000a81548160ff0219169083151502179055507f0000000000000000000000007fdbf4fe2dbbdf956c010b3dd83177cb86eb1b1473ffffffffffffffffffffffffffffffffffffffff16635fbbc0d26040518163ffffffff1660e01b815260040161012060405180830381865afa158015610c5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c819190611e06565b9091929394959650909192939495509091929394509091929350909192509091509050506002808291906101000a81548163ffffffff021916908363ffffffff160217905550505050505050565b60008060008060008060028054906101000a900463ffffffff16600260069054906101000a900463ffffffff166002600a9054906101000a900463ffffffff166002600e9054906101000a900460ff16600354600460009054906101000a900460ff16955095509550955095509550909192939495565b600060149054906101000a900463ffffffff1681565b610d64611074565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610dd3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dca90611f42565b60405180910390fd5b610ddc816111e6565b50565b610de7611074565b610df182826112aa565b5050565b60015481565b6000600560008481526020019081526020016000206040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016000820160149054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160018201548152602001600282015481525050905060056000848152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556000820160146101000a81549063ffffffff0219169055600182016000905560028201600090555050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff1603610f84576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7b90611fae565b60405180910390fd5b600080631fe543e360e01b8585604051602401610fa292919061208c565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600061101c846020015163ffffffff16856000015184611325565b90508061106c57836000015173ffffffffffffffffffffffffffffffffffffffff16867fc551b83c151f2d1c7eeb938ac59008e0409f1c1dc1e2f112449d4d79b458902260405160405180910390a35b505050505050565b61107c611372565b73ffffffffffffffffffffffffffffffffffffffff1661109a610ac2565b73ffffffffffffffffffffffffffffffffffffffff16146110f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110e790612108565b60405180910390fd5b565b6000806002600a9054906101000a900463ffffffff1663ffffffff16600260069054906101000a900463ffffffff1663ffffffff16856111329190612128565b61113c9190612128565b83611147919061215c565b905060006064806002600e9054906101000a900460ff16611168919061219e565b60ff1683611176919061215c565b6111809190612202565b9050600060028054906101000a900463ffffffff1663ffffffff1664e8d4a510006111ab919061215c565b826111b69190612128565b905080935050505092915050565b60006001603f836111d59190612233565b6111df9190611cda565b9050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b60008273ffffffffffffffffffffffffffffffffffffffff16826040516112d090612295565b60006040518083038185875af1925050503d806000811461130d576040519150601f19603f3d011682016040523d82523d6000602084013e611312565b606091505b505090508061132057600080fd5b505050565b60005a61138881101561133757600080fd5b61138881039050846040820482031161134f57600080fd5b833b61135a57600080fd5b6000808451602086016000888af19150509392505050565b600033905090565b600067ffffffffffffffff82169050919050565b6113978161137a565b82525050565b60006020820190506113b2600083018461138e565b92915050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6113df816113cc565b81146113ea57600080fd5b50565b6000813590506113fc816113d6565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61145082611407565b810181811067ffffffffffffffff8211171561146f5761146e611418565b5b80604052505050565b60006114826113b8565b905061148e8282611447565b919050565b600067ffffffffffffffff8211156114ae576114ad611418565b5b602082029050602081019050919050565b600080fd5b60006114d76114d284611493565b611478565b905080838252602082019050602084028301858111156114fa576114f96114bf565b5b835b81811015611523578061150f88826113ed565b8452602084019350506020810190506114fc565b5050509392505050565b600082601f83011261154257611541611402565b5b81356115528482602086016114c4565b91505092915050565b60008060408385031215611572576115716113c2565b5b6000611580858286016113ed565b925050602083013567ffffffffffffffff8111156115a1576115a06113c7565b5b6115ad8582860161152d565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006115fc6115f76115f2846115b7565b6115d7565b6115b7565b9050919050565b600061160e826115e1565b9050919050565b600061162082611603565b9050919050565b61163081611615565b82525050565b600060208201905061164b6000830184611627565b92915050565b600063ffffffff82169050919050565b61166a81611651565b811461167557600080fd5b50565b60008135905061168781611661565b92915050565b6000602082840312156116a3576116a26113c2565b5b60006116b184828501611678565b91505092915050565b6116c3816113cc565b82525050565b60006020820190506116de60008301846116ba565b92915050565b6000602082840312156116fa576116f96113c2565b5b6000611708848285016113ed565b91505092915050565b600061171c826115b7565b9050919050565b61172c81611711565b82525050565b61173b81611651565b82525050565b60006080820190506117566000830187611723565b6117636020830186611732565b61177060408301856116ba565b61177d60608301846116ba565b95945050505050565b600061ffff82169050919050565b61179d81611786565b81146117a857600080fd5b50565b6000813590506117ba81611794565b92915050565b6000806000606084860312156117d9576117d86113c2565b5b60006117e786828701611678565b93505060206117f8868287016117ab565b925050604061180986828701611678565b9150509250925092565b60008115159050919050565b61182881611813565b82525050565b6000602082019050611843600083018461181f565b92915050565b600080604083850312156118605761185f6113c2565b5b600061186e85828601611678565b925050602061187f858286016113ed565b9150509250929050565b600060208201905061189e6000830184611723565b92915050565b600060ff82169050919050565b6118ba816118a4565b81146118c557600080fd5b50565b6000813590506118d7816118b1565b92915050565b6000819050919050565b6118f0816118dd565b81146118fb57600080fd5b50565b60008135905061190d816118e7565b92915050565b600080600080600060a0868803121561192f5761192e6113c2565b5b600061193d88828901611678565b955050602061194e88828901611678565b945050604061195f888289016118c8565b9350506060611970888289016118fe565b9250506080611981888289016118c8565b9150509295509295909350565b611997816118a4565b82525050565b6119a6816118dd565b82525050565b600060c0820190506119c16000830189611732565b6119ce6020830188611732565b6119db6040830187611732565b6119e8606083018661198e565b6119f5608083018561199d565b611a0260a083018461198e565b979650505050505050565b6000602082019050611a226000830184611732565b92915050565b611a3181611711565b8114611a3c57600080fd5b50565b600081359050611a4e81611a28565b92915050565b600060208284031215611a6a57611a696113c2565b5b6000611a7884828501611a3f565b91505092915050565b60008060408385031215611a9857611a976113c2565b5b6000611aa685828601611a3f565b9250506020611ab7858286016113ed565b9150509250929050565b6000604082019050611ad66000830185611723565b611ae36020830184611723565b9392505050565b600082825260208201905092915050565b7f77726170706572206973206e6f7420636f6e6669677572656400000000000000600082015250565b6000611b31601983611aea565b9150611b3c82611afb565b602082019050919050565b60006020820190508181036000830152611b6081611b24565b9050919050565b7f777261707065722069732064697361626c656400000000000000000000000000600082015250565b6000611b9d601383611aea565b9150611ba882611b67565b602082019050919050565b60006020820190508181036000830152611bcc81611b90565b9050919050565b7f6e756d576f72647320746f6f2068696768000000000000000000000000000000600082015250565b6000611c09601183611aea565b9150611c1482611bd3565b602082019050919050565b60006020820190508181036000830152611c3881611bfc565b9050919050565b7f496e73756666696369656e74207061796d656e7420616d6f756e740000000000600082015250565b6000611c75601b83611aea565b9150611c8082611c3f565b602082019050919050565b60006020820190508181036000830152611ca481611c68565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611ce582611651565b9150611cf083611651565b9250828201905063ffffffff811115611d0c57611d0b611cab565b5b92915050565b611d1b81611786565b82525050565b600060a082019050611d36600083018861199d565b611d43602083018761138e565b611d506040830186611d12565b611d5d6060830185611732565b611d6a6080830184611732565b9695505050505050565b600081519050611d83816113d6565b92915050565b600060208284031215611d9f57611d9e6113c2565b5b6000611dad84828501611d74565b91505092915050565b600081519050611dc581611661565b92915050565b600062ffffff82169050919050565b611de381611dcb565b8114611dee57600080fd5b50565b600081519050611e0081611dda565b92915050565b60008060008060008060008060006101208a8c031215611e2957611e286113c2565b5b6000611e378c828d01611db6565b9950506020611e488c828d01611db6565b9850506040611e598c828d01611db6565b9750506060611e6a8c828d01611db6565b9650506080611e7b8c828d01611db6565b95505060a0611e8c8c828d01611df1565b94505060c0611e9d8c828d01611df1565b93505060e0611eae8c828d01611df1565b925050610100611ec08c828d01611df1565b9150509295985092959850929598565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000611f2c602683611aea565b9150611f3782611ed0565b604082019050919050565b60006020820190508181036000830152611f5b81611f1f565b9050919050565b7f72657175657374206e6f7420666f756e64000000000000000000000000000000600082015250565b6000611f98601183611aea565b9150611fa382611f62565b602082019050919050565b60006020820190508181036000830152611fc781611f8b565b9050919050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b612003816113cc565b82525050565b60006120158383611ffa565b60208301905092915050565b6000602082019050919050565b600061203982611fce565b6120438185611fd9565b935061204e83611fea565b8060005b8381101561207f5781516120668882612009565b975061207183612021565b925050600181019050612052565b5085935050505092915050565b60006040820190506120a160008301856116ba565b81810360208301526120b3818461202e565b90509392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006120f2602083611aea565b91506120fd826120bc565b602082019050919050565b60006020820190508181036000830152612121816120e5565b9050919050565b6000612133826113cc565b915061213e836113cc565b925082820190508082111561215657612155611cab565b5b92915050565b6000612167826113cc565b9150612172836113cc565b9250828202612180816113cc565b9150828204841483151761219757612196611cab565b5b5092915050565b60006121a9826118a4565b91506121b4836118a4565b9250828201905060ff8111156121cd576121cc611cab565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061220d826113cc565b9150612218836113cc565b925082612228576122276121d3565b5b828204905092915050565b600061223e82611651565b915061224983611651565b925082612259576122586121d3565b5b828204905092915050565b600081905092915050565b50565b600061227f600083612264565b915061228a8261226f565b600082019050919050565b60006122a082612272565b915081905091905056fea2646970667358221220030cf753ab60649245258de0393a3010c042590af7aa5c51ecee056d684ffc2564736f6c63430008140033