Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- ConsumerMock
- Optimization enabled
- true
- Compiler version
- v0.8.6+commit.11564f7e
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2024-05-20T14:54:03.337413Z
Constructor Arguments
000000000000000000000000564115c47d47ff4628155b9d9f9c3b61cf1f89f2
Arg [0] (address) : 0x564115c47d47ff4628155b9d9f9c3b61cf1f89f2
contracts/Mocks/ConsumerMock.sol
pragma solidity ^0.8.6; import "../vrf/VRFConsumerBaseV2.sol"; import "../vrf/ErinaceusVRF.sol"; import "hardhat/console.sol"; contract ConsumerMock is VRFConsumerBaseV2 { ErinaceusVRF public erinaceus; uint256[] public random; // uint public constant totalSupply = 10**27; // string public constant name = "ChainLink Token"; // uint8 public constant decimals = 18; // string public constant symbol = "LINK"; // function LinkToken() // public // { // balances[msg.sender] = totalSupply; // } mapping(uint256 => uint256[]) public randomWord; uint256 public currentId; constructor(address erinaceusVRF)VRFConsumerBaseV2(erinaceusVRF){ erinaceus = ErinaceusVRF(erinaceusVRF); } function getRandom(uint256 id) public view returns (uint256[] memory){ return randomWord[id]; } function getRandom1() public view returns (uint256[] memory){ return random; } function generateRandomWords( bytes32 keyHash, uint64 subId, uint16 requestConfirmations, uint32 callbackGasLimit, uint32 numWords ) external { erinaceus.requestRandomWords( keyHash, subId, requestConfirmations, callbackGasLimit, numWords ); } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual override { // random = randomWords; randomWord[currentId] = randomWords; currentId++; } function test(uint256[] memory randomWords) external { random = randomWords; randomWord[currentId] = randomWords; } }
@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, uint256 _reward) external; 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/interfaces/IWFTN.sol
// SPDX-License-Identifier: MIT // Copyright (C) 2024 Fasttoken // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.8.6; interface IWFTN { receive() external payable; function deposit() external payable; function withdraw(uint256 amount_) external; function balanceOf(address account) external view returns (uint256); function totalSupply() external view returns (uint256); function approve(address to_, uint256 amount_) external returns (bool); function transfer(address to_, uint256 amount_) external returns (bool); function transferFrom(address src_, address to_, uint256 amount_) external returns (bool); }
contracts/vrf/ErinaceusVRF.sol
// SPDX-License-Identifier: MIT pragma solidity 0.8.6; import {VRF} from "./VRF.sol"; import {IWFTN} from "../interfaces/IWFTN.sol"; import {VRFConsumerBaseV2} from "./VRFConsumerBaseV2.sol"; import {IHashHub} from "../interfaces/IHashHub.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ErinaceusVRFInterface} from "./interfaces/ErinaceusVRFInterface.sol"; import "hardhat/console.sol"; contract ErinaceusVRF is VRF, Ownable, ErinaceusVRFInterface { // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i IWFTN public wFTN; IHashHub public HashHub; address public team; uint256 public withdrawableForTeam; uint256 public feePercentage; // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i // 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 wFTN 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 wFTN sent to/from // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. // A discrepancy with this contract's wFTN 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 */ /* wFTN 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 wFTN // So fee range is [0, 2^32/10^6]. uint32 fulfillmentFlatFeeWFTNPPMTier1; uint32 fulfillmentFlatFeeWFTNPPMTier2; uint32 fulfillmentFlatFeeWFTNPPMTier3; uint32 fulfillmentFlatFeeWFTNPPMTier4; uint32 fulfillmentFlatFeeWFTNPPMTier5; uint24 reqsForTier2; uint24 reqsForTier3; uint24 reqsForTier4; uint24 reqsForTier5; } event ConfigSet( uint16 minimumRequestConfirmations, uint32 maxGasLimit, uint32 gasAfterPaymentCalculation, FeeConfig feeConfig ); constructor( address payable _wFTN, address _hashHub, address _owner ){ wFTN = IWFTN(_wFTN); 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 fulfillmentFlatFeeWFTNPPMTier1, uint32 fulfillmentFlatFeeWFTNPPMTier2, uint32 fulfillmentFlatFeeWFTNPPMTier3, uint32 fulfillmentFlatFeeWFTNPPMTier4, uint32 fulfillmentFlatFeeWFTNPPMTier5, uint24 reqsForTier2, uint24 reqsForTier3, uint24 reqsForTier4, uint24 reqsForTier5 ) { return ( s_feeConfig.fulfillmentFlatFeeWFTNPPMTier1, s_feeConfig.fulfillmentFlatFeeWFTNPPMTier2, s_feeConfig.fulfillmentFlatFeeWFTNPPMTier3, s_feeConfig.fulfillmentFlatFeeWFTNPPMTier4, s_feeConfig.fulfillmentFlatFeeWFTNPPMTier5, 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 = wFTN.balanceOf(address(this)) - HashHubBalance; uint256 internalBalance = uint256(s_totalBalance); if (internalBalance > externalBalance) { revert BalanceInvariantViolated(internalBalance, externalBalance); } if (internalBalance < externalBalance) { uint256 amount = externalBalance - internalBalance; wFTN.transfer(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){ wFTN.approve(address(HashHub), HashHubReward); HashHub.requestBlockhash(block.number, HashHubReward); 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 wFTN PPM */ function getFeeTier(uint256 reqCount) public view returns (uint32) { FeeConfig memory fc = s_feeConfig; if (0 <= reqCount && reqCount <= fc.reqsForTier2) { return fc.fulfillmentFlatFeeWFTNPPMTier1; } if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) { return fc.fulfillmentFlatFeeWFTNPPMTier2; } if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) { return fc.fulfillmentFlatFeeWFTNPPMTier3; } if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) { return fc.fulfillmentFlatFeeWFTNPPMTier4; } return fc.fulfillmentFlatFeeWFTNPPMTier5; } /* * @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 wFTN fee to the payment amount. // Its specified in millionths of wFTN, if s_config.fulfillmentFlatFeeWFTNPPM = 1 // 1 wFTN / 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 fulfillmentFlatFeeWFTNPPM, uint256 weiPerUnitGas ) internal view returns (uint256) { //(juels/link) (wei/gas * gas) = juels uint256 paymentNoFee = weiPerUnitGas * (gasAfterPaymentCalculation + startGas - gasleft()); uint256 fee = 1e12 * uint256(fulfillmentFlatFeeWFTNPPM); return uint96(paymentNoFee + fee); } /* * @notice Oracle withdraw wFTN 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 (!wFTN.transfer(recipient, amount)) { revert InsufficientBalance(); } } function withdrawForTeam(uint256 amount) external nonReentrant{ if (withdrawableForTeam < amount) { revert InsufficientBalance(); } withdrawableForTeam -= amount; s_totalBalance -= amount; if (!wFTN.transfer(team, amount)) { revert InsufficientBalance(); } } function fundSubscribtion(uint256 amount, uint64 subId) external 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(amount); s_totalBalance += uint96(amount); wFTN.transferFrom(msg.sender, address(this), amount); emit SubscriptionFunded(subId, oldBalance, oldBalance + amount); } function fundHashHub(uint256 amount) external nonReentrant{ wFTN.transferFrom(msg.sender, address(this), amount); uint256 oldBalance = HashHubBalance; HashHubBalance += amount; 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 (!wFTN.transfer(to, uint256(balance))) { revert InsufficientBalance(); } 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; } 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(); } _; } }
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); }
hardhat/console.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; library console { address constant CONSOLE_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67; function _sendLogPayloadImplementation(bytes memory payload) internal view { address consoleAddress = CONSOLE_ADDRESS; /// @solidity memory-safe-assembly assembly { pop( staticcall( gas(), consoleAddress, add(payload, 32), mload(payload), 0, 0 ) ) } } function _castToPure( function(bytes memory) internal view fnIn ) internal pure returns (function(bytes memory) pure fnOut) { assembly { fnOut := fnIn } } function _sendLogPayload(bytes memory payload) internal pure { _castToPure(_sendLogPayloadImplementation)(payload); } function log() internal pure { _sendLogPayload(abi.encodeWithSignature("log()")); } function logInt(int256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); } function logUint(uint256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function logString(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function logBool(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function logAddress(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function logBytes(bytes memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } function logBytes1(bytes1 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } function logBytes2(bytes2 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } function logBytes3(bytes3 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } function logBytes4(bytes4 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } function logBytes5(bytes5 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } function logBytes6(bytes6 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } function logBytes7(bytes7 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } function logBytes8(bytes8 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } function logBytes9(bytes9 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } function logBytes10(bytes10 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } function logBytes11(bytes11 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } function logBytes12(bytes12 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } function logBytes13(bytes13 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } function logBytes14(bytes14 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } function logBytes15(bytes15 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } function logBytes16(bytes16 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } function logBytes17(bytes17 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } function logBytes18(bytes18 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } function logBytes19(bytes19 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } function logBytes20(bytes20 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } function logBytes21(bytes21 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } function logBytes22(bytes22 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } function logBytes23(bytes23 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } function logBytes24(bytes24 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } function logBytes25(bytes25 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } function logBytes26(bytes26 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } function logBytes27(bytes27 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } function logBytes28(bytes28 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } function logBytes29(bytes29 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } function logBytes30(bytes30 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } function logBytes31(bytes31 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } function logBytes32(bytes32 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } function log(uint256 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } function log(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function log(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function log(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function log(uint256 p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); } function log(uint256 p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); } function log(uint256 p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); } function log(uint256 p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); } function log(string memory p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); } function log(string memory p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } function log(string memory p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } function log(string memory p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } function log(bool p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); } function log(bool p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } function log(bool p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } function log(bool p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } function log(address p0, uint256 p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); } function log(address p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } function log(address p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } function log(address p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } function log(uint256 p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); } function log(uint256 p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); } function log(uint256 p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); } function log(uint256 p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); } function log(uint256 p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); } function log(uint256 p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); } function log(uint256 p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); } function log(uint256 p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); } function log(uint256 p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); } function log(uint256 p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); } function log(uint256 p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); } function log(uint256 p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); } function log(uint256 p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); } function log(string memory p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); } function log(string memory p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); } function log(string memory p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); } function log(string memory p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); } function log(string memory p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); } function log(string memory p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } function log(string memory p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } function log(string memory p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } function log(string memory p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); } function log(string memory p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } function log(string memory p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } function log(string memory p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } function log(string memory p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); } function log(string memory p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } function log(string memory p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } function log(string memory p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } function log(bool p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); } function log(bool p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); } function log(bool p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); } function log(bool p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); } function log(bool p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); } function log(bool p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } function log(bool p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } function log(bool p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } function log(bool p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); } function log(bool p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } function log(bool p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } function log(bool p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } function log(bool p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); } function log(bool p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } function log(bool p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } function log(bool p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } function log(address p0, uint256 p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); } function log(address p0, uint256 p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); } function log(address p0, uint256 p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); } function log(address p0, uint256 p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); } function log(address p0, string memory p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); } function log(address p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } function log(address p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } function log(address p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } function log(address p0, bool p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); } function log(address p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } function log(address p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } function log(address p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } function log(address p0, address p1, uint256 p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); } function log(address p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } function log(address p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } function log(address p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); } function log(uint256 p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); } function log(bool p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); } function log(address p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } }
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"metadata":{"useLiteralContent":true},"libraries":{}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"erinaceusVRF","internalType":"address"}]},{"type":"error","name":"OnlyErinaceusCanFulfill","inputs":[{"type":"address","name":"have","internalType":"address"},{"type":"address","name":"want","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ErinaceusVRF"}],"name":"erinaceus","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"generateRandomWords","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":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getRandom","inputs":[{"type":"uint256","name":"id","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getRandom1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"random","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"randomWord","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rawFulfillRandomWords","inputs":[{"type":"uint256","name":"requestId","internalType":"uint256"},{"type":"uint256[]","name":"randomWords","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"test","inputs":[{"type":"uint256[]","name":"randomWords","internalType":"uint256[]"}]}]
Contract Creation Code
0x60a060405234801561001057600080fd5b5060405161080538038061080583398101604081905261002f91610065565b6001600160601b0319606082901b16608052600080546001600160a01b0319166001600160a01b03909216919091179055610095565b60006020828403121561007757600080fd5b81516001600160a01b038116811461008e57600080fd5b9392505050565b60805160601c61074b6100ba6000396000818161017701526101b9015261074b6000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063ca16068411610066578063ca160684146100ff578063cd4b691414610112578063dd4e592014610125578063e00dd16114610138578063fe1d45b01461014157600080fd5b80631fe543e31461009857806395d430f2146100ad578063b863bd37146100cb578063c92d973f146100ec575b600080fd5b6100ab6100a6366004610629565b61016c565b005b6100b56101f8565b6040516100c29190610692565b60405180910390f35b6100de6100d93660046105f7565b610250565b6040519081526020016100c2565b6100ab6100fa366004610581565b610271565b6100ab61010d366004610544565b610325565b6100b56101203660046105f7565b61035b565b6100de610133366004610670565b6103bd565b6100de60035481565b600054610154906001600160a01b031681565b6040516001600160a01b0390911681526020016100c2565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101ea57604051631cdc5ebb60e01b81523360048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016602482015260440160405180910390fd5b6101f482826103ee565b5050565b6060600180548060200260200160405190810160405280929190818152602001828054801561024657602002820191906000526020600020905b815481526020019060010190808311610232575b5050505050905090565b6001818154811061026057600080fd5b600091825260209091200154905081565b6000546040516305d3b1d360e41b81526004810187905267ffffffffffffffff8616602482015261ffff8516604482015263ffffffff8085166064830152831660848201526001600160a01b0390911690635d3b1d309060a401602060405180830381600087803b1580156102e557600080fd5b505af11580156102f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061031d9190610610565b505050505050565b805161033890600190602084019061042a565b50600354600090815260026020908152604090912082516101f49284019061042a565b6000818152600260209081526040918290208054835181840281018401909452808452606093928301828280156103b157602002820191906000526020600020905b81548152602001906001019080831161039d575b50505050509050919050565b600260205281600052604060002081815481106103d957600080fd5b90600052602060002001600091509150505481565b600354600090815260026020908152604090912082516104109284019061042a565b5060038054906000610421836106d6565b91905055505050565b828054828255906000526020600020908101928215610465579160200282015b8281111561046557825182559160200191906001019061044a565b50610471929150610475565b5090565b5b808211156104715760008155600101610476565b600082601f83011261049b57600080fd5b8135602067ffffffffffffffff808311156104b8576104b86106ff565b8260051b604051601f19603f830116810181811084821117156104dd576104dd6106ff565b604052848152838101925086840182880185018910156104fc57600080fd5b600092505b8583101561051f578035845292840192600192909201918401610501565b50979650505050505050565b803563ffffffff8116811461053f57600080fd5b919050565b60006020828403121561055657600080fd5b813567ffffffffffffffff81111561056d57600080fd5b6105798482850161048a565b949350505050565b600080600080600060a0868803121561059957600080fd5b85359450602086013567ffffffffffffffff811681146105b857600080fd5b9350604086013561ffff811681146105cf57600080fd5b92506105dd6060870161052b565b91506105eb6080870161052b565b90509295509295909350565b60006020828403121561060957600080fd5b5035919050565b60006020828403121561062257600080fd5b5051919050565b6000806040838503121561063c57600080fd5b82359150602083013567ffffffffffffffff81111561065a57600080fd5b6106668582860161048a565b9150509250929050565b6000806040838503121561068357600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156106ca578351835292840192918401916001016106ae565b50909695505050505050565b60006000198214156106f857634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052604160045260246000fdfea26469706673582212205ae26ee7ff09efde67974496d9b74a1a6fef13e7f1f459e62b0adf3d33df4e3c64736f6c63430008060033000000000000000000000000564115c47d47ff4628155b9d9f9c3b61cf1f89f2
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100935760003560e01c8063ca16068411610066578063ca160684146100ff578063cd4b691414610112578063dd4e592014610125578063e00dd16114610138578063fe1d45b01461014157600080fd5b80631fe543e31461009857806395d430f2146100ad578063b863bd37146100cb578063c92d973f146100ec575b600080fd5b6100ab6100a6366004610629565b61016c565b005b6100b56101f8565b6040516100c29190610692565b60405180910390f35b6100de6100d93660046105f7565b610250565b6040519081526020016100c2565b6100ab6100fa366004610581565b610271565b6100ab61010d366004610544565b610325565b6100b56101203660046105f7565b61035b565b6100de610133366004610670565b6103bd565b6100de60035481565b600054610154906001600160a01b031681565b6040516001600160a01b0390911681526020016100c2565b336001600160a01b037f000000000000000000000000564115c47d47ff4628155b9d9f9c3b61cf1f89f216146101ea57604051631cdc5ebb60e01b81523360048201526001600160a01b037f000000000000000000000000564115c47d47ff4628155b9d9f9c3b61cf1f89f216602482015260440160405180910390fd5b6101f482826103ee565b5050565b6060600180548060200260200160405190810160405280929190818152602001828054801561024657602002820191906000526020600020905b815481526020019060010190808311610232575b5050505050905090565b6001818154811061026057600080fd5b600091825260209091200154905081565b6000546040516305d3b1d360e41b81526004810187905267ffffffffffffffff8616602482015261ffff8516604482015263ffffffff8085166064830152831660848201526001600160a01b0390911690635d3b1d309060a401602060405180830381600087803b1580156102e557600080fd5b505af11580156102f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061031d9190610610565b505050505050565b805161033890600190602084019061042a565b50600354600090815260026020908152604090912082516101f49284019061042a565b6000818152600260209081526040918290208054835181840281018401909452808452606093928301828280156103b157602002820191906000526020600020905b81548152602001906001019080831161039d575b50505050509050919050565b600260205281600052604060002081815481106103d957600080fd5b90600052602060002001600091509150505481565b600354600090815260026020908152604090912082516104109284019061042a565b5060038054906000610421836106d6565b91905055505050565b828054828255906000526020600020908101928215610465579160200282015b8281111561046557825182559160200191906001019061044a565b50610471929150610475565b5090565b5b808211156104715760008155600101610476565b600082601f83011261049b57600080fd5b8135602067ffffffffffffffff808311156104b8576104b86106ff565b8260051b604051601f19603f830116810181811084821117156104dd576104dd6106ff565b604052848152838101925086840182880185018910156104fc57600080fd5b600092505b8583101561051f578035845292840192600192909201918401610501565b50979650505050505050565b803563ffffffff8116811461053f57600080fd5b919050565b60006020828403121561055657600080fd5b813567ffffffffffffffff81111561056d57600080fd5b6105798482850161048a565b949350505050565b600080600080600060a0868803121561059957600080fd5b85359450602086013567ffffffffffffffff811681146105b857600080fd5b9350604086013561ffff811681146105cf57600080fd5b92506105dd6060870161052b565b91506105eb6080870161052b565b90509295509295909350565b60006020828403121561060957600080fd5b5035919050565b60006020828403121561062257600080fd5b5051919050565b6000806040838503121561063c57600080fd5b82359150602083013567ffffffffffffffff81111561065a57600080fd5b6106668582860161048a565b9150509250929050565b6000806040838503121561068357600080fd5b50508035926020909101359150565b6020808252825182820181905260009190848201906040850190845b818110156106ca578351835292840192918401916001016106ae565b50909695505050505050565b60006000198214156106f857634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052604160045260246000fdfea26469706673582212205ae26ee7ff09efde67974496d9b74a1a6fef13e7f1f459e62b0adf3d33df4e3c64736f6c63430008060033