Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- ErinaceusVRF
- Optimization enabled
- true
- Compiler version
- v0.8.6+commit.11564f7e
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2024-09-13T13:57:57.996940Z
Constructor Arguments
0000000000000000000000005159596d244c97348b9553e06f6aa75492677a09000000000000000000000000bb78efaaaf9223b4840ea7defdc379a13b16399b
Arg [0] (address) : 0x5159596d244c97348b9553e06f6aa75492677a09
Arg [1] (address) : 0xbb78efaaaf9223b4840ea7defdc379a13b16399b
contracts/vrf/ErinaceusVRF.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.6; import {ErinaceusVRFInterface} from "./interfaces/ErinaceusVRFInterface.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {VRFConsumerBaseV2} from "./VRFConsumerBaseV2.sol"; import {IHashHub} from "../interfaces/IHashHub.sol"; import {VRF} from "./VRF.sol"; contract ErinaceusVRF is VRF, Ownable, ErinaceusVRFInterface { IHashHub public HashHub; address public team; uint256 public withdrawableForTeam; uint256 public feePercentage; // We need to maintain a list of consuming addresses. // This bound ensures we are able to loop over them as needed. // Should a user require more consumers, they can use multiple subscriptions. uint16 public constant MAX_CONSUMERS = 100; error InvalidCalldata(); error CantBeAddressZero(); error TooManyConsumers(); error InsufficientBalance(); error InvalidSubscription(); error OnlyCallableFromLink(); error PendingRequestExists(); error PercentageIsNotInRange(); error MustBeSubOwner(address owner); error BlockhashNotInStore(uint256 blocNumber); event FundsRecovered(address to, uint256 amount); error MustBeRequestedOwner(address proposedOwner); error InvalidConsumer(uint64 subId, address consumer); error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen // We use the subscription struct (1 word) // at fulfillment time. struct Subscription { uint256 balance; // Common FTN balance used for all consumer requests. uint256 reqCount; // For fee tiers } // We use the config for the mgmt APIs struct SubscriptionConfig { address owner; // Owner can fund/withdraw/cancel the sub. address requestedOwner; // For safely transferring sub ownership. // Maintains the list of keys in s_consumers. // We do this for 2 reasons: // 1. To be able to clean up all keys from s_consumers when canceling a subscription. // 2. To be able to return the list of all consumers in getSubscription. // Note that we need the s_consumers map to be able to directly check if a // consumer is valid without reading all the consumers from storage. address[] consumers; } // Note a nonce of 0 indicates an the consumer is not assigned to that subscription. mapping(address => mapping(uint64 => uint64)) /* consumer */ /* subId */ /* nonce */ private s_consumers; mapping(uint64 => SubscriptionConfig) /* subId */ /* subscriptionConfig */ public s_subscriptionConfigs; mapping(uint64 => Subscription) /* subId */ /* subscription */ public s_subscriptions; mapping(uint256 => bool) private isRequested; // We make the sub count public so that its possible to // get all the current subscriptions via getSubscription. uint64 private s_currentSubId; // s_totalBalance tracks the total FTN sent to/from // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. // A discrepancy with this contract's FTN balance indicates someone // sent tokens using transfer and so we may need to use recoverFunds. uint256 private s_totalBalance; uint256 public HashHubBalance; uint256 public HashHubReward; event RewardSet(uint256 reward); event HashHubFunded(uint256 oldBalance, uint256 newBalance); event HashHubChanged(address oldHashHub, address newHashHub); event SubscriptionCreated(uint64 indexed subId, address owner); event SubscriptionConsumerAdded(uint64 indexed subId, address consumer); event SubscriptionConsumerRemoved(uint64 indexed subId, address consumer); event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); event SubscriptionOwnerTransferred(uint64 indexed subId, address from, address to); event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); event SubscriptionOwnerTransferRequested(uint64 indexed subId, address from, address to); // Set this maximum to 200 to give us a 56 block window to fulfill // the request before requiring the block hash feeder. uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200; uint32 public constant MAX_NUM_WORDS = 500; // 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; error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max); error GasLimitTooBig(uint32 have, uint32 want); error NumWordsTooBig(uint32 have, uint32 want); error ProvingKeyAlreadyRegistered(bytes32 keyHash); error NoSuchProvingKey(bytes32 keyHash); error InsufficientGasForConsumer(uint256 have, uint256 want); error NoCorrespondingRequest(); error IncorrectCommitment(); error InvalidBlockhash(uint256 blockNum); error PaymentTooLarge(); error Reentrant(); struct RequestCommitment { uint64 blockNum; uint64 subId; uint32 callbackGasLimit; uint32 numWords; address sender; } mapping(bytes32 => address) /* keyHash */ /* oracle */ private s_provingKeys; bytes32[] private s_provingKeyHashes; mapping(address => uint256) /* oracle */ /* FTN balance */ private s_withdrawableTokens; mapping(uint256 => bytes32) /* requestID */ /* commitment */ private s_requestCommitments; event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle); event ProvingKeyDeregistered(bytes32 keyHash, address indexed oracle); event RandomWordsRequested( bytes32 indexed keyHash, uint256 requestId, uint256 preSeed, uint64 indexed subId, uint16 minimumRequestConfirmations, uint32 callbackGasLimit, uint32 numWords, address indexed sender ); event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint256 payment, bool success); struct Config { uint16 minimumRequestConfirmations; uint32 maxGasLimit; // Reentrancy protection. bool reentrancyLock; // Gas to cover oracle payment after we calculate the payment. // We make it configurable in case those operations are repriced. uint32 gasAfterPaymentCalculation; } Config private s_config; FeeConfig private s_feeConfig; struct FeeConfig { // Flat fee charged per fulfillment in millionths of FTN // So fee range is [0, 2^32/10^6]. uint32 fulfillmentFlatFeeFTNPPMTier1; uint32 fulfillmentFlatFeeFTNPPMTier2; uint32 fulfillmentFlatFeeFTNPPMTier3; uint32 fulfillmentFlatFeeFTNPPMTier4; uint32 fulfillmentFlatFeeFTNPPMTier5; uint24 reqsForTier2; uint24 reqsForTier3; uint24 reqsForTier4; uint24 reqsForTier5; } event ConfigSet( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 gasAfterPaymentCalculation, FeeConfig feeConfig ); constructor( address _hashHub, address _owner ){ HashHub = IHashHub(_hashHub); _transferOwnership(_owner); } /** * @notice Registers a proving key to an oracle. * @param oracle address of the oracle * @param publicProvingKey key that oracle can use to submit vrf fulfillments */ function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner { bytes32 kh = hashOfKey(publicProvingKey); if (s_provingKeys[kh] != address(0)) { revert ProvingKeyAlreadyRegistered(kh); } s_provingKeys[kh] = oracle; s_provingKeyHashes.push(kh); emit ProvingKeyRegistered(kh, oracle); } /** * @notice Deregisters a proving key to an oracle. * @param publicProvingKey key that oracle can use to submit vrf fulfillments */ function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { bytes32 kh = hashOfKey(publicProvingKey); address oracle = s_provingKeys[kh]; if (oracle == address(0)) { revert NoSuchProvingKey(kh); } delete s_provingKeys[kh]; for (uint256 i = 0; i < s_provingKeyHashes.length; i++) { if (s_provingKeyHashes[i] == kh) { bytes32 last = s_provingKeyHashes[s_provingKeyHashes.length - 1]; // Copy last element and overwrite kh to be deleted with it s_provingKeyHashes[i] = last; s_provingKeyHashes.pop(); } } emit ProvingKeyDeregistered(kh, oracle); } /** * @notice Returns the proving key hash key associated with this public key * @param publicKey the key to return the hash of */ function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) { return keccak256(abi.encode(publicKey)); } /** * @notice Sets the configuration of the vrfv2 erinaceus * @param minimumRequestConfirmations global min for request confirmations * @param maxGasLimit global max for request gas limit * @param gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement * @param feeConfig fee tier configuration */ function setConfig( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 gasAfterPaymentCalculation, FeeConfig memory feeConfig ) external onlyOwner { if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) { revert InvalidRequestConfirmations( minimumRequestConfirmations, minimumRequestConfirmations, MAX_REQUEST_CONFIRMATIONS ); } s_config = Config({ minimumRequestConfirmations: minimumRequestConfirmations, maxGasLimit: maxGasLimit, gasAfterPaymentCalculation: gasAfterPaymentCalculation, reentrancyLock: false }); s_feeConfig = feeConfig; emit ConfigSet( minimumRequestConfirmations, maxGasLimit, gasAfterPaymentCalculation, s_feeConfig ); } function setRewardForHashHub( uint256 _amount ) external onlyOwner { HashHubReward = _amount; emit RewardSet(_amount); } function setHushHub( address _hashHub ) external onlyOwner { address oldHashHub = address(HashHub); HashHub = IHashHub(_hashHub); emit HashHubChanged(oldHashHub, address(HashHub)); } function setTeam(address _teamAddress, uint256 _feePercentage) external onlyOwner { if(_teamAddress == address(0)){ revert CantBeAddressZero(); } if(_feePercentage > 100){ revert PercentageIsNotInRange(); } team = _teamAddress; feePercentage = _feePercentage; } function getConfig() external view returns ( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 gasAfterPaymentCalculation ) { return ( s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_config.gasAfterPaymentCalculation ); } function getFeeConfig() external view returns ( uint32 fulfillmentFlatFeeFTNPPMTier1, uint32 fulfillmentFlatFeeFTNPPMTier2, uint32 fulfillmentFlatFeeFTNPPMTier3, uint32 fulfillmentFlatFeeFTNPPMTier4, uint32 fulfillmentFlatFeeFTNPPMTier5, uint24 reqsForTier2, uint24 reqsForTier3, uint24 reqsForTier4, uint24 reqsForTier5 ) { return ( s_feeConfig.fulfillmentFlatFeeFTNPPMTier1, s_feeConfig.fulfillmentFlatFeeFTNPPMTier2, s_feeConfig.fulfillmentFlatFeeFTNPPMTier3, s_feeConfig.fulfillmentFlatFeeFTNPPMTier4, s_feeConfig.fulfillmentFlatFeeFTNPPMTier5, s_feeConfig.reqsForTier2, s_feeConfig.reqsForTier3, s_feeConfig.reqsForTier4, s_feeConfig.reqsForTier5 ); } function getTotalBalance() external view returns (uint256) { return s_totalBalance; } function getWithdrawableAmount() external view returns(uint256) { return s_withdrawableTokens[msg.sender]; } /** * @notice Owner cancel subscription, sends remaining link directly to the subscription owner. * @param subId subscription id * @dev notably can be called even if there are pending requests, outstanding ones may fail onchain */ function ownerCancelSubscription(uint64 subId) external onlyOwner { if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } _cancelSubscriptionHelper(subId, s_subscriptionConfigs[subId].owner); } /** * @notice Recover link sent with transfer instead of transferAndCall. * @param to address to send link to */ function recoverFunds(address to) external onlyOwner { uint256 externalBalance = (address(this)).balance - HashHubBalance; uint256 internalBalance = uint256(s_totalBalance); if (internalBalance > externalBalance) { revert BalanceInvariantViolated(internalBalance, externalBalance); } if (internalBalance < externalBalance) { uint256 amount = externalBalance - internalBalance; // FTN.transfer(to, amount); _sendViaCall(payable(to), amount); emit FundsRecovered(to, amount); } // If the balances are equal, nothing to be done. } /** * @inheritdoc ErinaceusVRFInterface */ function getRequestConfig() external view override returns (uint16, uint32, bytes32[] memory) { return (s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_provingKeyHashes); } /** * @inheritdoc ErinaceusVRFInterface */ function requestRandomWords( bytes32 keyHash, uint64 subId, uint16 requestConfirmations, uint32 callbackGasLimit, uint32 numWords ) external override nonReentrant returns (uint256) { // Input validation using the subscription storage. if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } // Its important to ensure that the consumer is in fact who they say they // are, otherwise they could use someone else's subscription balance. // A nonce of 0 indicates consumer is not allocated to the sub. uint64 currentNonce = s_consumers[msg.sender][subId]; if (currentNonce == 0) { revert InvalidConsumer(subId, msg.sender); } // Input validation using the config storage word. if ( requestConfirmations < s_config.minimumRequestConfirmations || requestConfirmations > MAX_REQUEST_CONFIRMATIONS ) { revert InvalidRequestConfirmations( requestConfirmations, s_config.minimumRequestConfirmations, MAX_REQUEST_CONFIRMATIONS ); } // No lower bound on the requested gas limit. A user could request 0 // and they would simply be billed for the proof verification and wouldn't be // able to do anything with the random value. if (callbackGasLimit > s_config.maxGasLimit) { revert GasLimitTooBig(callbackGasLimit, s_config.maxGasLimit); } if (numWords > MAX_NUM_WORDS) { revert NumWordsTooBig(numWords, MAX_NUM_WORDS); } // Note we do not check whether the keyHash is valid to save gas. // The consequence for users is that they can send requests // for invalid keyHashes which will simply not be fulfilled. uint64 nonce = currentNonce + 1; (uint256 requestId, uint256 preSeed) = _computeRequestId(keyHash, msg.sender, subId, nonce); s_requestCommitments[requestId] = keccak256( abi.encode(requestId, block.number, subId, callbackGasLimit, numWords, msg.sender) ); if(!isRequested[block.number]){ if(HashHubBalance >= HashHubReward && HashHubBalance > 0){ HashHub.requestBlockhash{value: HashHubReward}(block.number); HashHubBalance -= HashHubReward; isRequested[block.number] = true; } } emit RandomWordsRequested( keyHash, requestId, preSeed, subId, requestConfirmations, callbackGasLimit, numWords, msg.sender ); s_consumers[msg.sender][subId] = nonce; return requestId; } /** * @notice Get request commitment * @param requestId id of request * @dev used to determine if a request is fulfilled or not */ function getCommitment(uint256 requestId) external view returns (bytes32) { return s_requestCommitments[requestId]; } function _computeRequestId( bytes32 keyHash, address sender, uint64 subId, uint64 nonce ) private pure returns (uint256, uint256) { uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); } /** * @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; } function _getRandomnessFromProof( Proof memory proof, RequestCommitment memory rc ) private view returns (bytes32 keyHash, uint256 requestId, uint256 randomness) { keyHash = hashOfKey(proof.pk); // Only registered proving keys are permitted. address oracle = s_provingKeys[keyHash]; if (oracle == address(0)) { revert NoSuchProvingKey(keyHash); } requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); bytes32 commitment = s_requestCommitments[requestId]; if (commitment == 0) { revert NoCorrespondingRequest(); } if ( commitment != keccak256(abi.encode(requestId, rc.blockNum, rc.subId, rc.callbackGasLimit, rc.numWords, rc.sender)) ) { revert IncorrectCommitment(); } bytes32 blockHash = blockhash(rc.blockNum); if (blockHash == bytes32(0)) { blockHash = HashHub.getBlockhash(rc.blockNum); if (blockHash == bytes32(0)) { revert BlockhashNotInStore(rc.blockNum); } } // The seed actually used by the VRF machinery, mixing in the blockhash uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash))); randomness = VRF._randomValueFromVRFProof(proof, actualSeed); // Reverts on failure return (keyHash, requestId, randomness); } /* * @notice Compute fee based on the request count * @param reqCount number of requests * @return feePPM fee in FTN PPM */ function getFeeTier(uint256 reqCount) public view returns (uint32) { FeeConfig memory fc = s_feeConfig; if (0 <= reqCount && reqCount <= fc.reqsForTier2) { return fc.fulfillmentFlatFeeFTNPPMTier1; } if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) { return fc.fulfillmentFlatFeeFTNPPMTier2; } if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) { return fc.fulfillmentFlatFeeFTNPPMTier3; } if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) { return fc.fulfillmentFlatFeeFTNPPMTier4; } return fc.fulfillmentFlatFeeFTNPPMTier5; } /* * @notice Fulfill a randomness request * @param proof contains the proof and randomness * @param rc request commitment pre-image, committed to at request time * @return payment amount billed to the subscription * @dev simulated offchain to determine if sufficient balance is present to fulfill the request */ function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant returns (uint256) { uint256 startGas = gasleft(); (bytes32 keyHash, uint256 requestId, uint256 randomness) = _getRandomnessFromProof(proof, rc); // TEST uint256[] memory randomWords = new uint256[](rc.numWords); for (uint256 i = 0; i < rc.numWords; i++) { randomWords[i] = uint256(keccak256(abi.encode(randomness, i))); } delete s_requestCommitments[requestId]; VRFConsumerBaseV2 v; bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, requestId, randomWords); // Call with explicitly the amount of callback gas requested // Important to not let them exhaust the gas budget and avoid oracle payment. // Do not allow any non-view/non-pure erinaceus functions to be called // during the consumers callback code via reentrancyLock. // Note that _callWithExactGas will revert if we do not have sufficient gas // to give the callee their requested amount. s_config.reentrancyLock = true; bool success = _callWithExactGas(rc.callbackGasLimit, rc.sender, resp); s_config.reentrancyLock = false; // Increment the req count for fee tier selection. uint256 reqCount = s_subscriptions[rc.subId].reqCount; s_subscriptions[rc.subId].reqCount += 1; // We want to charge users exactly for how much gas they use in their callback. // The gasAfterPaymentCalculation is meant to cover these additional operations where we // decrement the subscription balance and increment the oracles withdrawable balance. // We also add the flat FTN fee to the payment amount. // Its specified in millionths of FTN, if s_config.fulfillmentFlatFeeFTNPPM = 1 // 1 FTN / 1e6 = 1e18 juels / 1e6 = 1e12 juels. uint256 payment = _calculatePaymentAmount( startGas, s_config.gasAfterPaymentCalculation, getFeeTier(reqCount), tx.gasprice ); if (s_subscriptions[rc.subId].balance < payment) { revert InsufficientBalance(); } s_subscriptions[rc.subId].balance -= payment; s_withdrawableTokens[s_provingKeys[keyHash]] += payment * (100 - feePercentage) / 100; withdrawableForTeam += payment * feePercentage / 100; // Include payment in the event for tracking costs. emit RandomWordsFulfilled(requestId, randomness, payment, success); return payment; } // Get the amount of gas used for fulfillment function _calculatePaymentAmount( uint256 startGas, uint256 gasAfterPaymentCalculation, uint32 fulfillmentFlatFeeFTNPPM, uint256 weiPerUnitGas ) internal view returns (uint256) { //(juels/link) (wei/gas * gas) = juels uint256 paymentNoFee = weiPerUnitGas * (gasAfterPaymentCalculation + startGas - gasleft()); uint256 fee = 1e12 * uint256(fulfillmentFlatFeeFTNPPM); return uint96(paymentNoFee + fee); } /* * @notice Oracle withdraw FTN earned through fulfilling requests * @param recipient where to send the funds * @param amount amount to withdraw */ function oracleWithdraw(address recipient, uint96 amount) external nonReentrant { if (s_withdrawableTokens[msg.sender] < amount) { revert InsufficientBalance(); } s_withdrawableTokens[msg.sender] -= amount; s_totalBalance -= amount; // if (!FTN.transfer(recipient, amount)) { // revert InsufficientBalance(); // } _sendViaCall(payable(recipient), amount); } function withdrawForTeam(uint256 amount) external nonReentrant{ if (withdrawableForTeam < amount) { revert InsufficientBalance(); } withdrawableForTeam -= amount; s_totalBalance -= amount; // if (!FTN.transfer(team, amount)) { // revert InsufficientBalance(); // } _sendViaCall(payable(team), amount); } function fundSubscribtion(uint64 subId) external payable nonReentrant { if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } // We do not check that the msg.sender is the subscription owner, // anyone can fund a subscription. uint256 oldBalance = s_subscriptions[subId].balance; s_subscriptions[subId].balance += uint96(msg.value); s_totalBalance += uint96(msg.value); // FTN.transferFrom(msg.sender, address(this), amount); emit SubscriptionFunded(subId, oldBalance, oldBalance + msg.value); } function fundHashHub() external payable nonReentrant{ // FTN.transferFrom(msg.sender, address(this), amount); uint256 oldBalance = HashHubBalance; HashHubBalance += msg.value; emit HashHubFunded(oldBalance, HashHubBalance); } function getCurrentSubId() external view returns (uint64) { return s_currentSubId; } /** * @inheritdoc ErinaceusVRFInterface */ function getSubscription( uint64 subId ) external view override returns (uint256 balance, uint256 reqCount, address owner, address[] memory consumers) { if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } return ( s_subscriptions[subId].balance, s_subscriptions[subId].reqCount, s_subscriptionConfigs[subId].owner, s_subscriptionConfigs[subId].consumers ); } /** * @inheritdoc ErinaceusVRFInterface */ function createSubscription() external override nonReentrant returns (uint64) { s_currentSubId++; uint64 currentSubId = s_currentSubId; address[] memory consumers = new address[](0); s_subscriptions[currentSubId] = Subscription({balance: 0, reqCount: 0}); s_subscriptionConfigs[currentSubId] = SubscriptionConfig({ owner: msg.sender, requestedOwner: address(0), consumers: consumers }); emit SubscriptionCreated(currentSubId, msg.sender); return currentSubId; } /** * @inheritdoc ErinaceusVRFInterface */ function requestSubscriptionOwnerTransfer( uint64 subId, address newOwner ) external override onlySubOwner(subId) nonReentrant { // Proposing to address(0) would never be claimable so don't need to check. if (s_subscriptionConfigs[subId].requestedOwner != newOwner) { s_subscriptionConfigs[subId].requestedOwner = newOwner; emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner); } } /** * @inheritdoc ErinaceusVRFInterface */ function acceptSubscriptionOwnerTransfer(uint64 subId) external override nonReentrant { if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } if (s_subscriptionConfigs[subId].requestedOwner != msg.sender) { revert MustBeRequestedOwner(s_subscriptionConfigs[subId].requestedOwner); } address oldOwner = s_subscriptionConfigs[subId].owner; s_subscriptionConfigs[subId].owner = msg.sender; s_subscriptionConfigs[subId].requestedOwner = address(0); emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender); } /** * @inheritdoc ErinaceusVRFInterface */ function removeConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { if (pendingRequestExists(subId)) { revert PendingRequestExists(); } if (s_consumers[consumer][subId] == 0) { revert InvalidConsumer(subId, consumer); } // Note bounded by MAX_CONSUMERS address[] memory consumers = s_subscriptionConfigs[subId].consumers; uint256 lastConsumerIndex = consumers.length - 1; for (uint256 i = 0; i < consumers.length; i++) { if (consumers[i] == consumer) { address last = consumers[lastConsumerIndex]; // Storage write to preserve last element s_subscriptionConfigs[subId].consumers[i] = last; // Storage remove last element s_subscriptionConfigs[subId].consumers.pop(); break; } } delete s_consumers[consumer][subId]; emit SubscriptionConsumerRemoved(subId, consumer); } /** * @inheritdoc ErinaceusVRFInterface */ function addConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant { // Already maxed, cannot add any more consumers. if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) { revert TooManyConsumers(); } if (s_consumers[consumer][subId] != 0) { // Idempotence - do nothing if already added. // Ensures uniqueness in s_subscriptions[subId].consumers. return; } // Initialize the nonce to 1, indicating the consumer is allocated. s_consumers[consumer][subId] = 1; s_subscriptionConfigs[subId].consumers.push(consumer); emit SubscriptionConsumerAdded(subId, consumer); } /** * @inheritdoc ErinaceusVRFInterface */ function cancelSubscription(uint64 subId, address to) external override onlySubOwner(subId) nonReentrant { if (pendingRequestExists(subId)) { revert PendingRequestExists(); } _cancelSubscriptionHelper(subId, to); } function _cancelSubscriptionHelper(uint64 subId, address to) private nonReentrant { SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; Subscription memory sub = s_subscriptions[subId]; uint256 balance = sub.balance; // Note bounded by MAX_CONSUMERS; // If no consumers, does nothing. for (uint256 i = 0; i < subConfig.consumers.length; i++) { delete s_consumers[subConfig.consumers[i]][subId]; } delete s_subscriptionConfigs[subId]; delete s_subscriptions[subId]; s_totalBalance -= balance; // if (!FTN.transfer(to, uint256(balance))) { // revert InsufficientBalance(); // } _sendViaCall(payable(to), uint256(balance)); emit SubscriptionCanceled(subId, to, balance); } /** * @inheritdoc ErinaceusVRFInterface * @dev Looping is bounded to MAX_CONSUMERS*(number of keyhashes). * @dev Used to disable subscription canceling while outstanding request are present. */ function pendingRequestExists(uint64 subId) public view override returns (bool) { SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; for (uint256 i = 0; i < subConfig.consumers.length; i++) { for (uint256 j = 0; j < s_provingKeyHashes.length; j++) { (uint256 reqId, ) = _computeRequestId( s_provingKeyHashes[j], subConfig.consumers[i], subId, s_consumers[subConfig.consumers[i]][subId] ); if (s_requestCommitments[reqId] != 0) { return true; } } } return false; } /** * @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(); } } modifier onlySubOwner(uint64 subId) { address owner = s_subscriptionConfigs[subId].owner; if (owner == address(0)) { revert InvalidSubscription(); } if (msg.sender != owner) { revert MustBeSubOwner(owner); } _; } modifier nonReentrant() { if (s_config.reentrancyLock) { revert Reentrant(); } _; } }
@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/interfaces/IHashHub.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.6; /** * @title HashHub * @notice This contract provides a way to access blockhashes older than * the 256 block limit imposed by the BLOCKHASH opcode. * You may assume that any blockhash stored by the contract is correct. * Note that the contract depends on the format of serialized Ethereum * blocks. If a future hardfork of Ethereum changes that format, the * logic in this contract may become incorrect and an updated version * would have to be deployed. */ interface IHashHub { function store(uint256 n) external; function requestBlockhash(uint256 _blockNumber) external payable; function claimRewardsForUnregisteredBlocks(uint256[] memory _blocks) external; function getBlockhash(uint256 n) external view returns (bytes32); function rewardForStoring(uint256) external view returns(uint256); function usersRequestedBlocks(address) external view returns(uint256[] memory); function providedRewards(address, uint256) external view returns(uint256); }
contracts/vrf/VRF.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** **************************************************************************** * @notice Verification of verifiable-random-function (VRF) proofs, following * @notice https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 * @notice See https://eprint.iacr.org/2017/099.pdf for security proofs. * @dev Bibliographic references: * @dev Goldberg, et al., "Verifiable Random Functions (VRFs)", Internet Draft * @dev draft-irtf-cfrg-vrf-05, IETF, Aug 11 2019, * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05 * @dev Papadopoulos, et al., "Making NSEC5 Practical for DNSSEC", Cryptology * @dev ePrint Archive, Report 2017/099, https://eprint.iacr.org/2017/099.pdf * **************************************************************************** * @dev USAGE * @dev The main entry point is _randomValueFromVRFProof. See its docstring. * **************************************************************************** * @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 computationally indistinguishable to her from a uniform * @dev random sample from the output space. * @dev The purpose of this contract is to perform that verification. * **************************************************************************** * @dev DESIGN NOTES * @dev The VRF algorithm verified here satisfies the full uniqueness, full * @dev collision resistance, and full pseudo-randomness security properties. * @dev See "SECURITY PROPERTIES" below, and * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-3 * @dev An elliptic curve point is generally represented in the solidity code * @dev as a uint256[2], corresponding to its affine coordinates in * @dev GF(FIELD_SIZE). * @dev For the sake of efficiency, this implementation deviates from the spec * @dev in some minor ways: * @dev - Keccak hash rather than the SHA256 hash recommended in * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 * @dev Keccak costs much less gas on the EVM, and provides similar security. * @dev - Secp256k1 curve instead of the P-256 or ED25519 curves recommended in * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 * @dev For curve-point multiplication, it's much cheaper to abuse ECRECOVER * @dev - _hashToCurve recursively hashes until it finds a curve x-ordinate. On * @dev the EVM, this is slightly more efficient than the recommendation in * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 * @dev step 5, to concatenate with a nonce then hash, and rehash with the * @dev nonce updated until a valid x-ordinate is found. * @dev - _hashToCurve does not include a cipher version string or the byte 0x1 * @dev in the hash message, as recommended in step 5.B of the draft * @dev standard. They are unnecessary here because no variation in the * @dev cipher suite is allowed. * @dev - Similarly, the hash input in _scalarFromCurvePoints does not include a * @dev commitment to the cipher suite, either, which differs from step 2 of * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 * @dev . Also, the hash input is the concatenation of the uncompressed * @dev points, not the compressed points as recommended in step 3. * @dev - In the calculation of the challenge value "c", the "u" value (i.e. * @dev the value computed by Reggie as the nonce times the secp256k1 * @dev generator point, see steps 5 and 7 of * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 * @dev ) is replaced by its ethereum address, i.e. the lower 160 bits of the * @dev keccak hash of the original u. This is because we only verify the * @dev calculation of u up to its address, by abusing ECRECOVER. * **************************************************************************** * @dev SECURITY PROPERTIES * @dev Here are the security properties for this VRF: * @dev Full uniqueness: For any seed and valid VRF public key, there is * @dev exactly one VRF output which can be proved to come from that seed, in * @dev the sense that the proof will pass _verifyVRFProof. * @dev Full collision resistance: It's cryptographically infeasible to find * @dev two seeds with same VRF output from a fixed, valid VRF key * @dev Full pseudorandomness: Absent the proofs that the VRF outputs are * @dev derived from a given seed, the outputs are computationally * @dev indistinguishable from randomness. * @dev https://eprint.iacr.org/2017/099.pdf, Appendix B contains the proofs * @dev for these properties. * @dev For secp256k1, the key validation described in section * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.6 * @dev is unnecessary, because secp256k1 has cofactor 1, and the * @dev representation of the public key used here (affine x- and y-ordinates * @dev of the secp256k1 point on the standard y^2=x^3+7 curve) cannot refer to * @dev the point at infinity. * **************************************************************************** * @dev OTHER SECURITY CONSIDERATIONS * * @dev The seed input to the VRF could in principle force an arbitrary amount * @dev of work in _hashToCurve, by requiring extra rounds of hashing and * @dev checking whether that's yielded the x ordinate of a secp256k1 point. * @dev However, under the Random Oracle Model the probability of choosing a * @dev point which forces n extra rounds in _hashToCurve is 2⁻ⁿ. The base cost * @dev for calling _hashToCurve is about 25,000 gas, and each round of checking * @dev for a valid x ordinate costs about 15,555 gas, so to find a seed for * @dev which _hashToCurve would cost more than 2,017,000 gas, one would have to * @dev try, in expectation, about 2¹²⁸ seeds, which is infeasible for any * @dev foreseeable computational resources. (25,000 + 128 * 15,555 < 2,017,000.) * @dev Since the gas block limit for the Ethereum main net is 10,000,000 gas, * @dev this means it is infeasible for an adversary to prevent correct * @dev operation of this contract by choosing an adverse seed. * @dev (See TestMeasureHashToCurveGasCost for verification of the gas cost for * @dev _hashToCurve.) * @dev It may be possible to make a secure constant-time _hashToCurve function. * @dev See notes in _hashToCurve docstring. */ contract VRF { // See https://www.secg.org/sec2-v2.pdf, section 2.4.1, for these constants. // Number of points in Secp256k1 uint256 private constant GROUP_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; // Prime characteristic of the galois field over which Secp256k1 is defined uint256 private constant FIELD_SIZE = // solium-disable-next-line indentation 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; uint256 private constant WORD_LENGTH_BYTES = 0x20; // (base^exponent) % FIELD_SIZE // Cribbed from https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 function _bigModExp(uint256 base, uint256 exponent) internal view returns (uint256 exponentiation) { uint256 callResult; uint256[6] memory bigModExpContractInputs; bigModExpContractInputs[0] = WORD_LENGTH_BYTES; // Length of base bigModExpContractInputs[1] = WORD_LENGTH_BYTES; // Length of exponent bigModExpContractInputs[2] = WORD_LENGTH_BYTES; // Length of modulus bigModExpContractInputs[3] = base; bigModExpContractInputs[4] = exponent; bigModExpContractInputs[5] = FIELD_SIZE; uint256[1] memory output; assembly { callResult := staticcall( not(0), // Gas cost: no limit 0x05, // Bigmodexp contract address bigModExpContractInputs, 0xc0, // Length of input segment: 6*0x20-bytes output, 0x20 // Length of output segment ) } if (callResult == 0) { // solhint-disable-next-line custom-errors revert("bigModExp failure!"); } return output[0]; } // Let q=FIELD_SIZE. q % 4 = 3, ∴ x≡r^2 mod q ⇒ x^SQRT_POWER≡±r mod q. See // https://en.wikipedia.org/wiki/Modular_square_root#Prime_or_prime_power_modulus uint256 private constant SQRT_POWER = (FIELD_SIZE + 1) >> 2; // Computes a s.t. a^2 = x in the field. Assumes a exists function _squareRoot(uint256 x) internal view returns (uint256) { return _bigModExp(x, SQRT_POWER); } // The value of y^2 given that (x,y) is on secp256k1. function _ySquared(uint256 x) internal pure returns (uint256) { // Curve is y^2=x^3+7. See section 2.4.1 of https://www.secg.org/sec2-v2.pdf uint256 xCubed = mulmod(x, mulmod(x, x, FIELD_SIZE), FIELD_SIZE); return addmod(xCubed, 7, FIELD_SIZE); } // True iff p is on secp256k1 function _isOnCurve(uint256[2] memory p) internal pure returns (bool) { // Section 2.3.6. in https://www.secg.org/sec1-v2.pdf // requires each ordinate to be in [0, ..., FIELD_SIZE-1] // solhint-disable-next-line custom-errors require(p[0] < FIELD_SIZE, "invalid x-ordinate"); // solhint-disable-next-line custom-errors require(p[1] < FIELD_SIZE, "invalid y-ordinate"); return _ySquared(p[0]) == mulmod(p[1], p[1], FIELD_SIZE); } // Hash x uniformly into {0, ..., FIELD_SIZE-1}. function _fieldHash(bytes memory b) internal pure returns (uint256 x_) { x_ = uint256(keccak256(b)); // Rejecting if x >= FIELD_SIZE corresponds to step 2.1 in section 2.3.4 of // http://www.secg.org/sec1-v2.pdf , which is part of the definition of // string_to_point in the IETF draft while (x_ >= FIELD_SIZE) { x_ = uint256(keccak256(abi.encodePacked(x_))); } return x_; } // Hash b to a random point which hopefully lies on secp256k1. The y ordinate // is always even, due to // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 // step 5.C, which references arbitrary_string_to_point, defined in // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 as // returning the point with given x ordinate, and even y ordinate. function _newCandidateSecp256k1Point(bytes memory b) internal view returns (uint256[2] memory p) { unchecked { p[0] = _fieldHash(b); p[1] = _squareRoot(_ySquared(p[0])); if (p[1] % 2 == 1) { // Note that 0 <= p[1] < FIELD_SIZE // so this cannot wrap, we use unchecked to save gas. p[1] = FIELD_SIZE - p[1]; } } return p; } // Domain-separation tag for initial hash in _hashToCurve. Corresponds to // vrf.go/hashToCurveHashPrefix uint256 internal constant HASH_TO_CURVE_HASH_PREFIX = 1; // Cryptographic hash function onto the curve. // // Corresponds to algorithm in section 5.4.1.1 of the draft standard. (But see // DESIGN NOTES above for slight differences.) // // TODO(alx): Implement a bounded-computation hash-to-curve, as described in // "Construction of Rational Points on Elliptic Curves over Finite Fields" // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.831.5299&rep=rep1&type=pdf // and suggested by // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-01#section-5.2.2 // (Though we can't used exactly that because secp256k1's j-invariant is 0.) // // This would greatly simplify the analysis in "OTHER SECURITY CONSIDERATIONS" // https://www.pivotaltracker.com/story/show/171120900 function _hashToCurve(uint256[2] memory pk, uint256 input) internal view returns (uint256[2] memory rv) { rv = _newCandidateSecp256k1Point(abi.encodePacked(HASH_TO_CURVE_HASH_PREFIX, pk, input)); while (!_isOnCurve(rv)) { rv = _newCandidateSecp256k1Point(abi.encodePacked(rv[0])); } return rv; } /** ********************************************************************* * @notice Check that product==scalar*multiplicand * * @dev Based on Vitalik Buterin's idea in ethresear.ch post cited below. * * @param multiplicand: secp256k1 point * @param scalar: non-zero GF(GROUP_ORDER) scalar * @param product: secp256k1 expected to be multiplier * multiplicand * @return verifies true iff product==scalar*multiplicand, with cryptographically high probability */ function _ecmulVerify( uint256[2] memory multiplicand, uint256 scalar, uint256[2] memory product ) internal pure returns (bool verifies) { // solhint-disable-next-line custom-errors require(scalar != 0, "zero scalar"); // Rules out an ecrecover failure case uint256 x = multiplicand[0]; // x ordinate of multiplicand uint8 v = multiplicand[1] % 2 == 0 ? 27 : 28; // parity of y ordinate // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 // Point corresponding to address ecrecover(0, v, x, s=scalar*x) is // (x⁻¹ mod GROUP_ORDER) * (scalar * x * multiplicand - 0 * g), i.e. // scalar*multiplicand. See https://crypto.stackexchange.com/a/18106 bytes32 scalarTimesX = bytes32(mulmod(scalar, x, GROUP_ORDER)); address actual = ecrecover(bytes32(0), v, bytes32(x), scalarTimesX); // Explicit conversion to address takes bottom 160 bits address expected = address(uint160(uint256(keccak256(abi.encodePacked(product))))); return (actual == expected); } // Returns x1/z1-x2/z2=(x1z2-x2z1)/(z1z2) in projective coordinates on P¹(𝔽ₙ) function _projectiveSub( uint256 x1, uint256 z1, uint256 x2, uint256 z2 ) internal pure returns (uint256 x3, uint256 z3) { unchecked { uint256 num1 = mulmod(z2, x1, FIELD_SIZE); // Note this cannot wrap since x2 is a point in [0, FIELD_SIZE-1] // we use unchecked to save gas. uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE); (x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); } return (x3, z3); } // Returns x1/z1*x2/z2=(x1x2)/(z1z2), in projective coordinates on P¹(𝔽ₙ) function _projectiveMul( uint256 x1, uint256 z1, uint256 x2, uint256 z2 ) internal pure returns (uint256 x3, uint256 z3) { (x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); return (x3, z3); } /** ************************************************************************** @notice Computes elliptic-curve sum, in projective co-ordinates @dev Using projective coordinates avoids costly divisions @dev To use this with p and q in affine coordinates, call @dev _projectiveECAdd(px, py, qx, qy). This will return @dev the addition of (px, py, 1) and (qx, qy, 1), in the @dev secp256k1 group. @dev This can be used to calculate the z which is the inverse to zInv @dev in isValidVRFOutput. But consider using a faster @dev re-implementation such as ProjectiveECAdd in the golang vrf package. @dev This function assumes [px,py,1],[qx,qy,1] are valid projective coordinates of secp256k1 points. That is safe in this contract, because this method is only used by _linearCombination, which checks points are on the curve via ecrecover. ************************************************************************** @param px The first affine coordinate of the first summand @param py The second affine coordinate of the first summand @param qx The first affine coordinate of the second summand @param qy The second affine coordinate of the second summand (px,py) and (qx,qy) must be distinct, valid secp256k1 points. ************************************************************************** Return values are projective coordinates of [px,py,1]+[qx,qy,1] as points on secp256k1, in P²(𝔽ₙ) @return sx @return sy @return sz */ function _projectiveECAdd( uint256 px, uint256 py, uint256 qx, uint256 qy ) internal pure returns (uint256 sx, uint256 sy, uint256 sz) { unchecked { // See "Group law for E/K : y^2 = x^3 + ax + b", in section 3.1.2, p. 80, // "Guide to Elliptic Curve Cryptography" by Hankerson, Menezes and Vanstone // We take the equations there for (sx,sy), and homogenize them to // projective coordinates. That way, no inverses are required, here, and we // only need the one inverse in _affineECAdd. // We only need the "point addition" equations from Hankerson et al. Can // skip the "point doubling" equations because p1 == p2 is cryptographically // impossible, and required not to be the case in _linearCombination. // Add extra "projective coordinate" to the two points (uint256 z1, uint256 z2) = (1, 1); // (lx, lz) = (qy-py)/(qx-px), i.e., gradient of secant line. // Cannot wrap since px and py are in [0, FIELD_SIZE-1] uint256 lx = addmod(qy, FIELD_SIZE - py, FIELD_SIZE); uint256 lz = addmod(qx, FIELD_SIZE - px, FIELD_SIZE); uint256 dx; // Accumulates denominator from sx calculation // sx=((qy-py)/(qx-px))^2-px-qx (sx, dx) = _projectiveMul(lx, lz, lx, lz); // ((qy-py)/(qx-px))^2 (sx, dx) = _projectiveSub(sx, dx, px, z1); // ((qy-py)/(qx-px))^2-px (sx, dx) = _projectiveSub(sx, dx, qx, z2); // ((qy-py)/(qx-px))^2-px-qx uint256 dy; // Accumulates denominator from sy calculation // sy=((qy-py)/(qx-px))(px-sx)-py (sy, dy) = _projectiveSub(px, z1, sx, dx); // px-sx (sy, dy) = _projectiveMul(sy, dy, lx, lz); // ((qy-py)/(qx-px))(px-sx) (sy, dy) = _projectiveSub(sy, dy, py, z1); // ((qy-py)/(qx-px))(px-sx)-py if (dx != dy) { // Cross-multiply to put everything over a common denominator sx = mulmod(sx, dy, FIELD_SIZE); sy = mulmod(sy, dx, FIELD_SIZE); sz = mulmod(dx, dy, FIELD_SIZE); } else { // Already over a common denominator, use that for z ordinate sz = dx; } } return (sx, sy, sz); } // p1+p2, as affine points on secp256k1. // // invZ must be the inverse of the z returned by _projectiveECAdd(p1, p2). // It is computed off-chain to save gas. // // p1 and p2 must be distinct, because _projectiveECAdd doesn't handle // point doubling. function _affineECAdd( uint256[2] memory p1, uint256[2] memory p2, uint256 invZ ) internal pure returns (uint256[2] memory) { uint256 x; uint256 y; uint256 z; (x, y, z) = _projectiveECAdd(p1[0], p1[1], p2[0], p2[1]); // solhint-disable-next-line custom-errors require(mulmod(z, invZ, FIELD_SIZE) == 1, "invZ must be inverse of z"); // Clear the z ordinate of the projective representation by dividing through // by it, to obtain the affine representation return [mulmod(x, invZ, FIELD_SIZE), mulmod(y, invZ, FIELD_SIZE)]; } // True iff address(c*p+s*g) == lcWitness, where g is generator. (With // cryptographically high probability.) function _verifyLinearCombinationWithGenerator( uint256 c, uint256[2] memory p, uint256 s, address lcWitness ) internal pure returns (bool) { // Rule out ecrecover failure modes which return address 0. unchecked { // solhint-disable-next-line custom-errors require(lcWitness != address(0), "bad witness"); uint8 v = (p[1] % 2 == 0) ? 27 : 28; // parity of y-ordinate of p // Note this cannot wrap (X - Y % X), but we use unchecked to save // gas. bytes32 pseudoHash = bytes32(GROUP_ORDER - mulmod(p[0], s, GROUP_ORDER)); // -s*p[0] bytes32 pseudoSignature = bytes32(mulmod(c, p[0], GROUP_ORDER)); // c*p[0] // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 // The point corresponding to the address returned by // ecrecover(-s*p[0],v,p[0],c*p[0]) is // (p[0]⁻¹ mod GROUP_ORDER)*(c*p[0]-(-s)*p[0]*g)=c*p+s*g. // See https://crypto.stackexchange.com/a/18106 // https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v address computed = ecrecover(pseudoHash, v, bytes32(p[0]), pseudoSignature); return computed == lcWitness; } } // c*p1 + s*p2. Requires cp1Witness=c*p1 and sp2Witness=s*p2. Also // requires cp1Witness != sp2Witness (which is fine for this application, // since it is cryptographically impossible for them to be equal. In the // (cryptographically impossible) case that a prover accidentally derives // a proof with equal c*p1 and s*p2, they should retry with a different // proof nonce.) Assumes that all points are on secp256k1 // (which is checked in _verifyVRFProof below.) function _linearCombination( uint256 c, uint256[2] memory p1, uint256[2] memory cp1Witness, uint256 s, uint256[2] memory p2, uint256[2] memory sp2Witness, uint256 zInv ) internal pure returns (uint256[2] memory) { unchecked { // Note we are relying on the wrap around here // solhint-disable-next-line custom-errors require((cp1Witness[0] % FIELD_SIZE) != (sp2Witness[0] % FIELD_SIZE), "points in sum must be distinct"); // solhint-disable-next-line custom-errors require(_ecmulVerify(p1, c, cp1Witness), "First mul check failed"); // solhint-disable-next-line custom-errors require(_ecmulVerify(p2, s, sp2Witness), "Second mul check failed"); return _affineECAdd(cp1Witness, sp2Witness, zInv); } } // Domain-separation tag for the hash taken in _scalarFromCurvePoints. // Corresponds to scalarFromCurveHashPrefix in vrf.go uint256 internal constant SCALAR_FROM_CURVE_POINTS_HASH_PREFIX = 2; // Pseudo-random number from inputs. Matches vrf.go/_scalarFromCurvePoints, and // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 // The draft calls (in step 7, via the definition of string_to_int, in // https://datatracker.ietf.org/doc/html/rfc8017#section-4.2 ) for taking the // first hash without checking that it corresponds to a number less than the // group order, which will lead to a slight bias in the sample. // // TODO(alx): We could save a bit of gas by following the standard here and // using the compressed representation of the points, if we collated the y // parities into a single bytes32. // https://www.pivotaltracker.com/story/show/171120588 function _scalarFromCurvePoints( uint256[2] memory hash, uint256[2] memory pk, uint256[2] memory gamma, address uWitness, uint256[2] memory v ) internal pure returns (uint256 s) { return uint256(keccak256(abi.encodePacked(SCALAR_FROM_CURVE_POINTS_HASH_PREFIX, hash, pk, gamma, v, uWitness))); } // True if (gamma, c, s) is a correctly constructed randomness proof from pk // and seed. zInv must be the inverse of the third ordinate from // _projectiveECAdd applied to cGammaWitness and sHashWitness. Corresponds to // section 5.3 of the IETF draft. // // TODO(alx): Since I'm only using pk in the ecrecover call, I could only pass // the x ordinate, and the parity of the y ordinate in the top bit of uWitness // (which I could make a uint256 without using any extra space.) Would save // about 2000 gas. https://www.pivotaltracker.com/story/show/170828567 function _verifyVRFProof( uint256[2] memory pk, uint256[2] memory gamma, uint256 c, uint256 s, uint256 seed, address uWitness, uint256[2] memory cGammaWitness, uint256[2] memory sHashWitness, uint256 zInv ) internal view { unchecked { // solhint-disable-next-line custom-errors require(_isOnCurve(pk), "public key is not on curve"); // solhint-disable-next-line custom-errors require(_isOnCurve(gamma), "gamma is not on curve"); // solhint-disable-next-line custom-errors require(_isOnCurve(cGammaWitness), "cGammaWitness is not on curve"); // solhint-disable-next-line custom-errors require(_isOnCurve(sHashWitness), "sHashWitness is not on curve"); // Step 5. of IETF draft section 5.3 (pk corresponds to 5.3's Y, and here // we use the address of u instead of u itself. Also, here we add the // terms instead of taking the difference, and in the proof construction in // vrf.GenerateProof, we correspondingly take the difference instead of // taking the sum as they do in step 7 of section 5.1.) // solhint-disable-next-line custom-errors require(_verifyLinearCombinationWithGenerator(c, pk, s, uWitness), "addr(c*pk+s*g)!=_uWitness"); // Step 4. of IETF draft section 5.3 (pk corresponds to Y, seed to alpha_string) uint256[2] memory hash = _hashToCurve(pk, seed); // Step 6. of IETF draft section 5.3, but see note for step 5 about +/- terms uint256[2] memory v = _linearCombination(c, gamma, cGammaWitness, s, hash, sHashWitness, zInv); // Steps 7. and 8. of IETF draft section 5.3 uint256 derivedC = _scalarFromCurvePoints(hash, pk, gamma, uWitness, v); // solhint-disable-next-line custom-errors require(c == derivedC, "invalid proof"); } } // Domain-separation tag for the hash used as the final VRF output. // Corresponds to vrfRandomOutputHashPrefix in vrf.go uint256 internal constant VRF_RANDOM_OUTPUT_HASH_PREFIX = 3; struct Proof { uint256[2] pk; uint256[2] gamma; uint256 c; uint256 s; uint256 seed; address uWitness; uint256[2] cGammaWitness; uint256[2] sHashWitness; uint256 zInv; } /* *************************************************************************** * @notice Returns proof's output, if proof is valid. Otherwise reverts * @param proof vrf proof components * @param seed seed used to generate the vrf output * * Throws if proof is invalid, otherwise: * @return output i.e., the random output implied by the proof * *************************************************************************** */ function _randomValueFromVRFProof(Proof memory proof, uint256 seed) internal view returns (uint256 output) { _verifyVRFProof( proof.pk, proof.gamma, proof.c, proof.s, seed, proof.uWitness, proof.cGammaWitness, proof.sHashWitness, proof.zInv ); output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, proof.gamma))); return output; } }
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/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); }
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":true},"libraries":{}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_hashHub","internalType":"address"},{"type":"address","name":"_owner","internalType":"address"}]},{"type":"error","name":"BalanceInvariantViolated","inputs":[{"type":"uint256","name":"internalBalance","internalType":"uint256"},{"type":"uint256","name":"externalBalance","internalType":"uint256"}]},{"type":"error","name":"BlockhashNotInStore","inputs":[{"type":"uint256","name":"blocNumber","internalType":"uint256"}]},{"type":"error","name":"CantBeAddressZero","inputs":[]},{"type":"error","name":"GasLimitTooBig","inputs":[{"type":"uint32","name":"have","internalType":"uint32"},{"type":"uint32","name":"want","internalType":"uint32"}]},{"type":"error","name":"IncorrectCommitment","inputs":[]},{"type":"error","name":"InsufficientBalance","inputs":[]},{"type":"error","name":"InsufficientGasForConsumer","inputs":[{"type":"uint256","name":"have","internalType":"uint256"},{"type":"uint256","name":"want","internalType":"uint256"}]},{"type":"error","name":"InvalidBlockhash","inputs":[{"type":"uint256","name":"blockNum","internalType":"uint256"}]},{"type":"error","name":"InvalidCalldata","inputs":[]},{"type":"error","name":"InvalidConsumer","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"},{"type":"address","name":"consumer","internalType":"address"}]},{"type":"error","name":"InvalidRequestConfirmations","inputs":[{"type":"uint16","name":"have","internalType":"uint16"},{"type":"uint16","name":"min","internalType":"uint16"},{"type":"uint16","name":"max","internalType":"uint16"}]},{"type":"error","name":"InvalidSubscription","inputs":[]},{"type":"error","name":"MustBeRequestedOwner","inputs":[{"type":"address","name":"proposedOwner","internalType":"address"}]},{"type":"error","name":"MustBeSubOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"NoCorrespondingRequest","inputs":[]},{"type":"error","name":"NoSuchProvingKey","inputs":[{"type":"bytes32","name":"keyHash","internalType":"bytes32"}]},{"type":"error","name":"NumWordsTooBig","inputs":[{"type":"uint32","name":"have","internalType":"uint32"},{"type":"uint32","name":"want","internalType":"uint32"}]},{"type":"error","name":"OnlyCallableFromLink","inputs":[]},{"type":"error","name":"PaymentTooLarge","inputs":[]},{"type":"error","name":"PendingRequestExists","inputs":[]},{"type":"error","name":"PercentageIsNotInRange","inputs":[]},{"type":"error","name":"ProvingKeyAlreadyRegistered","inputs":[{"type":"bytes32","name":"keyHash","internalType":"bytes32"}]},{"type":"error","name":"Reentrant","inputs":[]},{"type":"error","name":"TooManyConsumers","inputs":[]},{"type":"event","name":"ConfigSet","inputs":[{"type":"uint16","name":"minimumRequestConfirmations","internalType":"uint16","indexed":false},{"type":"uint32","name":"maxGasLimit","internalType":"uint32","indexed":false},{"type":"uint32","name":"gasAfterPaymentCalculation","internalType":"uint32","indexed":false},{"type":"tuple","name":"feeConfig","internalType":"struct ErinaceusVRF.FeeConfig","indexed":false,"components":[{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier1","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier2","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier3","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier4","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier5","internalType":"uint32"},{"type":"uint24","name":"reqsForTier2","internalType":"uint24"},{"type":"uint24","name":"reqsForTier3","internalType":"uint24"},{"type":"uint24","name":"reqsForTier4","internalType":"uint24"},{"type":"uint24","name":"reqsForTier5","internalType":"uint24"}]}],"anonymous":false},{"type":"event","name":"FundsRecovered","inputs":[{"type":"address","name":"to","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"HashHubChanged","inputs":[{"type":"address","name":"oldHashHub","internalType":"address","indexed":false},{"type":"address","name":"newHashHub","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"HashHubFunded","inputs":[{"type":"uint256","name":"oldBalance","internalType":"uint256","indexed":false},{"type":"uint256","name":"newBalance","internalType":"uint256","indexed":false}],"anonymous":false},{"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":"ProvingKeyDeregistered","inputs":[{"type":"bytes32","name":"keyHash","internalType":"bytes32","indexed":false},{"type":"address","name":"oracle","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ProvingKeyRegistered","inputs":[{"type":"bytes32","name":"keyHash","internalType":"bytes32","indexed":false},{"type":"address","name":"oracle","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RandomWordsFulfilled","inputs":[{"type":"uint256","name":"requestId","internalType":"uint256","indexed":true},{"type":"uint256","name":"outputSeed","internalType":"uint256","indexed":false},{"type":"uint256","name":"payment","internalType":"uint256","indexed":false},{"type":"bool","name":"success","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"RandomWordsRequested","inputs":[{"type":"bytes32","name":"keyHash","internalType":"bytes32","indexed":true},{"type":"uint256","name":"requestId","internalType":"uint256","indexed":false},{"type":"uint256","name":"preSeed","internalType":"uint256","indexed":false},{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"uint16","name":"minimumRequestConfirmations","internalType":"uint16","indexed":false},{"type":"uint32","name":"callbackGasLimit","internalType":"uint32","indexed":false},{"type":"uint32","name":"numWords","internalType":"uint32","indexed":false},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RewardSet","inputs":[{"type":"uint256","name":"reward","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SubscriptionCanceled","inputs":[{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SubscriptionConsumerAdded","inputs":[{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"address","name":"consumer","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SubscriptionConsumerRemoved","inputs":[{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"address","name":"consumer","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SubscriptionCreated","inputs":[{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SubscriptionFunded","inputs":[{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"uint256","name":"oldBalance","internalType":"uint256","indexed":false},{"type":"uint256","name":"newBalance","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SubscriptionOwnerTransferRequested","inputs":[{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"address","name":"from","internalType":"address","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SubscriptionOwnerTransferred","inputs":[{"type":"uint64","name":"subId","internalType":"uint64","indexed":true},{"type":"address","name":"from","internalType":"address","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IHashHub"}],"name":"HashHub","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"HashHubBalance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"HashHubReward","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"MAX_CONSUMERS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"MAX_NUM_WORDS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"MAX_REQUEST_CONFIRMATIONS","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"acceptSubscriptionOwnerTransfer","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addConsumer","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"},{"type":"address","name":"consumer","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelSubscription","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"createSubscription","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deregisterProvingKey","inputs":[{"type":"uint256[2]","name":"publicProvingKey","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"feePercentage","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"fulfillRandomWords","inputs":[{"type":"tuple","name":"proof","internalType":"struct VRF.Proof","components":[{"type":"uint256[2]","name":"pk","internalType":"uint256[2]"},{"type":"uint256[2]","name":"gamma","internalType":"uint256[2]"},{"type":"uint256","name":"c","internalType":"uint256"},{"type":"uint256","name":"s","internalType":"uint256"},{"type":"uint256","name":"seed","internalType":"uint256"},{"type":"address","name":"uWitness","internalType":"address"},{"type":"uint256[2]","name":"cGammaWitness","internalType":"uint256[2]"},{"type":"uint256[2]","name":"sHashWitness","internalType":"uint256[2]"},{"type":"uint256","name":"zInv","internalType":"uint256"}]},{"type":"tuple","name":"rc","internalType":"struct ErinaceusVRF.RequestCommitment","components":[{"type":"uint64","name":"blockNum","internalType":"uint64"},{"type":"uint64","name":"subId","internalType":"uint64"},{"type":"uint32","name":"callbackGasLimit","internalType":"uint32"},{"type":"uint32","name":"numWords","internalType":"uint32"},{"type":"address","name":"sender","internalType":"address"}]}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"fundHashHub","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"fundSubscribtion","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getCommitment","inputs":[{"type":"uint256","name":"requestId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"minimumRequestConfirmations","internalType":"uint16"},{"type":"uint32","name":"maxGasLimit","internalType":"uint32"},{"type":"uint32","name":"gasAfterPaymentCalculation","internalType":"uint32"}],"name":"getConfig","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint64","name":"","internalType":"uint64"}],"name":"getCurrentSubId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier1","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier2","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier3","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier4","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier5","internalType":"uint32"},{"type":"uint24","name":"reqsForTier2","internalType":"uint24"},{"type":"uint24","name":"reqsForTier3","internalType":"uint24"},{"type":"uint24","name":"reqsForTier4","internalType":"uint24"},{"type":"uint24","name":"reqsForTier5","internalType":"uint24"}],"name":"getFeeConfig","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"}],"name":"getFeeTier","inputs":[{"type":"uint256","name":"reqCount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"},{"type":"uint32","name":"","internalType":"uint32"},{"type":"bytes32[]","name":"","internalType":"bytes32[]"}],"name":"getRequestConfig","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"uint256","name":"reqCount","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"},{"type":"address[]","name":"consumers","internalType":"address[]"}],"name":"getSubscription","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTotalBalance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getWithdrawableAmount","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"hashOfKey","inputs":[{"type":"uint256[2]","name":"publicKey","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"oracleWithdraw","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint96","name":"amount","internalType":"uint96"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"ownerCancelSubscription","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"pendingRequestExists","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverFunds","inputs":[{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"registerProvingKey","inputs":[{"type":"address","name":"oracle","internalType":"address"},{"type":"uint256[2]","name":"publicProvingKey","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeConsumer","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"},{"type":"address","name":"consumer","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"requestRandomWords","inputs":[{"type":"bytes32","name":"keyHash","internalType":"bytes32"},{"type":"uint64","name":"subId","internalType":"uint64"},{"type":"uint16","name":"requestConfirmations","internalType":"uint16"},{"type":"uint32","name":"callbackGasLimit","internalType":"uint32"},{"type":"uint32","name":"numWords","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"requestSubscriptionOwnerTransfer","inputs":[{"type":"uint64","name":"subId","internalType":"uint64"},{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"requestedOwner","internalType":"address"}],"name":"s_subscriptionConfigs","inputs":[{"type":"uint64","name":"","internalType":"uint64"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"uint256","name":"reqCount","internalType":"uint256"}],"name":"s_subscriptions","inputs":[{"type":"uint64","name":"","internalType":"uint64"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setConfig","inputs":[{"type":"uint16","name":"minimumRequestConfirmations","internalType":"uint16"},{"type":"uint32","name":"maxGasLimit","internalType":"uint32"},{"type":"uint32","name":"gasAfterPaymentCalculation","internalType":"uint32"},{"type":"tuple","name":"feeConfig","internalType":"struct ErinaceusVRF.FeeConfig","components":[{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier1","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier2","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier3","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier4","internalType":"uint32"},{"type":"uint32","name":"fulfillmentFlatFeeFTNPPMTier5","internalType":"uint32"},{"type":"uint24","name":"reqsForTier2","internalType":"uint24"},{"type":"uint24","name":"reqsForTier3","internalType":"uint24"},{"type":"uint24","name":"reqsForTier4","internalType":"uint24"},{"type":"uint24","name":"reqsForTier5","internalType":"uint24"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setHushHub","inputs":[{"type":"address","name":"_hashHub","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardForHashHub","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setTeam","inputs":[{"type":"address","name":"_teamAddress","internalType":"address"},{"type":"uint256","name":"_feePercentage","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"team","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawForTeam","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"withdrawableForTeam","inputs":[]}]
Contract Creation Code
0x60806040523480156200001157600080fd5b506040516200468d3803806200468d8339810160408190526200003491620000da565b6200003f336200006d565b600180546001600160a01b0319166001600160a01b03841617905562000065816200006d565b505062000112565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114620000d557600080fd5b919050565b60008060408385031215620000ee57600080fd5b620000f983620000bd565b91506200010960208401620000bd565b90509250929050565b61456b80620001226000396000f3fe6080604052600436106102715760003560e01c806385f2aef21161014f578063bc14edcf116100c1578063e4e1b45d1161007a578063e4e1b45d14610882578063e72f6e30146108a2578063e82ad7d4146108c2578063eccea3e2146108f2578063ee4201fe14610912578063f2fde38b1461092557600080fd5b8063bc14edcf14610748578063c3f909d414610768578063c8f7facc146107c1578063caf70c4a146107e1578063d7ae1d3014610801578063d7ca6f9b1461082157600080fd5b8063a0a19abd11610113578063a0a19abd146106a5578063a21a23e4146106c5578063a47c7696146106da578063af198b971461070a578063aff4fb231461072a578063b5a697a81461074057600080fd5b806385f2aef2146105f75780638da5cb5b1461062f57806390be10cc1461064d5780639f87fad71461066f578063a001ecdd1461068f57600080fd5b8063572727b0116101e85780636840c05e116101ac5780636840c05e1461050c57806369bcdb7d146105555780636f64f03f14610582578063715018a6146105a25780637341c10c146105b757806382359740146105d757600080fd5b8063572727b0146103f55780635d3b1d30146104155780635fbbc0d21461043557806364d51a2a146104d757806366316d8d146104ec57600080fd5b806312b583491161023a57806312b583491461033757806315c48b8414610356578063167957bd1461037e5780631d16919b1461039457806340d6bb82146103b45780634c6ad91c146103df57600080fd5b80620122911461027657806302bcc5b6146102a357806304c357cb146102c557806306bfa637146102e557806308821d5814610317575b600080fd5b34801561028257600080fd5b5061028b610945565b60405161029a939291906141c5565b60405180910390f35b3480156102af57600080fd5b506102c36102be3660046140bd565b6109c1565b005b3480156102d157600080fd5b506102c36102e03660046140d8565b610a38565b3480156102f157600080fd5b506009546001600160401b03165b6040516001600160401b03909116815260200161029a565b34801561032357600080fd5b506102c3610332366004613e2c565b610b83565b34801561034357600080fd5b50600a545b60405190815260200161029a565b34801561036257600080fd5b5061036b60c881565b60405161ffff909116815260200161029a565b34801561038a57600080fd5b5061034860035481565b3480156103a057600080fd5b506102c36103af366004613fa3565b610d17565b3480156103c057600080fd5b506103ca6101f481565b60405163ffffffff909116815260200161029a565b3480156103eb57600080fd5b50610348600b5481565b34801561040157600080fd5b506102c36104103660046140a4565b610f17565b34801561042157600080fd5b50610348610430366004613e7d565b610fab565b34801561044157600080fd5b506012546040805163ffffffff8084168252640100000000840481166020830152600160401b8404811692820192909252600160601b830482166060820152600160801b8304909116608082015262ffffff600160a01b8304811660a0830152600160b81b8304811660c0830152600160d01b8304811660e0830152600160e81b9092049091166101008201526101200161029a565b3480156104e357600080fd5b5061036b606481565b3480156104f857600080fd5b506102c3610507366004613de9565b611342565b34801561051857600080fd5b506105406105273660046140bd565b6007602052600090815260409020805460019091015482565b6040805192835260208301919091520161029a565b34801561056157600080fd5b506103486105703660046140a4565b60009081526010602052604090205490565b34801561058e57600080fd5b506102c361059d366004613d8b565b61140d565b3480156105ae57600080fd5b506102c361150c565b3480156105c357600080fd5b506102c36105d23660046140d8565b611520565b3480156105e357600080fd5b506102c36105f23660046140bd565b6116d2565b34801561060357600080fd5b50600254610617906001600160a01b031681565b6040516001600160a01b03909116815260200161029a565b34801561063b57600080fd5b506000546001600160a01b0316610617565b34801561065957600080fd5b50336000908152600f6020526040902054610348565b34801561067b57600080fd5b506102c361068a3660046140d8565b61182f565b34801561069b57600080fd5b5061034860045481565b3480156106b157600080fd5b50600154610617906001600160a01b031681565b3480156106d157600080fd5b506102ff611b80565b3480156106e657600080fd5b506106fa6106f53660046140bd565b611d07565b60405161029a9493929190614351565b34801561071657600080fd5b50610348610725366004613edb565b611df5565b34801561073657600080fd5b50610348600c5481565b6102c361216f565b34801561075457600080fd5b506102c3610763366004613dbf565b6121f3565b34801561077457600080fd5b5061079a60115461ffff81169163ffffffff620100008304811692600160381b90041690565b6040805161ffff909416845263ffffffff928316602085015291169082015260600161029a565b3480156107cd57600080fd5b506102c36107dc3660046140a4565b61226a565b3480156107ed57600080fd5b506103486107fc366004613e48565b6122a7565b34801561080d57600080fd5b506102c361081c3660046140d8565b6122d7565b34801561082d57600080fd5b5061086261083c3660046140bd565b600660205260009081526040902080546001909101546001600160a01b03918216911682565b604080516001600160a01b0393841681529290911660208301520161029a565b34801561088e57600080fd5b506102c361089d366004613d70565b6123a9565b3480156108ae57600080fd5b506102c36108bd366004613d70565b612412565b3480156108ce57600080fd5b506108e26108dd3660046140bd565b6124c0565b604051901515815260200161029a565b3480156108fe57600080fd5b506103ca61090d3660046140a4565b61266b565b6102c36109203660046140bd565b6127a9565b34801561093157600080fd5b506102c3610940366004613d70565b6128b6565b601154600e805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff169391928391908301828280156109af57602002820191906000526020600020905b81548152602001906001019080831161099b575b50505050509050925092509250909192565b6109c961292c565b6001600160401b0381166000908152600660205260409020546001600160a01b0316610a0857604051630fb532db60e11b815260040160405180910390fd5b6001600160401b038116600090815260066020526040902054610a359082906001600160a01b0316612986565b50565b6001600160401b03821660009081526006602052604090205482906001600160a01b031680610a7a57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614610ab357604051636c51fda960e11b81526001600160a01b03821660048201526024015b60405180910390fd5b601154600160301b900460ff1615610ade5760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0384166000908152600660205260409020600101546001600160a01b03848116911614610b7d576001600160401b03841660008181526006602090815260409182902060010180546001600160a01b0319166001600160a01b0388169081179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be91015b60405180910390a25b50505050565b610b8b61292c565b604080518082018252600091610bba9190849060029083908390808284376000920191909152506122a7915050565b6000818152600d60205260409020549091506001600160a01b031680610bf657604051631dfd6e1360e21b815260048101839052602401610aaa565b6000828152600d6020526040812080546001600160a01b03191690555b600e54811015610cce5782600e8281548110610c3157610c31614509565b90600052602060002001541415610cbc57600e805460009190610c569060019061445a565b81548110610c6657610c66614509565b9060005260206000200154905080600e8381548110610c8757610c87614509565b600091825260209091200155600e805480610ca457610ca46144f3565b60019003818190600052602060002001600090559055505b80610cc681614471565b915050610c13565b50806001600160a01b03167f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d83604051610d0a91815260200190565b60405180910390a2505050565b610d1f61292c565b60c861ffff85161115610d595760405163539c34bb60e11b815261ffff851660048201819052602482015260c86044820152606401610aaa565b604080516080808201835261ffff871680835263ffffffff878116602080860182905260008688015288831660609687018190526011805465ffffffffffff191690951762010000909302929092176affffffffff0000000000001916600160381b909202919091179092558551601280549388015188880151968901519589015160a08a015160c08b015160e08c01516101008d015196881667ffffffffffffffff199099169890981764010000000094881694909402939093176fffffffffffffffff00000000000000001916600160401b9987169990990263ffffffff60601b191698909817600160601b978616979097029690961766ffffffffffffff60801b1916600160801b969094169590950262ffffff60a01b191692909217600160a01b62ffffff968716021765ffffffffffff60b81b1916600160b81b9486169490940262ffffff60d01b191693909317600160d01b92851692909202919091176001600160e81b0316600160e81b939092169290920217815590517f3248fab4375f32e0d851d39a71c0750d4652d98bcc7d32cec9d178c9824d796b91610f099187918791879190614224565b60405180910390a150505050565b601154600160301b900460ff1615610f425760405163769dd35360e11b815260040160405180910390fd5b806003541015610f6557604051631e9acf1760e31b815260040160405180910390fd5b8060036000828254610f77919061445a565b9250508190555080600a6000828254610f90919061445a565b9091555050600254610a35906001600160a01b031682612be1565b601154600090600160301b900460ff1615610fd95760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0385166000908152600660205260409020546001600160a01b031661101857604051630fb532db60e11b815260040160405180910390fd5b3360009081526005602090815260408083206001600160401b03808a168552925290912054168061106d57604051637800cff360e11b81526001600160401b0387166004820152336024820152604401610aaa565b60115461ffff9081169086161080611089575060c861ffff8616115b156110c05760115460405163539c34bb60e11b815261ffff8088166004830152909116602482015260c86044820152606401610aaa565b60115463ffffffff620100009091048116908516111561110e57601154604051637aebf00f60e11b815263ffffffff8087166004830152620100009092049091166024820152604401610aaa565b6101f463ffffffff84161115611147576040516311ce1afb60e21b815263ffffffff841660048201526101f46024820152604401610aaa565b60006111548260016143fc565b90506000806111658a338b86612c41565b604080516020810184905243918101919091526001600160401b038c16606082015263ffffffff808b166080830152891660a08201523360c0820152919350915060e00160408051601f19818403018152918152815160209283012060008581526010845282812091909155438152600890925290205460ff1661129357600c54600b54101580156111f957506000600b54115b1561129357600154600c546040516378c4059760e11b81524360048201526001600160a01b039092169163f1880b2e91906024016000604051808303818588803b15801561124657600080fd5b505af115801561125a573d6000803e3d6000fd5b5050505050600c54600b6000828254611273919061445a565b9091555050436000908152600860205260409020805460ff191660011790555b604080518381526020810183905261ffff8a168183015263ffffffff898116606083015288166080820152905133916001600160401b038c16918d917f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772919081900360a00190a4503360009081526005602090815260408083206001600160401b03808d168552925290912080549190931667ffffffffffffffff199091161790915591505095945050505050565b601154600160301b900460ff161561136d5760405163769dd35360e11b815260040160405180910390fd5b336000908152600f60205260409020546001600160601b03821611156113a657604051631e9acf1760e31b815260040160405180910390fd5b336000908152600f6020526040812080546001600160601b03841692906113ce90849061445a565b92505081905550806001600160601b0316600a60008282546113f0919061445a565b909155506114099050826001600160601b038316612be1565b5050565b61141561292c565b6040805180820182526000916114449190849060029083908390808284376000920191909152506122a7915050565b6000818152600d60205260409020549091506001600160a01b03161561148057604051634a0b8fa760e01b815260048101829052602401610aaa565b6000818152600d6020908152604080832080546001600160a01b0319166001600160a01b038816908117909155600e805460018101825594527fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610d0a565b61151461292c565b61151e6000612cbb565b565b6001600160401b03821660009081526006602052604090205482906001600160a01b03168061156257604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b0382161461159657604051636c51fda960e11b81526001600160a01b0382166004820152602401610aaa565b601154600160301b900460ff16156115c15760405163769dd35360e11b815260040160405180910390fd5b6001600160401b038416600090815260066020526040902060020154606414156115fe576040516305a48e0f60e01b815260040160405180910390fd5b6001600160a01b03831660009081526005602090815260408083206001600160401b038089168552925290912054161561163757610b7d565b6001600160a01b03831660008181526005602090815260408083206001600160401b038916808552908352818420805467ffffffffffffffff19166001908117909155600684528285206002018054918201815585529383902090930180546001600160a01b031916851790555192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e09101610b74565b601154600160301b900460ff16156116fd5760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0381166000908152600660205260409020546001600160a01b031661173c57604051630fb532db60e11b815260040160405180910390fd5b6001600160401b0381166000908152600660205260409020600101546001600160a01b031633146117a9576001600160401b0381166000908152600660205260409081902060010154905163d084e97560e01b81526001600160a01b039091166004820152602401610aaa565b6001600160401b0381166000818152600660209081526040918290208054336001600160a01b0319808316821784556001909301805490931690925583516001600160a01b03909116808252928101919091529092917f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f091015b60405180910390a25050565b6001600160401b03821660009081526006602052604090205482906001600160a01b03168061187157604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b038216146118a557604051636c51fda960e11b81526001600160a01b0382166004820152602401610aaa565b601154600160301b900460ff16156118d05760405163769dd35360e11b815260040160405180910390fd5b6118d9846124c0565b156118f757604051631685ecdd60e31b815260040160405180910390fd5b6001600160a01b03831660009081526005602090815260408083206001600160401b0380891685529252909120541661195d57604051637800cff360e11b81526001600160401b03851660048201526001600160a01b0384166024820152604401610aaa565b6001600160401b0384166000908152600660209081526040808320600201805482518185028101850190935280835291929091908301828280156119ca57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116119ac575b505050505090506000600182516119e1919061445a565b905060005b8251811015611b0857856001600160a01b0316838281518110611a0b57611a0b614509565b60200260200101516001600160a01b03161415611af6576000838381518110611a3657611a36614509565b6020026020010151905080600660008a6001600160401b03166001600160401b031681526020019081526020016000206002018381548110611a7a57611a7a614509565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394909416939093179092556001600160401b038a168152600690915260409020600201805480611ace57611ace6144f3565b600082815260209020810160001990810180546001600160a01b031916905501905550611b08565b80611b0081614471565b9150506119e6565b506001600160a01b03851660008181526005602090815260408083206001600160401b038b1680855290835292819020805467ffffffffffffffff191690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b910160405180910390a2505050505050565b601154600090600160301b900460ff1615611bae5760405163769dd35360e11b815260040160405180910390fd5b600980546001600160401b0316906000611bc78361448c565b82546101009290920a6001600160401b03818102199093169183160217909155600954169050600080604051908082528060200260200182016040528015611c19578160200160208202803683370190505b50604080518082018252600080825260208083018281526001600160401b0388168084526007835285842094518555905160019485015584516060810186523381528083018481528187018881529285526006845295909320835181546001600160a01b03199081166001600160a01b03928316178355965195820180549097169516949094179094559251805194955090939192611cc092600285019290910190613aff565b50506040513381526001600160401b03841691507f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a250905090565b6001600160401b038116600090815260066020526040812054819081906060906001600160a01b0316611d4d57604051630fb532db60e11b815260040160405180910390fd5b6001600160401b0385166000908152600760209081526040808320805460019091015460068452938290208054600290910180548451818702810187019095528085529295946001600160a01b039092169390929091839190830182828015611ddf57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611dc1575b5050505050905093509350935093509193509193565b601154600090600160301b900460ff1615611e235760405163769dd35360e11b815260040160405180910390fd5b60005a90506000806000611e378787612d0b565b9250925092506000866060015163ffffffff166001600160401b03811115611e6157611e6161451f565b604051908082528060200260200182016040528015611e8a578160200160208202803683370190505b50905060005b876060015163ffffffff16811015611efe5760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c828281518110611ee157611ee1614509565b602090810291909101015280611ef681614471565b915050611e90565b50600083815260106020526040808220829055518190631fe543e360e01b90611f2d9087908690602401614303565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092526011805466ff0000000000001916600160301b179055908a015160808b0151919250600091611f959163ffffffff169084612f78565b6011805466ff000000000000191690556020808c0180516001600160401b03908116600090815260079093526040808420600190810154935190921684528320810180549495509193909290611fec9084906143e4565b9091555050601154600090612019908b90600160381b900463ffffffff166120138561266b565b3a612fc6565b6020808e01516001600160401b031660009081526007909152604090205490915081111561205a57604051631e9acf1760e31b815260040160405180910390fd5b6020808d01516001600160401b03166000908152600790915260408120805483929061208790849061445a565b909155505060045460649061209c908261445a565b6120a6908361443b565b6120b09190614427565b60008a8152600d60209081526040808320546001600160a01b03168352600f909152812080549091906120e49084906143e4565b90915550506004546064906120f9908361443b565b6121039190614427565b6003600082825461211491906143e4565b9091555050604080518881526020810183905284151581830152905189917f221ad2e5b871cead1dd7f75c2fb223c0cfa34bdc049a15f3f82a1f0e943e605a919081900360600190a299505050505050505050505b92915050565b601154600160301b900460ff161561219a5760405163769dd35360e11b815260040160405180910390fd5b600b805490349060006121ad83856143e4565b9091555050600b546040805183815260208101929092527ff09ca6cef38280114ac05d60379acf68fa7f85ea69ec4a8ed2a28626acafc06391015b60405180910390a150565b6121fb61292c565b6001600160a01b0382166122225760405163f0a7640b60e01b815260040160405180910390fd5b60648111156122445760405163197fcbd760e11b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b039390931692909217909155600455565b61227261292c565b600c8190556040518181527f4c42db8a799110fdd6a26148a21a5fbe4e581c926bccfd3b2d8a7f3aed4a87c8906020016121e8565b6000816040516020016122ba91906141b7565b604051602081830303815290604052805190602001209050919050565b6001600160401b03821660009081526006602052604090205482906001600160a01b03168061231957604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b0382161461234d57604051636c51fda960e11b81526001600160a01b0382166004820152602401610aaa565b601154600160301b900460ff16156123785760405163769dd35360e11b815260040160405180910390fd5b612381846124c0565b1561239f57604051631685ecdd60e31b815260040160405180910390fd5b610b7d8484612986565b6123b161292c565b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527fa5a618fb1d2db8fcece4bcc171d33525bb774a19485486ba6f8ee0c50d59442a910160405180910390a15050565b61241a61292c565b600b5460009061242b90303161445a565b600a549091508181111561245c576040516354ced18160e11b81526004810182905260248101839052604401610aaa565b818110156124bb576000612470828461445a565b905061247c8482612be1565b604080516001600160a01b0386168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b4366009101610f09565b505050565b6001600160401b0381166000908152600660209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561255457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612536575b505050505081525050905060005b8160400151518110156126615760005b600e5481101561264e576000612617600e838154811061259457612594614509565b9060005260206000200154856040015185815181106125b5576125b5614509565b60200260200101518860056000896040015189815181106125d8576125d8614509565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081206001600160401b03808f1683529352205416612c41565b506000818152601060205260409020549091501561263b5750600195945050505050565b508061264681614471565b915050612572565b508061265981614471565b915050612562565b5060009392505050565b604080516101208101825260125463ffffffff8082168352640100000000820481166020840152600160401b8204811693830193909352600160601b810483166060830152600160801b8104909216608082015262ffffff600160a01b8304811660a08301819052600160b81b8404821660c0840152600160d01b8404821660e0840152600160e81b90930416610100820152600091831161270e575192915050565b828160a0015162ffffff1610801561272f57508060c0015162ffffff168311155b1561273e576020015192915050565b828160c0015162ffffff1610801561275f57508060e0015162ffffff168311155b1561276e576040015192915050565b828160e0015162ffffff16108015612790575080610100015162ffffff168311155b1561279f576060015192915050565b6080015192915050565b601154600160301b900460ff16156127d45760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0381166000908152600660205260409020546001600160a01b031661281357604051630fb532db60e11b815260040160405180910390fd5b6001600160401b03811660009081526007602052604081208054916001600160601b033416919061284483856143e4565b92505081905550346001600160601b0316600a600082825461286691906143e4565b90915550506001600160401b0382167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f8826128a134826143e4565b60408051928352602083019190915201611823565b6128be61292c565b6001600160a01b0381166129235760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610aaa565b610a3581612cbb565b6000546001600160a01b0316331461151e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610aaa565b601154600160301b900460ff16156129b15760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0382166000908152600660209081526040808320815160608101835281546001600160a01b03908116825260018301541681850152600282018054845181870281018701865281815292959394860193830182828015612a4157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612a23575b505050919092525050506001600160401b03841660009081526007602090815260408083208151808301909252805480835260019091015492820192909252929350905b836040015151811015612b06576005600085604001518381518110612aac57612aac614509565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081206001600160401b038a1682529092529020805467ffffffffffffffff1916905580612afe81614471565b915050612a85565b506001600160401b038516600090815260066020526040812080546001600160a01b03199081168255600182018054909116905590612b486002830182613b64565b50506001600160401b0385166000908152600760205260408120818155600101819055600a8054839290612b7d90849061445a565b90915550612b8d90508482612be1565b604080516001600160a01b0386168152602081018390526001600160401b038716917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612c2e576040519150601f19603f3d011682016040523d82523d6000602084013e612c33565b606091505b50509050806124bb57600080fd5b6040805160208082018790526001600160a01b0395909516818301526001600160401b039384166060820152919092166080808301919091528251808303909101815260a08201835280519084012060c082019490945260e080820185905282518083039091018152610100909101909152805191012091565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000806000612d1d85600001516122a7565b6000818152600d60205260409020549093506001600160a01b031680612d5957604051631dfd6e1360e21b815260048101859052602401610aaa565b6080860151604051612d78918691602001918252602082015260400190565b60408051601f1981840301815291815281516020928301206000818152601090935291205490935080612dbe57604051631b44092560e11b815260040160405180910390fd5b85516020808801516040808a015160608b015160808c01519251612e29968b9690959491019586526001600160401b03948516602087015292909316604085015263ffffffff90811660608501529190911660808301526001600160a01b031660a082015260c00190565b604051602081830303815290604052805190602001208114612e5e5760405163354a450b60e21b815260040160405180910390fd5b85516001600160401b03164080612f24576001548751604051631d2827a760e31b81526001600160401b0390911660048201526001600160a01b039091169063e9413d389060240160206040518083038186803b158015612ebe57600080fd5b505afa158015612ed2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef69190613e64565b905080612f2457865160405163175dadad60e01b81526001600160401b039091166004820152602401610aaa565b6000886080015182604051602001612f46929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c9050612f6b8982613021565b9450505050509250925092565b60005a611388811015612f8a57600080fd5b611388810390508460408204820311612fa257600080fd5b50823b612fae57600080fd5b60008083516020850160008789f190505b9392505050565b6000805a612fd487876143e4565b612fde919061445a565b612fe8908461443b565b9050600061300163ffffffff861664e8d4a5100061443b565b905061300d81836143e4565b6001600160601b0316979650505050505050565b60006130558360000151846020015185604001518660600151868860a001518960c001518a60e001518b610100015161308c565b6003836020015160405160200161306d9291906142ef565b60408051601f1981840301815291905280516020909101209392505050565b613095896132af565b6130e15760405162461bcd60e51b815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e2063757276650000000000006044820152606401610aaa565b6130ea886132af565b61312e5760405162461bcd60e51b815260206004820152601560248201527467616d6d61206973206e6f74206f6e20637572766560581b6044820152606401610aaa565b613137836132af565b6131835760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e2063757276650000006044820152606401610aaa565b61318c826132af565b6131d85760405162461bcd60e51b815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e206375727665000000006044820152606401610aaa565b6131e4878a8887613372565b6132305760405162461bcd60e51b815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e657373000000000000006044820152606401610aaa565b600061323c8a87613495565b9050600061324f898b878b8689896134f9565b90506000613260838d8d8a8661361e565b9050808a146132a15760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b210383937b7b360991b6044820152606401610aaa565b505050505050505050505050565b80516000906401000003d019116132fd5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420782d6f7264696e61746560701b6044820152606401610aaa565b60208201516401000003d0191161334b5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420792d6f7264696e61746560701b6044820152606401610aaa565b60208201516401000003d01990800961336b8360005b602002015161365e565b1492915050565b60006001600160a01b0382166133b85760405162461bcd60e51b815260206004820152600b60248201526a626164207769746e65737360a81b6044820152606401610aaa565b6020840151600090600116156133cf57601c6133d2565b601b5b9050600070014551231950b75fc4402da1732fc9bebe1985876000602002015109865170014551231950b75fc4402da1732fc9bebe19918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa15801561346d573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b61349d613b82565b6134ca600184846040516020016134b693929190614196565b604051602081830303815290604052613682565b90505b6134d6816132af565b6121695780516040805160208101929092526134f291016134b6565b90506134cd565b613501613b82565b825186516401000003d01990819006910614156135605760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e637400006044820152606401610aaa565b61356b8789886136d1565b6135b05760405162461bcd60e51b8152602060048201526016602482015275119a5c9cdd081b5d5b0818da1958dac819985a5b195960521b6044820152606401610aaa565b6135bb8486856136d1565b6136075760405162461bcd60e51b815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c65640000000000000000006044820152606401610aaa565b6136128684846137f9565b98975050505050505050565b60006002868686858760405160200161363c96959493929190614137565b60408051601f1981840301815291905280516020909101209695505050505050565b6000806401000003d01980848509840990506401000003d019600782089392505050565b61368a613b82565b613693826138c0565b81526136a86136a3826000613361565b6138fb565b6020820181905260029006600114156136cc576020810180516401000003d0190390525b919050565b60008261370e5760405162461bcd60e51b815260206004820152600b60248201526a3d32b9379039b1b0b630b960a91b6044820152606401610aaa565b83516020850151600090613724906002906144b3565b1561373057601c613733565b601b5b9050600070014551231950b75fc4402da1732fc9bebe198387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa1580156137a5573d6000803e3d6000fd5b5050506020604051035190506000866040516020016137c49190614125565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b613801613b82565b8351602080860151855191860151600093849384936138229390919061391b565b919450925090506401000003d0198582096001146138825760405162461bcd60e51b815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a000000000000006044820152606401610aaa565b60405180604001604052806401000003d019806138a1576138a16144dd565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d01981106136cc576040805160208082019390935281518082038401815290820190915280519101206138c8565b60006121698260026139146401000003d01960016143e4565b901c6139fb565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a089050600061395b83838585613a92565b909850905061396c88828e88613ab6565b909850905061397d88828c87613ab6565b909850905060006139908d878b85613ab6565b90985090506139a188828686613a92565b90985090506139b288828e89613ab6565b90985090508181146139e7576401000003d019818a0998506401000003d01982890997506401000003d01981830996506139eb565b8196505b5050505050509450945094915050565b600080613a06613ba0565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a0820152613a38613bbe565b60208160c0846005600019fa925082613a885760405162461bcd60e51b81526020600482015260126024820152716269674d6f64457870206661696c7572652160701b6044820152606401610aaa565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b828054828255906000526020600020908101928215613b54579160200282015b82811115613b5457825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613b1f565b50613b60929150613bdc565b5090565b5080546000825590600052602060002090810190610a359190613bdc565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b80821115613b605760008155600101613bdd565b80356001600160a01b03811681146136cc57600080fd5b806040810183101561216957600080fd5b600082601f830112613c2a57600080fd5b604051604081018181106001600160401b0382111715613c4c57613c4c61451f565b8060405250808385604086011115613c6357600080fd5b60005b6002811015613c85578135835260209283019290910190600101613c66565b509195945050505050565b600060a08284031215613ca257600080fd5b60405160a081018181106001600160401b0382111715613cc457613cc461451f565b604052905080613cd383613d59565b8152613ce160208401613d59565b6020820152613cf260408401613d45565b6040820152613d0360608401613d45565b6060820152613d1460808401613bf1565b60808201525092915050565b803561ffff811681146136cc57600080fd5b803562ffffff811681146136cc57600080fd5b803563ffffffff811681146136cc57600080fd5b80356001600160401b03811681146136cc57600080fd5b600060208284031215613d8257600080fd5b612fbf82613bf1565b60008060608385031215613d9e57600080fd5b613da783613bf1565b9150613db68460208501613c08565b90509250929050565b60008060408385031215613dd257600080fd5b613ddb83613bf1565b946020939093013593505050565b60008060408385031215613dfc57600080fd5b613e0583613bf1565b915060208301356001600160601b0381168114613e2157600080fd5b809150509250929050565b600060408284031215613e3e57600080fd5b612fbf8383613c08565b600060408284031215613e5a57600080fd5b612fbf8383613c19565b600060208284031215613e7657600080fd5b5051919050565b600080600080600060a08688031215613e9557600080fd5b85359450613ea560208701613d59565b9350613eb360408701613d20565b9250613ec160608701613d45565b9150613ecf60808701613d45565b90509295509295909350565b600080828403610240811215613ef057600080fd5b6101a080821215613f0057600080fd5b613f086143bb565b9150613f148686613c19565b8252613f238660408701613c19565b60208301526080850135604083015260a0850135606083015260c08501356080830152613f5260e08601613bf1565b60a0830152610100613f6687828801613c19565b60c0840152613f79876101408801613c19565b60e08401526101808601358184015250819350613f9886828701613c90565b925050509250929050565b600080600080848603610180811215613fbb57600080fd5b613fc486613d20565b9450613fd260208701613d45565b9350613fe060408701613d45565b925061012080605f1983011215613ff657600080fd5b613ffe6143bb565b915061400c60608801613d45565b825261401a60808801613d45565b602083015261402b60a08801613d45565b604083015261403c60c08801613d45565b606083015261404d60e08801613d45565b6080830152610100614060818901613d32565b60a0840152614070828901613d32565b60c08401526140826101408901613d32565b60e08401526140946101608901613d32565b9083015250939692955090935050565b6000602082840312156140b657600080fd5b5035919050565b6000602082840312156140cf57600080fd5b612fbf82613d59565b600080604083850312156140eb57600080fd5b6140f483613d59565b9150613db660208401613bf1565b8060005b6002811015610b7d578151845260209384019390910190600101614106565b61412f8183614102565b604001919050565b8681526141476020820187614102565b6141546060820186614102565b61416160a0820185614102565b61416e60e0820184614102565b60609190911b6bffffffffffffffffffffffff19166101208201526101340195945050505050565b8381526141a66020820184614102565b606081019190915260800192915050565b604081016121698284614102565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b81811015614216578451835293830193918301916001016141fa565b509098975050505050505050565b60006101808201905061ffff8616825263ffffffff808616602084015280851660408401528354818116606085015261426a60808501838360201c1663ffffffff169052565b61428160a08501838360401c1663ffffffff169052565b61429860c08501838360601c1663ffffffff169052565b6142af60e08501838360801c1663ffffffff169052565b62ffffff60a082901c811661010086015260b882901c811661012086015260d082901c1661014085015260e81c6101609093019290925295945050505050565b82815260608101612fbf6020830184614102565b6000604082018483526020604081850152818551808452606086019150828701935060005b8181101561434457845183529383019391830191600101614328565b5090979650505050505050565b84815260208082018590526001600160a01b038481166040840152608060608401819052845190840181905260009285810192909160a0860190855b818110156143ab57855184168352948401949184019160010161438d565b50909a9950505050505050505050565b60405161012081016001600160401b03811182821017156143de576143de61451f565b60405290565b600082198211156143f7576143f76144c7565b500190565b60006001600160401b0380831681851680830382111561441e5761441e6144c7565b01949350505050565b600082614436576144366144dd565b500490565b6000816000190483118215151615614455576144556144c7565b500290565b60008282101561446c5761446c6144c7565b500390565b6000600019821415614485576144856144c7565b5060010190565b60006001600160401b03808316818114156144a9576144a96144c7565b6001019392505050565b6000826144c2576144c26144dd565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfea2646970667358221220119a51b9e3a6e198294494ca5d1018d1aa522d9be4880f69a71cd441a3cc49d264736f6c634300080600330000000000000000000000005159596d244c97348b9553e06f6aa75492677a09000000000000000000000000bb78efaaaf9223b4840ea7defdc379a13b16399b
Deployed ByteCode
0x6080604052600436106102715760003560e01c806385f2aef21161014f578063bc14edcf116100c1578063e4e1b45d1161007a578063e4e1b45d14610882578063e72f6e30146108a2578063e82ad7d4146108c2578063eccea3e2146108f2578063ee4201fe14610912578063f2fde38b1461092557600080fd5b8063bc14edcf14610748578063c3f909d414610768578063c8f7facc146107c1578063caf70c4a146107e1578063d7ae1d3014610801578063d7ca6f9b1461082157600080fd5b8063a0a19abd11610113578063a0a19abd146106a5578063a21a23e4146106c5578063a47c7696146106da578063af198b971461070a578063aff4fb231461072a578063b5a697a81461074057600080fd5b806385f2aef2146105f75780638da5cb5b1461062f57806390be10cc1461064d5780639f87fad71461066f578063a001ecdd1461068f57600080fd5b8063572727b0116101e85780636840c05e116101ac5780636840c05e1461050c57806369bcdb7d146105555780636f64f03f14610582578063715018a6146105a25780637341c10c146105b757806382359740146105d757600080fd5b8063572727b0146103f55780635d3b1d30146104155780635fbbc0d21461043557806364d51a2a146104d757806366316d8d146104ec57600080fd5b806312b583491161023a57806312b583491461033757806315c48b8414610356578063167957bd1461037e5780631d16919b1461039457806340d6bb82146103b45780634c6ad91c146103df57600080fd5b80620122911461027657806302bcc5b6146102a357806304c357cb146102c557806306bfa637146102e557806308821d5814610317575b600080fd5b34801561028257600080fd5b5061028b610945565b60405161029a939291906141c5565b60405180910390f35b3480156102af57600080fd5b506102c36102be3660046140bd565b6109c1565b005b3480156102d157600080fd5b506102c36102e03660046140d8565b610a38565b3480156102f157600080fd5b506009546001600160401b03165b6040516001600160401b03909116815260200161029a565b34801561032357600080fd5b506102c3610332366004613e2c565b610b83565b34801561034357600080fd5b50600a545b60405190815260200161029a565b34801561036257600080fd5b5061036b60c881565b60405161ffff909116815260200161029a565b34801561038a57600080fd5b5061034860035481565b3480156103a057600080fd5b506102c36103af366004613fa3565b610d17565b3480156103c057600080fd5b506103ca6101f481565b60405163ffffffff909116815260200161029a565b3480156103eb57600080fd5b50610348600b5481565b34801561040157600080fd5b506102c36104103660046140a4565b610f17565b34801561042157600080fd5b50610348610430366004613e7d565b610fab565b34801561044157600080fd5b506012546040805163ffffffff8084168252640100000000840481166020830152600160401b8404811692820192909252600160601b830482166060820152600160801b8304909116608082015262ffffff600160a01b8304811660a0830152600160b81b8304811660c0830152600160d01b8304811660e0830152600160e81b9092049091166101008201526101200161029a565b3480156104e357600080fd5b5061036b606481565b3480156104f857600080fd5b506102c3610507366004613de9565b611342565b34801561051857600080fd5b506105406105273660046140bd565b6007602052600090815260409020805460019091015482565b6040805192835260208301919091520161029a565b34801561056157600080fd5b506103486105703660046140a4565b60009081526010602052604090205490565b34801561058e57600080fd5b506102c361059d366004613d8b565b61140d565b3480156105ae57600080fd5b506102c361150c565b3480156105c357600080fd5b506102c36105d23660046140d8565b611520565b3480156105e357600080fd5b506102c36105f23660046140bd565b6116d2565b34801561060357600080fd5b50600254610617906001600160a01b031681565b6040516001600160a01b03909116815260200161029a565b34801561063b57600080fd5b506000546001600160a01b0316610617565b34801561065957600080fd5b50336000908152600f6020526040902054610348565b34801561067b57600080fd5b506102c361068a3660046140d8565b61182f565b34801561069b57600080fd5b5061034860045481565b3480156106b157600080fd5b50600154610617906001600160a01b031681565b3480156106d157600080fd5b506102ff611b80565b3480156106e657600080fd5b506106fa6106f53660046140bd565b611d07565b60405161029a9493929190614351565b34801561071657600080fd5b50610348610725366004613edb565b611df5565b34801561073657600080fd5b50610348600c5481565b6102c361216f565b34801561075457600080fd5b506102c3610763366004613dbf565b6121f3565b34801561077457600080fd5b5061079a60115461ffff81169163ffffffff620100008304811692600160381b90041690565b6040805161ffff909416845263ffffffff928316602085015291169082015260600161029a565b3480156107cd57600080fd5b506102c36107dc3660046140a4565b61226a565b3480156107ed57600080fd5b506103486107fc366004613e48565b6122a7565b34801561080d57600080fd5b506102c361081c3660046140d8565b6122d7565b34801561082d57600080fd5b5061086261083c3660046140bd565b600660205260009081526040902080546001909101546001600160a01b03918216911682565b604080516001600160a01b0393841681529290911660208301520161029a565b34801561088e57600080fd5b506102c361089d366004613d70565b6123a9565b3480156108ae57600080fd5b506102c36108bd366004613d70565b612412565b3480156108ce57600080fd5b506108e26108dd3660046140bd565b6124c0565b604051901515815260200161029a565b3480156108fe57600080fd5b506103ca61090d3660046140a4565b61266b565b6102c36109203660046140bd565b6127a9565b34801561093157600080fd5b506102c3610940366004613d70565b6128b6565b601154600e805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff169391928391908301828280156109af57602002820191906000526020600020905b81548152602001906001019080831161099b575b50505050509050925092509250909192565b6109c961292c565b6001600160401b0381166000908152600660205260409020546001600160a01b0316610a0857604051630fb532db60e11b815260040160405180910390fd5b6001600160401b038116600090815260066020526040902054610a359082906001600160a01b0316612986565b50565b6001600160401b03821660009081526006602052604090205482906001600160a01b031680610a7a57604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b03821614610ab357604051636c51fda960e11b81526001600160a01b03821660048201526024015b60405180910390fd5b601154600160301b900460ff1615610ade5760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0384166000908152600660205260409020600101546001600160a01b03848116911614610b7d576001600160401b03841660008181526006602090815260409182902060010180546001600160a01b0319166001600160a01b0388169081179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be91015b60405180910390a25b50505050565b610b8b61292c565b604080518082018252600091610bba9190849060029083908390808284376000920191909152506122a7915050565b6000818152600d60205260409020549091506001600160a01b031680610bf657604051631dfd6e1360e21b815260048101839052602401610aaa565b6000828152600d6020526040812080546001600160a01b03191690555b600e54811015610cce5782600e8281548110610c3157610c31614509565b90600052602060002001541415610cbc57600e805460009190610c569060019061445a565b81548110610c6657610c66614509565b9060005260206000200154905080600e8381548110610c8757610c87614509565b600091825260209091200155600e805480610ca457610ca46144f3565b60019003818190600052602060002001600090559055505b80610cc681614471565b915050610c13565b50806001600160a01b03167f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d83604051610d0a91815260200190565b60405180910390a2505050565b610d1f61292c565b60c861ffff85161115610d595760405163539c34bb60e11b815261ffff851660048201819052602482015260c86044820152606401610aaa565b604080516080808201835261ffff871680835263ffffffff878116602080860182905260008688015288831660609687018190526011805465ffffffffffff191690951762010000909302929092176affffffffff0000000000001916600160381b909202919091179092558551601280549388015188880151968901519589015160a08a015160c08b015160e08c01516101008d015196881667ffffffffffffffff199099169890981764010000000094881694909402939093176fffffffffffffffff00000000000000001916600160401b9987169990990263ffffffff60601b191698909817600160601b978616979097029690961766ffffffffffffff60801b1916600160801b969094169590950262ffffff60a01b191692909217600160a01b62ffffff968716021765ffffffffffff60b81b1916600160b81b9486169490940262ffffff60d01b191693909317600160d01b92851692909202919091176001600160e81b0316600160e81b939092169290920217815590517f3248fab4375f32e0d851d39a71c0750d4652d98bcc7d32cec9d178c9824d796b91610f099187918791879190614224565b60405180910390a150505050565b601154600160301b900460ff1615610f425760405163769dd35360e11b815260040160405180910390fd5b806003541015610f6557604051631e9acf1760e31b815260040160405180910390fd5b8060036000828254610f77919061445a565b9250508190555080600a6000828254610f90919061445a565b9091555050600254610a35906001600160a01b031682612be1565b601154600090600160301b900460ff1615610fd95760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0385166000908152600660205260409020546001600160a01b031661101857604051630fb532db60e11b815260040160405180910390fd5b3360009081526005602090815260408083206001600160401b03808a168552925290912054168061106d57604051637800cff360e11b81526001600160401b0387166004820152336024820152604401610aaa565b60115461ffff9081169086161080611089575060c861ffff8616115b156110c05760115460405163539c34bb60e11b815261ffff8088166004830152909116602482015260c86044820152606401610aaa565b60115463ffffffff620100009091048116908516111561110e57601154604051637aebf00f60e11b815263ffffffff8087166004830152620100009092049091166024820152604401610aaa565b6101f463ffffffff84161115611147576040516311ce1afb60e21b815263ffffffff841660048201526101f46024820152604401610aaa565b60006111548260016143fc565b90506000806111658a338b86612c41565b604080516020810184905243918101919091526001600160401b038c16606082015263ffffffff808b166080830152891660a08201523360c0820152919350915060e00160408051601f19818403018152918152815160209283012060008581526010845282812091909155438152600890925290205460ff1661129357600c54600b54101580156111f957506000600b54115b1561129357600154600c546040516378c4059760e11b81524360048201526001600160a01b039092169163f1880b2e91906024016000604051808303818588803b15801561124657600080fd5b505af115801561125a573d6000803e3d6000fd5b5050505050600c54600b6000828254611273919061445a565b9091555050436000908152600860205260409020805460ff191660011790555b604080518381526020810183905261ffff8a168183015263ffffffff898116606083015288166080820152905133916001600160401b038c16918d917f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a9772919081900360a00190a4503360009081526005602090815260408083206001600160401b03808d168552925290912080549190931667ffffffffffffffff199091161790915591505095945050505050565b601154600160301b900460ff161561136d5760405163769dd35360e11b815260040160405180910390fd5b336000908152600f60205260409020546001600160601b03821611156113a657604051631e9acf1760e31b815260040160405180910390fd5b336000908152600f6020526040812080546001600160601b03841692906113ce90849061445a565b92505081905550806001600160601b0316600a60008282546113f0919061445a565b909155506114099050826001600160601b038316612be1565b5050565b61141561292c565b6040805180820182526000916114449190849060029083908390808284376000920191909152506122a7915050565b6000818152600d60205260409020549091506001600160a01b03161561148057604051634a0b8fa760e01b815260048101829052602401610aaa565b6000818152600d6020908152604080832080546001600160a01b0319166001600160a01b038816908117909155600e805460018101825594527fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610d0a565b61151461292c565b61151e6000612cbb565b565b6001600160401b03821660009081526006602052604090205482906001600160a01b03168061156257604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b0382161461159657604051636c51fda960e11b81526001600160a01b0382166004820152602401610aaa565b601154600160301b900460ff16156115c15760405163769dd35360e11b815260040160405180910390fd5b6001600160401b038416600090815260066020526040902060020154606414156115fe576040516305a48e0f60e01b815260040160405180910390fd5b6001600160a01b03831660009081526005602090815260408083206001600160401b038089168552925290912054161561163757610b7d565b6001600160a01b03831660008181526005602090815260408083206001600160401b038916808552908352818420805467ffffffffffffffff19166001908117909155600684528285206002018054918201815585529383902090930180546001600160a01b031916851790555192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e09101610b74565b601154600160301b900460ff16156116fd5760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0381166000908152600660205260409020546001600160a01b031661173c57604051630fb532db60e11b815260040160405180910390fd5b6001600160401b0381166000908152600660205260409020600101546001600160a01b031633146117a9576001600160401b0381166000908152600660205260409081902060010154905163d084e97560e01b81526001600160a01b039091166004820152602401610aaa565b6001600160401b0381166000818152600660209081526040918290208054336001600160a01b0319808316821784556001909301805490931690925583516001600160a01b03909116808252928101919091529092917f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f091015b60405180910390a25050565b6001600160401b03821660009081526006602052604090205482906001600160a01b03168061187157604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b038216146118a557604051636c51fda960e11b81526001600160a01b0382166004820152602401610aaa565b601154600160301b900460ff16156118d05760405163769dd35360e11b815260040160405180910390fd5b6118d9846124c0565b156118f757604051631685ecdd60e31b815260040160405180910390fd5b6001600160a01b03831660009081526005602090815260408083206001600160401b0380891685529252909120541661195d57604051637800cff360e11b81526001600160401b03851660048201526001600160a01b0384166024820152604401610aaa565b6001600160401b0384166000908152600660209081526040808320600201805482518185028101850190935280835291929091908301828280156119ca57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116119ac575b505050505090506000600182516119e1919061445a565b905060005b8251811015611b0857856001600160a01b0316838281518110611a0b57611a0b614509565b60200260200101516001600160a01b03161415611af6576000838381518110611a3657611a36614509565b6020026020010151905080600660008a6001600160401b03166001600160401b031681526020019081526020016000206002018381548110611a7a57611a7a614509565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394909416939093179092556001600160401b038a168152600690915260409020600201805480611ace57611ace6144f3565b600082815260209020810160001990810180546001600160a01b031916905501905550611b08565b80611b0081614471565b9150506119e6565b506001600160a01b03851660008181526005602090815260408083206001600160401b038b1680855290835292819020805467ffffffffffffffff191690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b910160405180910390a2505050505050565b601154600090600160301b900460ff1615611bae5760405163769dd35360e11b815260040160405180910390fd5b600980546001600160401b0316906000611bc78361448c565b82546101009290920a6001600160401b03818102199093169183160217909155600954169050600080604051908082528060200260200182016040528015611c19578160200160208202803683370190505b50604080518082018252600080825260208083018281526001600160401b0388168084526007835285842094518555905160019485015584516060810186523381528083018481528187018881529285526006845295909320835181546001600160a01b03199081166001600160a01b03928316178355965195820180549097169516949094179094559251805194955090939192611cc092600285019290910190613aff565b50506040513381526001600160401b03841691507f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a250905090565b6001600160401b038116600090815260066020526040812054819081906060906001600160a01b0316611d4d57604051630fb532db60e11b815260040160405180910390fd5b6001600160401b0385166000908152600760209081526040808320805460019091015460068452938290208054600290910180548451818702810187019095528085529295946001600160a01b039092169390929091839190830182828015611ddf57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611dc1575b5050505050905093509350935093509193509193565b601154600090600160301b900460ff1615611e235760405163769dd35360e11b815260040160405180910390fd5b60005a90506000806000611e378787612d0b565b9250925092506000866060015163ffffffff166001600160401b03811115611e6157611e6161451f565b604051908082528060200260200182016040528015611e8a578160200160208202803683370190505b50905060005b876060015163ffffffff16811015611efe5760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c828281518110611ee157611ee1614509565b602090810291909101015280611ef681614471565b915050611e90565b50600083815260106020526040808220829055518190631fe543e360e01b90611f2d9087908690602401614303565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092526011805466ff0000000000001916600160301b179055908a015160808b0151919250600091611f959163ffffffff169084612f78565b6011805466ff000000000000191690556020808c0180516001600160401b03908116600090815260079093526040808420600190810154935190921684528320810180549495509193909290611fec9084906143e4565b9091555050601154600090612019908b90600160381b900463ffffffff166120138561266b565b3a612fc6565b6020808e01516001600160401b031660009081526007909152604090205490915081111561205a57604051631e9acf1760e31b815260040160405180910390fd5b6020808d01516001600160401b03166000908152600790915260408120805483929061208790849061445a565b909155505060045460649061209c908261445a565b6120a6908361443b565b6120b09190614427565b60008a8152600d60209081526040808320546001600160a01b03168352600f909152812080549091906120e49084906143e4565b90915550506004546064906120f9908361443b565b6121039190614427565b6003600082825461211491906143e4565b9091555050604080518881526020810183905284151581830152905189917f221ad2e5b871cead1dd7f75c2fb223c0cfa34bdc049a15f3f82a1f0e943e605a919081900360600190a299505050505050505050505b92915050565b601154600160301b900460ff161561219a5760405163769dd35360e11b815260040160405180910390fd5b600b805490349060006121ad83856143e4565b9091555050600b546040805183815260208101929092527ff09ca6cef38280114ac05d60379acf68fa7f85ea69ec4a8ed2a28626acafc06391015b60405180910390a150565b6121fb61292c565b6001600160a01b0382166122225760405163f0a7640b60e01b815260040160405180910390fd5b60648111156122445760405163197fcbd760e11b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b039390931692909217909155600455565b61227261292c565b600c8190556040518181527f4c42db8a799110fdd6a26148a21a5fbe4e581c926bccfd3b2d8a7f3aed4a87c8906020016121e8565b6000816040516020016122ba91906141b7565b604051602081830303815290604052805190602001209050919050565b6001600160401b03821660009081526006602052604090205482906001600160a01b03168061231957604051630fb532db60e11b815260040160405180910390fd5b336001600160a01b0382161461234d57604051636c51fda960e11b81526001600160a01b0382166004820152602401610aaa565b601154600160301b900460ff16156123785760405163769dd35360e11b815260040160405180910390fd5b612381846124c0565b1561239f57604051631685ecdd60e31b815260040160405180910390fd5b610b7d8484612986565b6123b161292c565b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527fa5a618fb1d2db8fcece4bcc171d33525bb774a19485486ba6f8ee0c50d59442a910160405180910390a15050565b61241a61292c565b600b5460009061242b90303161445a565b600a549091508181111561245c576040516354ced18160e11b81526004810182905260248101839052604401610aaa565b818110156124bb576000612470828461445a565b905061247c8482612be1565b604080516001600160a01b0386168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b4366009101610f09565b505050565b6001600160401b0381166000908152600660209081526040808320815160608101835281546001600160a01b039081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561255457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612536575b505050505081525050905060005b8160400151518110156126615760005b600e5481101561264e576000612617600e838154811061259457612594614509565b9060005260206000200154856040015185815181106125b5576125b5614509565b60200260200101518860056000896040015189815181106125d8576125d8614509565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081206001600160401b03808f1683529352205416612c41565b506000818152601060205260409020549091501561263b5750600195945050505050565b508061264681614471565b915050612572565b508061265981614471565b915050612562565b5060009392505050565b604080516101208101825260125463ffffffff8082168352640100000000820481166020840152600160401b8204811693830193909352600160601b810483166060830152600160801b8104909216608082015262ffffff600160a01b8304811660a08301819052600160b81b8404821660c0840152600160d01b8404821660e0840152600160e81b90930416610100820152600091831161270e575192915050565b828160a0015162ffffff1610801561272f57508060c0015162ffffff168311155b1561273e576020015192915050565b828160c0015162ffffff1610801561275f57508060e0015162ffffff168311155b1561276e576040015192915050565b828160e0015162ffffff16108015612790575080610100015162ffffff168311155b1561279f576060015192915050565b6080015192915050565b601154600160301b900460ff16156127d45760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0381166000908152600660205260409020546001600160a01b031661281357604051630fb532db60e11b815260040160405180910390fd5b6001600160401b03811660009081526007602052604081208054916001600160601b033416919061284483856143e4565b92505081905550346001600160601b0316600a600082825461286691906143e4565b90915550506001600160401b0382167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f8826128a134826143e4565b60408051928352602083019190915201611823565b6128be61292c565b6001600160a01b0381166129235760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610aaa565b610a3581612cbb565b6000546001600160a01b0316331461151e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610aaa565b601154600160301b900460ff16156129b15760405163769dd35360e11b815260040160405180910390fd5b6001600160401b0382166000908152600660209081526040808320815160608101835281546001600160a01b03908116825260018301541681850152600282018054845181870281018701865281815292959394860193830182828015612a4157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612a23575b505050919092525050506001600160401b03841660009081526007602090815260408083208151808301909252805480835260019091015492820192909252929350905b836040015151811015612b06576005600085604001518381518110612aac57612aac614509565b6020908102919091018101516001600160a01b0316825281810192909252604090810160009081206001600160401b038a1682529092529020805467ffffffffffffffff1916905580612afe81614471565b915050612a85565b506001600160401b038516600090815260066020526040812080546001600160a01b03199081168255600182018054909116905590612b486002830182613b64565b50506001600160401b0385166000908152600760205260408120818155600101819055600a8054839290612b7d90849061445a565b90915550612b8d90508482612be1565b604080516001600160a01b0386168152602081018390526001600160401b038716917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612c2e576040519150601f19603f3d011682016040523d82523d6000602084013e612c33565b606091505b50509050806124bb57600080fd5b6040805160208082018790526001600160a01b0395909516818301526001600160401b039384166060820152919092166080808301919091528251808303909101815260a08201835280519084012060c082019490945260e080820185905282518083039091018152610100909101909152805191012091565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000806000612d1d85600001516122a7565b6000818152600d60205260409020549093506001600160a01b031680612d5957604051631dfd6e1360e21b815260048101859052602401610aaa565b6080860151604051612d78918691602001918252602082015260400190565b60408051601f1981840301815291815281516020928301206000818152601090935291205490935080612dbe57604051631b44092560e11b815260040160405180910390fd5b85516020808801516040808a015160608b015160808c01519251612e29968b9690959491019586526001600160401b03948516602087015292909316604085015263ffffffff90811660608501529190911660808301526001600160a01b031660a082015260c00190565b604051602081830303815290604052805190602001208114612e5e5760405163354a450b60e21b815260040160405180910390fd5b85516001600160401b03164080612f24576001548751604051631d2827a760e31b81526001600160401b0390911660048201526001600160a01b039091169063e9413d389060240160206040518083038186803b158015612ebe57600080fd5b505afa158015612ed2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef69190613e64565b905080612f2457865160405163175dadad60e01b81526001600160401b039091166004820152602401610aaa565b6000886080015182604051602001612f46929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c9050612f6b8982613021565b9450505050509250925092565b60005a611388811015612f8a57600080fd5b611388810390508460408204820311612fa257600080fd5b50823b612fae57600080fd5b60008083516020850160008789f190505b9392505050565b6000805a612fd487876143e4565b612fde919061445a565b612fe8908461443b565b9050600061300163ffffffff861664e8d4a5100061443b565b905061300d81836143e4565b6001600160601b0316979650505050505050565b60006130558360000151846020015185604001518660600151868860a001518960c001518a60e001518b610100015161308c565b6003836020015160405160200161306d9291906142ef565b60408051601f1981840301815291905280516020909101209392505050565b613095896132af565b6130e15760405162461bcd60e51b815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e2063757276650000000000006044820152606401610aaa565b6130ea886132af565b61312e5760405162461bcd60e51b815260206004820152601560248201527467616d6d61206973206e6f74206f6e20637572766560581b6044820152606401610aaa565b613137836132af565b6131835760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e2063757276650000006044820152606401610aaa565b61318c826132af565b6131d85760405162461bcd60e51b815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e206375727665000000006044820152606401610aaa565b6131e4878a8887613372565b6132305760405162461bcd60e51b815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e657373000000000000006044820152606401610aaa565b600061323c8a87613495565b9050600061324f898b878b8689896134f9565b90506000613260838d8d8a8661361e565b9050808a146132a15760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b210383937b7b360991b6044820152606401610aaa565b505050505050505050505050565b80516000906401000003d019116132fd5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420782d6f7264696e61746560701b6044820152606401610aaa565b60208201516401000003d0191161334b5760405162461bcd60e51b8152602060048201526012602482015271696e76616c696420792d6f7264696e61746560701b6044820152606401610aaa565b60208201516401000003d01990800961336b8360005b602002015161365e565b1492915050565b60006001600160a01b0382166133b85760405162461bcd60e51b815260206004820152600b60248201526a626164207769746e65737360a81b6044820152606401610aaa565b6020840151600090600116156133cf57601c6133d2565b601b5b9050600070014551231950b75fc4402da1732fc9bebe1985876000602002015109865170014551231950b75fc4402da1732fc9bebe19918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa15801561346d573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b61349d613b82565b6134ca600184846040516020016134b693929190614196565b604051602081830303815290604052613682565b90505b6134d6816132af565b6121695780516040805160208101929092526134f291016134b6565b90506134cd565b613501613b82565b825186516401000003d01990819006910614156135605760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e637400006044820152606401610aaa565b61356b8789886136d1565b6135b05760405162461bcd60e51b8152602060048201526016602482015275119a5c9cdd081b5d5b0818da1958dac819985a5b195960521b6044820152606401610aaa565b6135bb8486856136d1565b6136075760405162461bcd60e51b815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c65640000000000000000006044820152606401610aaa565b6136128684846137f9565b98975050505050505050565b60006002868686858760405160200161363c96959493929190614137565b60408051601f1981840301815291905280516020909101209695505050505050565b6000806401000003d01980848509840990506401000003d019600782089392505050565b61368a613b82565b613693826138c0565b81526136a86136a3826000613361565b6138fb565b6020820181905260029006600114156136cc576020810180516401000003d0190390525b919050565b60008261370e5760405162461bcd60e51b815260206004820152600b60248201526a3d32b9379039b1b0b630b960a91b6044820152606401610aaa565b83516020850151600090613724906002906144b3565b1561373057601c613733565b601b5b9050600070014551231950b75fc4402da1732fc9bebe198387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa1580156137a5573d6000803e3d6000fd5b5050506020604051035190506000866040516020016137c49190614125565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b613801613b82565b8351602080860151855191860151600093849384936138229390919061391b565b919450925090506401000003d0198582096001146138825760405162461bcd60e51b815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a000000000000006044820152606401610aaa565b60405180604001604052806401000003d019806138a1576138a16144dd565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d01981106136cc576040805160208082019390935281518082038401815290820190915280519101206138c8565b60006121698260026139146401000003d01960016143e4565b901c6139fb565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a089050600061395b83838585613a92565b909850905061396c88828e88613ab6565b909850905061397d88828c87613ab6565b909850905060006139908d878b85613ab6565b90985090506139a188828686613a92565b90985090506139b288828e89613ab6565b90985090508181146139e7576401000003d019818a0998506401000003d01982890997506401000003d01981830996506139eb565b8196505b5050505050509450945094915050565b600080613a06613ba0565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a0820152613a38613bbe565b60208160c0846005600019fa925082613a885760405162461bcd60e51b81526020600482015260126024820152716269674d6f64457870206661696c7572652160701b6044820152606401610aaa565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b828054828255906000526020600020908101928215613b54579160200282015b82811115613b5457825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190613b1f565b50613b60929150613bdc565b5090565b5080546000825590600052602060002090810190610a359190613bdc565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b80821115613b605760008155600101613bdd565b80356001600160a01b03811681146136cc57600080fd5b806040810183101561216957600080fd5b600082601f830112613c2a57600080fd5b604051604081018181106001600160401b0382111715613c4c57613c4c61451f565b8060405250808385604086011115613c6357600080fd5b60005b6002811015613c85578135835260209283019290910190600101613c66565b509195945050505050565b600060a08284031215613ca257600080fd5b60405160a081018181106001600160401b0382111715613cc457613cc461451f565b604052905080613cd383613d59565b8152613ce160208401613d59565b6020820152613cf260408401613d45565b6040820152613d0360608401613d45565b6060820152613d1460808401613bf1565b60808201525092915050565b803561ffff811681146136cc57600080fd5b803562ffffff811681146136cc57600080fd5b803563ffffffff811681146136cc57600080fd5b80356001600160401b03811681146136cc57600080fd5b600060208284031215613d8257600080fd5b612fbf82613bf1565b60008060608385031215613d9e57600080fd5b613da783613bf1565b9150613db68460208501613c08565b90509250929050565b60008060408385031215613dd257600080fd5b613ddb83613bf1565b946020939093013593505050565b60008060408385031215613dfc57600080fd5b613e0583613bf1565b915060208301356001600160601b0381168114613e2157600080fd5b809150509250929050565b600060408284031215613e3e57600080fd5b612fbf8383613c08565b600060408284031215613e5a57600080fd5b612fbf8383613c19565b600060208284031215613e7657600080fd5b5051919050565b600080600080600060a08688031215613e9557600080fd5b85359450613ea560208701613d59565b9350613eb360408701613d20565b9250613ec160608701613d45565b9150613ecf60808701613d45565b90509295509295909350565b600080828403610240811215613ef057600080fd5b6101a080821215613f0057600080fd5b613f086143bb565b9150613f148686613c19565b8252613f238660408701613c19565b60208301526080850135604083015260a0850135606083015260c08501356080830152613f5260e08601613bf1565b60a0830152610100613f6687828801613c19565b60c0840152613f79876101408801613c19565b60e08401526101808601358184015250819350613f9886828701613c90565b925050509250929050565b600080600080848603610180811215613fbb57600080fd5b613fc486613d20565b9450613fd260208701613d45565b9350613fe060408701613d45565b925061012080605f1983011215613ff657600080fd5b613ffe6143bb565b915061400c60608801613d45565b825261401a60808801613d45565b602083015261402b60a08801613d45565b604083015261403c60c08801613d45565b606083015261404d60e08801613d45565b6080830152610100614060818901613d32565b60a0840152614070828901613d32565b60c08401526140826101408901613d32565b60e08401526140946101608901613d32565b9083015250939692955090935050565b6000602082840312156140b657600080fd5b5035919050565b6000602082840312156140cf57600080fd5b612fbf82613d59565b600080604083850312156140eb57600080fd5b6140f483613d59565b9150613db660208401613bf1565b8060005b6002811015610b7d578151845260209384019390910190600101614106565b61412f8183614102565b604001919050565b8681526141476020820187614102565b6141546060820186614102565b61416160a0820185614102565b61416e60e0820184614102565b60609190911b6bffffffffffffffffffffffff19166101208201526101340195945050505050565b8381526141a66020820184614102565b606081019190915260800192915050565b604081016121698284614102565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b81811015614216578451835293830193918301916001016141fa565b509098975050505050505050565b60006101808201905061ffff8616825263ffffffff808616602084015280851660408401528354818116606085015261426a60808501838360201c1663ffffffff169052565b61428160a08501838360401c1663ffffffff169052565b61429860c08501838360601c1663ffffffff169052565b6142af60e08501838360801c1663ffffffff169052565b62ffffff60a082901c811661010086015260b882901c811661012086015260d082901c1661014085015260e81c6101609093019290925295945050505050565b82815260608101612fbf6020830184614102565b6000604082018483526020604081850152818551808452606086019150828701935060005b8181101561434457845183529383019391830191600101614328565b5090979650505050505050565b84815260208082018590526001600160a01b038481166040840152608060608401819052845190840181905260009285810192909160a0860190855b818110156143ab57855184168352948401949184019160010161438d565b50909a9950505050505050505050565b60405161012081016001600160401b03811182821017156143de576143de61451f565b60405290565b600082198211156143f7576143f76144c7565b500190565b60006001600160401b0380831681851680830382111561441e5761441e6144c7565b01949350505050565b600082614436576144366144dd565b500490565b6000816000190483118215151615614455576144556144c7565b500290565b60008282101561446c5761446c6144c7565b500390565b6000600019821415614485576144856144c7565b5060010190565b60006001600160401b03808316818114156144a9576144a96144c7565b6001019392505050565b6000826144c2576144c26144dd565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfea2646970667358221220119a51b9e3a6e198294494ca5d1018d1aa522d9be4880f69a71cd441a3cc49d264736f6c63430008060033