ETH Price: $2,414.99 (-0.26%)

Contract

0x013C5C46db9914A19A58E57AD539eD5B125aFA15
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Op Poke_optimize...184756562023-11-01 7:06:11319 days ago1698822371IN
0x013C5C46...B125aFA15
0 ETH0.0006367712.24216633
Op Poke_optimize...184720742023-10-31 19:04:11319 days ago1698779051IN
0x013C5C46...B125aFA15
0 ETH0.001585832.55613935
Poke184705552023-10-31 13:58:35319 days ago1698760715IN
0x013C5C46...B125aFA15
0 ETH0.0021176529.32270767
Op Poke184702102023-10-31 12:48:35319 days ago1698756515IN
0x013C5C46...B125aFA15
0 ETH0.0011410323.15608321
Poke184699512023-10-31 11:56:35319 days ago1698753395IN
0x013C5C46...B125aFA15
0 ETH0.001360118.5423992
Op Poke184690712023-10-31 8:58:35320 days ago1698742715IN
0x013C5C46...B125aFA15
0 ETH0.0008106716.45576784
Poke184683652023-10-31 6:36:35320 days ago1698734195IN
0x013C5C46...B125aFA15
0 ETH0.0012495417.03507838
Op Poke184672502023-10-31 2:50:35320 days ago1698720635IN
0x013C5C46...B125aFA15
0 ETH0.0011846224.03476569
Poke184657152023-10-30 21:40:35320 days ago1698702035IN
0x013C5C46...B125aFA15
0 ETH0.0022159230.7737581
Op Poke184647002023-10-30 18:16:35320 days ago1698689795IN
0x013C5C46...B125aFA15
0 ETH0.0016540233.55836954
Poke184644102023-10-30 17:18:35320 days ago1698686315IN
0x013C5C46...B125aFA15
0 ETH0.0035011547.51125184
Poke184643702023-10-30 17:10:35320 days ago1698685835IN
0x013C5C46...B125aFA15
0 ETH0.0025405134.90149676
Op Poke184635802023-10-30 14:30:35320 days ago1698676235IN
0x013C5C46...B125aFA15
0 ETH0.0016452533.38043193
Poke184634712023-10-30 14:08:35320 days ago1698674915IN
0x013C5C46...B125aFA15
0 ETH0.0026036735.49616025
Op Poke184633322023-10-30 13:40:35320 days ago1698673235IN
0x013C5C46...B125aFA15
0 ETH0.0014796528.14046121
Op Poke184631262023-10-30 12:58:35320 days ago1698670715IN
0x013C5C46...B125aFA15
0 ETH0.0015461529.40526595
Op Poke184630162023-10-30 12:36:35320 days ago1698669395IN
0x013C5C46...B125aFA15
0 ETH0.0012117523.04554524
Op Poke184623912023-10-30 10:30:35320 days ago1698661835IN
0x013C5C46...B125aFA15
0 ETH0.000910818.47916968
Poke184618642023-10-30 8:44:35321 days ago1698655475IN
0x013C5C46...B125aFA15
0 ETH0.0010791814.65443088
Op Poke184617742023-10-30 8:26:35321 days ago1698654395IN
0x013C5C46...B125aFA15
0 ETH0.0005804211.7791283
Poke184600292023-10-30 2:34:35321 days ago1698633275IN
0x013C5C46...B125aFA15
0 ETH0.0010269813.93713366
Op Poke184592312023-10-29 23:52:35321 days ago1698623555IN
0x013C5C46...B125aFA15
0 ETH0.0006849413.02940357
Op Poke184588732023-10-29 22:40:35321 days ago1698619235IN
0x013C5C46...B125aFA15
0 ETH0.0006327912.03468337
Op Poke184577132023-10-29 18:46:35321 days ago1698605195IN
0x013C5C46...B125aFA15
0 ETH0.000783214.89512716
Op Poke184574202023-10-29 17:46:35321 days ago1698601595IN
0x013C5C46...B125aFA15
0 ETH0.0010284520.86616354
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
180412582023-09-01 10:58:47379 days ago1693565927  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Chronicle_WSTETH_USD_1

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 10000 runs

Other Settings:
london EvmVersion
File 1 of 13 : ScribeOptimistic.sol
// 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;
    }

    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_WSTETH_USD_1 is ScribeOptimistic {
    constructor(address initialAuthed, bytes32 wat_)
        ScribeOptimistic(initialAuthed, wat_)
    {}
}

File 2 of 13 : IChronicle.sol
// 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);
}

File 3 of 13 : IScribeOptimistic.sol
// 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;
}

File 4 of 13 : IScribe.sol
// 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 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);
}

File 5 of 13 : Scribe.sol
// 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.1.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;
    }

    // -- 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_)
    {}
}

File 6 of 13 : LibSchnorr.sol
// 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;
    }
}

File 7 of 13 : LibSecp256k1.sol
// 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;
    }
}

File 8 of 13 : Auth.sol
// 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];
    }
}

File 9 of 13 : Toll.sol
// 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];
    }
}

File 10 of 13 : LibSchnorrData.sol
// 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;
    }
}

File 11 of 13 : IAuth.sol
// 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);
}

File 12 of 13 : IToll.sol
// 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);
}

File 13 of 13 : LibBytes.sol
// 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;
    }
}

