Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 230 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Op Poke_optimize... | 18896901 | 372 days ago | IN | 0 ETH | 0.0006854 | ||||
Op Poke_optimize... | 18893342 | 373 days ago | IN | 0 ETH | 0.00104874 | ||||
Op Poke_optimize... | 18892803 | 373 days ago | IN | 0 ETH | 0.00118239 | ||||
Op Poke_optimize... | 18892380 | 373 days ago | IN | 0 ETH | 0.00161371 | ||||
Op Poke_optimize... | 18892211 | 373 days ago | IN | 0 ETH | 0.00207338 | ||||
Op Poke_optimize... | 18890509 | 373 days ago | IN | 0 ETH | 0.00123478 | ||||
Op Poke_optimize... | 18884698 | 374 days ago | IN | 0 ETH | 0.00252306 | ||||
Op Poke_optimize... | 18884401 | 374 days ago | IN | 0 ETH | 0.00216148 | ||||
Op Poke_optimize... | 18882693 | 374 days ago | IN | 0 ETH | 0.00142155 | ||||
Op Poke_optimize... | 18880586 | 375 days ago | IN | 0 ETH | 0.00169064 | ||||
Op Poke_optimize... | 18880456 | 375 days ago | IN | 0 ETH | 0.00160276 | ||||
Op Poke_optimize... | 18879888 | 375 days ago | IN | 0 ETH | 0.00147011 | ||||
Op Poke_optimize... | 18878746 | 375 days ago | IN | 0 ETH | 0.00185426 | ||||
Op Poke_optimize... | 18876678 | 375 days ago | IN | 0 ETH | 0.0012175 | ||||
Op Poke_optimize... | 18876372 | 375 days ago | IN | 0 ETH | 0.00149281 | ||||
Op Poke_optimize... | 18875791 | 375 days ago | IN | 0 ETH | 0.00089158 | ||||
Op Poke_optimize... | 18872214 | 376 days ago | IN | 0 ETH | 0.00083573 | ||||
Op Poke_optimize... | 18864261 | 377 days ago | IN | 0 ETH | 0.00154767 | ||||
Op Poke_optimize... | 18860705 | 377 days ago | IN | 0 ETH | 0.00068664 | ||||
Op Poke_optimize... | 18851829 | 379 days ago | IN | 0 ETH | 0.00099475 | ||||
Op Poke_optimize... | 18842700 | 380 days ago | IN | 0 ETH | 0.00234634 | ||||
Op Poke_optimize... | 18842156 | 380 days ago | IN | 0 ETH | 0.00295299 | ||||
Op Poke_optimize... | 18840421 | 380 days ago | IN | 0 ETH | 0.00210017 | ||||
Op Poke_optimize... | 18839938 | 380 days ago | IN | 0 ETH | 0.00199861 | ||||
Op Poke_optimize... | 18839798 | 380 days ago | IN | 0 ETH | 0.0015848 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
18419932 | 439 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
Chronicle_ETH_USD_2
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 10000 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.16; import {IChronicle} from "chronicle-std/IChronicle.sol"; import {IScribeOptimistic} from "./IScribeOptimistic.sol"; import {IScribe} from "./IScribe.sol"; import {Scribe} from "./Scribe.sol"; import {LibSchnorr} from "./libs/LibSchnorr.sol"; import {LibSecp256k1} from "./libs/LibSecp256k1.sol"; /** * @title ScribeOptimistic * * @notice Scribe based optimistic Oracle with onchain fault resolution */ contract ScribeOptimistic is IScribeOptimistic, Scribe { using LibSchnorr for LibSecp256k1.Point; using LibSecp256k1 for LibSecp256k1.Point; using LibSecp256k1 for LibSecp256k1.Point[]; /// @dev The initial opChallengePeriod set during construction. uint16 private constant _INITIAL_OP_CHALLENGE_PERIOD = 1 hours; // -- Storage -- /// @inheritdoc IScribeOptimistic uint16 public opChallengePeriod; /// @inheritdoc IScribeOptimistic uint8 public opFeedIndex; /// @dev The truncated hash of the schnorrData provided in last opPoke. /// Binds the opFeed to their schnorrData. uint160 internal _schnorrDataCommitment; /// @dev The age of the pokeData provided in last opPoke. /// Ensures Schnorr signature can be verified after setting pokeData's /// age to block.timestamp during opPoke. uint32 internal _originalOpPokeDataAge; /// @dev opScribe's last opPoke'd value and corresponding age. PokeData internal _opPokeData; /// @inheritdoc IScribeOptimistic uint public maxChallengeReward; // -- Constructor and Receive Functionality -- constructor(address initialAuthed, bytes32 wat_) Scribe(initialAuthed, wat_) { // Note to have a non-zero challenge period. _setOpChallengePeriod(_INITIAL_OP_CHALLENGE_PERIOD); } receive() external payable {} // -- Poke Functionality -- function _poke(PokeData calldata pokeData, SchnorrData calldata schnorrData) internal override(Scribe) { // Load current age from storage. uint32 age = _currentPokeData().age; // Revert if pokeData stale. if (pokeData.age <= age) { revert StaleMessage(pokeData.age, age); } // Revert if pokeData from the future. if (pokeData.age > uint32(block.timestamp)) { revert FutureMessage(pokeData.age, uint32(block.timestamp)); } // Revert if schnorrData does not prove integrity of pokeData. bool ok; bytes memory err; // forgefmt: disable-next-item (ok, err) = _verifySchnorrSignature( constructPokeMessage(pokeData), schnorrData ); if (!ok) { _revert(err); } // Store pokeData's val in _pokeData storage and set its age to now. _pokeData.val = pokeData.val; _pokeData.age = uint32(block.timestamp); emit Poked(msg.sender, pokeData.val, pokeData.age); } // -- opPoke Functionality -- /// @dev Optimized function selector: 0x00000000. /// Note that this function is _not_ defined via the IScribe interface /// and one should _not_ depend on it. function opPoke_optimized_397084999( PokeData calldata pokeData, SchnorrData calldata schnorrData, ECDSAData calldata ecdsaData ) external payable { _opPoke(pokeData, schnorrData, ecdsaData); } /// @inheritdoc IScribeOptimistic function opPoke( PokeData calldata pokeData, SchnorrData calldata schnorrData, ECDSAData calldata ecdsaData ) external { _opPoke(pokeData, schnorrData, ecdsaData); } function _opPoke( PokeData calldata pokeData, SchnorrData calldata schnorrData, ECDSAData calldata ecdsaData ) internal { // Load _opPokeData from storage. PokeData memory opPokeData = _opPokeData; // Decide whether _opPokeData finalized. bool opPokeDataFinalized = opPokeData.age + opChallengePeriod <= uint32(block.timestamp); // Revert if _opPokeData not finalized, i.e. still challengeable. if (!opPokeDataFinalized) { revert InChallengePeriod(); } // Decide current age. uint32 age = opPokeData.age > _pokeData.age ? opPokeData.age : _pokeData.age; // Revert if pokeData stale. if (pokeData.age <= age) { revert StaleMessage(pokeData.age, age); } // Revert if pokeData from the future. if (pokeData.age > uint32(block.timestamp)) { revert FutureMessage(pokeData.age, uint32(block.timestamp)); } // Recover ECDSA signer. address signer = ecrecover( _constructOpPokeMessage(pokeData, schnorrData), ecdsaData.v, ecdsaData.r, ecdsaData.s ); // Load signer's index. uint signerIndex = _feeds[signer]; // Revert if signer not feed. if (signerIndex == 0) { revert SignerNotFeed(signer); } // Store the signerIndex as opFeedIndex and bind them to their provided // schnorrData. // // Note that cast is safe as _feed's image is [0, _pubKeys.length) and // _pubKeys' length is bounded by maxFeeds, i.e. type(uint8).max - 1. opFeedIndex = uint8(signerIndex); _schnorrDataCommitment = uint160( uint( keccak256( abi.encodePacked( schnorrData.signature, schnorrData.commitment, schnorrData.signersBlob ) ) ) ); // If _opPokeData provides the current val, move it to the _pokeData // storage to free _opPokeData storage. If the current val is provided // by _pokeData, _opPokeData can be overwritten. if (opPokeData.age == age) { _pokeData = opPokeData; } // Store provided pokeData's val in _opPokeData storage. _opPokeData.val = pokeData.val; _opPokeData.age = uint32(block.timestamp); // Store pokeData's age to allow recreating original pokeMessage. _originalOpPokeDataAge = pokeData.age; emit OpPoked(msg.sender, signer, schnorrData, pokeData); } /// @inheritdoc IScribeOptimistic function opChallenge(SchnorrData calldata schnorrData) external returns (bool) { // Load _opPokeData from storage. PokeData memory opPokeData = _opPokeData; // Decide whether _opPokeData is challengeable. bool opPokeDataChallengeable = opPokeData.age + opChallengePeriod > uint32(block.timestamp); // Revert if _opPokeData is not challengeable. if (!opPokeDataChallengeable) { revert NoOpPokeToChallenge(); } // Construct truncated hash from schnorrData. uint160 schnorrDataHash = uint160( uint( keccak256( abi.encodePacked( schnorrData.signature, schnorrData.commitment, schnorrData.signersBlob ) ) ) ); // Revert if schnorrDataHash does not match _schnorrDataCommitment. if (schnorrDataHash != _schnorrDataCommitment) { revert SchnorrDataMismatch(schnorrDataHash, _schnorrDataCommitment); } // Decide whether schnorrData verifies opPokeData. bool ok; bytes memory err; (ok, err) = _verifySchnorrSignature( constructPokeMessage( PokeData({val: opPokeData.val, age: _originalOpPokeDataAge}) ), schnorrData ); if (ok) { // Decide whether _opPokeData stale already. bool opPokeDataStale = opPokeData.age <= _pokeData.age; // If _opPokeData not stale, finalize it by moving it to the // _pokeData storage. Note to also clean the _opPokeData storage to // not block new opPoke's as _opPokeData's challenge period not over. if (!opPokeDataStale) { _pokeData = _opPokeData; delete _opPokeData; } emit OpPokeChallengedUnsuccessfully(msg.sender); } else { // Drop opFeed and delete invalid _opPokeData. // Note to use address(this) as caller to indicate self-governed // drop of feed. _drop(address(this), opFeedIndex); // Pay ETH reward to challenger. uint reward = challengeReward(); if (_sendETH(payable(msg.sender), reward)) { emit OpChallengeRewardPaid(msg.sender, reward); } emit OpPokeChallengedSuccessfully(msg.sender, err); } // Return whether challenging was successful. return !ok; } /// @inheritdoc IScribeOptimistic function constructOpPokeMessage( PokeData calldata pokeData, SchnorrData calldata schnorrData ) external view returns (bytes32) { return _constructOpPokeMessage(pokeData, schnorrData); } function _constructOpPokeMessage( PokeData calldata pokeData, SchnorrData calldata schnorrData ) internal view returns (bytes32) { return keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", keccak256( abi.encodePacked( wat, pokeData.val, pokeData.age, schnorrData.signature, schnorrData.commitment, schnorrData.signersBlob ) ) ) ); } // -- Toll'ed Read Functionality -- // - IChronicle Functions /// @inheritdoc IChronicle /// @dev Only callable by toll'ed address. function read() external view override(IChronicle, Scribe) toll returns (uint) { uint val = _currentPokeData().val; require(val != 0); return val; } /// @inheritdoc IChronicle /// @dev Only callable by toll'ed address. function tryRead() external view override(IChronicle, Scribe) toll returns (bool, uint) { uint val = _currentPokeData().val; return (val != 0, val); } /// @inheritdoc IChronicle /// @dev Only callable by toll'ed address. function readWithAge() external view override(IChronicle, Scribe) toll returns (uint, uint) { PokeData memory pokeData = _currentPokeData(); require(pokeData.val != 0); return (pokeData.val, pokeData.age); } function tryReadWithAge() external view override(IChronicle, Scribe) toll returns (bool, uint, uint) { PokeData memory pokeData = _currentPokeData(); return (pokeData.val != 0, pokeData.val, pokeData.age); } // - MakerDAO Compatibility /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function peek() external view override(IScribe, Scribe) toll returns (uint, bool) { uint val = _currentPokeData().val; return (val, val != 0); } /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function peep() external view override(IScribe, Scribe) toll returns (uint, bool) { uint val = _currentPokeData().val; return (val, val != 0); } // - Chainlink Compatibility /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function latestRoundData() external view override(IScribe, Scribe) toll returns ( uint80 roundId, int answer, uint startedAt, uint updatedAt, uint80 answeredInRound ) { PokeData memory pokeData = _currentPokeData(); roundId = 1; answer = int(uint(pokeData.val)); // assert(uint(answer) == uint(pokeData.val)); startedAt = 0; updatedAt = pokeData.age; answeredInRound = roundId; } /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function latestAnswer() external view virtual override(IScribe, Scribe) toll returns (int) { uint val = _currentPokeData().val; return int(val); } function _currentPokeData() internal view returns (PokeData memory) { // Load pokeData slots from storage. PokeData memory pokeData = _pokeData; PokeData memory opPokeData = _opPokeData; // Decide whether _opPokeData is finalized. bool opPokeDataFinalized = opPokeData.age + opChallengePeriod <= uint32(block.timestamp); // Decide and return current pokeData. if (opPokeDataFinalized && opPokeData.age > pokeData.age) { return opPokeData; } else { return pokeData; } } // -- Auth'ed Functionality -- /// @inheritdoc IScribeOptimistic function setOpChallengePeriod(uint16 opChallengePeriod_) external auth { _setOpChallengePeriod(opChallengePeriod_); } function _setOpChallengePeriod(uint16 opChallengePeriod_) internal { require(opChallengePeriod_ != 0); if (opChallengePeriod != opChallengePeriod_) { emit OpChallengePeriodUpdated( msg.sender, opChallengePeriod, opChallengePeriod_ ); opChallengePeriod = opChallengePeriod_; } _afterAuthedAction(); } function _drop(address caller, uint feedIndex) internal override(Scribe) { super._drop(caller, feedIndex); _afterAuthedAction(); } function _setBar(uint8 bar_) internal override(Scribe) { super._setBar(bar_); _afterAuthedAction(); } /// @dev Ensures an auth'ed configuration update does not enable /// successfully challenging a prior to the update valid opPoke. /// /// @custom:invariant Val is provided if _pokeData prior to the tx is /// non-empty. Note that this is the case if there were /// at least two valid calls ∊ {poke, opPoke}. /// preTx(_pokeData) != (0, 0) /// → (true, _) = postTx(tryRead()) /// @custom:invariant Val is provided via _pokeData after the tx. /// postTx(readWithAge()) = postTx(_pokeData) /// @custom:invariant _opPokeData is empty after the tx. /// (0, 0) = postTx(_opPokeData) function _afterAuthedAction() internal { // Do nothing during deployment. if (address(this).code.length == 0) return; // Load _opPokeData from storage. PokeData memory opPokeData = _opPokeData; // Decide whether _opPokeData is finalized. // // Note that the decision is based on the possibly updated // opChallengePeriod! This means a once finalized opPoke may be dropped // if the opChallengePeriod was increased. bool opPokeDataFinalized = opPokeData.age + opChallengePeriod <= uint32(block.timestamp); // Note that _opPokeData is in one of the following three states: // 1. finalized and newer than _pokeData // 2. finalized but older than _pokeData // 3. non-finalized // // Note that for state 1 _opPokeData can be moved to _pokeData and // afterwards deleted. // Note that for state 2 and 3 _opPokeData can be directly deleted. // If _opPokeData is in state 1, move it to the _pokeData storage. // // Note that this ensures the current value is provided via _pokeData. if (opPokeDataFinalized && opPokeData.age > _pokeData.age) { _pokeData = opPokeData; } // If _opPokeData is in state 3, emit event to indicate a possibly valid // opPoke was dropped. if (!opPokeDataFinalized) { emit OpPokeDataDropped(msg.sender, opPokeData); } // Now it is safe to delete _opPokeData. delete _opPokeData; // Note that the current value is now provided via _pokeData. // assert(_currentPokeData().val == _pokeData.val); // assert(_currentPokeData().age == _pokeData.age); // Set the age of contract's current value to block.timestamp. // // Note that this ensures an already signed, but now possibly invalid // with regards to contract configurations, opPoke payload cannot be // opPoke'd anymore. _pokeData.age = uint32(block.timestamp); } // -- Searcher Incentivization Logic -- /// @inheritdoc IScribeOptimistic function challengeReward() public view returns (uint) { uint balance = address(this).balance; return balance > maxChallengeReward ? maxChallengeReward : balance; } /// @inheritdoc IScribeOptimistic function setMaxChallengeReward(uint maxChallengeReward_) external auth { if (maxChallengeReward != maxChallengeReward_) { emit MaxChallengeRewardUpdated( msg.sender, maxChallengeReward, maxChallengeReward_ ); maxChallengeReward = maxChallengeReward_; } } function _sendETH(address payable to, uint amount) internal returns (bool) { (bool ok,) = to.call{value: amount}(""); return ok; } } /** * @dev Contract overwrite to deploy contract instances with specific naming. * * For more info, see docs/Deployment.md. */ contract Chronicle_ETH_USD_2 is ScribeOptimistic { constructor(address initialAuthed, bytes32 wat_) ScribeOptimistic(initialAuthed, wat_) {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; /** * @title IChronicle * * @notice Interface for Chronicle Protocol's oracle products */ interface IChronicle { /// @notice Returns the oracle's identifier. /// @return wat The oracle's identifier. function wat() external view returns (bytes32 wat); /// @notice Returns the oracle's current value. /// @dev Reverts if no value set. /// @return value The oracle's current value. function read() external view returns (uint value); /// @notice Returns the oracle's current value and its age. /// @dev Reverts if no value set. /// @return value The oracle's current value. /// @return age The value's age. function readWithAge() external view returns (uint value, uint age); /// @notice Returns the oracle's current value. /// @return isValid True if value exists, false otherwise. /// @return value The oracle's current value if it exists, zero otherwise. function tryRead() external view returns (bool isValid, uint value); /// @notice Returns the oracle's current value and its age. /// @return isValid True if value exists, false otherwise. /// @return value The oracle's current value if it exists, zero otherwise. /// @return age The value's age if value exists, zero otherwise. function tryReadWithAge() external view returns (bool isValid, uint value, uint age); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; import {IScribe} from "./IScribe.sol"; interface IScribeOptimistic is IScribe { /// @notice Thrown if attempted to opPoke while a previous opPoke is still /// in challenge period. error InChallengePeriod(); /// @notice Thrown if opChallenge called while no opPoke exists thats /// challengeable. error NoOpPokeToChallenge(); /// @notice Thrown if opChallenge called with SchnorrData not matching /// opPoke's SchnorrData. /// @param gotHash The truncated keccak256 hash of the SchnorrData argument. /// @param wantHash The truncated expected keccak256 hash of the SchnorrData /// argument. error SchnorrDataMismatch(uint160 gotHash, uint160 wantHash); /// @notice Emitted when oracles was successfully opPoked. /// @param caller The caller's address. /// @param opFeed The feed that signed the opPoke. /// @param schnorrData The schnorrData opPoked. /// @param pokeData The pokeData opPoked. event OpPoked( address indexed caller, address indexed opFeed, IScribe.SchnorrData schnorrData, IScribe.PokeData pokeData ); /// @notice Emitted when successfully challenged an opPoke. /// @param caller The caller's address. /// @param schnorrErr The abi-encoded custom error returned from the failed /// Schnorr signature verification. event OpPokeChallengedSuccessfully( address indexed caller, bytes schnorrErr ); /// @notice Emitted when unsuccessfully challenged an opPoke. /// @param caller The caller's address. event OpPokeChallengedUnsuccessfully(address indexed caller); /// @notice Emitted when ETH reward paid for successfully challenging an /// opPoke. /// @param challenger The challenger to which the reward was send. /// @param reward The ETH rewards paid. event OpChallengeRewardPaid(address indexed challenger, uint reward); /// @notice Emitted when an opPoke dropped. /// @dev opPoke's are dropped if security parameters are updated that could /// lead to an initially valid opPoke becoming invalid or an opPoke was /// was successfully challenged. /// @param caller The caller's address. /// @param pokeData The pokeData dropped. event OpPokeDataDropped(address indexed caller, IScribe.PokeData pokeData); /// @notice Emitted when length of opChallengePeriod updated. /// @param caller The caller's address. /// @param oldOpChallengePeriod The old opChallengePeriod's length. /// @param newOpChallengePeriod The new opChallengePeriod's length. event OpChallengePeriodUpdated( address indexed caller, uint16 oldOpChallengePeriod, uint16 newOpChallengePeriod ); /// @notice Emitted when maxChallengeReward updated. /// @param caller The caller's address. /// @param oldMaxChallengeReward The old maxChallengeReward. /// @param newMaxChallengeReward The new maxChallengeReward. event MaxChallengeRewardUpdated( address indexed caller, uint oldMaxChallengeReward, uint newMaxChallengeReward ); /// @notice Optimistically pokes the oracle. /// @dev Expects `pokeData`'s age to be greater than the timestamp of the /// last successful poke. /// @dev Expects `ecdsaData` to be a signature from a feed. /// @dev Expects `ecdsaData` to prove the integrity of the `pokeData` and /// `schnorrData`. /// @dev If the `schnorrData` is proven to be invalid via the opChallenge /// function, the `ecdsaData` signing feed will be dropped. /// @param pokeData The PokeData being poked. /// @param schnorrData The SchnorrData optimistically assumed to be /// proving the `pokeData`'s integrity. /// @param ecdsaData The ECDSAData proving the integrity of the /// `pokeData` and `schnorrData`. function opPoke( PokeData calldata pokeData, SchnorrData calldata schnorrData, ECDSAData calldata ecdsaData ) external; /// @notice Challenges the current challengeable opPoke. /// @dev If opPoke is determined to be invalid, the caller receives an ETH /// bounty. The bounty is defined via the `challengeReward()(uint)` /// function. /// @dev If opPoke is determined to be invalid, the corresponding feed is /// dropped. /// @param schnorrData The SchnorrData initially provided via /// opPoke. /// @return ok True if opPoke declared invalid, false otherwise. function opChallenge(SchnorrData calldata schnorrData) external returns (bool ok); /// @notice Returns the message expected to be signed via ECDSA for calling /// opPoke. /// @dev The message is defined as: /// H(tag ‖ H(wat ‖ pokeData ‖ schnorrData)), where H() is the keccak256 function. /// @param pokeData The pokeData being optimistically poked. /// @param schnorrData The schnorrData proving `pokeData`'s integrity. /// @return opPokeMessage Message to be signed for an opPoke for `pokeData` /// and `schnorrData`. function constructOpPokeMessage( PokeData calldata pokeData, SchnorrData calldata schnorrData ) external view returns (bytes32 opPokeMessage); /// @notice Returns the feed index of the feed last opPoke'd. /// @return opFeedIndex Feed index of the feed last opPoke'd. function opFeedIndex() external view returns (uint8 opFeedIndex); /// @notice Returns the opChallengePeriod security parameter. /// @return opChallengePeriod The opChallengePeriod security parameter. function opChallengePeriod() external view returns (uint16 opChallengePeriod); /// @notice Returns the maxChallengeRewards parameter. /// @return maxChallengeReward The maxChallengeReward parameter. function maxChallengeReward() external view returns (uint maxChallengeReward); /// @notice Returns the ETH rewards being paid for successfully challenging /// an opPoke. /// @return challengeReward The ETH reward for successfully challenging an /// opPoke. function challengeReward() external view returns (uint challengeReward); /// @notice Updates the opChallengePeriod security parameter. /// @dev Only callable by auth'ed address. /// @dev Reverts if opChallengePeriod is zero. /// @dev Note that evaluating whether an opPoke is finalized happens via the /// _current_ opChallengePeriod. /// This means a finalized opPoke is dropped if opChallengePeriod is /// decreased to a value less than opPoke's age. /// @param opChallengePeriod The value to update opChallengePeriod to. function setOpChallengePeriod(uint16 opChallengePeriod) external; /// @notice Updates the maxChallengeReward parameter. /// @dev Only callable by auth'ed address. /// @param maxChallengeReward The value to update maxChallengeReward to. function setMaxChallengeReward(uint maxChallengeReward) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; import {IChronicle} from "chronicle-std/IChronicle.sol"; import {LibSecp256k1} from "./libs/LibSecp256k1.sol"; interface IScribe is IChronicle { /// @dev PokeData encapsulates a value and its age. struct PokeData { uint128 val; uint32 age; } /// @dev SchnorrData encapsulates a (aggregated) Schnorr signature. /// Schnorr signatures are used to prove a PokeData's integrity. struct SchnorrData { bytes32 signature; address commitment; bytes signersBlob; } /// @dev ECDSAData encapsulates an ECDSA signature. struct ECDSAData { uint8 v; bytes32 r; bytes32 s; } /// @notice Thrown if a poked value's age is not greater than the oracle's /// current value's age. /// @param givenAge The poked value's age. /// @param currentAge The oracle's current value's age. error StaleMessage(uint32 givenAge, uint32 currentAge); /// @notice Thrown if a poked value's age is greater than the current /// time. /// @param givenAge The poked value's age. /// @param currentTimestamp The current time. error FutureMessage(uint32 givenAge, uint32 currentTimestamp); /// @notice Thrown if Schnorr signature not signed by exactly bar many /// signers. /// @param numberSigners The number of signers for given Schnorr signature. /// @param bar The bar security parameter. error BarNotReached(uint8 numberSigners, uint8 bar); /// @notice Thrown if signature signed by non-feed. /// @param signer The signer's address not being a feed. error SignerNotFeed(address signer); /// @notice Thrown if signer indexes are not encoded so that their /// addresses are in ascending order. error SignersNotOrdered(); /// @notice Thrown if Schnorr signature verification failed. error SchnorrSignatureInvalid(); /// @notice Emitted when oracle was successfully poked. /// @param caller The caller's address. /// @param val The value poked. /// @param age The age of the value poked. event Poked(address indexed caller, uint128 val, uint32 age); /// @notice Emitted when new feed lifted. /// @param caller The caller's address. /// @param feed The feed address lifted. /// @param index The feed's index identifier. event FeedLifted( address indexed caller, address indexed feed, uint indexed index ); /// @notice Emitted when feed dropped. /// @param caller The caller's address. /// @param feed The feed address dropped. /// @param index The feed's index identifier. event FeedDropped( address indexed caller, address indexed feed, uint indexed index ); /// @notice Emitted when bar updated. /// @param caller The caller's address. /// @param oldBar The old bar's value. /// @param newBar The new bar's value. event BarUpdated(address indexed caller, uint8 oldBar, uint8 newBar); /// @notice Returns the feed registration message. /// @dev This message must be signed by a feed in order to be lifted. /// @return feedRegistrationMessage Chronicle Protocol's feed registration /// message. function feedRegistrationMessage() external view returns (bytes32 feedRegistrationMessage); /// @notice The maximum number of feed lifts supported. /// @dev Note that the constraint comes from feed's indexes being encoded as /// uint8 in SchnorrData.signersBlob. /// @return maxFeeds The maximum number of feed lifts supported. function maxFeeds() external view returns (uint maxFeeds); /// @notice Returns the bar security parameter. /// @return bar The bar security parameter. function bar() external view returns (uint8 bar); /// @notice Returns the number of decimals of the oracle's value. /// @dev Provides partial compatibility with Chainlink's /// IAggregatorV3Interface. /// @return decimals The oracle value's number of decimals. function decimals() external view returns (uint8 decimals); /// @notice Returns the oracle's latest value. /// @dev Provides partial compatibility with Chainlink's /// IAggregatorV3Interface. /// @return roundId 1. /// @return answer The oracle's latest value. /// @return startedAt 0. /// @return updatedAt The timestamp of oracle's latest update. /// @return answeredInRound 1. function latestRoundData() external view returns ( uint80 roundId, int answer, uint startedAt, uint updatedAt, uint80 answeredInRound ); /// @notice Returns the oracle's latest value. /// @dev Provides partial compatibility with Chainlink's /// IAggregatorV3Interface. /// @custom:deprecated See https://docs.chain.link/data-feeds/api-reference/#latestanswer. /// @return answer The oracle's latest value. function latestAnswer() external view returns (int); /// @notice Pokes the oracle. /// @dev Expects `pokeData`'s age to be greater than the timestamp of the /// last successful poke. /// @dev Expects `schnorrData` to prove `pokeData`'s integrity. /// See `isAcceptableSchnorrSignatureNow(bytes32,SchnorrData)(bool)`. /// @param pokeData The PokeData being poked. /// @param schnorrData The SchnorrData proving the `pokeData`'s /// integrity. function poke(PokeData calldata pokeData, SchnorrData calldata schnorrData) external; /// @notice Returns whether the Schnorr signature `schnorrData` is /// currently acceptable for message `message`. /// @dev Note that a valid Schnorr signature is only acceptable if the /// signature was signed by exactly bar many feeds. /// For more info, see `bar()(uint8)` and `feeds()(address[],uint[])`. /// @dev Note that bar and feeds are configurable, meaning a once acceptable /// Schnorr signature may become unacceptable in the future. /// @param message The message expected to be signed via `schnorrData`. /// @param schnorrData The SchnorrData to verify whether it proves /// the `message`'s integrity. /// @return ok True if Schnorr signature is acceptable, false otherwise. function isAcceptableSchnorrSignatureNow( bytes32 message, SchnorrData calldata schnorrData ) external view returns (bool ok); /// @notice Returns the message expected to be signed via Schnorr for /// `pokeData`. /// @dev The message is defined as: /// H(tag ‖ H(wat ‖ pokeData)), where H() is the keccak256 function. /// @param pokeData The pokeData to create the message for. /// @return Message for `pokeData`. function constructPokeMessage(PokeData calldata pokeData) external view returns (bytes32); /// @notice Returns whether address `who` is a feed and its feed index /// identifier. /// @param who The address to check. /// @return isFeed True if `who` is feed, false otherwise. /// @return feedIndex Non-zero if `who` is feed, zero otherwise. function feeds(address who) external view returns (bool isFeed, uint feedIndex); /// @notice Returns whether feedIndex `index` maps to a feed and, if so, /// the feed's address. /// @param index The feedIndex to check. /// @return isFeed True if `index` maps to a feed, false otherwise. /// @return feed Address of the feed with feedIndex `index` if `index` maps /// to feed, zero-address otherwise. function feeds(uint index) external view returns (bool isFeed, address feed); /// @notice Returns list of feed addresses and their index identifiers. /// @return feeds List of feed addresses. /// @return feedIndexes List of feed's indexes. function feeds() external view returns (address[] memory feeds, uint[] memory feedIndexes); /// @notice Lifts public key `pubKey` to being a feed. /// @dev Only callable by auth'ed address. /// @dev The message expected to be signed by `ecdsaData` is defined as via /// `feedRegistrationMessage()(bytes32)` function. /// @param pubKey The public key of the feed. /// @param ecdsaData ECDSA signed message by the feed's public key. /// @return The feed index of the newly lifted feed. function lift(LibSecp256k1.Point memory pubKey, ECDSAData memory ecdsaData) external returns (uint); /// @notice Lifts public keys `pubKeys` to being feeds. /// @dev Only callable by auth'ed address. /// @dev The message expected to be signed by `ecdsaDatas` is defined as via /// `feedRegistrationMessage()(bytes32)` function. /// @param pubKeys The public keys of the feeds. /// @param ecdsaDatas ECDSA signed message by the feeds' public keys. /// @return List of feed indexes of the newly lifted feeds. function lift( LibSecp256k1.Point[] memory pubKeys, ECDSAData[] memory ecdsaDatas ) external returns (uint[] memory); /// @notice Drops feed with index `feedIndex` from being a feed. /// @dev Only callable by auth'ed address. /// @param feedIndex The feed index identifier of the feed to drop. function drop(uint feedIndex) external; /// @notice Drops feeds with indexes `feedIndexes` from being feeds. /// @dev Only callable by auth'ed address. /// @param feedIndexes The feed's index identifiers of the feeds to drop. function drop(uint[] memory feedIndexes) external; /// @notice Updates the bar security parameters to `bar`. /// @dev Only callable by auth'ed address. /// @dev Reverts if `bar` is zero. /// @param bar The value to update bar to. function setBar(uint8 bar) external; /// @notice Returns the oracle's current value. /// @custom:deprecated Use `tryRead()(bool,uint)` instead. /// @return value The oracle's current value if it exists, zero otherwise. /// @return isValid True if value exists, false otherwise. function peek() external view returns (uint value, bool isValid); /// @notice Returns the oracle's current value. /// @custom:deprecated Use `tryRead()(bool,uint)` instead. /// @return value The oracle's current value if it exists, zero otherwise. /// @return isValid True if value exists, false otherwise. function peep() external view returns (uint value, bool isValid); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.16; import {IChronicle} from "chronicle-std/IChronicle.sol"; import {Auth} from "chronicle-std/auth/Auth.sol"; import {Toll} from "chronicle-std/toll/Toll.sol"; import {IScribe} from "./IScribe.sol"; import {LibSchnorr} from "./libs/LibSchnorr.sol"; import {LibSecp256k1} from "./libs/LibSecp256k1.sol"; import {LibSchnorrData} from "./libs/LibSchnorrData.sol"; /** * @title Scribe * @custom:version 1.2.0 * * @notice Efficient Schnorr multi-signature based Oracle */ contract Scribe is IScribe, Auth, Toll { using LibSchnorr for LibSecp256k1.Point; using LibSecp256k1 for LibSecp256k1.Point; using LibSecp256k1 for LibSecp256k1.JacobianPoint; using LibSchnorrData for SchnorrData; /// @inheritdoc IScribe uint public constant maxFeeds = type(uint8).max - 1; /// @inheritdoc IScribe uint8 public constant decimals = 18; /// @inheritdoc IScribe bytes32 public constant feedRegistrationMessage = keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", keccak256("Chronicle Feed Registration") ) ); /// @inheritdoc IChronicle bytes32 public immutable wat; /// @dev The storage slot of _pubKeys[0]. uint internal immutable SLOT_pubKeys; // -- Storage -- /// @dev Scribe's current value and corresponding age. PokeData internal _pokeData; /// @dev List of feeds' public keys. LibSecp256k1.Point[] internal _pubKeys; /// @dev Mapping of feeds' addresses to their public key indexes in /// _pubKeys. mapping(address => uint) internal _feeds; /// @inheritdoc IScribe /// @dev Note to have as last in storage to enable downstream contracts to /// pack the slot. uint8 public bar; // -- Constructor -- constructor(address initialAuthed, bytes32 wat_) Auth(initialAuthed) { require(wat_ != 0); // Set wat immutable. wat = wat_; // Let initial bar be 2. _setBar(2); // Let _pubKeys[0] be the zero point. _pubKeys.push(LibSecp256k1.ZERO_POINT()); // Let SLOT_pubKeys be _pubKeys[0].slot. uint pubKeysSlot; assembly ("memory-safe") { mstore(0x00, _pubKeys.slot) pubKeysSlot := keccak256(0x00, 0x20) } SLOT_pubKeys = pubKeysSlot; } // -- Poke Functionality -- /// @dev Optimized function selector: 0x00000082. /// Note that this function is _not_ defined via the IScribe interface /// and one should _not_ depend on it. function poke_optimized_7136211( PokeData calldata pokeData, SchnorrData calldata schnorrData ) external { _poke(pokeData, schnorrData); } /// @inheritdoc IScribe function poke(PokeData calldata pokeData, SchnorrData calldata schnorrData) external { _poke(pokeData, schnorrData); } function _poke(PokeData calldata pokeData, SchnorrData calldata schnorrData) internal virtual { // Revert if pokeData stale. if (pokeData.age <= _pokeData.age) { revert StaleMessage(pokeData.age, _pokeData.age); } // Revert if pokeData from the future. if (pokeData.age > uint32(block.timestamp)) { revert FutureMessage(pokeData.age, uint32(block.timestamp)); } // Revert if schnorrData does not prove integrity of pokeData. bool ok; bytes memory err; // forgefmt: disable-next-item (ok, err) = _verifySchnorrSignature( constructPokeMessage(pokeData), schnorrData ); if (!ok) { _revert(err); } // Store pokeData's val in _pokeData storage and set its age to now. _pokeData.val = pokeData.val; _pokeData.age = uint32(block.timestamp); emit Poked(msg.sender, pokeData.val, pokeData.age); } /// @inheritdoc IScribe function constructPokeMessage(PokeData memory pokeData) public view returns (bytes32) { return keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", keccak256(abi.encodePacked(wat, pokeData.val, pokeData.age)) ) ); } // -- Schnorr Signature Verification -- /// @inheritdoc IScribe function isAcceptableSchnorrSignatureNow( bytes32 message, SchnorrData calldata schnorrData ) external view returns (bool) { bool ok; (ok, /*err*/ ) = _verifySchnorrSignature(message, schnorrData); return ok; } /// @custom:invariant Reverts iff out of gas. /// @custom:invariant Runtime is Θ(bar). function _verifySchnorrSignature( bytes32 message, SchnorrData calldata schnorrData ) internal view returns (bool, bytes memory) { // Let signerIndex be the current signer's index read from schnorrData. uint signerIndex; // Let signerPubKey be the public key stored for signerIndex. LibSecp256k1.Point memory signerPubKey; // Let signer be the address of signerPubKey. address signer; // Let lastSigner be the previous processed signer. address lastSigner; // Let aggPubKey be the sum of processed signers' public keys. // Note that Jacobian coordinates are used. LibSecp256k1.JacobianPoint memory aggPubKey; // Fail if number signers unequal to bar. // // Note that requiring equality constrains the verification's runtime // from Ω(bar) to Θ(bar). uint numberSigners = schnorrData.getSignerIndexLength(); if (numberSigners != bar) { return (false, _errorBarNotReached(uint8(numberSigners), bar)); } // Initiate signer variables with schnorrData's 0's signer index. signerIndex = schnorrData.getSignerIndex(0); signerPubKey = _unsafeLoadPubKeyAt(signerIndex); signer = signerPubKey.toAddress(); // Fail if signer not feed. if (signerPubKey.isZeroPoint()) { return (false, _errorSignerNotFeed(signer)); } // Initiate aggPubKey with value of first signerPubKey. aggPubKey = signerPubKey.toJacobian(); // Aggregate remaining encoded signers. for (uint i = 1; i < bar;) { // Update Signer Variables. lastSigner = signer; signerIndex = schnorrData.getSignerIndex(i); signerPubKey = _unsafeLoadPubKeyAt(signerIndex); signer = signerPubKey.toAddress(); // Fail if signer not feed. if (signerPubKey.isZeroPoint()) { return (false, _errorSignerNotFeed(signer)); } // Fail if signers not strictly monotonically increasing. // // Note that this prevents double signing attacks and enforces // strict ordering. if (uint160(lastSigner) >= uint160(signer)) { return (false, _errorSignersNotOrdered()); } // assert(aggPubKey.x != signerPubKey.x); // Indicates rogue-key attack // Add signerPubKey to already aggregated public keys. aggPubKey.addAffinePoint(signerPubKey); // forgefmt: disable-next-item unchecked { ++i; } } // Fail if signature verification fails. bool ok = aggPubKey.toAffine().verifySignature( message, schnorrData.signature, schnorrData.commitment ); if (!ok) { return (false, _errorSchnorrSignatureInvalid()); } // Otherwise Schnorr signature is valid. return (true, new bytes(0)); } // -- Toll'ed Read Functionality -- // - IChronicle Functions /// @inheritdoc IChronicle /// @dev Only callable by toll'ed address. function read() external view virtual toll returns (uint) { uint val = _pokeData.val; require(val != 0); return val; } /// @inheritdoc IChronicle /// @dev Only callable by toll'ed address. function tryRead() external view virtual toll returns (bool, uint) { uint val = _pokeData.val; return (val != 0, val); } /// @inheritdoc IChronicle /// @dev Only callable by toll'ed address. function readWithAge() external view virtual toll returns (uint, uint) { uint val = _pokeData.val; uint age = _pokeData.age; require(val != 0); return (val, age); } /// @inheritdoc IChronicle /// @dev Only callable by toll'ed address. function tryReadWithAge() external view virtual toll returns (bool, uint, uint) { uint val = _pokeData.val; uint age = _pokeData.age; return (val != 0, val, age); } // - MakerDAO Compatibility /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function peek() external view virtual toll returns (uint, bool) { uint val = _pokeData.val; return (val, val != 0); } /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function peep() external view virtual toll returns (uint, bool) { uint val = _pokeData.val; return (val, val != 0); } // - Chainlink Compatibility /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function latestRoundData() external view virtual toll returns ( uint80 roundId, int answer, uint startedAt, uint updatedAt, uint80 answeredInRound ) { roundId = 1; answer = int(uint(_pokeData.val)); // assert(uint(answer) == uint(_pokeData.val)); startedAt = 0; updatedAt = _pokeData.age; answeredInRound = roundId; } /// @inheritdoc IScribe /// @dev Only callable by toll'ed address. function latestAnswer() external view virtual toll returns (int) { uint val = _pokeData.val; return int(val); } // -- Public Read Functionality -- /// @inheritdoc IScribe function feeds(address who) external view returns (bool, uint) { uint index = _feeds[who]; // assert(index != 0 ? !_pubKeys[index].isZeroPoint() : true); return (index != 0, index); } /// @inheritdoc IScribe function feeds(uint index) external view returns (bool, address) { if (index >= _pubKeys.length) { return (false, address(0)); } LibSecp256k1.Point memory pubKey = _pubKeys[index]; if (pubKey.isZeroPoint()) { return (false, address(0)); } return (true, pubKey.toAddress()); } /// @inheritdoc IScribe function feeds() external view returns (address[] memory, uint[] memory) { // Initiate arrays with upper limit length. uint upperLimitLength = _pubKeys.length; address[] memory feedsList = new address[](upperLimitLength); uint[] memory feedsIndexesList = new uint[](upperLimitLength); // Iterate over feeds' public keys. If a public key is non-zero, their // corresponding address is a feed. uint ctr; LibSecp256k1.Point memory pubKey; address feed; uint feedIndex; for (uint i; i < upperLimitLength;) { pubKey = _pubKeys[i]; if (!pubKey.isZeroPoint()) { feed = pubKey.toAddress(); // assert(feed != address(0)); feedIndex = _feeds[feed]; // assert(feedIndex != 0); feedsList[ctr] = feed; feedsIndexesList[ctr] = feedIndex; ctr++; } // forgefmt: disable-next-item unchecked { ++i; } } // Set length of arrays to number of feeds actually included. assembly ("memory-safe") { mstore(feedsList, ctr) mstore(feedsIndexesList, ctr) } return (feedsList, feedsIndexesList); } // -- Auth'ed Functionality -- /// @inheritdoc IScribe function lift(LibSecp256k1.Point memory pubKey, ECDSAData memory ecdsaData) external auth returns (uint) { return _lift(pubKey, ecdsaData); } /// @inheritdoc IScribe function lift( LibSecp256k1.Point[] memory pubKeys, ECDSAData[] memory ecdsaDatas ) external auth returns (uint[] memory) { require(pubKeys.length == ecdsaDatas.length); uint[] memory indexes = new uint[](pubKeys.length); for (uint i; i < pubKeys.length;) { indexes[i] = _lift(pubKeys[i], ecdsaDatas[i]); // forgefmt: disable-next-item unchecked { ++i; } } // Note that indexes contains duplicates iff duplicate pubKeys provided. return indexes; } function _lift(LibSecp256k1.Point memory pubKey, ECDSAData memory ecdsaData) internal returns (uint) { address feed = pubKey.toAddress(); // assert(feed != address(0)); // forgefmt: disable-next-item address recovered = ecrecover( feedRegistrationMessage, ecdsaData.v, ecdsaData.r, ecdsaData.s ); require(feed == recovered); uint index = _feeds[feed]; if (index == 0) { _pubKeys.push(pubKey); index = _pubKeys.length - 1; _feeds[feed] = index; emit FeedLifted(msg.sender, feed, index); require(index <= maxFeeds); } return index; } /// @inheritdoc IScribe function drop(uint feedIndex) external auth { _drop(msg.sender, feedIndex); } /// @inheritdoc IScribe function drop(uint[] memory feedIndexes) external auth { for (uint i; i < feedIndexes.length;) { _drop(msg.sender, feedIndexes[i]); // forgefmt: disable-next-item unchecked { ++i; } } } function _drop(address caller, uint feedIndex) internal virtual { require(feedIndex < _pubKeys.length); address feed = _pubKeys[feedIndex].toAddress(); if (_feeds[feed] != 0) { emit FeedDropped(caller, feed, _feeds[feed]); _feeds[feed] = 0; _pubKeys[feedIndex] = LibSecp256k1.ZERO_POINT(); } } /// @inheritdoc IScribe function setBar(uint8 bar_) external auth { _setBar(bar_); } function _setBar(uint8 bar_) internal virtual { require(bar_ != 0); if (bar != bar_) { emit BarUpdated(msg.sender, bar, bar_); bar = bar_; } } // -- Internal Helpers -- /// @dev Halts execution by reverting with `err`. function _revert(bytes memory err) internal pure { // assert(err.length != 0); assembly ("memory-safe") { let size := mload(err) let offset := add(err, 0x20) revert(offset, size) } } /// @dev Returns the public key at `_pubKeys[index]`, or zero point if /// `index` out of bounds. function _unsafeLoadPubKeyAt(uint index) internal view returns (LibSecp256k1.Point memory) { // Push immutable to stack as accessing through assembly not supported. uint slotPubKeys = SLOT_pubKeys; LibSecp256k1.Point memory pubKey; assembly ("memory-safe") { // Note that a pubKey consists of two words. let realIndex := mul(index, 2) // Compute slot of _pubKeys[index]. let slot := add(slotPubKeys, realIndex) // Load _pubKeys[index]'s coordinates to stack. let x := sload(slot) let y := sload(add(slot, 1)) // Store coordinates in pubKey memory location. mstore(pubKey, x) mstore(add(pubKey, 0x20), y) } // assert(index < _pubKeys.length || pubKey.isZeroPoint()); // Note that pubKey is zero if index out of bounds. return pubKey; } function _errorBarNotReached(uint8 got, uint8 want) internal pure returns (bytes memory) { // assert(got != want); return abi.encodeWithSelector(IScribe.BarNotReached.selector, got, want); } function _errorSignerNotFeed(address signer) internal pure returns (bytes memory) { // assert(_feeds[signer] == 0); return abi.encodeWithSelector(IScribe.SignerNotFeed.selector, signer); } function _errorSignersNotOrdered() internal pure returns (bytes memory) { return abi.encodeWithSelector(IScribe.SignersNotOrdered.selector); } function _errorSchnorrSignatureInvalid() internal pure returns (bytes memory) { return abi.encodeWithSelector(IScribe.SchnorrSignatureInvalid.selector); } // -- Overridden Toll Functions -- /// @dev Defines authorization for IToll's authenticated functions. function toll_auth() internal override(Toll) auth {} } /** * @dev Contract overwrite to deploy contract instances with specific naming. * * For more info, see docs/Deployment.md. */ contract Chronicle_BASE_QUOTE_COUNTER is Scribe { // @todo ^^^^ ^^^^^ ^^^^^^^ Adjust name of Scribe instance. constructor(address initialAuthed, bytes32 wat_) Scribe(initialAuthed, wat_) {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; import {LibSecp256k1} from "./LibSecp256k1.sol"; /** * @title LibSchnorr * * @notice Custom-purpose library for Schnorr signature verification on the * secp256k1 curve */ library LibSchnorr { using LibSecp256k1 for LibSecp256k1.Point; /// @dev Returns whether `signature` and `commitment` sign via `pubKey` /// message `message`. /// /// @custom:invariant Reverts iff out of gas. /// @custom:invariant Uses constant amount of gas. function verifySignature( LibSecp256k1.Point memory pubKey, bytes32 message, bytes32 signature, address commitment ) internal pure returns (bool) { // Return false if signature or commitment is zero. if (signature == 0 || commitment == address(0)) { return false; } // Note to enforce pubKey is valid secp256k1 point. // // While the Scribe contract ensures to only verify signatures for valid // public keys, this check is enabled as an additional defense // mechanism. if (!pubKey.isOnCurve()) { return false; } // Note to enforce signature is less than Q to prevent signature // malleability. // // While the Scribe contract only accepts messages with strictly // monotonically increasing timestamps, circumventing replay attack // vectors and therefore also signature malleability issues at a higher // level, this check is enabled as an additional defense mechanism. if (uint(signature) >= LibSecp256k1.Q()) { return false; } // Construct challenge = H(Pₓ ‖ Pₚ ‖ m ‖ Rₑ) mod Q uint challenge = uint( keccak256( abi.encodePacked( pubKey.x, uint8(pubKey.yParity()), message, commitment ) ) ) % LibSecp256k1.Q(); // Compute msgHash = -sig * Pₓ (mod Q) // = Q - (sig * Pₓ) (mod Q) // // Unchecked because the only protected operation performed is the // subtraction from Q where the subtrahend is the result of a (mod Q) // computation, i.e. the subtrahend is guaranteed to be less than Q. uint msgHash; unchecked { msgHash = LibSecp256k1.Q() - mulmod(uint(signature), pubKey.x, LibSecp256k1.Q()); } // Compute v = Pₚ + 27 // // Unchecked because pubKey.yParity() ∊ {0, 1} which cannot overflow // by adding 27. uint v; unchecked { v = pubKey.yParity() + 27; } // Set r = Pₓ uint r = pubKey.x; // Compute s = Q - (e * Pₓ) (mod Q) // // Unchecked because the only protected operation performed is the // subtraction from Q where the subtrahend is the result of a (mod Q) // computation, i.e. the subtrahend is guaranteed to be less than Q. uint s; unchecked { s = LibSecp256k1.Q() - mulmod(challenge, pubKey.x, LibSecp256k1.Q()); } // Compute ([s]G - [e]P)ₑ via ecrecover. address recovered = ecrecover(bytes32(msgHash), uint8(v), bytes32(r), bytes32(s)); // Verification succeeds iff ([s]G - [e]P)ₑ = Rₑ. // // Note that commitment is guaranteed to not be zero. return commitment == recovered; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; /** * @title LibSecp256k1 * * @notice Library for secp256k1 elliptic curve computations * * @dev This library was developed to efficiently compute aggregated public * keys for Schnorr signatures based on secp256k1, i.e. it is _not_ a * general purpose elliptic curve library! * * References to the Ethereum Yellow Paper are based on the following * version: "BERLIN VERSION beacfbd – 2022-10-24". */ library LibSecp256k1 { using LibSecp256k1 for LibSecp256k1.Point; using LibSecp256k1 for LibSecp256k1.JacobianPoint; uint private constant ADDRESS_MASK = 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // -- Secp256k1 Constants -- // // Taken from https://www.secg.org/sec2-v2.pdf. // See section 2.4.1 "Recommended Parameters secp256k1". uint private constant _A = 0; uint private constant _B = 7; uint private constant _P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; /// @dev Returns the order of the group. function Q() internal pure returns (uint) { return 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; } /// @dev Returns the generator G. /// Note that the generator is also called base point. function G() internal pure returns (Point memory) { return Point({ x: 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, y: 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 }); } /// @dev Returns the zero point. function ZERO_POINT() internal pure returns (Point memory) { return Point({x: 0, y: 0}); } // -- (Affine) Point -- /// @dev Point encapsulates a secp256k1 point in Affine coordinates. struct Point { uint x; uint y; } /// @dev Returns the Ethereum address of `self`. /// /// @dev An Ethereum address is defined as the rightmost 160 bits of the /// keccak256 hash of the concatenation of the hex-encoded x and y /// coordinates of the corresponding ECDSA public key. /// See "Appendix F: Signing Transactions" §134 in the Yellow Paper. function toAddress(Point memory self) internal pure returns (address) { address addr; // Functionally equivalent Solidity code: // addr = address(uint160(uint(keccak256(abi.encode(self.x, self.y))))); assembly ("memory-safe") { addr := and(keccak256(self, 0x40), ADDRESS_MASK) } return addr; } /// @dev Returns Affine point `self` in Jacobian coordinates. function toJacobian(Point memory self) internal pure returns (JacobianPoint memory) { return JacobianPoint({x: self.x, y: self.y, z: 1}); } /// @dev Returns whether `self` is the zero point. function isZeroPoint(Point memory self) internal pure returns (bool) { return (self.x | self.y) == 0; } /// @dev Returns whether `self` is a point on the curve. /// /// @dev The secp256k1 curve is specified as y² ≡ x³ + ax + b (mod P) /// where: /// a = 0 /// b = 7 function isOnCurve(Point memory self) internal pure returns (bool) { uint left = mulmod(self.y, self.y, _P); // Note that adding a * x can be waived as ∀x: a * x = 0. uint right = addmod(mulmod(self.x, mulmod(self.x, self.x, _P), _P), _B, _P); return left == right; } /// @dev Returns the parity of `self`'s y coordinate. /// /// @dev The value 0 represents an even y value and 1 represents an odd y /// value. /// See "Appendix F: Signing Transactions" in the Yellow Paper. function yParity(Point memory self) internal pure returns (uint) { return self.y & 1; } // -- Jacobian Point -- /// @dev JacobianPoint encapsulates a secp256k1 point in Jacobian /// coordinates. struct JacobianPoint { uint x; uint y; uint z; } /// @dev Returns Jacobian point `self` in Affine coordinates. /// /// @custom:invariant Reverts iff out of gas. /// @custom:invariant Does not run into an infinite loop. function toAffine(JacobianPoint memory self) internal pure returns (Point memory) { Point memory result; // Compute z⁻¹, i.e. the modular inverse of self.z. uint zInv = _invMod(self.z); // Compute (z⁻¹)² (mod P) uint zInv_2 = mulmod(zInv, zInv, _P); // Compute self.x * (z⁻¹)² (mod P), i.e. the x coordinate of given // Jacobian point in Affine representation. result.x = mulmod(self.x, zInv_2, _P); // Compute self.y * (z⁻¹)³ (mod P), i.e. the y coordinate of given // Jacobian point in Affine representation. result.y = mulmod(self.y, mulmod(zInv, zInv_2, _P), _P); return result; } /// @dev Adds Affine point `p` to Jacobian point `self`. /// /// It is the caller's responsibility to ensure given points are on the /// curve! /// /// Computation based on: https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl. /// /// Note that the formula assumes z2 = 1, which always holds if z2's /// point is given in Affine coordinates. /// /// Note that eventhough the function is marked as pure, to be /// understood as only being dependent on the input arguments, it /// nevertheless has side effects by writing the result into the /// `self` memory variable. /// /// @custom:invariant Only mutates `self` memory variable. /// @custom:invariant Reverts iff out of gas. /// @custom:invariant Uses constant amount of gas. function addAffinePoint(JacobianPoint memory self, Point memory p) internal pure { // Addition formula: // x = r² - j - (2 * v) (mod P) // y = (r * (v - x)) - (2 * y1 * j) (mod P) // z = (z1 + h)² - z1² - h² (mod P) // // where: // r = 2 * (s - y1) (mod P) // j = h * i (mod P) // v = x1 * i (mod P) // h = u - x1 (mod P) // s = y2 * z1³ (mod P) Called s2 in reference // i = 4 * h² (mod P) // u = x2 * z1² (mod P) Called u2 in reference // // and: // x1 = self.x // y1 = self.y // z1 = self.z // x2 = p.x // y2 = p.y // // Note that in order to save memory allocations the result is stored // in the self variable, i.e. the following holds true after the // functions execution: // x = self.x // y = self.y // z = self.z // Cache self's coordinates on stack. uint x1 = self.x; uint y1 = self.y; uint z1 = self.z; // Compute z1_2 = z1² (mod P) // = z1 * z1 (mod P) uint z1_2 = mulmod(z1, z1, _P); // Compute h = u - x1 (mod P) // = u + (P - x1) (mod P) // = x2 * z1² + (P - x1) (mod P) // // Unchecked because the only protected operation performed is P - x1 // where x1 is guaranteed by the caller to be an x coordinate belonging // to a point on the curve, i.e. being less than P. uint h; unchecked { h = addmod(mulmod(p.x, z1_2, _P), _P - x1, _P); } // Compute h_2 = h² (mod P) // = h * h (mod P) uint h_2 = mulmod(h, h, _P); // Compute i = 4 * h² (mod P) uint i = mulmod(4, h_2, _P); // Compute z = (z1 + h)² - z1² - h² (mod P) // = (z1 + h)² - z1² + (P - h²) (mod P) // = (z1 + h)² + (P - z1²) + (P - h²) (mod P) // ╰───────╯ ╰───────╯ ╰──────╯ // left mid right // // Unchecked because the only protected operations performed are // subtractions from P where the subtrahend is the result of a (mod P) // computation, i.e. the subtrahend being guaranteed to be less than P. unchecked { uint left = mulmod(addmod(z1, h, _P), addmod(z1, h, _P), _P); uint mid = _P - z1_2; uint right = _P - h_2; self.z = addmod(left, addmod(mid, right, _P), _P); } // Compute v = x1 * i (mod P) uint v = mulmod(x1, i, _P); // Compute j = h * i (mod P) uint j = mulmod(h, i, _P); // Compute r = 2 * (s - y1) (mod P) // = 2 * (s + (P - y1)) (mod P) // = 2 * ((y2 * z1³) + (P - y1)) (mod P) // = 2 * ((y2 * z1² * z1) + (P - y1)) (mod P) // // Unchecked because the only protected operation performed is P - y1 // where y1 is guaranteed by the caller to be an y coordinate belonging // to a point on the curve, i.e. being less than P. uint r; unchecked { r = mulmod( 2, addmod(mulmod(p.y, mulmod(z1_2, z1, _P), _P), _P - y1, _P), _P ); } // Compute x = r² - j - (2 * v) (mod P) // = r² - j + (P - (2 * v)) (mod P) // = r² + (P - j) + (P - (2 * v)) (mod P) // ╰─────╯ ╰───────────╯ // mid right // // Unchecked because the only protected operations performed are // subtractions from P where the subtrahend is the result of a (mod P) // computation, i.e. the subtrahend being guaranteed to be less than P. unchecked { uint r_2 = mulmod(r, r, _P); uint mid = _P - j; uint right = _P - mulmod(2, v, _P); self.x = addmod(r_2, addmod(mid, right, _P), _P); } // Compute y = (r * (v - x)) - (2 * y1 * j) (mod P) // = (r * (v - x)) + (P - (2 * y1 * j)) (mod P) // = (r * (v + (P - x))) + (P - (2 * y1 * j)) (mod P) // ╰─────────────────╯ ╰────────────────╯ // left right // // Unchecked because the only protected operations performed are // subtractions from P where the subtrahend is the result of a (mod P) // computation, i.e. the subtrahend being guaranteed to be less than P. unchecked { uint left = mulmod(r, addmod(v, _P - self.x, _P), _P); uint right = _P - mulmod(2, mulmod(y1, j, _P), _P); self.y = addmod(left, right, _P); } } // -- Private Helpers -- /// @dev Returns the modular inverse of `x` for modulo `_P`. /// /// It is the caller's responsibility to ensure `x` is less than `_P`! /// /// The modular inverse of `x` is x⁻¹ such that x * x⁻¹ ≡ 1 (mod P). /// /// @dev Modified from Jordi Baylina's [ecsol](https://github.com/jbaylina/ecsol/blob/c2256afad126b7500e6f879a9369b100e47d435d/ec.sol#L51-L67). /// /// @custom:invariant Reverts iff out of gas. /// @custom:invariant Does not run into an infinite loop. function _invMod(uint x) private pure returns (uint) { uint t; uint q; uint newT = 1; uint r = _P; assembly ("memory-safe") { // Implemented in assembly to circumvent division-by-zero // and over-/underflow protection. // // Functionally equivalent Solidity code: // while (x != 0) { // q = r / x; // (t, newT) = (newT, addmod(t, (_P - mulmod(q, newT, _P)), _P)); // (r, x) = (x, r - (q * x)); // } // // For the division r / x, x is guaranteed to not be zero via the // loop condition. // // The subtraction of form P - mulmod(_, _, P) is guaranteed to not // underflow due to the subtrahend being a (mod P) result, // i.e. the subtrahend being guaranteed to be less than P. // // The subterm q * x is guaranteed to not overflow because // q * x ≤ r due to q = ⎣r / x⎦. // // The term r - (q * x) is guaranteed to not underflow because // q * x ≤ r and therefore r - (q * x) ≥ 0. for {} x {} { q := div(r, x) let tmp := t t := newT newT := addmod(tmp, sub(_P, mulmod(q, newT, _P)), _P) tmp := r r := x x := sub(tmp, mul(q, x)) } } return t; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; import {IAuth} from "./IAuth.sol"; /** * @title Auth Module * * @dev The `Auth` contract module provides a basic access control mechanism, * where a set of addresses are granted access to protected functions. * These addresses are said to be _auth'ed_. * * Initially, the address given as constructor argument is the only address * auth'ed. Through the `rely(address)` and `deny(address)` functions, * auth'ed callers are able to grant/renounce auth to/from addresses. * * This module is used through inheritance. It will make available the * modifier `auth`, which can be applied to functions to restrict their * use to only auth'ed callers. */ abstract contract Auth is IAuth { /// @dev Mapping storing whether address is auth'ed. /// @custom:invariant Image of mapping is {0, 1}. /// ∀x ∊ Address: _wards[x] ∊ {0, 1} /// @custom:invariant Only address given as constructor argument is authenticated after deployment. /// deploy(initialAuthed) → (∀x ∊ Address: _wards[x] == 1 → x == initialAuthed) /// @custom:invariant Only functions `rely` and `deny` may mutate the mapping's state. /// ∀x ∊ Address: preTx(_wards[x]) != postTx(_wards[x]) /// → (msg.sig == "rely" ∨ msg.sig == "deny") /// @custom:invariant Mapping's state may only be mutated by authenticated caller. /// ∀x ∊ Address: preTx(_wards[x]) != postTx(_wards[x]) → _wards[msg.sender] = 1 mapping(address => uint) private _wards; /// @dev List of addresses possibly being auth'ed. /// @dev May contain duplicates. /// @dev May contain addresses not being auth'ed anymore. /// @custom:invariant Every address being auth'ed once is element of the list. /// ∀x ∊ Address: authed(x) -> x ∊ _wardsTouched address[] private _wardsTouched; /// @dev Ensures caller is auth'ed. modifier auth() { assembly ("memory-safe") { // Compute slot of _wards[msg.sender]. mstore(0x00, caller()) mstore(0x20, _wards.slot) let slot := keccak256(0x00, 0x40) // Revert if caller not auth'ed. let isAuthed := sload(slot) if iszero(isAuthed) { // Store selector of `NotAuthorized(address)`. mstore(0x00, 0x4a0bfec1) // Store msg.sender. mstore(0x20, caller()) // Revert with (offset, size). revert(0x1c, 0x24) } } _; } constructor(address initialAuthed) { _wards[initialAuthed] = 1; _wardsTouched.push(initialAuthed); // Note to use address(0) as caller to indicate address was auth'ed // during deployment. emit AuthGranted(address(0), initialAuthed); } /// @inheritdoc IAuth function rely(address who) external auth { if (_wards[who] == 1) return; _wards[who] = 1; _wardsTouched.push(who); emit AuthGranted(msg.sender, who); } /// @inheritdoc IAuth function deny(address who) external auth { if (_wards[who] == 0) return; _wards[who] = 0; emit AuthRenounced(msg.sender, who); } /// @inheritdoc IAuth function authed(address who) public view returns (bool) { return _wards[who] == 1; } /// @inheritdoc IAuth /// @custom:invariant Only contains auth'ed addresses. /// ∀x ∊ authed(): _wards[x] == 1 /// @custom:invariant Contains all auth'ed addresses. /// ∀x ∊ Address: _wards[x] == 1 → x ∊ authed() function authed() public view returns (address[] memory) { // Initiate array with upper limit length. address[] memory wardsList = new address[](_wardsTouched.length); // Iterate through all possible auth'ed addresses. uint ctr; for (uint i; i < wardsList.length; i++) { // Add address only if still auth'ed. if (_wards[_wardsTouched[i]] == 1) { wardsList[ctr++] = _wardsTouched[i]; } } // Set length of array to number of auth'ed addresses actually included. assembly ("memory-safe") { mstore(wardsList, ctr) } return wardsList; } /// @inheritdoc IAuth function wards(address who) public view returns (uint) { return _wards[who]; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; import {IToll} from "./IToll.sol"; /** * @title Toll Module * * @notice "Toll paid, we kiss - but dissension looms, maybe diss?" * * @dev The `Toll` contract module provides a basic access control mechanism, * where a set of addresses are granted access to protected functions. * These addresses are said the be _tolled_. * * Initially, no address is tolled. Through the `kiss(address)` and * `diss(address)` functions, auth'ed callers are able to toll/de-toll * addresses. Authentication for these functions is defined via the * downstream implemented `toll_auth()` function. * * This module is used through inheritance. It will make available the * modifier `toll`, which can be applied to functions to restrict their * use to only tolled callers. */ abstract contract Toll is IToll { /// @dev Mapping storing whether address is tolled. /// @custom:invariant Image of mapping is {0, 1}. /// ∀x ∊ Address: _buds[x] ∊ {0, 1} /// @custom:invariant Only functions `kiss` and `diss` may mutate the mapping's state. /// ∀x ∊ Address: preTx(_buds[x]) != postTx(_buds[x]) /// → (msg.sig == "kiss" ∨ msg.sig == "diss") /// @custom:invariant Mapping's state may only be mutated by authenticated caller. /// ∀x ∊ Address: preTx(_buds[x]) != postTx(_buds[x]) /// → toll_auth() mapping(address => uint) private _buds; /// @dev List of addresses possibly being tolled. /// @dev May contain duplicates. /// @dev May contain addresses not being tolled anymore. /// @custom:invariant Every address being tolled once is element of the list. /// ∀x ∊ Address: tolled(x) → x ∊ _budsTouched address[] private _budsTouched; /// @dev Ensures caller is tolled. modifier toll() { assembly ("memory-safe") { // Compute slot of _buds[msg.sender]. mstore(0x00, caller()) mstore(0x20, _buds.slot) let slot := keccak256(0x00, 0x40) // Revert if caller not tolled. let isTolled := sload(slot) if iszero(isTolled) { // Store selector of `NotTolled(address)`. mstore(0x00, 0xd957b595) // Store msg.sender. mstore(0x20, caller()) // Revert with (offset, size). revert(0x1c, 0x24) } } _; } /// @dev Reverts if caller not allowed to access protected function. /// @dev Must be implemented in downstream contract. function toll_auth() internal virtual; /// @inheritdoc IToll function kiss(address who) external { toll_auth(); if (_buds[who] == 1) return; _buds[who] = 1; _budsTouched.push(who); emit TollGranted(msg.sender, who); } /// @inheritdoc IToll function diss(address who) external { toll_auth(); if (_buds[who] == 0) return; _buds[who] = 0; emit TollRenounced(msg.sender, who); } /// @inheritdoc IToll function tolled(address who) public view returns (bool) { return _buds[who] == 1; } /// @inheritdoc IToll /// @custom:invariant Only contains tolled addresses. /// ∀x ∊ tolled(): _tolled[x] /// @custom:invariant Contains all tolled addresses. /// ∀x ∊ Address: _tolled[x] == 1 → x ∊ tolled() function tolled() public view returns (address[] memory) { // Initiate array with upper limit length. address[] memory budsList = new address[](_budsTouched.length); // Iterate through all possible tolled addresses. uint ctr; for (uint i; i < budsList.length; i++) { // Add address only if still tolled. if (_buds[_budsTouched[i]] == 1) { budsList[ctr++] = _budsTouched[i]; } } // Set length of array to number of tolled addresses actually included. assembly ("memory-safe") { mstore(budsList, ctr) } return budsList; } /// @inheritdoc IToll function bud(address who) public view returns (uint) { return _buds[who]; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; import {IScribe} from "../IScribe.sol"; import {LibBytes} from "./LibBytes.sol"; /** * @title LibSchnorrData * * @notice Library for working with IScribe.SchnorrData */ library LibSchnorrData { using LibBytes for uint; /// @dev Size of a word is 32 bytes, i.e. 256 bits. uint private constant WORD_SIZE = 32; /// @dev Returns the signer index from schnorrData.signersBlob with index /// `index`. /// /// @dev Note that schnorrData.signersBlob is big-endian encoded and /// counting starts at the highest order byte, i.e. the signer index 0 /// is the highest order byte of schnorrData.signersBlob. /// /// @custom:example SignersBlob encoding via Solidity: /// /// ```solidity /// bytes memory signersBlob; /// uint8[] memory indexes = someFuncReturningUint8Array(); /// for (uint i; i < indexes.length; i++) { /// signersBlob = abi.encodePacked(signersBlob, indexes[i]); /// } /// ``` /// /// @dev Calldata layout for `schnorrData`: /// /// [schnorrData] signature -> schnorrData.signature /// [schnorrData + 0x20] commitment -> schnorrData.commitment /// [schnorrData + 0x40] offset(signersBlob) /// [schnorrData + 0x60] len(signersBlob) -> schnorrData.signersBlob.length /// [schnorrData + 0x80] signersBlob[0] -> schnorrData.signersBlob[0] /// ... /// /// Note that the `schnorrData` variable holds the offset to the /// `schnorrData` struct: /// /// ```solidity /// bytes32 signature; /// assembly { /// signature := calldataload(schnorrData) /// } /// assert(signature == schnorrData.signature) /// ``` /// /// Note that `offset(signersBlob)` is the offset to `signersBlob[0]` /// from the index `offset(signersBlob)`. /// /// @custom:invariant Reverts iff out of gas. function getSignerIndex( IScribe.SchnorrData calldata schnorrData, uint index ) internal pure returns (uint) { uint word; assembly ("memory-safe") { let wordIndex := mul(div(index, WORD_SIZE), WORD_SIZE) // Calldata index for schnorrData.signersBlob[0] is schnorrData's // offset plus 4 words, i.e. 0x80. let start := add(schnorrData, 0x80) // Note that reading non-existing calldata returns zero. // Note that overflow is no concern because index's upper limit is // bounded by bar, which is of type uint8. word := calldataload(add(start, wordIndex)) } // Unchecked because the subtrahend is guaranteed to be less than or // equal to 31 due to being a (mod 32) result. uint byteIndex; unchecked { byteIndex = 31 - (index % WORD_SIZE); } return word.getByteAtIndex(byteIndex); } /// @dev Returns the number of signers encoded in schnorrData.signersBlob. function getSignerIndexLength(IScribe.SchnorrData calldata schnorrData) internal pure returns (uint) { uint index; assembly ("memory-safe") { // Calldata index for schnorrData.signersBlob.length is // schnorrData's offset plus 3 words, i.e. 0x60. index := calldataload(add(schnorrData, 0x60)) } return index; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; interface IAuth { /// @notice Thrown by protected function if caller not auth'ed. /// @param caller The caller's address. error NotAuthorized(address caller); /// @notice Emitted when auth granted to address. /// @param caller The caller's address. /// @param who The address auth got granted to. event AuthGranted(address indexed caller, address indexed who); /// @notice Emitted when auth renounced from address. /// @param caller The caller's address. /// @param who The address auth got renounced from. event AuthRenounced(address indexed caller, address indexed who); /// @notice Grants address `who` auth. /// @dev Only callable by auth'ed address. /// @param who The address to grant auth. function rely(address who) external; /// @notice Renounces address `who`'s auth. /// @dev Only callable by auth'ed address. /// @param who The address to renounce auth. function deny(address who) external; /// @notice Returns whether address `who` is auth'ed. /// @param who The address to check. /// @return True if `who` is auth'ed, false otherwise. function authed(address who) external view returns (bool); /// @notice Returns full list of addresses granted auth. /// @dev May contain duplicates. /// @return List of addresses granted auth. function authed() external view returns (address[] memory); /// @notice Returns whether address `who` is auth'ed. /// @custom:deprecated Use `authed(address)(bool)` instead. /// @param who The address to check. /// @return 1 if `who` is auth'ed, 0 otherwise. function wards(address who) external view returns (uint); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; interface IToll { /// @notice Thrown by protected function if caller not tolled. /// @param caller The caller's address. error NotTolled(address caller); /// @notice Emitted when toll granted to address. /// @param caller The caller's address. /// @param who The address toll got granted to. event TollGranted(address indexed caller, address indexed who); /// @notice Emitted when toll renounced from address. /// @param caller The caller's address. /// @param who The address toll got renounced from. event TollRenounced(address indexed caller, address indexed who); /// @notice Grants address `who` toll. /// @dev Only callable by auth'ed address. /// @param who The address to grant toll. function kiss(address who) external; /// @notice Renounces address `who`'s toll. /// @dev Only callable by auth'ed address. /// @param who The address to renounce toll. function diss(address who) external; /// @notice Returns whether address `who` is tolled. /// @param who The address to check. /// @return True if `who` is tolled, false otherwise. function tolled(address who) external view returns (bool); /// @notice Returns full list of addresses tolled. /// @dev May contain duplicates. /// @return List of addresses tolled. function tolled() external view returns (address[] memory); /// @notice Returns whether address `who` is tolled. /// @custom:deprecated Use `tolled(address)(bool)` instead. /// @param who The address to check. /// @return 1 if `who` is tolled, 0 otherwise. function bud(address who) external view returns (uint); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.16; /** * @title LibBytes * * @notice Library for common byte operations */ library LibBytes { /// @dev Returns the `index`'s byte from `word`. /// /// It is the caller's responsibility to ensure `index < 32`! /// /// @custom:invariant Uses constant amount of gas. function getByteAtIndex(uint word, uint index) internal pure returns (uint) { uint result; assembly ("memory-safe") { result := byte(sub(31, index), word) } // Note that the resulting byte is returned as word. return result; } }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "chronicle-std/=lib/chronicle-std/src/", "@script/chronicle-std/=lib/chronicle-std/script/", "lib/chronicle-std:src/=lib/chronicle-std/src/", "lib/chronicle-std:ds-test/=lib/chronicle-std/lib/forge-std/lib/ds-test/src/", "lib/chronicle-std:forge-std/=lib/chronicle-std/lib/forge-std/src/", "greenhouse/=lib/greenhouse/src/" ], "optimizer": { "enabled": true, "runs": 10000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"initialAuthed","type":"address"},{"internalType":"bytes32","name":"wat_","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint8","name":"numberSigners","type":"uint8"},{"internalType":"uint8","name":"bar","type":"uint8"}],"name":"BarNotReached","type":"error"},{"inputs":[{"internalType":"uint32","name":"givenAge","type":"uint32"},{"internalType":"uint32","name":"currentTimestamp","type":"uint32"}],"name":"FutureMessage","type":"error"},{"inputs":[],"name":"InChallengePeriod","type":"error"},{"inputs":[],"name":"NoOpPokeToChallenge","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"NotAuthorized","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"NotTolled","type":"error"},{"inputs":[{"internalType":"uint160","name":"gotHash","type":"uint160"},{"internalType":"uint160","name":"wantHash","type":"uint160"}],"name":"SchnorrDataMismatch","type":"error"},{"inputs":[],"name":"SchnorrSignatureInvalid","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"SignerNotFeed","type":"error"},{"inputs":[],"name":"SignersNotOrdered","type":"error"},{"inputs":[{"internalType":"uint32","name":"givenAge","type":"uint32"},{"internalType":"uint32","name":"currentAge","type":"uint32"}],"name":"StaleMessage","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"who","type":"address"}],"name":"AuthGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"who","type":"address"}],"name":"AuthRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint8","name":"oldBar","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"newBar","type":"uint8"}],"name":"BarUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"feed","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"}],"name":"FeedDropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"feed","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"}],"name":"FeedLifted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldMaxChallengeReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxChallengeReward","type":"uint256"}],"name":"MaxChallengeRewardUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint16","name":"oldOpChallengePeriod","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"newOpChallengePeriod","type":"uint16"}],"name":"OpChallengePeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"challenger","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"OpChallengeRewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"bytes","name":"schnorrErr","type":"bytes"}],"name":"OpPokeChallengedSuccessfully","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"OpPokeChallengedUnsuccessfully","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"indexed":false,"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"}],"name":"OpPokeDataDropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"opFeed","type":"address"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"indexed":false,"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"},{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"indexed":false,"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"}],"name":"OpPoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint128","name":"val","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"age","type":"uint32"}],"name":"Poked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"who","type":"address"}],"name":"TollGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"who","type":"address"}],"name":"TollRenounced","type":"event"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"authed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authed","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bar","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"bud","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"challengeReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"}],"name":"constructOpPokeMessage","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"}],"name":"constructPokeMessage","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"deny","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"diss","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"feedIndex","type":"uint256"}],"name":"drop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"feedIndexes","type":"uint256[]"}],"name":"drop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feedRegistrationMessage","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"feeds","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"feeds","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeds","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"message","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"}],"name":"isAcceptableSchnorrSignatureNow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"kiss","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct LibSecp256k1.Point","name":"pubKey","type":"tuple"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct IScribe.ECDSAData","name":"ecdsaData","type":"tuple"}],"name":"lift","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct LibSecp256k1.Point[]","name":"pubKeys","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct IScribe.ECDSAData[]","name":"ecdsaDatas","type":"tuple[]"}],"name":"lift","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxChallengeReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxFeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"}],"name":"opChallenge","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"opChallengePeriod","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"opFeedIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct IScribe.ECDSAData","name":"ecdsaData","type":"tuple"}],"name":"opPoke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct IScribe.ECDSAData","name":"ecdsaData","type":"tuple"}],"name":"opPoke_optimized_397084999","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"peek","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"peep","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"}],"name":"poke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"val","type":"uint128"},{"internalType":"uint32","name":"age","type":"uint32"}],"internalType":"struct IScribe.PokeData","name":"pokeData","type":"tuple"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"signersBlob","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"}],"name":"poke_optimized_7136211","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"read","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"readWithAge","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"rely","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"bar_","type":"uint8"}],"name":"setBar","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxChallengeReward_","type":"uint256"}],"name":"setMaxChallengeReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"opChallengePeriod_","type":"uint16"}],"name":"setOpChallengePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"tolled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tolled","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tryRead","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tryReadWithAge","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wat","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60c034620002825762003540601f38829003908101601f19168301906001600160401b03821184831017620002875780849160409485948552833981010312620002825781516001600160a01b038116929083900362000282576020809101519260009080825281835260019081858420558154956801000000000000000096878110156200026e578381018085558110156200025a578385528585200180546001600160a01b03191683179055855191847fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f8185a38015620002565760805260075460ff8116916002830362000216575b505050620000fe620002bd565b81836200010a6200029d565b8281520152620001196200029d565b9082825283820183815260055496871015620002025781870180600555871015620001ee578495966005855285852090831b0192518355519101552060a05260075461ffff8160081c16610e10808203620001a8575b846200017a620002bd565b5161315f9081620003e182396080518181816115af015281816122760152612be3015260a051816129a50152f35b620e10009385519283528201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a1118843392a262ffff00191617600755388080806200016f565b634e487b7160e01b84526032600452602484fd5b634e487b7160e01b84526041600452602484fd5b600292815282868201527f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea873392a260ff191617600755388080620000f1565b8380fd5b634e487b7160e01b85526032600452602485fd5b634e487b7160e01b85526041600452602485fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b60408051919082016001600160401b038111838210176200028757604052565b303b15620003de57620002cf6200029d565b60085460018060801b03808216835263ffffffff809260801c1690602084019180835261ffff60075460081c1601838111620003c85783804216911611801580620003b4575b62000389575b6200034a575b5050600060085550506004805463ffffffff60801b19164260801b63ffffffff60801b16179055565b6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a23880808062000321565b818551166004549063ffffffff60801b855160801b169160018060a01b03191617176004556200031b565b50838351168460045460801c161062000315565b634e487b7160e01b600052601160045260246000fd5b56fe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c80156119b857806082146117465780630760861b146119105780630e5a6c701461148a5780630fce3415146118c657806310b07b7114611869578063211d9a5314611838578063224242ca1461177057806322ba9cd1146117545780632f529d73146117465780632fba4aa9146116f7578063313ce567146116db578063393e5ede1461167b5780633bee58f9146116315780633ea0c15e146116165780634a5479f3146115d25780634ca29923146115975780634fce7a2a1461155057806350d25bcd1461150657806357de26a4146114b557806359e02dd71461148a578063646edb681461146557806365c4ce7a1461144457806365fae35e1461140e5780636712af9e14610ec65780638928a1f814610b1e5780638b0b044c14610b055780639954b0dc14610a485780639c52a7f114610a125780639dadc886146109535780639fd001f614610935578063ab06ee16146108ca578063acf40b6f1461086f578063bf353dbb14610828578063bfe5861f14610659578063ceed3ef2146105f6578063d0a5882a146105d3578063d63605b8146104db578063dac42ad81461048e578063f15732c21461046a578063f29c29c414610449578063fc5a33a41461037b578063fe663495146102af578063feaf968c1461022d5763febb0f7e0361000e573461022857600060031936011261022857602060ff60075416604051908152f35b600080fd5b34610228576000600319360112610228573360005260026020526040600020541561029d5760a061025c612c75565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b346102285760206003193601126102285760043560ff81169081810361022857336000526000602052604060002054156103695781156102285760075460ff8116838103610300575b610019612deb565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617600755808080806102f8565b634a0bfec1600052336020526024601cfd5b34610228576020806003193601126102285760043567ffffffffffffffff81116102285736602382011215610228578060040135906103b982611dd3565b916103c76040519384611cf4565b80835260248484019160051b8301019136831161022857602401905b82821061043a578385336000526000815260406000205415610429575060005b8151811015610019578061042361041c600193856120d5565b5133612d1a565b01610403565b634a0bfec16000523390526024601cfd5b813581529084019084016103e3565b3461022857602060031936011261022857610019610465611d17565b6120e9565b3461022857600060031936011261022857602060ff60075460181c16604051908152f35b3461022857600319604081360112610228576024359067ffffffffffffffff8211610228576060908236030112610228576104d0602091600401600435612347565b506040519015158152f35b34610228576000600319360112610228576005546104f881612048565b61050182612048565b60006040519361051085611cbc565b60008552600060208096015260005b81811061055a57610549856105568887878085528152604051948594604086526040860190611d5b565b9184830390850152611deb565b0390f35b61056c610566826126ec565b50612726565b80518782015117610581575b5060010161051f565b6001919373ffffffffffffffffffffffffffffffffffffffff60406105cc9320168060005260068952604060002054906105bb838a6120d5565b526105c682886120d5565b52612079565b9290610578565b346102285760006003193601126102285760206105ee611e1f565b604051908152f35b34610228576000600319360112610228573360005260026020526040600020541561029d576060610625612c75565b63ffffffff60206fffffffffffffffffffffffffffffffff8351169201511660405191801515835260208301526040820152f35b346102285760406003193601126102285767ffffffffffffffff60043581811161022857366023820112156102285780600401359161069783611dd3565b926106a56040519485611cf4565b808452602092838501906024809360061b82010190368211610228578301915b8183106107f857505050803591821161022857366023830112156102285781600401356106f181611dd3565b926106ff6040519485611cf4565b81845284840183606080940283010191368311610228578401905b8282106107b257505050503360005260008352604060002054156107a357508251815181036102285761074c90612048565b9260005b815181101561078c578061077b610769600193856120d5565b5161077483876120d5565b51906127d6565b61078582886120d5565b5201610750565b505050610556604051928284938452830190611deb565b634a0bfec1600052338352601cfd5b838236031261022857604051906107c882611cd8565b82359060ff821682036102285782899287945282850135838201526040850135604082015281520191019061071a565b6040833603126102285785604091825161081181611cbc565b8535815282860135838201528152019201916106c5565b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff610856611d17565b1660005260006020526020604060002054604051908152f35b346102285760406003193601126102285760405161088c81611cbc565b6004356fffffffffffffffffffffffffffffffff8116810361022857815260243563ffffffff8116810361022857816105ee91602080940152612222565b346102285760206003193601126102285760043533600052600060205260406000205415610369576009548181036108fe57005b6040519081528160208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a2600955005b34610228576000600319360112610228576020600954604051908152f35b346102285760206003193601126102285761ffff600435818116808203610228573360005260006020526040600020541561036957801561022857600754928360081c16918183036109a757610019612deb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617600755808080806102f8565b3461022857602060031936011261022857610a2b611d17565b336000526000602052604060002054156103695761001990611fec565b34610228576000600319360112610228576003610a658154612048565b9060009160005b8151811015610aec5780610a82610abd92611ee9565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610ac2575b50612079565b610a6c565b610acb82611ee9565b905490861b1c16610ae5610ade87612079565b96856120d5565b5285610ab7565b8382526040516020808252819061055690820185611d5b565b346102285760206105ee610b1836611b26565b90612b36565b3461022857602060031981813601126102285767ffffffffffffffff6004358181116102285760608160040193823603011261022857610b5c612a57565b918483019063ffffffff928383511695610b816007549761ffff8960081c1690612a8c565b8580421691161115610e9c57610ba66044610b9e60248501612326565b930182612aa4565b9290610bbd6040519485928c840194863586612af5565b0392610bd1601f1994858101835282611cf4565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610e65575050610c3990610c346fffffffffffffffffffffffffffffffff8098511660405190610c2382611cbc565b8152878a60c01c168b820152612222565b612347565b92909687600014610d2f5750505050819051168160045460801c1610610c8e575b5050337fb687b04e722c3f49a1145e5cb4994bfdfdb5c8a971eaa232c7a4129b0567f4546000604051a25b60405190158152f35b610cd6610d2392600854166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b60085460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b60006008558280610c5a565b909350610d4792955060ff91945060181c1630612d1a565b610d4f612f43565b906000808060405185335af1903d15610e5f573d908111610e305760405190610d7f8886601f8401160183611cf4565b81526000873d92013e5b610dff575b506040519084825282519283868401528560005b858110610deb57505050906040817fac50cef58b3aef7f7c30349f5e4a342a29d2325a02eafc8dacfdba391e6d5db3936000838784010152601f339601168101030190a2610c85565b828101820151858201604001528101610da2565b6040519081527f50a7dd333474394c853a6d334bd7301b1362432233dcf7f04d2b0fc10fd7200d853392a284610d8e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b50610d89565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461022857610ed436611abc565b9190610ede612a57565b63ffffffff60208201511693610eff6007549561ffff8760081c1690612a8c565b63ffffffff8042169116116113e45763ffffffff60208301511663ffffffff60045460801c168082116000146113dc5750905b610f3e60208601612a29565b63ffffffff808416911611156113905763ffffffff421663ffffffff610f6660208801612a29565b161161133757610f768486612b36565b9080359160ff83168303610228576040805191825260ff939093166020808301919091528281013582850152919092013560608301526000808052909160809060015afa1561132b5773ffffffffffffffffffffffffffffffffffffffff60005116948560005260066020526040600020549081156112fa577fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff0000000061103a60208901612326565b61106d8961105f61104e6040830183612aa4565b906040519586943560208601612af5565b03601f198101835282611cf4565b6020815191012060201b169360181b169116171760075563ffffffff8060208401511691168114611256575b50506fffffffffffffffffffffffffffffffff90816110b784612a3a565b16600854907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b169216171760085561110a60208401612a29565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff0000000000000000000000000000000000000000000000006007549260c01b1691161760075560405190606082528035606083015273ffffffffffffffffffffffffffffffffffffffff61118660208301611d3a565b16608083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156102285701926020843594019267ffffffffffffffff851161022857843603841361022857602063ffffffff9261123c92877fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c6397606060a08901528160c089015260e0880137600060e0898801015261123182611da5565b168286015201611dc2565b16604082015260e081601f19601f339601168101030190a3005b6112ad6fffffffffffffffffffffffffffffffff6112f39351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b8380611099565b602487604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61138c61134660208701612a29565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff91821660048201524290911660248201529081906044820190565b0390fd5b5061139d60208501612a29565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b905090610f32565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b3461022857602060031936011261022857611427611d17565b336000526000602052604060002054156103695761001990611f20565b3461022857602060031936011261022857610019611460611d17565b6121a5565b3461022857600060031936011261022857602061ffff60075460081c16604051908152f35b34610228576000600319360112610228576114a3612c3f565b60408051928352901515602083015290f35b34610228576000600319360112610228573360005260026020526040600020541561029d576fffffffffffffffffffffffffffffffff6114f3612c75565b5116801561022857602090604051908152f35b34610228576000600319360112610228573360005260026020526040600020541561029d5760206fffffffffffffffffffffffffffffffff611546612c75565b5116604051908152f35b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff61157e611d17565b1660005260026020526020604060002054604051908152f35b346102285760006003193601126102285760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461022857602060031936011261022857604073ffffffffffffffffffffffffffffffffffffffff611605600435612744565b835191151582529091166020820152f35b346102285760006003193601126102285760206105ee612f43565b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff61165f611d17565b1660005260026020526020600160406000205414604051908152f35b34610228576000600319360112610228573360005260026020526040600020541561029d576116a8612c75565b6fffffffffffffffffffffffffffffffff81511680156102285763ffffffff602060409301511682519182526020820152f35b3461022857600060031936011261022857602060405160128152f35b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff611725611d17565b16600090815260066020908152604091829020548251811515815291820152f35b5061174f611b62565b61000e565b3461022857600060031936011261022857602060405160fe8152f35b3461022857600060031936011261022857600161178d8154612048565b60009182815b6117b1575b8383526040516020808252819061055690820186611d5b565b825181101561183357611803816117c88493611e83565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c1660005260006020528460406000205414611809575b5050612079565b90611793565b61181283611e83565b9054911b1c1661182b61182488612079565b97876120d5565b5286806117fc565b611798565b3461022857602060031936011261022857336000526000602052604060002054156103695761001960043533612d1a565b34610228576000600319360112610228573360005260026020526040600020541561029d576105566fffffffffffffffffffffffffffffffff6118aa612c75565b5160408051929091168015158352602083015290918291820190565b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff6118f4611d17565b1660005260006020526020600160406000205414604051908152f35b3461022857600319360160a08112610228576040136102285760405161193581611cbc565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc360112610228576040519061197982611cd8565b60443560ff811681036102285782526064356020830152608435604083015233600052600060205260406000205415610369576020916105ee916127d6565b6119c136611abc565b91906119cb612a57565b63ffffffff80602083015116946119ed6007549661ffff8860081c1690612a8c565b82421692839116116113e45763ffffffff60208401511663ffffffff60045460801c16808211600014611ab45750915b611a2960208701612a29565b63ffffffff80851691161115611aa7578063ffffffff611a4b60208901612a29565b1611611a5c5750610f768486612b36565b611a6860208701612a29565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b8261139d60208801612a29565b905091611a1d565b6003199182820160c0811261022857604013610228576004926044359067ffffffffffffffff8211610228576060908285030112610228577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261022857606490565b906003198083016060811261022857604013610228576004926044359167ffffffffffffffff8311610228578260609203011261022857820190565b503461022857611b7136611b26565b9063ffffffff90816020611b83612c75565b0151169260208201938084611b9787612a29565b161115611cb3575082611ba985612a29565b8142169182911611611caa575060408236031261022857611bf190610c34604051611bd381611cbc565b611bdc85611da5565b8152611be787611dc2565b6020820152612222565b9015611ca25750611c6c611c666fffffffffffffffffffffffffffffffff9283611c1a82612a3a565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b1692161717600455612a3a565b93612a29565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b611a6885612a29565b61139d85612a29565b6040810190811067ffffffffffffffff821117610e3057604052565b6060810190811067ffffffffffffffff821117610e3057604052565b90601f601f19910116810190811067ffffffffffffffff821117610e3057604052565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361022857565b359073ffffffffffffffffffffffffffffffffffffffff8216820361022857565b90815180825260208080930193019160005b828110611d7b575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611d6d565b35906fffffffffffffffffffffffffffffffff8216820361022857565b359063ffffffff8216820361022857565b67ffffffffffffffff8111610e305760051b60200190565b90815180825260208080930193019160005b828110611e0b575050505090565b835185529381019392810192600101611dfd565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c8152611e7d81611cd8565b51902090565b600154811015611eba5760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354811015611eba5760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff80911690600090828252816020526001604083205414611fe7576001604083205560015468010000000000000000811015611fba57611f7b816001869301600155611e83565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff16600081815280602052604081205415612044578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b9061205282611dd3565b61205f6040519182611cf4565b828152601f1961206f8294611dd3565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146120a65760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8051821015611eba5760209160051b010190565b6000338152806020526040812054156121945773ffffffffffffffffffffffffffffffffffffffff8092169182825260026020526001604083205414611fe7576001604083205560035468010000000000000000811015611fba57612155816001869301600355611ee9565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156122115773ffffffffffffffffffffffffffffffffffffffff16908181526002602052604081205415612044578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f0000000000000000000000000000000000000000000000000000000000000000865260801b16604084015260e01b166050820152603481526122b681611cd8565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c8152611e7d81611cd8565b604051906060820182811067ffffffffffffffff821117610e305760405260006040838281528260208201520152565b3573ffffffffffffffffffffffffffffffffffffffff811681036102285790565b6040805193929061235785611cbc565b6000918286528260208097015261236c6122f6565b5060075460ff1693606082013585810361269c575060809061239282840135861a61297e565b9173ffffffffffffffffffffffffffffffffffffffff8086852016978451948b81019586511715612689576123c56122f6565b50519451918751956123d687611cd8565b86528b8601928352878601938c60019b8c87528c905b858210612545575050915050889150875161240681611cbc565b828152015285519361241785611cbc565b8785528a850192888452519188928a90807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b61250857505050917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80926124a3989796959482808580098093510988525192099009905261249b898401612326565b923591612f53565b156124ce5780519485019085821067ffffffffffffffff831117611fba578190528185523690379190565b7fbd2a556b0000000000000000000000000000000000000000000000000000000091925051938401526004835261250483611cbc565b9190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f818394979304928188850982039008929091820290038061244b565b9161255d84601f1984168d010135601f84161a61297e565b90858d832016938251918301918251171561266c5786859116101561262a57908a8f93928151928a51908c51948d7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f9687809681808097818080988189818d81808480098099510990820390089c8d9383808680098180808084600409998a98089281038c820308918009089052099a099451816002988d82039409900908840996828080888709810384820308818a800908809a52099009820394820390089009088752018e916123ec565b5050505050505050505050507fb550c5700000000000000000000000000000000000000000000000000000000091925051938401526004835261250483611cbc565b50505050999c5050505050505050505061268691506129d7565b90565b50505050505050509250612686906129d7565b92517fce818a240000000000000000000000000000000000000000000000000000000097810197909752505060ff9081166024860152919091166044808501919091528352612504606484611cf4565b600554811015611eba57600560005260011b7f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00190600090565b9060405161273381611cbc565b602060018294805484520154910152565b6005548110156127885761056661275a916126ec565b80516020820151171561278857604073ffffffffffffffffffffffffffffffffffffffff9120169060019190565b50600090600090565b91906127a7576020816001925184550151910155565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b9073ffffffffffffffffffffffffffffffffffffffff906040918083852016906020612833612803611e1f565b85518387015196880151885192835260ff9091166020830152604082019690965260608101959095526080850190565b846000958692838052039060015afa156129745782511681036129705780825260066020528282205493841561286b575b5050505090565b909192935060055468010000000000000000811015612943579061289882600161289e94016005556126ec565b90612791565b600554907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161291657908060fe928195818652600660205282818720557f02652e7fe7a64eb217d52e6eec6ef55e181ceac19d9f5deb15aa62ea2b1bf9aa86339251a41161291357808080612864565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b5080fd5b83513d84823e3d90fd5b6000602060405161298e81611cbc565b8281520152604051906129a082611cbc565b60011b7f00000000000000000000000000000000000000000000000000000000000000000160018154910154908252602082015290565b73ffffffffffffffffffffffffffffffffffffffff604051917f56d7d2e80000000000000000000000000000000000000000000000000000000060208401521660248201526024815261268681611cd8565b3563ffffffff811681036102285790565b356fffffffffffffffffffffffffffffffff811681036102285790565b60405190612a6482611cbc565b6008546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff808094169116019182116120a657565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610228570180359067ffffffffffffffff82116102285760200191813603831361022857565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b906122b660887fffffffff0000000000000000000000000000000000000000000000000000000092612b736020612b6c87612a3a565b9601612a29565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612ba160208301612326565b91612baf6040820182612aa4565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f0000000000000000000000000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b166074850152848401378101600083820152036068810184520182611cf4565b3360005260026020526040600020541561029d576fffffffffffffffffffffffffffffffff612c6c612c75565b51169081151590565b60006020604051612c8581611cbc565b8281520152604051612c9681611cbc565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612cc9612a57565b926020840191612ce68184511661ffff60075460081c1690612a8c565b81804216911611159283612d08575b505050600014612d03575090565b905090565b81929350511691511610388080612cf5565b9060055481101561022857612d5591612d32826126ec565b509173ffffffffffffffffffffffffffffffffffffffff80936040958691612726565b201693600093858552600660205281852054612d7c575b505050505050612d7a612deb565b565b6020612898948692612de0988585205491865193167f6283f4f916bd1d3f7d6c69e150298bdd3840d8bc88f02a2740b98c4f198283518685a48284812055612dc381611cbc565b82815201525192612dd384611cbc565b80845260208401526126ec565b388080808080612d6c565b303b15612d7a57612dfa612a57565b6020810163ffffffff90612e1b8282511661ffff60075460081c1690612a8c565b82804216911611801580612f30575b612ed0575b612e82575b50505060006008556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a2388080612e34565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b1692161717600455612e2f565b50828251168360045460801c1610612e2a565b6009544781811115612d03575090565b9190918115801561310b575b61310257602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a090890838009036130f6577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414192838610156130e9577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff861117610e3057601b6000968580600160ff826130ba987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa1561132b5773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff841615612f5f56fea264697066735822122091c5c6f9a1f294e2967b091ff06b9c25536288cfba62e21c5a855cdaa5b3a50364736f6c63430008100033000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b4554482f55534400000000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c80156119b857806082146117465780630760861b146119105780630e5a6c701461148a5780630fce3415146118c657806310b07b7114611869578063211d9a5314611838578063224242ca1461177057806322ba9cd1146117545780632f529d73146117465780632fba4aa9146116f7578063313ce567146116db578063393e5ede1461167b5780633bee58f9146116315780633ea0c15e146116165780634a5479f3146115d25780634ca29923146115975780634fce7a2a1461155057806350d25bcd1461150657806357de26a4146114b557806359e02dd71461148a578063646edb681461146557806365c4ce7a1461144457806365fae35e1461140e5780636712af9e14610ec65780638928a1f814610b1e5780638b0b044c14610b055780639954b0dc14610a485780639c52a7f114610a125780639dadc886146109535780639fd001f614610935578063ab06ee16146108ca578063acf40b6f1461086f578063bf353dbb14610828578063bfe5861f14610659578063ceed3ef2146105f6578063d0a5882a146105d3578063d63605b8146104db578063dac42ad81461048e578063f15732c21461046a578063f29c29c414610449578063fc5a33a41461037b578063fe663495146102af578063feaf968c1461022d5763febb0f7e0361000e573461022857600060031936011261022857602060ff60075416604051908152f35b600080fd5b34610228576000600319360112610228573360005260026020526040600020541561029d5760a061025c612c75565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b346102285760206003193601126102285760043560ff81169081810361022857336000526000602052604060002054156103695781156102285760075460ff8116838103610300575b610019612deb565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617600755808080806102f8565b634a0bfec1600052336020526024601cfd5b34610228576020806003193601126102285760043567ffffffffffffffff81116102285736602382011215610228578060040135906103b982611dd3565b916103c76040519384611cf4565b80835260248484019160051b8301019136831161022857602401905b82821061043a578385336000526000815260406000205415610429575060005b8151811015610019578061042361041c600193856120d5565b5133612d1a565b01610403565b634a0bfec16000523390526024601cfd5b813581529084019084016103e3565b3461022857602060031936011261022857610019610465611d17565b6120e9565b3461022857600060031936011261022857602060ff60075460181c16604051908152f35b3461022857600319604081360112610228576024359067ffffffffffffffff8211610228576060908236030112610228576104d0602091600401600435612347565b506040519015158152f35b34610228576000600319360112610228576005546104f881612048565b61050182612048565b60006040519361051085611cbc565b60008552600060208096015260005b81811061055a57610549856105568887878085528152604051948594604086526040860190611d5b565b9184830390850152611deb565b0390f35b61056c610566826126ec565b50612726565b80518782015117610581575b5060010161051f565b6001919373ffffffffffffffffffffffffffffffffffffffff60406105cc9320168060005260068952604060002054906105bb838a6120d5565b526105c682886120d5565b52612079565b9290610578565b346102285760006003193601126102285760206105ee611e1f565b604051908152f35b34610228576000600319360112610228573360005260026020526040600020541561029d576060610625612c75565b63ffffffff60206fffffffffffffffffffffffffffffffff8351169201511660405191801515835260208301526040820152f35b346102285760406003193601126102285767ffffffffffffffff60043581811161022857366023820112156102285780600401359161069783611dd3565b926106a56040519485611cf4565b808452602092838501906024809360061b82010190368211610228578301915b8183106107f857505050803591821161022857366023830112156102285781600401356106f181611dd3565b926106ff6040519485611cf4565b81845284840183606080940283010191368311610228578401905b8282106107b257505050503360005260008352604060002054156107a357508251815181036102285761074c90612048565b9260005b815181101561078c578061077b610769600193856120d5565b5161077483876120d5565b51906127d6565b61078582886120d5565b5201610750565b505050610556604051928284938452830190611deb565b634a0bfec1600052338352601cfd5b838236031261022857604051906107c882611cd8565b82359060ff821682036102285782899287945282850135838201526040850135604082015281520191019061071a565b6040833603126102285785604091825161081181611cbc565b8535815282860135838201528152019201916106c5565b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff610856611d17565b1660005260006020526020604060002054604051908152f35b346102285760406003193601126102285760405161088c81611cbc565b6004356fffffffffffffffffffffffffffffffff8116810361022857815260243563ffffffff8116810361022857816105ee91602080940152612222565b346102285760206003193601126102285760043533600052600060205260406000205415610369576009548181036108fe57005b6040519081528160208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a2600955005b34610228576000600319360112610228576020600954604051908152f35b346102285760206003193601126102285761ffff600435818116808203610228573360005260006020526040600020541561036957801561022857600754928360081c16918183036109a757610019612deb565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617600755808080806102f8565b3461022857602060031936011261022857610a2b611d17565b336000526000602052604060002054156103695761001990611fec565b34610228576000600319360112610228576003610a658154612048565b9060009160005b8151811015610aec5780610a82610abd92611ee9565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610ac2575b50612079565b610a6c565b610acb82611ee9565b905490861b1c16610ae5610ade87612079565b96856120d5565b5285610ab7565b8382526040516020808252819061055690820185611d5b565b346102285760206105ee610b1836611b26565b90612b36565b3461022857602060031981813601126102285767ffffffffffffffff6004358181116102285760608160040193823603011261022857610b5c612a57565b918483019063ffffffff928383511695610b816007549761ffff8960081c1690612a8c565b8580421691161115610e9c57610ba66044610b9e60248501612326565b930182612aa4565b9290610bbd6040519485928c840194863586612af5565b0392610bd1601f1994858101835282611cf4565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610e65575050610c3990610c346fffffffffffffffffffffffffffffffff8098511660405190610c2382611cbc565b8152878a60c01c168b820152612222565b612347565b92909687600014610d2f5750505050819051168160045460801c1610610c8e575b5050337fb687b04e722c3f49a1145e5cb4994bfdfdb5c8a971eaa232c7a4129b0567f4546000604051a25b60405190158152f35b610cd6610d2392600854166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b60085460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b60006008558280610c5a565b909350610d4792955060ff91945060181c1630612d1a565b610d4f612f43565b906000808060405185335af1903d15610e5f573d908111610e305760405190610d7f8886601f8401160183611cf4565b81526000873d92013e5b610dff575b506040519084825282519283868401528560005b858110610deb57505050906040817fac50cef58b3aef7f7c30349f5e4a342a29d2325a02eafc8dacfdba391e6d5db3936000838784010152601f339601168101030190a2610c85565b828101820151858201604001528101610da2565b6040519081527f50a7dd333474394c853a6d334bd7301b1362432233dcf7f04d2b0fc10fd7200d853392a284610d8e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b50610d89565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461022857610ed436611abc565b9190610ede612a57565b63ffffffff60208201511693610eff6007549561ffff8760081c1690612a8c565b63ffffffff8042169116116113e45763ffffffff60208301511663ffffffff60045460801c168082116000146113dc5750905b610f3e60208601612a29565b63ffffffff808416911611156113905763ffffffff421663ffffffff610f6660208801612a29565b161161133757610f768486612b36565b9080359160ff83168303610228576040805191825260ff939093166020808301919091528281013582850152919092013560608301526000808052909160809060015afa1561132b5773ffffffffffffffffffffffffffffffffffffffff60005116948560005260066020526040600020549081156112fa577fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff0000000061103a60208901612326565b61106d8961105f61104e6040830183612aa4565b906040519586943560208601612af5565b03601f198101835282611cf4565b6020815191012060201b169360181b169116171760075563ffffffff8060208401511691168114611256575b50506fffffffffffffffffffffffffffffffff90816110b784612a3a565b16600854907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b169216171760085561110a60208401612a29565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff0000000000000000000000000000000000000000000000006007549260c01b1691161760075560405190606082528035606083015273ffffffffffffffffffffffffffffffffffffffff61118660208301611d3a565b16608083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156102285701926020843594019267ffffffffffffffff851161022857843603841361022857602063ffffffff9261123c92877fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c6397606060a08901528160c089015260e0880137600060e0898801015261123182611da5565b168286015201611dc2565b16604082015260e081601f19601f339601168101030190a3005b6112ad6fffffffffffffffffffffffffffffffff6112f39351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b8380611099565b602487604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61138c61134660208701612a29565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff91821660048201524290911660248201529081906044820190565b0390fd5b5061139d60208501612a29565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b905090610f32565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b3461022857602060031936011261022857611427611d17565b336000526000602052604060002054156103695761001990611f20565b3461022857602060031936011261022857610019611460611d17565b6121a5565b3461022857600060031936011261022857602061ffff60075460081c16604051908152f35b34610228576000600319360112610228576114a3612c3f565b60408051928352901515602083015290f35b34610228576000600319360112610228573360005260026020526040600020541561029d576fffffffffffffffffffffffffffffffff6114f3612c75565b5116801561022857602090604051908152f35b34610228576000600319360112610228573360005260026020526040600020541561029d5760206fffffffffffffffffffffffffffffffff611546612c75565b5116604051908152f35b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff61157e611d17565b1660005260026020526020604060002054604051908152f35b346102285760006003193601126102285760206040517f4554482f555344000000000000000000000000000000000000000000000000008152f35b3461022857602060031936011261022857604073ffffffffffffffffffffffffffffffffffffffff611605600435612744565b835191151582529091166020820152f35b346102285760006003193601126102285760206105ee612f43565b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff61165f611d17565b1660005260026020526020600160406000205414604051908152f35b34610228576000600319360112610228573360005260026020526040600020541561029d576116a8612c75565b6fffffffffffffffffffffffffffffffff81511680156102285763ffffffff602060409301511682519182526020820152f35b3461022857600060031936011261022857602060405160128152f35b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff611725611d17565b16600090815260066020908152604091829020548251811515815291820152f35b5061174f611b62565b61000e565b3461022857600060031936011261022857602060405160fe8152f35b3461022857600060031936011261022857600161178d8154612048565b60009182815b6117b1575b8383526040516020808252819061055690820186611d5b565b825181101561183357611803816117c88493611e83565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c1660005260006020528460406000205414611809575b5050612079565b90611793565b61181283611e83565b9054911b1c1661182b61182488612079565b97876120d5565b5286806117fc565b611798565b3461022857602060031936011261022857336000526000602052604060002054156103695761001960043533612d1a565b34610228576000600319360112610228573360005260026020526040600020541561029d576105566fffffffffffffffffffffffffffffffff6118aa612c75565b5160408051929091168015158352602083015290918291820190565b346102285760206003193601126102285773ffffffffffffffffffffffffffffffffffffffff6118f4611d17565b1660005260006020526020600160406000205414604051908152f35b3461022857600319360160a08112610228576040136102285760405161193581611cbc565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc360112610228576040519061197982611cd8565b60443560ff811681036102285782526064356020830152608435604083015233600052600060205260406000205415610369576020916105ee916127d6565b6119c136611abc565b91906119cb612a57565b63ffffffff80602083015116946119ed6007549661ffff8860081c1690612a8c565b82421692839116116113e45763ffffffff60208401511663ffffffff60045460801c16808211600014611ab45750915b611a2960208701612a29565b63ffffffff80851691161115611aa7578063ffffffff611a4b60208901612a29565b1611611a5c5750610f768486612b36565b611a6860208701612a29565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b8261139d60208801612a29565b905091611a1d565b6003199182820160c0811261022857604013610228576004926044359067ffffffffffffffff8211610228576060908285030112610228577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261022857606490565b906003198083016060811261022857604013610228576004926044359167ffffffffffffffff8311610228578260609203011261022857820190565b503461022857611b7136611b26565b9063ffffffff90816020611b83612c75565b0151169260208201938084611b9787612a29565b161115611cb3575082611ba985612a29565b8142169182911611611caa575060408236031261022857611bf190610c34604051611bd381611cbc565b611bdc85611da5565b8152611be787611dc2565b6020820152612222565b9015611ca25750611c6c611c666fffffffffffffffffffffffffffffffff9283611c1a82612a3a565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b1692161717600455612a3a565b93612a29565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b611a6885612a29565b61139d85612a29565b6040810190811067ffffffffffffffff821117610e3057604052565b6060810190811067ffffffffffffffff821117610e3057604052565b90601f601f19910116810190811067ffffffffffffffff821117610e3057604052565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361022857565b359073ffffffffffffffffffffffffffffffffffffffff8216820361022857565b90815180825260208080930193019160005b828110611d7b575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611d6d565b35906fffffffffffffffffffffffffffffffff8216820361022857565b359063ffffffff8216820361022857565b67ffffffffffffffff8111610e305760051b60200190565b90815180825260208080930193019160005b828110611e0b575050505090565b835185529381019392810192600101611dfd565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c8152611e7d81611cd8565b51902090565b600154811015611eba5760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354811015611eba5760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff80911690600090828252816020526001604083205414611fe7576001604083205560015468010000000000000000811015611fba57611f7b816001869301600155611e83565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff16600081815280602052604081205415612044578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b9061205282611dd3565b61205f6040519182611cf4565b828152601f1961206f8294611dd3565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146120a65760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8051821015611eba5760209160051b010190565b6000338152806020526040812054156121945773ffffffffffffffffffffffffffffffffffffffff8092169182825260026020526001604083205414611fe7576001604083205560035468010000000000000000811015611fba57612155816001869301600355611ee9565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156122115773ffffffffffffffffffffffffffffffffffffffff16908181526002602052604081205415612044578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f4554482f55534400000000000000000000000000000000000000000000000000865260801b16604084015260e01b166050820152603481526122b681611cd8565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c8152611e7d81611cd8565b604051906060820182811067ffffffffffffffff821117610e305760405260006040838281528260208201520152565b3573ffffffffffffffffffffffffffffffffffffffff811681036102285790565b6040805193929061235785611cbc565b6000918286528260208097015261236c6122f6565b5060075460ff1693606082013585810361269c575060809061239282840135861a61297e565b9173ffffffffffffffffffffffffffffffffffffffff8086852016978451948b81019586511715612689576123c56122f6565b50519451918751956123d687611cd8565b86528b8601928352878601938c60019b8c87528c905b858210612545575050915050889150875161240681611cbc565b828152015285519361241785611cbc565b8785528a850192888452519188928a90807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b61250857505050917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80926124a3989796959482808580098093510988525192099009905261249b898401612326565b923591612f53565b156124ce5780519485019085821067ffffffffffffffff831117611fba578190528185523690379190565b7fbd2a556b0000000000000000000000000000000000000000000000000000000091925051938401526004835261250483611cbc565b9190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f818394979304928188850982039008929091820290038061244b565b9161255d84601f1984168d010135601f84161a61297e565b90858d832016938251918301918251171561266c5786859116101561262a57908a8f93928151928a51908c51948d7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f9687809681808097818080988189818d81808480098099510990820390089c8d9383808680098180808084600409998a98089281038c820308918009089052099a099451816002988d82039409900908840996828080888709810384820308818a800908809a52099009820394820390089009088752018e916123ec565b5050505050505050505050507fb550c5700000000000000000000000000000000000000000000000000000000091925051938401526004835261250483611cbc565b50505050999c5050505050505050505061268691506129d7565b90565b50505050505050509250612686906129d7565b92517fce818a240000000000000000000000000000000000000000000000000000000097810197909752505060ff9081166024860152919091166044808501919091528352612504606484611cf4565b600554811015611eba57600560005260011b7f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00190600090565b9060405161273381611cbc565b602060018294805484520154910152565b6005548110156127885761056661275a916126ec565b80516020820151171561278857604073ffffffffffffffffffffffffffffffffffffffff9120169060019190565b50600090600090565b91906127a7576020816001925184550151910155565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b9073ffffffffffffffffffffffffffffffffffffffff906040918083852016906020612833612803611e1f565b85518387015196880151885192835260ff9091166020830152604082019690965260608101959095526080850190565b846000958692838052039060015afa156129745782511681036129705780825260066020528282205493841561286b575b5050505090565b909192935060055468010000000000000000811015612943579061289882600161289e94016005556126ec565b90612791565b600554907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161291657908060fe928195818652600660205282818720557f02652e7fe7a64eb217d52e6eec6ef55e181ceac19d9f5deb15aa62ea2b1bf9aa86339251a41161291357808080612864565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b5080fd5b83513d84823e3d90fd5b6000602060405161298e81611cbc565b8281520152604051906129a082611cbc565b60011b7f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00160018154910154908252602082015290565b73ffffffffffffffffffffffffffffffffffffffff604051917f56d7d2e80000000000000000000000000000000000000000000000000000000060208401521660248201526024815261268681611cd8565b3563ffffffff811681036102285790565b356fffffffffffffffffffffffffffffffff811681036102285790565b60405190612a6482611cbc565b6008546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff808094169116019182116120a657565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610228570180359067ffffffffffffffff82116102285760200191813603831361022857565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b906122b660887fffffffff0000000000000000000000000000000000000000000000000000000092612b736020612b6c87612a3a565b9601612a29565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612ba160208301612326565b91612baf6040820182612aa4565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f4554482f55534400000000000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b166074850152848401378101600083820152036068810184520182611cf4565b3360005260026020526040600020541561029d576fffffffffffffffffffffffffffffffff612c6c612c75565b51169081151590565b60006020604051612c8581611cbc565b8281520152604051612c9681611cbc565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612cc9612a57565b926020840191612ce68184511661ffff60075460081c1690612a8c565b81804216911611159283612d08575b505050600014612d03575090565b905090565b81929350511691511610388080612cf5565b9060055481101561022857612d5591612d32826126ec565b509173ffffffffffffffffffffffffffffffffffffffff80936040958691612726565b201693600093858552600660205281852054612d7c575b505050505050612d7a612deb565b565b6020612898948692612de0988585205491865193167f6283f4f916bd1d3f7d6c69e150298bdd3840d8bc88f02a2740b98c4f198283518685a48284812055612dc381611cbc565b82815201525192612dd384611cbc565b80845260208401526126ec565b388080808080612d6c565b303b15612d7a57612dfa612a57565b6020810163ffffffff90612e1b8282511661ffff60075460081c1690612a8c565b82804216911611801580612f30575b612ed0575b612e82575b50505060006008556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a2388080612e34565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b1692161717600455612e2f565b50828251168360045460801c1610612e2a565b6009544781811115612d03575090565b9190918115801561310b575b61310257602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a090890838009036130f6577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414192838610156130e9577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff861117610e3057601b6000968580600160ff826130ba987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa1561132b5773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff841615612f5f56fea264697066735822122091c5c6f9a1f294e2967b091ff06b9c25536288cfba62e21c5a855cdaa5b3a50364736f6c63430008100033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b4554482f55534400000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : initialAuthed (address): 0xc50dFeDb7E93eF7A3DacCAd7987D0960c4e2CD4b
Arg [1] : wat_ (bytes32): 0x4554482f55534400000000000000000000000000000000000000000000000000
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b
Arg [1] : 4554482f55534400000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.