Settings
{
  "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/",
    "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

Contract ABI

[{"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":"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"}]

60c0346200028257620034eb601f38829003908101601f19168301906001600160401b03821184831017620002875780849160409485948552833981010312620002825781516001600160a01b038116929083900362000282576020809101519260009080825281835260019081858420558154956801000000000000000096878110156200026e578381018085558110156200025a578385528585200180546001600160a01b03191683179055855191847fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f8185a38015620002565760805260075460ff8116916002830362000216575b505050620000fe620002bd565b81836200010a6200029d565b8281520152620001196200029d565b9082825283820183815260055496871015620002025781870180600555871015620001ee578495966005855285852090831b0192518355519101552060a05260075461ffff8160081c16610e10808203620001a8575b846200017a620002bd565b5161310a9081620003e1823960805181818161155a015281816122210152612b8e015260a051816129500152f35b620e10009385519283528201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a1118843392a262ffff00191617600755388080806200016f565b634e487b7160e01b84526032600452602484fd5b634e487b7160e01b84526041600452602484fd5b600292815282868201527f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea873392a260ff191617600755388080620000f1565b8380fd5b634e487b7160e01b85526032600452602485fd5b634e487b7160e01b85526041600452602485fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b60408051919082016001600160401b038111838210176200028757604052565b303b15620003de57620002cf6200029d565b60085460018060801b03808216835263ffffffff809260801c1690602084019180835261ffff60075460081c1601838111620003c85783804216911611801580620003b4575b62000389575b6200034a575b5050600060085550506004805463ffffffff60801b19164260801b63ffffffff60801b16179055565b6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a23880808062000321565b818551166004549063ffffffff60801b855160801b169160018060a01b03191617176004556200031b565b50838351168460045460801c161062000315565b634e487b7160e01b600052601160045260246000fd5b56fe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c801561196357806082146116f15780630760861b146118bb5780630e5a6c701461147f5780630fce34151461187157806310b07b7114611814578063211d9a53146117e3578063224242ca1461171b57806322ba9cd1146116ff5780632f529d73146116f15780632fba4aa9146116a2578063313ce56714611686578063393e5ede146116265780633bee58f9146115dc5780633ea0c15e146115c15780634a5479f31461157d5780634ca29923146115425780634fce7a2a146114fb57806357de26a4146114aa57806359e02dd71461147f578063646edb681461145a57806365c4ce7a1461143957806365fae35e146114035780636712af9e14610ebb5780638928a1f814610b135780638b0b044c14610afa5780639954b0dc14610a3d5780639c52a7f114610a075780639dadc886146109485780639fd001f61461092a578063ab06ee16146108bf578063acf40b6f14610864578063bf353dbb1461081d578063bfe5861f1461064e578063ceed3ef2146105eb578063d0a5882a146105c8578063d63605b8146104d0578063dac42ad814610483578063f15732c21461045f578063f29c29c41461043e578063fc5a33a414610370578063fe663495146102a4578063feaf968c146102225763febb0f7e0361000e573461021d57600060031936011261021d57602060ff60075416604051908152f35b600080fd5b3461021d57600060031936011261021d57336000526002602052604060002054156102925760a0610251612c20565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b3461021d57602060031936011261021d5760043560ff81169081810361021d573360005260006020526040600020541561035e57811561021d5760075460ff81168381036102f5575b610019612d96565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617600755808080806102ed565b634a0bfec1600052336020526024601cfd5b3461021d5760208060031936011261021d5760043567ffffffffffffffff811161021d573660238201121561021d578060040135906103ae82611d7e565b916103bc6040519384611c9f565b80835260248484019160051b8301019136831161021d57602401905b82821061042f57838533600052600081526040600020541561041e575060005b8151811015610019578061041861041160019385612080565b5133612cc5565b016103f8565b634a0bfec16000523390526024601cfd5b813581529084019084016103d8565b3461021d57602060031936011261021d5761001961045a611cc2565b612094565b3461021d57600060031936011261021d57602060ff60075460181c16604051908152f35b3461021d5760031960408136011261021d576024359067ffffffffffffffff821161021d57606090823603011261021d576104c56020916004016004356122f2565b506040519015158152f35b3461021d57600060031936011261021d576005546104ed81611ff3565b6104f682611ff3565b60006040519361050585611c67565b60008552600060208096015260005b81811061054f5761053e8561054b8887878085528152604051948594604086526040860190611d06565b9184830390850152611d96565b0390f35b61056161055b82612697565b506126d1565b80518782015117610576575b50600101610514565b6001919373ffffffffffffffffffffffffffffffffffffffff60406105c19320168060005260068952604060002054906105b0838a612080565b526105bb8288612080565b52612024565b929061056d565b3461021d57600060031936011261021d5760206105e3611dca565b604051908152f35b3461021d57600060031936011261021d573360005260026020526040600020541561029257606061061a612c20565b63ffffffff60206fffffffffffffffffffffffffffffffff8351169201511660405191801515835260208301526040820152f35b3461021d57604060031936011261021d5767ffffffffffffffff60043581811161021d573660238201121561021d5780600401359161068c83611d7e565b9261069a6040519485611c9f565b808452602092838501906024809360061b8201019036821161021d578301915b8183106107ed57505050803591821161021d573660238301121561021d5781600401356106e681611d7e565b926106f46040519485611c9f565b8184528484018360608094028301019136831161021d578401905b8282106107a75750505050336000526000835260406000205415610798575082518151810361021d5761074190611ff3565b9260005b8151811015610781578061077061075e60019385612080565b516107698387612080565b5190612781565b61077a8288612080565b5201610745565b50505061054b604051928284938452830190611d96565b634a0bfec1600052338352601cfd5b838236031261021d57604051906107bd82611c83565b82359060ff8216820361021d5782899287945282850135838201526040850135604082015281520191019061070f565b60408336031261021d5785604091825161080681611c67565b8535815282860135838201528152019201916106ba565b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff61084b611cc2565b1660005260006020526020604060002054604051908152f35b3461021d57604060031936011261021d5760405161088181611c67565b6004356fffffffffffffffffffffffffffffffff8116810361021d57815260243563ffffffff8116810361021d57816105e3916020809401526121cd565b3461021d57602060031936011261021d576004353360005260006020526040600020541561035e576009548181036108f357005b6040519081528160208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a2600955005b3461021d57600060031936011261021d576020600954604051908152f35b3461021d57602060031936011261021d5761ffff60043581811680820361021d573360005260006020526040600020541561035e57801561021d57600754928360081c169181830361099c57610019612d96565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617600755808080806102ed565b3461021d57602060031936011261021d57610a20611cc2565b3360005260006020526040600020541561035e5761001990611f97565b3461021d57600060031936011261021d576003610a5a8154611ff3565b9060009160005b8151811015610ae15780610a77610ab292611e94565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610ab7575b50612024565b610a61565b610ac082611e94565b905490861b1c16610ada610ad387612024565b9685612080565b5285610aac565b8382526040516020808252819061054b90820185611d06565b3461021d5760206105e3610b0d36611ad1565b90612ae1565b3461021d576020600319818136011261021d5767ffffffffffffffff60043581811161021d5760608160040193823603011261021d57610b51612a02565b918483019063ffffffff928383511695610b766007549761ffff8960081c1690612a37565b8580421691161115610e9157610b9b6044610b93602485016122d1565b930182612a4f565b9290610bb26040519485928c840194863586612aa0565b0392610bc6601f1994858101835282611c9f565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610e5a575050610c2e90610c296fffffffffffffffffffffffffffffffff8098511660405190610c1882611c67565b8152878a60c01c168b8201526121cd565b6122f2565b92909687600014610d245750505050819051168160045460801c1610610c83575b5050337fb687b04e722c3f49a1145e5cb4994bfdfdb5c8a971eaa232c7a4129b0567f4546000604051a25b60405190158152f35b610ccb610d1892600854166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b60085460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b60006008558280610c4f565b909350610d3c92955060ff91945060181c1630612cc5565b610d44612eee565b906000808060405185335af1903d15610e54573d908111610e255760405190610d748886601f8401160183611c9f565b81526000873d92013e5b610df4575b506040519084825282519283868401528560005b858110610de057505050906040817fac50cef58b3aef7f7c30349f5e4a342a29d2325a02eafc8dacfdba391e6d5db3936000838784010152601f339601168101030190a2610c7a565b828101820151858201604001528101610d97565b6040519081527f50a7dd333474394c853a6d334bd7301b1362432233dcf7f04d2b0fc10fd7200d853392a284610d83565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b50610d7e565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461021d57610ec936611a67565b9190610ed3612a02565b63ffffffff60208201511693610ef46007549561ffff8760081c1690612a37565b63ffffffff8042169116116113d95763ffffffff60208301511663ffffffff60045460801c168082116000146113d15750905b610f33602086016129d4565b63ffffffff808416911611156113855763ffffffff421663ffffffff610f5b602088016129d4565b161161132c57610f6b8486612ae1565b9080359160ff8316830361021d576040805191825260ff939093166020808301919091528281013582850152919092013560608301526000808052909160809060015afa156113205773ffffffffffffffffffffffffffffffffffffffff60005116948560005260066020526040600020549081156112ef577fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff0000000061102f602089016122d1565b611062896110546110436040830183612a4f565b906040519586943560208601612aa0565b03601f198101835282611c9f565b6020815191012060201b169360181b169116171760075563ffffffff806020840151169116811461124b575b50506fffffffffffffffffffffffffffffffff90816110ac846129e5565b16600854907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617176008556110ff602084016129d4565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff0000000000000000000000000000000000000000000000006007549260c01b1691161760075560405190606082528035606083015273ffffffffffffffffffffffffffffffffffffffff61117b60208301611ce5565b16608083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561021d5701926020843594019267ffffffffffffffff851161021d57843603841361021d57602063ffffffff9261123192877fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c6397606060a08901528160c089015260e0880137600060e0898801015261122682611d50565b168286015201611d6d565b16604082015260e081601f19601f339601168101030190a3005b6112a26fffffffffffffffffffffffffffffffff6112e89351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b838061108e565b602487604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61138161133b602087016129d4565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff91821660048201524290911660248201529081906044820190565b0390fd5b50611392602085016129d4565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b905090610f27565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b3461021d57602060031936011261021d5761141c611cc2565b3360005260006020526040600020541561035e5761001990611ecb565b3461021d57602060031936011261021d57610019611455611cc2565b612150565b3461021d57600060031936011261021d57602061ffff60075460081c16604051908152f35b3461021d57600060031936011261021d57611498612bea565b60408051928352901515602083015290f35b3461021d57600060031936011261021d5733600052600260205260406000205415610292576fffffffffffffffffffffffffffffffff6114e8612c20565b5116801561021d57602090604051908152f35b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff611529611cc2565b1660005260026020526020604060002054604051908152f35b3461021d57600060031936011261021d5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461021d57602060031936011261021d57604073ffffffffffffffffffffffffffffffffffffffff6115b06004356126ef565b835191151582529091166020820152f35b3461021d57600060031936011261021d5760206105e3612eee565b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff61160a611cc2565b1660005260026020526020600160406000205414604051908152f35b3461021d57600060031936011261021d573360005260026020526040600020541561029257611653612c20565b6fffffffffffffffffffffffffffffffff815116801561021d5763ffffffff602060409301511682519182526020820152f35b3461021d57600060031936011261021d57602060405160128152f35b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff6116d0611cc2565b16600090815260066020908152604091829020548251811515815291820152f35b506116fa611b0d565b61000e565b3461021d57600060031936011261021d57602060405160fe8152f35b3461021d57600060031936011261021d5760016117388154611ff3565b60009182815b61175c575b8383526040516020808252819061054b90820186611d06565b82518110156117de576117ae816117738493611e2e565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c16600052600060205284604060002054146117b4575b5050612024565b9061173e565b6117bd83611e2e565b9054911b1c166117d66117cf88612024565b9787612080565b5286806117a7565b611743565b3461021d57602060031936011261021d573360005260006020526040600020541561035e5761001960043533612cc5565b3461021d57600060031936011261021d57336000526002602052604060002054156102925761054b6fffffffffffffffffffffffffffffffff611855612c20565b5160408051929091168015158352602083015290918291820190565b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff61189f611cc2565b1660005260006020526020600160406000205414604051908152f35b3461021d57600319360160a0811261021d5760401361021d576040516118e081611c67565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc36011261021d576040519061192482611c83565b60443560ff8116810361021d578252606435602083015260843560408301523360005260006020526040600020541561035e576020916105e391612781565b61196c36611a67565b9190611976612a02565b63ffffffff80602083015116946119986007549661ffff8860081c1690612a37565b82421692839116116113d95763ffffffff60208401511663ffffffff60045460801c16808211600014611a5f5750915b6119d4602087016129d4565b63ffffffff80851691161115611a52578063ffffffff6119f6602089016129d4565b1611611a075750610f6b8486612ae1565b611a13602087016129d4565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b82611392602088016129d4565b9050916119c8565b6003199182820160c0811261021d5760401361021d576004926044359067ffffffffffffffff821161021d57606090828503011261021d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261021d57606490565b906003198083016060811261021d5760401361021d576004926044359167ffffffffffffffff831161021d578260609203011261021d57820190565b503461021d57611b1c36611ad1565b9063ffffffff90816020611b2e612c20565b0151169260208201938084611b42876129d4565b161115611c5e575082611b54856129d4565b8142169182911611611c55575060408236031261021d57611b9c90610c29604051611b7e81611c67565b611b8785611d50565b8152611b9287611d6d565b60208201526121cd565b9015611c4d5750611c17611c116fffffffffffffffffffffffffffffffff9283611bc5826129e5565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617176004556129e5565b936129d4565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b611a13856129d4565b611392856129d4565b6040810190811067ffffffffffffffff821117610e2557604052565b6060810190811067ffffffffffffffff821117610e2557604052565b90601f601f19910116810190811067ffffffffffffffff821117610e2557604052565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361021d57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361021d57565b90815180825260208080930193019160005b828110611d26575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611d18565b35906fffffffffffffffffffffffffffffffff8216820361021d57565b359063ffffffff8216820361021d57565b67ffffffffffffffff8111610e255760051b60200190565b90815180825260208080930193019160005b828110611db6575050505090565b835185529381019392810192600101611da8565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c8152611e2881611c83565b51902090565b600154811015611e655760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354811015611e655760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff80911690600090828252816020526001604083205414611f92576001604083205560015468010000000000000000811015611f6557611f26816001869301600155611e2e565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff16600081815280602052604081205415611fef578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b90611ffd82611d7e565b61200a6040519182611c9f565b828152601f1961201a8294611d7e565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146120515760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8051821015611e655760209160051b010190565b60003381528060205260408120541561213f5773ffffffffffffffffffffffffffffffffffffffff8092169182825260026020526001604083205414611f92576001604083205560035468010000000000000000811015611f6557612100816001869301600355611e94565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156121bc5773ffffffffffffffffffffffffffffffffffffffff16908181526002602052604081205415611fef578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f0000000000000000000000000000000000000000000000000000000000000000865260801b16604084015260e01b1660508201526034815261226181611c83565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c8152611e2881611c83565b604051906060820182811067ffffffffffffffff821117610e255760405260006040838281528260208201520152565b3573ffffffffffffffffffffffffffffffffffffffff8116810361021d5790565b6040805193929061230285611c67565b600091828652826020809701526123176122a1565b5060075460ff16936060820135858103612647575060809061233d82840135861a612929565b9173ffffffffffffffffffffffffffffffffffffffff8086852016978451948b81019586511715612634576123706122a1565b505194519187519561238187611c83565b86528b8601928352878601938c60019b8c87528c905b8582106124f057505091505088915087516123b181611c67565b82815201528551936123c285611c67565b8785528a850192888452519188928a90807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b6124b357505050917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809261244e98979695948280858009809351098852519209900990526124468984016122d1565b923591612efe565b156124795780519485019085821067ffffffffffffffff831117611f65578190528185523690379190565b7fbd2a556b000000000000000000000000000000000000000000000000000000009192505193840152600483526124af83611c67565b9190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f81839497930492818885098203900892909182029003806123f6565b9161250884601f1984168d010135601f84161a612929565b90858d8320169382519183019182511715612617578685911610156125d557908a8f93928151928a51908c51948d7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f9687809681808097818080988189818d81808480098099510990820390089c8d9383808680098180808084600409998a98089281038c820308918009089052099a099451816002988d82039409900908840996828080888709810384820308818a800908809a52099009820394820390089009088752018e91612397565b5050505050505050505050507fb550c570000000000000000000000000000000000000000000000000000000009192505193840152600483526124af83611c67565b50505050999c505050505050505050506126319150612982565b90565b5050505050505050925061263190612982565b92517fce818a240000000000000000000000000000000000000000000000000000000097810197909752505060ff90811660248601529190911660448085019190915283526124af606484611c9f565b600554811015611e6557600560005260011b7f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00190600090565b906040516126de81611c67565b602060018294805484520154910152565b6005548110156127335761055b61270591612697565b80516020820151171561273357604073ffffffffffffffffffffffffffffffffffffffff9120169060019190565b50600090600090565b9190612752576020816001925184550151910155565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b9073ffffffffffffffffffffffffffffffffffffffff9060409180838520169060206127de6127ae611dca565b85518387015196880151885192835260ff9091166020830152604082019690965260608101959095526080850190565b846000958692838052039060015afa1561291f57825116810361291b57808252600660205282822054938415612816575b5050505090565b9091929350600554680100000000000000008110156128ee57906128438260016128499401600555612697565b9061273c565b600554907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116128c157908060fe928195818652600660205282818720557f02652e7fe7a64eb217d52e6eec6ef55e181ceac19d9f5deb15aa62ea2b1bf9aa86339251a4116128be5780808061280f565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b5080fd5b83513d84823e3d90fd5b6000602060405161293981611c67565b82815201526040519061294b82611c67565b60011b7f00000000000000000000000000000000000000000000000000000000000000000160018154910154908252602082015290565b73ffffffffffffffffffffffffffffffffffffffff604051917f56d7d2e80000000000000000000000000000000000000000000000000000000060208401521660248201526024815261263181611c83565b3563ffffffff8116810361021d5790565b356fffffffffffffffffffffffffffffffff8116810361021d5790565b60405190612a0f82611c67565b6008546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff8080941691160191821161205157565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561021d570180359067ffffffffffffffff821161021d5760200191813603831361021d57565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b9061226160887fffffffff0000000000000000000000000000000000000000000000000000000092612b1e6020612b17876129e5565b96016129d4565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612b4c602083016122d1565b91612b5a6040820182612a4f565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f0000000000000000000000000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b166074850152848401378101600083820152036068810184520182611c9f565b33600052600260205260406000205415610292576fffffffffffffffffffffffffffffffff612c17612c20565b51169081151590565b60006020604051612c3081611c67565b8281520152604051612c4181611c67565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612c74612a02565b926020840191612c918184511661ffff60075460081c1690612a37565b81804216911611159283612cb3575b505050600014612cae575090565b905090565b81929350511691511610388080612ca0565b9060055481101561021d57612d0091612cdd82612697565b509173ffffffffffffffffffffffffffffffffffffffff809360409586916126d1565b201693600093858552600660205281852054612d27575b505050505050612d25612d96565b565b6020612843948692612d8b988585205491865193167f6283f4f916bd1d3f7d6c69e150298bdd3840d8bc88f02a2740b98c4f198283518685a48284812055612d6e81611c67565b82815201525192612d7e84611c67565b8084526020840152612697565b388080808080612d17565b303b15612d2557612da5612a02565b6020810163ffffffff90612dc68282511661ffff60075460081c1690612a37565b82804216911611801580612edb575b612e7b575b612e2d575b50505060006008556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a2388080612ddf565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b1692161717600455612dda565b50828251168360045460801c1610612dd5565b6009544781811115612cae575090565b919091811580156130b6575b6130ad57602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a090890838009036130a1577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641419283861015613094577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff861117610e2557601b6000968580600160ff82613065987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa156113205773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff841615612f0a56fea264697066735822122010cc985ce25442a4ec9aa0268525ec181be86cdf477689c4941883e28d41b1ec64736f6c63430008100033000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b5753544554482f55534400000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c801561196357806082146116f15780630760861b146118bb5780630e5a6c701461147f5780630fce34151461187157806310b07b7114611814578063211d9a53146117e3578063224242ca1461171b57806322ba9cd1146116ff5780632f529d73146116f15780632fba4aa9146116a2578063313ce56714611686578063393e5ede146116265780633bee58f9146115dc5780633ea0c15e146115c15780634a5479f31461157d5780634ca29923146115425780634fce7a2a146114fb57806357de26a4146114aa57806359e02dd71461147f578063646edb681461145a57806365c4ce7a1461143957806365fae35e146114035780636712af9e14610ebb5780638928a1f814610b135780638b0b044c14610afa5780639954b0dc14610a3d5780639c52a7f114610a075780639dadc886146109485780639fd001f61461092a578063ab06ee16146108bf578063acf40b6f14610864578063bf353dbb1461081d578063bfe5861f1461064e578063ceed3ef2146105eb578063d0a5882a146105c8578063d63605b8146104d0578063dac42ad814610483578063f15732c21461045f578063f29c29c41461043e578063fc5a33a414610370578063fe663495146102a4578063feaf968c146102225763febb0f7e0361000e573461021d57600060031936011261021d57602060ff60075416604051908152f35b600080fd5b3461021d57600060031936011261021d57336000526002602052604060002054156102925760a0610251612c20565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b3461021d57602060031936011261021d5760043560ff81169081810361021d573360005260006020526040600020541561035e57811561021d5760075460ff81168381036102f5575b610019612d96565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617600755808080806102ed565b634a0bfec1600052336020526024601cfd5b3461021d5760208060031936011261021d5760043567ffffffffffffffff811161021d573660238201121561021d578060040135906103ae82611d7e565b916103bc6040519384611c9f565b80835260248484019160051b8301019136831161021d57602401905b82821061042f57838533600052600081526040600020541561041e575060005b8151811015610019578061041861041160019385612080565b5133612cc5565b016103f8565b634a0bfec16000523390526024601cfd5b813581529084019084016103d8565b3461021d57602060031936011261021d5761001961045a611cc2565b612094565b3461021d57600060031936011261021d57602060ff60075460181c16604051908152f35b3461021d5760031960408136011261021d576024359067ffffffffffffffff821161021d57606090823603011261021d576104c56020916004016004356122f2565b506040519015158152f35b3461021d57600060031936011261021d576005546104ed81611ff3565b6104f682611ff3565b60006040519361050585611c67565b60008552600060208096015260005b81811061054f5761053e8561054b8887878085528152604051948594604086526040860190611d06565b9184830390850152611d96565b0390f35b61056161055b82612697565b506126d1565b80518782015117610576575b50600101610514565b6001919373ffffffffffffffffffffffffffffffffffffffff60406105c19320168060005260068952604060002054906105b0838a612080565b526105bb8288612080565b52612024565b929061056d565b3461021d57600060031936011261021d5760206105e3611dca565b604051908152f35b3461021d57600060031936011261021d573360005260026020526040600020541561029257606061061a612c20565b63ffffffff60206fffffffffffffffffffffffffffffffff8351169201511660405191801515835260208301526040820152f35b3461021d57604060031936011261021d5767ffffffffffffffff60043581811161021d573660238201121561021d5780600401359161068c83611d7e565b9261069a6040519485611c9f565b808452602092838501906024809360061b8201019036821161021d578301915b8183106107ed57505050803591821161021d573660238301121561021d5781600401356106e681611d7e565b926106f46040519485611c9f565b8184528484018360608094028301019136831161021d578401905b8282106107a75750505050336000526000835260406000205415610798575082518151810361021d5761074190611ff3565b9260005b8151811015610781578061077061075e60019385612080565b516107698387612080565b5190612781565b61077a8288612080565b5201610745565b50505061054b604051928284938452830190611d96565b634a0bfec1600052338352601cfd5b838236031261021d57604051906107bd82611c83565b82359060ff8216820361021d5782899287945282850135838201526040850135604082015281520191019061070f565b60408336031261021d5785604091825161080681611c67565b8535815282860135838201528152019201916106ba565b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff61084b611cc2565b1660005260006020526020604060002054604051908152f35b3461021d57604060031936011261021d5760405161088181611c67565b6004356fffffffffffffffffffffffffffffffff8116810361021d57815260243563ffffffff8116810361021d57816105e3916020809401526121cd565b3461021d57602060031936011261021d576004353360005260006020526040600020541561035e576009548181036108f357005b6040519081528160208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a2600955005b3461021d57600060031936011261021d576020600954604051908152f35b3461021d57602060031936011261021d5761ffff60043581811680820361021d573360005260006020526040600020541561035e57801561021d57600754928360081c169181830361099c57610019612d96565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617600755808080806102ed565b3461021d57602060031936011261021d57610a20611cc2565b3360005260006020526040600020541561035e5761001990611f97565b3461021d57600060031936011261021d576003610a5a8154611ff3565b9060009160005b8151811015610ae15780610a77610ab292611e94565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610ab7575b50612024565b610a61565b610ac082611e94565b905490861b1c16610ada610ad387612024565b9685612080565b5285610aac565b8382526040516020808252819061054b90820185611d06565b3461021d5760206105e3610b0d36611ad1565b90612ae1565b3461021d576020600319818136011261021d5767ffffffffffffffff60043581811161021d5760608160040193823603011261021d57610b51612a02565b918483019063ffffffff928383511695610b766007549761ffff8960081c1690612a37565b8580421691161115610e9157610b9b6044610b93602485016122d1565b930182612a4f565b9290610bb26040519485928c840194863586612aa0565b0392610bc6601f1994858101835282611c9f565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610e5a575050610c2e90610c296fffffffffffffffffffffffffffffffff8098511660405190610c1882611c67565b8152878a60c01c168b8201526121cd565b6122f2565b92909687600014610d245750505050819051168160045460801c1610610c83575b5050337fb687b04e722c3f49a1145e5cb4994bfdfdb5c8a971eaa232c7a4129b0567f4546000604051a25b60405190158152f35b610ccb610d1892600854166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b60085460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b60006008558280610c4f565b909350610d3c92955060ff91945060181c1630612cc5565b610d44612eee565b906000808060405185335af1903d15610e54573d908111610e255760405190610d748886601f8401160183611c9f565b81526000873d92013e5b610df4575b506040519084825282519283868401528560005b858110610de057505050906040817fac50cef58b3aef7f7c30349f5e4a342a29d2325a02eafc8dacfdba391e6d5db3936000838784010152601f339601168101030190a2610c7a565b828101820151858201604001528101610d97565b6040519081527f50a7dd333474394c853a6d334bd7301b1362432233dcf7f04d2b0fc10fd7200d853392a284610d83565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b50610d7e565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461021d57610ec936611a67565b9190610ed3612a02565b63ffffffff60208201511693610ef46007549561ffff8760081c1690612a37565b63ffffffff8042169116116113d95763ffffffff60208301511663ffffffff60045460801c168082116000146113d15750905b610f33602086016129d4565b63ffffffff808416911611156113855763ffffffff421663ffffffff610f5b602088016129d4565b161161132c57610f6b8486612ae1565b9080359160ff8316830361021d576040805191825260ff939093166020808301919091528281013582850152919092013560608301526000808052909160809060015afa156113205773ffffffffffffffffffffffffffffffffffffffff60005116948560005260066020526040600020549081156112ef577fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff0000000061102f602089016122d1565b611062896110546110436040830183612a4f565b906040519586943560208601612aa0565b03601f198101835282611c9f565b6020815191012060201b169360181b169116171760075563ffffffff806020840151169116811461124b575b50506fffffffffffffffffffffffffffffffff90816110ac846129e5565b16600854907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617176008556110ff602084016129d4565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff0000000000000000000000000000000000000000000000006007549260c01b1691161760075560405190606082528035606083015273ffffffffffffffffffffffffffffffffffffffff61117b60208301611ce5565b16608083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561021d5701926020843594019267ffffffffffffffff851161021d57843603841361021d57602063ffffffff9261123192877fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c6397606060a08901528160c089015260e0880137600060e0898801015261122682611d50565b168286015201611d6d565b16604082015260e081601f19601f339601168101030190a3005b6112a26fffffffffffffffffffffffffffffffff6112e89351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b838061108e565b602487604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61138161133b602087016129d4565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff91821660048201524290911660248201529081906044820190565b0390fd5b50611392602085016129d4565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b905090610f27565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b3461021d57602060031936011261021d5761141c611cc2565b3360005260006020526040600020541561035e5761001990611ecb565b3461021d57602060031936011261021d57610019611455611cc2565b612150565b3461021d57600060031936011261021d57602061ffff60075460081c16604051908152f35b3461021d57600060031936011261021d57611498612bea565b60408051928352901515602083015290f35b3461021d57600060031936011261021d5733600052600260205260406000205415610292576fffffffffffffffffffffffffffffffff6114e8612c20565b5116801561021d57602090604051908152f35b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff611529611cc2565b1660005260026020526020604060002054604051908152f35b3461021d57600060031936011261021d5760206040517f5753544554482f555344000000000000000000000000000000000000000000008152f35b3461021d57602060031936011261021d57604073ffffffffffffffffffffffffffffffffffffffff6115b06004356126ef565b835191151582529091166020820152f35b3461021d57600060031936011261021d5760206105e3612eee565b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff61160a611cc2565b1660005260026020526020600160406000205414604051908152f35b3461021d57600060031936011261021d573360005260026020526040600020541561029257611653612c20565b6fffffffffffffffffffffffffffffffff815116801561021d5763ffffffff602060409301511682519182526020820152f35b3461021d57600060031936011261021d57602060405160128152f35b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff6116d0611cc2565b16600090815260066020908152604091829020548251811515815291820152f35b506116fa611b0d565b61000e565b3461021d57600060031936011261021d57602060405160fe8152f35b3461021d57600060031936011261021d5760016117388154611ff3565b60009182815b61175c575b8383526040516020808252819061054b90820186611d06565b82518110156117de576117ae816117738493611e2e565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c16600052600060205284604060002054146117b4575b5050612024565b9061173e565b6117bd83611e2e565b9054911b1c166117d66117cf88612024565b9787612080565b5286806117a7565b611743565b3461021d57602060031936011261021d573360005260006020526040600020541561035e5761001960043533612cc5565b3461021d57600060031936011261021d57336000526002602052604060002054156102925761054b6fffffffffffffffffffffffffffffffff611855612c20565b5160408051929091168015158352602083015290918291820190565b3461021d57602060031936011261021d5773ffffffffffffffffffffffffffffffffffffffff61189f611cc2565b1660005260006020526020600160406000205414604051908152f35b3461021d57600319360160a0811261021d5760401361021d576040516118e081611c67565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc36011261021d576040519061192482611c83565b60443560ff8116810361021d578252606435602083015260843560408301523360005260006020526040600020541561035e576020916105e391612781565b61196c36611a67565b9190611976612a02565b63ffffffff80602083015116946119986007549661ffff8860081c1690612a37565b82421692839116116113d95763ffffffff60208401511663ffffffff60045460801c16808211600014611a5f5750915b6119d4602087016129d4565b63ffffffff80851691161115611a52578063ffffffff6119f6602089016129d4565b1611611a075750610f6b8486612ae1565b611a13602087016129d4565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b82611392602088016129d4565b9050916119c8565b6003199182820160c0811261021d5760401361021d576004926044359067ffffffffffffffff821161021d57606090828503011261021d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261021d57606490565b906003198083016060811261021d5760401361021d576004926044359167ffffffffffffffff831161021d578260609203011261021d57820190565b503461021d57611b1c36611ad1565b9063ffffffff90816020611b2e612c20565b0151169260208201938084611b42876129d4565b161115611c5e575082611b54856129d4565b8142169182911611611c55575060408236031261021d57611b9c90610c29604051611b7e81611c67565b611b8785611d50565b8152611b9287611d6d565b60208201526121cd565b9015611c4d5750611c17611c116fffffffffffffffffffffffffffffffff9283611bc5826129e5565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617176004556129e5565b936129d4565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b611a13856129d4565b611392856129d4565b6040810190811067ffffffffffffffff821117610e2557604052565b6060810190811067ffffffffffffffff821117610e2557604052565b90601f601f19910116810190811067ffffffffffffffff821117610e2557604052565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361021d57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361021d57565b90815180825260208080930193019160005b828110611d26575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611d18565b35906fffffffffffffffffffffffffffffffff8216820361021d57565b359063ffffffff8216820361021d57565b67ffffffffffffffff8111610e255760051b60200190565b90815180825260208080930193019160005b828110611db6575050505090565b835185529381019392810192600101611da8565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c8152611e2881611c83565b51902090565b600154811015611e655760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600354811015611e655760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff80911690600090828252816020526001604083205414611f92576001604083205560015468010000000000000000811015611f6557611f26816001869301600155611e2e565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff16600081815280602052604081205415611fef578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b90611ffd82611d7e565b61200a6040519182611c9f565b828152601f1961201a8294611d7e565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146120515760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8051821015611e655760209160051b010190565b60003381528060205260408120541561213f5773ffffffffffffffffffffffffffffffffffffffff8092169182825260026020526001604083205414611f92576001604083205560035468010000000000000000811015611f6557612100816001869301600355611e94565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156121bc5773ffffffffffffffffffffffffffffffffffffffff16908181526002602052604081205415611fef578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f5753544554482f55534400000000000000000000000000000000000000000000865260801b16604084015260e01b1660508201526034815261226181611c83565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c8152611e2881611c83565b604051906060820182811067ffffffffffffffff821117610e255760405260006040838281528260208201520152565b3573ffffffffffffffffffffffffffffffffffffffff8116810361021d5790565b6040805193929061230285611c67565b600091828652826020809701526123176122a1565b5060075460ff16936060820135858103612647575060809061233d82840135861a612929565b9173ffffffffffffffffffffffffffffffffffffffff8086852016978451948b81019586511715612634576123706122a1565b505194519187519561238187611c83565b86528b8601928352878601938c60019b8c87528c905b8582106124f057505091505088915087516123b181611c67565b82815201528551936123c285611c67565b8785528a850192888452519188928a90807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b6124b357505050917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809261244e98979695948280858009809351098852519209900990526124468984016122d1565b923591612efe565b156124795780519485019085821067ffffffffffffffff831117611f65578190528185523690379190565b7fbd2a556b000000000000000000000000000000000000000000000000000000009192505193840152600483526124af83611c67565b9190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f81839497930492818885098203900892909182029003806123f6565b9161250884601f1984168d010135601f84161a612929565b90858d8320169382519183019182511715612617578685911610156125d557908a8f93928151928a51908c51948d7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f9687809681808097818080988189818d81808480098099510990820390089c8d9383808680098180808084600409998a98089281038c820308918009089052099a099451816002988d82039409900908840996828080888709810384820308818a800908809a52099009820394820390089009088752018e91612397565b5050505050505050505050507fb550c570000000000000000000000000000000000000000000000000000000009192505193840152600483526124af83611c67565b50505050999c505050505050505050506126319150612982565b90565b5050505050505050925061263190612982565b92517fce818a240000000000000000000000000000000000000000000000000000000097810197909752505060ff90811660248601529190911660448085019190915283526124af606484611c9f565b600554811015611e6557600560005260011b7f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00190600090565b906040516126de81611c67565b602060018294805484520154910152565b6005548110156127335761055b61270591612697565b80516020820151171561273357604073ffffffffffffffffffffffffffffffffffffffff9120169060019190565b50600090600090565b9190612752576020816001925184550151910155565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b9073ffffffffffffffffffffffffffffffffffffffff9060409180838520169060206127de6127ae611dca565b85518387015196880151885192835260ff9091166020830152604082019690965260608101959095526080850190565b846000958692838052039060015afa1561291f57825116810361291b57808252600660205282822054938415612816575b5050505090565b9091929350600554680100000000000000008110156128ee57906128438260016128499401600555612697565b9061273c565b600554907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182116128c157908060fe928195818652600660205282818720557f02652e7fe7a64eb217d52e6eec6ef55e181ceac19d9f5deb15aa62ea2b1bf9aa86339251a4116128be5780808061280f565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b5080fd5b83513d84823e3d90fd5b6000602060405161293981611c67565b82815201526040519061294b82611c67565b60011b7f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00160018154910154908252602082015290565b73ffffffffffffffffffffffffffffffffffffffff604051917f56d7d2e80000000000000000000000000000000000000000000000000000000060208401521660248201526024815261263181611c83565b3563ffffffff8116810361021d5790565b356fffffffffffffffffffffffffffffffff8116810361021d5790565b60405190612a0f82611c67565b6008546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff8080941691160191821161205157565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561021d570180359067ffffffffffffffff821161021d5760200191813603831361021d57565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b9061226160887fffffffff0000000000000000000000000000000000000000000000000000000092612b1e6020612b17876129e5565b96016129d4565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612b4c602083016122d1565b91612b5a6040820182612a4f565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f5753544554482f55534400000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b166074850152848401378101600083820152036068810184520182611c9f565b33600052600260205260406000205415610292576fffffffffffffffffffffffffffffffff612c17612c20565b51169081151590565b60006020604051612c3081611c67565b8281520152604051612c4181611c67565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612c74612a02565b926020840191612c918184511661ffff60075460081c1690612a37565b81804216911611159283612cb3575b505050600014612cae575090565b905090565b81929350511691511610388080612ca0565b9060055481101561021d57612d0091612cdd82612697565b509173ffffffffffffffffffffffffffffffffffffffff809360409586916126d1565b201693600093858552600660205281852054612d27575b505050505050612d25612d96565b565b6020612843948692612d8b988585205491865193167f6283f4f916bd1d3f7d6c69e150298bdd3840d8bc88f02a2740b98c4f198283518685a48284812055612d6e81611c67565b82815201525192612d7e84611c67565b8084526020840152612697565b388080808080612d17565b303b15612d2557612da5612a02565b6020810163ffffffff90612dc68282511661ffff60075460081c1690612a37565b82804216911611801580612edb575b612e7b575b612e2d575b50505060006008556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a2388080612ddf565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b1692161717600455612dda565b50828251168360045460801c1610612dd5565b6009544781811115612cae575090565b919091811580156130b6575b6130ad57602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a090890838009036130a1577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641419283861015613094577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff861117610e2557601b6000968580600160ff82613065987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa156113205773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff841615612f0a56fea264697066735822122010cc985ce25442a4ec9aa0268525ec181be86cdf477689c4941883e28d41b1ec64736f6c63430008100033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b5753544554482f55534400000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : initialAuthed (address): 0xc50dFeDb7E93eF7A3DacCAd7987D0960c4e2CD4b
Arg [1] : wat_ (bytes32): 0x5753544554482f55534400000000000000000000000000000000000000000000

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b
Arg [1] : 5753544554482f55534400000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.