ETH Price: $3,418.83 (+1.10%)
Gas: 4 Gwei

Contract

0x46ef0071b1E2fF6B42d36e5A177EA43Ae5917f4E
 

Overview

ETH Balance

1 ETH

Eth Value

$3,418.83 (@ $3,418.83/ETH)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Op Poke_optimize...202044492024-06-30 12:17:476 hrs ago1719749867IN
0x46ef0071...Ae5917f4E
0 ETH0.000134422.46415834
Op Poke_optimize...202013972024-06-30 2:03:4716 hrs ago1719713027IN
0x46ef0071...Ae5917f4E
0 ETH0.000074081.35803633
Op Poke_optimize...201983412024-06-29 15:49:4727 hrs ago1719676187IN
0x46ef0071...Ae5917f4E
0 ETH0.000149452.73965495
Op Poke_optimize...201952932024-06-29 5:35:4737 hrs ago1719639347IN
0x46ef0071...Ae5917f4E
0 ETH0.000109992.01636866
Op Poke_optimize...201910192024-06-28 15:15:472 days ago1719587747IN
0x46ef0071...Ae5917f4E
0 ETH0.000499349.15368612
Op Poke_optimize...201905812024-06-28 13:47:472 days ago1719582467IN
0x46ef0071...Ae5917f4E
0 ETH0.000340416.24025514
Op Poke_optimize...201903812024-06-28 13:07:472 days ago1719580067IN
0x46ef0071...Ae5917f4E
0 ETH0.000307325.63628219
Op Poke_optimize...201864112024-06-27 23:49:352 days ago1719532175IN
0x46ef0071...Ae5917f4E
0 ETH0.000147872.70950548
Op Poke_optimize...201833602024-06-27 13:35:353 days ago1719495335IN
0x46ef0071...Ae5917f4E
0 ETH0.0006063111.11469882
Op Poke_optimize...201821782024-06-27 9:37:593 days ago1719481079IN
0x46ef0071...Ae5917f4E
0 ETH0.000225894.14001363
Op Poke_optimize...201779252024-06-26 19:23:353 days ago1719429815IN
0x46ef0071...Ae5917f4E
0 ETH0.000462328.47326322
Op Poke_optimize...201754212024-06-26 10:59:354 days ago1719399575IN
0x46ef0071...Ae5917f4E
0 ETH0.000125182.29435252
Op Poke_optimize...201638412024-06-24 20:10:475 days ago1719259847IN
0x46ef0071...Ae5917f4E
0 ETH0.00042217.73945627
Op Poke_optimize...201630892024-06-24 17:39:116 days ago1719250751IN
0x46ef0071...Ae5917f4E
0 ETH0.000544099.97184461
Op Poke_optimize...201628392024-06-24 16:48:476 days ago1719247727IN
0x46ef0071...Ae5917f4E
0 ETH0.0008363815.3320914
Op Poke_optimize...201625102024-06-24 15:42:596 days ago1719243779IN
0x46ef0071...Ae5917f4E
0 ETH0.000423437.76038762
Op Poke_optimize...201604142024-06-24 8:40:596 days ago1719218459IN
0x46ef0071...Ae5917f4E
0 ETH0.000325725.97105963
Op Poke_optimize...201570002024-06-23 21:13:476 days ago1719177227IN
0x46ef0071...Ae5917f4E
0 ETH0.000160042.93378402
Op Poke_optimize...201549522024-06-23 14:21:357 days ago1719152495IN
0x46ef0071...Ae5917f4E
0 ETH0.000174923.20660452
Op Poke_optimize...201519102024-06-23 4:07:357 days ago1719115655IN
0x46ef0071...Ae5917f4E
0 ETH0.000104051.90663004
Op Poke_optimize...201488582024-06-22 17:53:358 days ago1719078815IN
0x46ef0071...Ae5917f4E
0 ETH0.00011532.11426663
Op Poke_optimize...201458102024-06-22 7:39:358 days ago1719041975IN
0x46ef0071...Ae5917f4E
0 ETH0.000115752.12199787
Op Poke_optimize...201427652024-06-21 21:25:358 days ago1719005135IN
0x46ef0071...Ae5917f4E
0 ETH0.000232854.27039662
Op Poke_optimize...201423892024-06-21 20:09:358 days ago1719000575IN
0x46ef0071...Ae5917f4E
0 ETH0.000237394.35172224
Op Poke_optimize...201420192024-06-21 18:55:359 days ago1718996135IN
0x46ef0071...Ae5917f4E
0 ETH0.000274055.02599383
View all transactions

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Block From To Value
197756972024-05-01 13:52:2360 days ago1714571543
0x46ef0071...Ae5917f4E
1 ETH
187914662023-12-15 12:17:59198 days ago1702642679  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Chronicle_ETH_USD_3

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 10000 runs

Other Settings:
london EvmVersion
File 1 of 11 : 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[];

    // -- Storage --

    /// @inheritdoc IScribeOptimistic
    uint16 public opChallengePeriod;

    /// @inheritdoc IScribeOptimistic
    uint8 public opFeedId;

    /// @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_)
        payable
        Scribe(initialAuthed, wat_)
    {
        // Note to have a non-zero challenge period.
        _setOpChallengePeriod(20 minutes);

        // Set maxChallengeReward to type(uint).max.
        _setMaxChallengeRewards(type(uint).max);
    }

    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 IScribeOptimistic
    ///      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 {
        // Revert if schnorrData.feedIds' length is higher than bar's maximum
        // value.
        //
        // Note that this prevents opPoke's with such big schnorrData that it
        // becomes economically unprofitable to challenge them.
        if (schnorrData.feedIds.length > type(uint8).max) {
            revert BarNotReached(type(uint8).max, bar);
        }

        // 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
        );

        // Compute feed id of signer.
        uint8 feedId = uint8(uint(uint160(signer)) >> 152);

        // Revert if signer not feed.
        // assert(_pubKeys[feedId].toAddress() != address(0));
        if (_pubKeys[feedId].toAddress() != signer) {
            revert SignerNotFeed(signer);
        }

        // Store the feed's id as opFeedId and bind them to their provided
        // schnorrData.
        opFeedId = feedId;
        _schnorrDataCommitment = uint160(
            uint(
                keccak256(
                    abi.encodePacked(
                        schnorrData.signature,
                        schnorrData.commitment,
                        schnorrData.feedIds
                    )
                )
            )
        );

        // 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.feedIds
                    )
                )
            )
        );

        // 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, schnorrData);
        } else {
            // Drop opFeed and delete invalid _opPokeData.
            // Note to use address(this) as caller to indicate self-governed
            // drop of feed.
            _drop(address(this), opFeedId);

            // Pay ETH reward to challenger.
            uint reward = challengeReward();
            if (_sendETH(payable(msg.sender), reward)) {
                emit OpChallengeRewardPaid(msg.sender, schnorrData, reward);
            }

            emit OpPokeChallengedSuccessfully(msg.sender, schnorrData, 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.feedIds
                    )
                )
            )
        );
    }

    // -- 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);
    }

    /// @inheritdoc IChronicle
    /// @dev Only callable by toll'ed address.
    function tryReadWithAge()
        external
        view
        override(IChronicle, Scribe)
        toll
        returns (bool, uint, uint)
    {
        PokeData memory pokeData = _currentPokeData();
        return pokeData.val != 0
            ? (true, pokeData.val, pokeData.age)
            : (false, 0, 0);
    }

    // - MakerDAO Compatibility

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function peek()
        external
        view
        override(IScribe, Scribe)
        toll
        returns (uint, bool)
    {
        uint val = _currentPokeData().val;
        return (val, val != 0);
    }

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function peep()
        external
        view
        override(IScribe, Scribe)
        toll
        returns (uint, bool)
    {
        uint val = _currentPokeData().val;
        return (val, val != 0);
    }

    // - Chainlink Compatibility

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function latestRoundData()
        external
        view
        override(IScribe, Scribe)
        toll
        returns (
            uint80 roundId,
            int answer,
            uint startedAt,
            uint updatedAt,
            uint80 answeredInRound
        )
    {
        PokeData memory pokeData = _currentPokeData();

        roundId = 1;
        answer = int(uint(pokeData.val));
        // assert(uint(answer) == uint(pokeData.val));
        startedAt = 0;
        updatedAt = pokeData.age;
        answeredInRound = roundId;
    }

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function latestAnswer()
        external
        view
        virtual
        override(IScribe, Scribe)
        toll
        returns (int)
    {
        uint val = _currentPokeData().val;
        return int(val);
    }

    function _currentPokeData() internal view returns (PokeData memory) {
        // Load pokeData slots from storage.
        PokeData memory pokeData = _pokeData;
        PokeData memory opPokeData = _opPokeData;

        // Decide whether _opPokeData is finalized.
        bool opPokeDataFinalized =
            opPokeData.age + opChallengePeriod <= uint32(block.timestamp);

        // Decide and return current pokeData.
        if (opPokeDataFinalized && opPokeData.age > pokeData.age) {
            return opPokeData;
        } else {
            return pokeData;
        }
    }

    // -- Auth'ed Functionality --

    /// @inheritdoc IScribeOptimistic
    function setOpChallengePeriod(uint16 opChallengePeriod_) external auth {
        _setOpChallengePeriod(opChallengePeriod_);
    }

    function _setOpChallengePeriod(uint16 opChallengePeriod_) internal {
        require(opChallengePeriod_ != 0);

        if (opChallengePeriod != opChallengePeriod_) {
            emit OpChallengePeriodUpdated(
                msg.sender, opChallengePeriod, opChallengePeriod_
            );
            opChallengePeriod = opChallengePeriod_;
        }

        _afterAuthedAction();
    }

    function _drop(address caller, uint8 feedId) internal override(Scribe) {
        super._drop(caller, feedId);

        _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 {
        _setMaxChallengeRewards(maxChallengeReward_);
    }

    function _setMaxChallengeRewards(uint maxChallengeReward_) internal {
        if (maxChallengeReward != maxChallengeReward_) {
            emit MaxChallengeRewardUpdated(
                msg.sender, maxChallengeReward, maxChallengeReward_
            );
            maxChallengeReward = maxChallengeReward_;
        }
    }

    function _sendETH(address payable to, uint amount)
        internal
        returns (bool)
    {
        (bool ok,) = to.call{value: amount}("");
        return ok;
    }
}

/**
 * @dev Contract overwrite to deploy contract instances with specific naming.
 *
 *      For more info, see docs/Deployment.md.
 */
contract Chronicle_ETH_USD_3 is ScribeOptimistic {
    constructor(address initialAuthed, bytes32 wat_)
        ScribeOptimistic(initialAuthed, wat_)
    {}
}

File 2 of 11 : 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 11 : 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 challengeable opPoke exists.
    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 Thrown if opPoke called with non-feed ECDSA signature.
    /// @param signer The ECDSA signature's signer.
    error SignerNotFeed(address signer);

    /// @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 schnorrData The schnorrData challenged.
    /// @param schnorrErr The abi-encoded custom error returned from the failed
    ///                   Schnorr signature verification.
    event OpPokeChallengedSuccessfully(
        address indexed caller,
        IScribe.SchnorrData schnorrData,
        bytes schnorrErr
    );

    /// @notice Emitted when unsuccessfully challenged an opPoke.
    /// @param caller The caller's address.
    /// @param schnorrData The schnorrData challenged.
    event OpPokeChallengedUnsuccessfully(
        address indexed caller, IScribe.SchnorrData schnorrData
    );

    /// @notice Emitted when ETH reward paid for successfully challenging an
    ///         opPoke.
    /// @param challenger The challenger to which the reward was send.
    /// @param schnorrData The schnorrData challenged.
    /// @param reward The ETH rewards paid.
    event OpChallengeRewardPaid(
        address indexed challenger, IScribe.SchnorrData schnorrData, 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 if an opPoke
    ///      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 `pokeData`'s age to not be greater than the current time.
    /// @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 id of the feed last opPoke'd.
    /// @return opFeedId Feed id of the feed last opPoke'd.
    function opFeedId() external view returns (uint8 opFeedId);

    /// @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 11 : 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 feedIds;
    }

    /// @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 given feed id invalid.
    /// @param feedId The invalid feed id.
    error InvalidFeedId(uint8 feedId);

    /// @notice Thrown if double signing attempted.
    /// @param feedId The id of the feed attempting to double sign.
    error DoubleSigningAttempted(uint8 feedId);

    /// @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.
    event FeedLifted(address indexed caller, address indexed feed);

    /// @notice Emitted when feed dropped.
    /// @param caller The caller's address.
    /// @param feed The feed address dropped.
    event FeedDropped(address indexed caller, address indexed feed);

    /// @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 Returns the bar security parameter.
    /// @return bar The bar security parameter.
    function bar() external view returns (uint8 bar);

    /// @notice Returns the number of decimals of the oracle's value.
    /// @dev Provides partial compatibility with Chainlink's
    ///      IAggregatorV3Interface.
    /// @return decimals The oracle value's number of decimals.
    function decimals() external view returns (uint8 decimals);

    /// @notice Returns the oracle's latest value.
    /// @dev Provides partial compatibility with Chainlink's
    ///      IAggregatorV3Interface.
    /// @return roundId 1.
    /// @return answer The oracle's latest value.
    /// @return startedAt 0.
    /// @return updatedAt The timestamp of oracle's latest update.
    /// @return answeredInRound 1.
    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int answer,
            uint startedAt,
            uint updatedAt,
            uint80 answeredInRound
        );

    /// @notice Returns the oracle's latest value.
    /// @dev Provides partial compatibility with Chainlink's
    ///      IAggregatorV3Interface.
    /// @custom:deprecated See https://docs.chain.link/data-feeds/api-reference/#latestanswer.
    /// @return answer The oracle's latest value.
    function latestAnswer() external view returns (int);

    /// @notice Pokes the oracle.
    /// @dev Expects `pokeData`'s age to be greater than the timestamp of the
    ///      last successful poke.
    /// @dev Expects `pokeData`'s age to not be greater than the current time.
    /// @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[])`.
    /// @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 pokeMessage Message for `pokeData`.
    function constructPokeMessage(PokeData calldata pokeData)
        external
        view
        returns (bytes32 pokeMessage);

    /// @notice Returns whether address `who` is a feed.
    /// @param who The address to check.
    /// @return isFeed True if `who` is feed, false otherwise.
    function feeds(address who) external view returns (bool isFeed);

    /// @notice Returns whether feed id `feedId` is a feed and, if so, the
    ///         feed's address.
    /// @param feedId The feed id to check.
    /// @return isFeed True if `feedId` is a feed, false otherwise.
    /// @return feed Address of the feed with id `feedId` if `feedId` is a feed,
    ///              zero-address otherwise.
    function feeds(uint8 feedId)
        external
        view
        returns (bool isFeed, address feed);

    /// @notice Returns list of feed addresses.
    /// @dev Note that this function has a high gas consumption and is not
    ///      intended to be called onchain.
    /// @return feeds List of feed addresses.
    function feeds() external view returns (address[] memory feeds);

    /// @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 via
    ///      `feedRegistrationMessage()(bytes32)`.
    /// @param pubKey The public key of the feed.
    /// @param ecdsaData ECDSA signed message by the feed's public key.
    /// @return feedId The id of the newly lifted feed.
    function lift(LibSecp256k1.Point memory pubKey, ECDSAData memory ecdsaData)
        external
        returns (uint8 feedId);

    /// @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 via
    ///      `feedRegistrationMessage()(bytes32)`.
    /// @param pubKeys The public keys of the feeds.
    /// @param ecdsaDatas ECDSA signed message by the feeds' public keys.
    /// @return List of feed ids of the newly lifted feeds.
    function lift(
        LibSecp256k1.Point[] memory pubKeys,
        ECDSAData[] memory ecdsaDatas
    ) external returns (uint8[] memory);

    /// @notice Drops feed with id `feedId`.
    /// @dev Only callable by auth'ed address.
    /// @param feedId The feed id to drop.
    function drop(uint8 feedId) external;

    /// @notice Drops feeds with ids' `feedIds`.
    /// @dev Only callable by auth'ed address.
    /// @param feedIds The feed ids to drop.
    function drop(uint8[] memory feedIds) 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 11 : 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";

/**
 * @title Scribe
 * @custom:version 2.0.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;

    /// @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;

    // -- Storage --

    /// @dev Scribe's current value and corresponding age.
    PokeData internal _pokeData;

    /// @dev Statically allocated array of feeds' public keys.
    ///      Indexed via the public keys address' highest-order byte.
    LibSecp256k1.Point[256] internal _pubKeys;

    /// @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_)
        payable
        Auth(initialAuthed)
    {
        require(wat_ != 0);

        // Set wat immutable.
        wat = wat_;

        // Let initial bar be 2.
        _setBar(2);
    }

    // -- 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 O(bar).
    function _verifySchnorrSignature(
        bytes32 message,
        SchnorrData calldata schnorrData
    ) internal view returns (bool, bytes memory) {
        // Let feedPubKey be the currently processed feed's public key.
        LibSecp256k1.Point memory feedPubKey;
        // Let feedId be the currently processed feed's id.
        uint8 feedId;
        // Let aggPubKey be the sum of processed feeds' public keys.
        // Note that Jacobian coordinates are used.
        LibSecp256k1.JacobianPoint memory aggPubKey;
        // Let bloom be a bloom filter to check for double signing attempts.
        uint bloom;

        // Fail if number feeds unequal to bar.
        //
        // Note that requiring equality constrains the verification's runtime
        // from Ω(bar) to Θ(bar).
        uint numberFeeds = schnorrData.feedIds.length;
        if (numberFeeds != bar) {
            return (false, _errorBarNotReached(uint8(numberFeeds), bar));
        }

        // Initiate feed variables with schnorrData's 0's feed index.
        feedId = uint8(schnorrData.feedIds[0]);
        feedPubKey = _pubKeys[feedId];

        // Fail if feed not lifted.
        if (feedPubKey.isZeroPoint()) {
            return (false, _errorInvalidFeedId(feedId));
        }

        // Initiate bloom filter with feedId set.
        bloom = 1 << feedId;

        // Initiate aggPubKey with value of first feed's public key.
        aggPubKey = feedPubKey.toJacobian();

        for (uint8 i = 1; i < numberFeeds;) {
            // Update feed variables.
            feedId = uint8(schnorrData.feedIds[i]);
            feedPubKey = _pubKeys[feedId];

            // Fail if feed not lifted.
            if (feedPubKey.isZeroPoint()) {
                return (false, _errorInvalidFeedId(feedId));
            }

            // Fail if double signing attempted.
            if (bloom & (1 << feedId) != 0) {
                return (false, _errorDoubleSigningAttempted(feedId));
            }
            // Update bloom filter.
            bloom |= 1 << feedId;

            // assert(aggPubKey.x != feedPubKey.x); // Indicates rogue-key attack

            // Add feedPubKey to already aggregated public keys.
            aggPubKey.addAffinePoint(feedPubKey);

            // 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 ? (true, val, age) : (false, 0, 0);
    }

    // - MakerDAO Compatibility

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function peek() external view virtual toll returns (uint, bool) {
        uint val = _pokeData.val;
        return (val, val != 0);
    }

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function peep() external view virtual toll returns (uint, bool) {
        uint val = _pokeData.val;
        return (val, val != 0);
    }

    // - Chainlink Compatibility

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function latestRoundData()
        external
        view
        virtual
        toll
        returns (
            uint80 roundId,
            int answer,
            uint startedAt,
            uint updatedAt,
            uint80 answeredInRound
        )
    {
        roundId = 1;
        answer = int(uint(_pokeData.val));
        // assert(uint(answer) == uint(_pokeData.val));
        startedAt = 0;
        updatedAt = _pokeData.age;
        answeredInRound = roundId;
    }

    /// @inheritdoc IScribe
    /// @dev Only callable by toll'ed address.
    function latestAnswer() external view virtual toll returns (int) {
        uint val = _pokeData.val;
        return int(val);
    }

    // -- Public Read Functionality --

    /// @inheritdoc IScribe
    function feeds(address who) external view returns (bool) {
        uint8 feedId = uint8(uint(uint160(who)) >> 152);

        LibSecp256k1.Point memory pubKey = _pubKeys[feedId];

        return !pubKey.isZeroPoint() && pubKey.toAddress() == who;
    }

    /// @inheritdoc IScribe
    function feeds(uint8 feedId) external view returns (bool, address) {
        LibSecp256k1.Point memory pubKey = _pubKeys[feedId];

        return pubKey.isZeroPoint()
            ? (false, address(0))
            : (true, pubKey.toAddress());
    }

    /// @inheritdoc IScribe
    function feeds() external view returns (address[] memory) {
        address[] memory feeds_ = new address[](256);

        LibSecp256k1.Point memory pubKey;
        address feed;
        uint ctr;
        for (uint i; i < 256;) {
            pubKey = _pubKeys[uint8(i)];

            if (!pubKey.isZeroPoint()) {
                feed = pubKey.toAddress();

                feeds_[ctr] = feed;

                // forgefmt: disable-next-item
                unchecked { ++ctr; }
            }

            // forgefmt: disable-next-item
            unchecked { ++i; }
        }

        assembly ("memory-safe") {
            mstore(feeds_, ctr)
        }

        return feeds_;
    }

    // -- Auth'ed Functionality --

    /// @inheritdoc IScribe
    function lift(LibSecp256k1.Point memory pubKey, ECDSAData memory ecdsaData)
        external
        auth
        returns (uint8)
    {
        return _lift(pubKey, ecdsaData);
    }

    /// @inheritdoc IScribe
    function lift(
        LibSecp256k1.Point[] memory pubKeys,
        ECDSAData[] memory ecdsaDatas
    ) external auth returns (uint8[] memory) {
        require(pubKeys.length == ecdsaDatas.length);

        uint8[] memory feedIds = new uint8[](pubKeys.length);
        for (uint i; i < pubKeys.length;) {
            feedIds[i] = _lift(pubKeys[i], ecdsaDatas[i]);

            // forgefmt: disable-next-item
            unchecked { ++i; }
        }

        return feedIds;
    }

    function _lift(LibSecp256k1.Point memory pubKey, ECDSAData memory ecdsaData)
        internal
        returns (uint8)
    {
        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);

        uint8 feedId = uint8(uint(uint160(feed)) >> 152);

        LibSecp256k1.Point memory sPubKey = _pubKeys[feedId];
        if (sPubKey.isZeroPoint()) {
            _pubKeys[feedId] = pubKey;

            emit FeedLifted(msg.sender, feed);
        } else {
            // Note to be idempotent. However, disallow updating an id's feed
            // via lifting without dropping the previous feed.
            require(feed == sPubKey.toAddress());
        }

        return feedId;
    }

    /// @inheritdoc IScribe
    function drop(uint8 feedId) external auth {
        _drop(msg.sender, feedId);
    }

    /// @inheritdoc IScribe
    function drop(uint8[] memory feedIds) external auth {
        for (uint i; i < feedIds.length;) {
            _drop(msg.sender, feedIds[i]);

            // forgefmt: disable-next-item
            unchecked { ++i; }
        }
    }

    function _drop(address caller, uint8 feedId) internal virtual {
        LibSecp256k1.Point memory pubKey = _pubKeys[feedId];
        if (!pubKey.isZeroPoint()) {
            delete _pubKeys[feedId];

            emit FeedDropped(caller, pubKey.toAddress());
        }
    }

    /// @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 --

    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)
        }
    }

    function _errorBarNotReached(uint8 got, uint8 want)
        internal
        pure
        returns (bytes memory)
    {
        // assert(got != want);
        return abi.encodeWithSelector(IScribe.BarNotReached.selector, got, want);
    }

    function _errorInvalidFeedId(uint8 feedId)
        internal
        pure
        returns (bytes memory)
    {
        // assert(_pubKeys[feedId].isZeroPoint());
        return abi.encodeWithSelector(IScribe.InvalidFeedId.selector, feedId);
    }

    function _errorDoubleSigningAttempted(uint8 feedId)
        internal
        pure
        returns (bytes memory)
    {
        return abi.encodeWithSelector(
            IScribe.DoubleSigningAttempted.selector, feedId
        );
    }

    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 11 : 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 11 : 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 11 : 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 11 : 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 11 : 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 11 of 11 : 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);
}

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/",
    "lib/chronicle-std:ds-test/=lib/chronicle-std/lib/forge-std/lib/ds-test/src/",
    "lib/chronicle-std:forge-std/=lib/chronicle-std/lib/forge-std/src/",
    "greenhouse/=lib/greenhouse/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

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":"uint8","name":"feedId","type":"uint8"}],"name":"DoubleSigningAttempted","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":[{"internalType":"uint8","name":"feedId","type":"uint8"}],"name":"InvalidFeedId","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":[{"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"}],"name":"FeedDropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"feed","type":"address"}],"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"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"feedIds","type":"bytes"}],"indexed":false,"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"OpChallengeRewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"feedIds","type":"bytes"}],"indexed":false,"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"},{"indexed":false,"internalType":"bytes","name":"schnorrErr","type":"bytes"}],"name":"OpPokeChallengedSuccessfully","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"components":[{"internalType":"bytes32","name":"signature","type":"bytes32"},{"internalType":"address","name":"commitment","type":"address"},{"internalType":"bytes","name":"feedIds","type":"bytes"}],"indexed":false,"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"}],"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":"feedIds","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":"feedIds","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":"uint8[]","name":"feedIds","type":"uint8[]"}],"name":"drop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"feedId","type":"uint8"}],"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"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"feedId","type":"uint8"}],"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[]"}],"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":"feedIds","type":"bytes"}],"internalType":"struct IScribe.SchnorrData","name":"schnorrData","type":"tuple"}],"name":"isAcceptableSchnorrSignatureNow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"kiss","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct LibSecp256k1.Point","name":"pubKey","type":"tuple"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct IScribe.ECDSAData","name":"ecdsaData","type":"tuple"}],"name":"lift","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"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":"uint8[]","name":"","type":"uint8[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxChallengeReward","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":"feedIds","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":"opFeedId","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":"feedIds","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":"feedIds","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":"feedIds","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":"feedIds","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"}]

60a0346200024857620037ac90601f38839003908101601f19168201906001600160401b038211838310176200024d5780839160409586948552833981010312620002485780516001600160a01b038116908190036200024857602080920151600082815280845260018582205560015468010000000000000000811015620002345760018101806001558110156200022057600182528482200180546001600160a01b03191684179055845192817fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f8186a381156200021d575060805261020590815460ff81169160028303620001de575b505050620000ff62000263565b805461ffff8160081c16906104b09182810362000198575b505050506200012562000263565b610207805460001992600182016200015e575b84516134089081620003a48239608051818181611714015281816125d90152612ec90152f35b839085519283528201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee37843392a2553880808062000138565b6204b000928651918252858201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a1118863392a262ffff0019161790553880808062000117565b600292815282858201527f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea863392a260ff1916178155388080620000f2565b80fd5b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b82526041600452602482fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b303b15620003a157604080519081016001600160401b038111828210176200024d5760405261020680546001600160801b03808216845260809190911c63ffffffff908116602085018181526102055460081c61ffff1690910194928286116200038b576000958380421691161180158062000377575b6200034c575b6200030d575b505050919055506004805463ffffffff60801b19164260801b63ffffffff60801b16179055565b6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a238808080620002e6565b818551166004549063ffffffff60801b855160801b169160018060a01b0319161717600455620002e0565b50838351168460045460801c1610620002da565b634e487b7160e01b600052601160045260246000fd5b56fe608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c908115611ab757508060821461189b5780630760861b14611a045780630e5a6c70146115ef5780630fce3415146119ba57806310b07b7114611969578063224242ca146118a95780632f529d731461189b5780632fba4aa914611818578063313ce567146117fc578063393e5ede1461179c5780633bee58f9146117525780633ea0c15e146117375780634ca29923146116fc5780634fce7a2a146116b557806350d25bcd1461166b57806357de26a41461161a57806359e02dd7146115ef578063646edb68146115c957806365c4ce7a146115a857806365fae35e146115725780636712af9e14611068578063789d819114610fc65780638928a1f814610c145780638b0b044c14610bfb5780639954b0dc14610b465780639c52a7f114610b105780639dadc88614610a4d5780639fd001f614610a2e578063ab06ee16146109c3578063acf40b6f14610968578063b259da5c1461088f578063b9ee3fc11461086a578063bf353dbb14610823578063bfe5861f14610602578063c83c6334146105cb578063ceed3ef214610544578063d0a5882a14610521578063d63605b8146103ea578063dac42ad81461039d578063f29c29c41461037c578063fe663495146102b0578063feaf968c1461022e5763febb0f7e14610207575b3861000f565b3461022957600060031936011261022957602060ff6102055416604051908152f35b600080fd5b34610229576000600319360112610229573360005260026020526040600020541561029e5760a061025d612f5b565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b34610229576020600319360112610229576102c96120d9565b3360005260006020526040600020541561036a5760ff811680156102295761020591825460ff8116838103610301575b61001b613091565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617905580808080806102f9565b634a0bfec1600052336020526024601cfd5b346102295760206003193601126102295761001b6103986120f7565b61244c565b3461022957600319604081360112610229576024359067ffffffffffffffff8211610229576060908236030112610229576103df602091600401600435612708565b506040519015158152f35b3461022957600060031936011261022957604051612020810181811067ffffffffffffffff8211176104f25760405261010090818152602061200036828401376000816040516104398161207e565b828152015260009060005b848110610460578284526040518061045c868261211a565b0390f35b8460ff821610156104c3576001906104806101fe82841b166005016126c9565b80518482015117610493575b5001610444565b9373ffffffffffffffffffffffffffffffffffffffff604084939620166104ba8288612438565b5201928661048c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3461022957600060031936011261022957602061053c6121b1565b604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576060610573612f5b565b80516fffffffffffffffffffffffffffffffff9081169182156105b8579063ffffffff60208193015116906001935b6040519415158552166020840152166040820152f35b506000915063ffffffff908290816105a2565b34610229576020600319360112610229576105e46120d9565b3360005260006020526040600020541561036a5761001b9033613001565b346102295760406003193601126102295767ffffffffffffffff600435818111610229573660238201121561022957806004013561063f81612199565b9061064d60405192836120b6565b808252602092838301906024809360061b82010190368211610229578301915b8183106107f3575050508035938411610229573660238501121561022957836004013561069981612199565b946106a760405196876120b6565b81865284860183606080940283010191368311610229578401905b8282106107b357505050503360005260008352604060002054156107a45750805190835182036102295761070e6106f883612199565b9261070660405194856120b6565b808452612199565b92601f19818401940136853760005b825181101561075e578061074861073660019386612438565b51610741838a612438565b5190612b38565b60ff6107548388612438565b911690520161071d565b508284604051928392818401908285525180915260408401929160005b82811061078a57505050500390f35b835160ff168552869550938101939281019260010161077b565b634a0bfec1600052338352601cfd5b8382360312610229578684916040516107cb8161209a565b6107d4856120e9565b81528285013583820152604085013560408201528152019101906106c2565b6040833603126102295785604091825161080c8161207e565b85358152828601358382015281520192019161066d565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6108516120f7565b1660005260006020526020604060002054604051908152f35b3461022957600060031936011261022957602060ff6102055460181c16604051908152f35b34610229576020806003193601126102295760043567ffffffffffffffff81116102295736602382011215610229578060040135906108cd82612199565b916108db60405193846120b6565b80835260248484019160051b8301019136831161022957602401905b828210610951578385336000526000815260406000205415610940575060005b815181101561001b578061093a60ff61093260019486612438565b511633613001565b01610917565b634a0bfec16000523390526024601cfd5b84809161095d846120e9565b8152019101906108f7565b34610229576040600319360112610229576040516109858161207e565b6004356fffffffffffffffffffffffffffffffff8116810361022957815260243563ffffffff81168103610229578161053c91602080940152612585565b34610229576020600319360112610229576004353360005260006020526040600020541561036a5761020780548281036109f957005b6040519081528260208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a255005b3461022957600060031936011261022957602061020754604051908152f35b346102295760206003193601126102295760043561ffff80821691828103610229573360005260006020526040600020541561036a57821561022957610205928354928360081c1691818303610aa55761001b613091565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617905580808080806102f9565b3461022957602060031936011261022957610b296120f7565b3360005260006020526040600020541561036a5761001b9061234f565b34610229576000600319360112610229576003610b6381546123ab565b9060009160005b8151811015610bea5780610b80610bbb9261224c565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610bc0575b506123dc565b610b6a565b610bc98261224c565b905490861b1c16610be3610bdc876123dc565b9685612438565b5285610bb5565b8382526040518061045c848261211a565b3461022957602061053c610c0e36611ee8565b90612e1c565b3461022957602060031981813601126102295767ffffffffffffffff906004358281116102295760608160040192823603011261022957610c53612ce5565b84810163ffffffff928382511695610c77610205549761ffff8960081c1690612d1b565b8580421691161115610f9c57610c9c6044610c94602485016126e7565b930187612678565b9290610cb36040519485928c8401948b3586612d33565b0392610cc7601f19948581018352826120b6565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610f65575050610d2f86610d2a6fffffffffffffffffffffffffffffffff8097511660405190610d198261207e565b8152888b60c01c168c820152612585565b612708565b91909788600014610e365750505050829051168260045460801c1610610d95575b50507f3f2cf79ad39c64280638c6fc485670c10ed4dad0a35ecd2857255ee7a130ba2060405184815280610d88339487830190612d74565b0390a25b60405190158152f35b610e2e600092610de2610206938454166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b825460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b558380610d50565b90935060ff919550610e50929796945060181c1630613001565b610e586131eb565b906000808060405185335af1903d15610f5f573d9081116104f25760405190610e888989601f84011601836120b6565b81526000883d92013e5b610f19575b5084610eae60405192604084526040840190612d74565b948286038284015283519384875260005b858110610f06575050858391600084877f2984f6d7ff4df266745fc0283d83a02c5125069524c8eeb85887de137e8f890c98999a010152601f3397011601030190a2610d8c565b8181018401518882018501528301610ebf565b7f7e4d8c9da9421f78d986605928e77f0920f2c995d1cbba819c8915d2b209aa4b60405160408152610f4e6040820185612d74565b92888201528033930390a285610e97565b50610e92565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461022957602060031936011261022957610fdf6120d9565b6101008110156104c357610ff89060011b6005016126c9565b805160208201511761103057506040600073ffffffffffffffffffffffffffffffffffffffff60005b83519215158352166020820152f35b73ffffffffffffffffffffffffffffffffffffffff60408092201673ffffffffffffffffffffffffffffffffffffffff600191611021565b346102295761107636611e7e565b9160ff6110866040840184612678565b9050116115345763ffffffff9161109b612ce5565b83602082015116946110b9610205549661ffff8860081c1690612d1b565b854216958691161161150a5763ffffffff60208301511663ffffffff60045460801c168082116000146115025750945b6110f560208601612cb7565b63ffffffff808816911611156114b6578063ffffffff61111760208801612cb7565b161161146b57506111288385612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519373ffffffffffffffffffffffffffffffffffffffff948581169561010060ff8360981c1610156104c357869060406111b26101fe8560971c166005016126c9565b20160361142e577fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639495967fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008761125161125f61122f602084016126e7565b61123c6040850185612678565b60409491945194859360208501973588612d33565b03601f1981018352826120b6565b51902060201b169360801c16911617176102055563ffffffff806020840151169116811461138a575b505063ffffffff61137c60206fffffffffffffffffffffffffffffffff95866112b082612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611304828201612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff000000000000000000000000000000000000000000000000610205549260c01b169116176102055561136760405195606087526060870190612d74565b966113718261216b565b168286015201612188565b1660408201528033930390a3005b6113e16fffffffffffffffffffffffffffffffff6114279351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b8480611288565b602486604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61147760208601612cb7565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b856114c360208701612cb7565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b9050946110e9565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b604460ff6102055416604051907fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b346102295760206003193601126102295761158b6120f7565b3360005260006020526040600020541561036a5761001b90612283565b346102295760206003193601126102295761001b6115c46120f7565b612508565b3461022957600060031936011261022957602061ffff6102055460081c16604051908152f35b3461022957600060031936011261022957611608612f25565b60408051928352901515602083015290f35b34610229576000600319360112610229573360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff611658612f5b565b5116801561022957602090604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e5760206fffffffffffffffffffffffffffffffff6116ab612f5b565b5116604051908152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6116e36120f7565b1660005260026020526020604060002054604051908152f35b346102295760006003193601126102295760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461022957600060031936011261022957602061053c6131eb565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6117806120f7565b1660005260026020526020600160406000205414604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576117c9612f5b565b6fffffffffffffffffffffffffffffffff81511680156102295763ffffffff602060409301511682519182526020820152f35b3461022957600060031936011261022957602060405160128152f35b34610229576020600319360112610229576118316120f7565b61010060ff8260981c1610156104c357806118576101fe60209360971c166005016126c9565b8051838201511715159182611873575b50506040519015158152f35b909150604073ffffffffffffffffffffffffffffffffffffffff809216922016148280611867565b506118a4611f24565b610201565b346102295760006003193601126102295760016118c681546123ab565b60009182815b6118e2575b8383526040518061045c858261211a565b825181101561196457611934816118f98493612215565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c166000526000602052846040600020541461193a575b50506123dc565b906118cc565b61194383612215565b9054911b1c1661195c611955886123dc565b9787612438565b52868061192d565b6118d1565b34610229576000600319360112610229573360005260026020526040600020541561029e5760406fffffffffffffffffffffffffffffffff6119a9612f5b565b511681519080151582526020820152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6119e86120f7565b1660005260006020526020600160406000205414604051908152f35b3461022957600319360160a081126102295760401361022957604051611a298161207e565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102295760405190611a6d8261209a565b60443560ff81168103610229578252606435602083015260843560408301523360005260006020526040600020541561036a57602091611aac91612b38565b60ff60405191168152f35b611ac036611e7e565b91604082019360ff611ad28685612678565b905011611e435750611ae2612ce5565b63ffffffff8060208301511661020595611b0687549261ffff8460081c1690612d1b565b834216938491161161150a5763ffffffff92836020860151168460045460801c16808211600014611e3c57505b611b3f60208801612cb7565b858216958691161115611e3057508063ffffffff611b5f60208901612cb7565b1611611e245750611b708686612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519073ffffffffffffffffffffffffffffffffffffffff978883169861010060ff8560981c1610156104c35789906040611bfa6101fe8760971c166005016126c9565b201603611df3579361137c936020938897937fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008b611251611ca57fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639f9d611c9063ffffffff9f8e611c8991016126e7565b9185612678565b604094919451948f9386948501973588612d33565b519020891b169360801c16911617178955858483015116908114611d95575b50506fffffffffffffffffffffffffffffffff9687611ce283612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611d36838301612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff00000000000000000000000000000000000000000000000083549260c01b16911617905561136760405195606087526060870190612d74565b6113e16fffffffffffffffffffffffffffffffff611dec9351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b8880611cc4565b602489604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b61147760208701612cb7565b6114c360208801612cb7565b9050611b33565b60449060ff61020554167fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b6003199182820160c0811261022957604013610229576004926044359067ffffffffffffffff8211610229576060908285030112610229577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261022957606490565b906003198083016060811261022957604013610229576004926044359167ffffffffffffffff8311610229578260609203011261022957820190565b503461022957611f3336611ee8565b9063ffffffff90816020611f45612f5b565b0151169260208201938084611f5987612cb7565b161115612075575082611f6b85612cb7565b814216918291161161206c575060408236031261022957611fb390610d2a604051611f958161207e565b611f9e8561216b565b8152611fa987612188565b6020820152612585565b9015612064575061202e6120286fffffffffffffffffffffffffffffffff9283611fdc82612cc8565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b1692161717600455612cc8565b93612cb7565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b61147785612cb7565b6114c385612cb7565b6040810190811067ffffffffffffffff8211176104f257604052565b6060810190811067ffffffffffffffff8211176104f257604052565b90601f601f19910116810190811067ffffffffffffffff8211176104f257604052565b6004359060ff8216820361022957565b359060ff8216820361022957565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361022957565b6020908160408183019282815285518094520193019160005b828110612141575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101612133565b35906fffffffffffffffffffffffffffffffff8216820361022957565b359063ffffffff8216820361022957565b67ffffffffffffffff81116104f25760051b60200190565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c815261220f8161209a565b51902090565b6001548110156104c35760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b6003548110156104c35760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff8091169060009082825281602052600160408320541461234a57600160408320556001546801000000000000000081101561231d576122de816001869301600155612215565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff166000818152806020526040812054156123a7578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b906123b582612199565b6123c260405191826120b6565b828152601f196123d28294612199565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124095760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80518210156104c35760209160051b010190565b6000338152806020526040812054156124f75773ffffffffffffffffffffffffffffffffffffffff809216918282526002602052600160408320541461234a57600160408320556003546801000000000000000081101561231d576124b881600186930160035561224c565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156125745773ffffffffffffffffffffffffffffffffffffffff169081815260026020526040812054156123a7578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f0000000000000000000000000000000000000000000000000000000000000000865260801b16604084015260e01b166050820152603481526126198161209a565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c815261220f8161209a565b604051906126668261209a565b60006040838281528260208201520152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610229570180359067ffffffffffffffff82116102295760200191813603831361022957565b906040516126d68161207e565b602060018294805484520154910152565b3573ffffffffffffffffffffffffffffffffffffffff811681036102295790565b90600060206040516127198161207e565b8281520152612726612659565b506127346040820182612678565b93905060ff6102055416808503612ae957506127536040830183612678565b156104c35735916101008360f81c10156104c35761277a6101fe8460f71c166005016126c9565b92835160208501511715612ad75760019060f81c1b90612798612659565b5060208451940151604051946127ad8661209a565b855260208501526001604085015260015b60ff8116928784101561295b576127d86040840184612678565b85919510156104c3576101008186013560f81c10156104c3576128076101fe8287013560f71c166005016126c9565b9485516020870151171561293e5760018282013560f81c1b83166128ed5791600160ff94928194013560f81c1b179487805160208201516020828460408101517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809881808099818099818099818a81808280099781898d510990820390089d8e936040848087800981808d8180856004099b8c9a089382039082030891800908910152099b09950151816002988d82039409900908840996828080888709810384820308818a800908809a5209900982039482039008900908602088015201166127be565b96509750505050509150604051927f1280090c000000000000000000000000000000000000000000000000000000006020850152602491013560f81c8184015282526129388261209a565b60009190565b9598509550505050506129569250013560f81c612c75565b600091565b50509294505091600060206040516129728161207e565b8281520152604051906129848261207e565b600082526020820160008152604086015195600096600190807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b612a9a57505050867ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809260208284612a199b9c0991838382510989520151920990099052612a11602084016126e7565b9235916131fc565b15612a4c57604051906020820182811067ffffffffffffffff8211176104f2578060009160405281845236903760019190565b604051907fbd2a556b000000000000000000000000000000000000000000000000000000006020830152600482526040820182811067ffffffffffffffff8211176104f25760405260009190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183949b930492818c85098203900892909182029003806129c0565b9250509250612956915060f81c612c75565b6040517fce818a2400000000000000000000000000000000000000000000000000000000602082015260ff958616602482015294166044808601919091528452915061293890506064836120b6565b6040918282209273ffffffffffffffffffffffffffffffffffffffff91828516926020612b96612b666121b1565b84518386015195870151875192835260ff9091166020830152604082019590955260608101949094526080840190565b836000948592838052039060015afa15612c6957808251168403612c655760ff8660981c1695610100871015612c385760971c6101fe16906005820190612bdc826126c9565b8051602082015117612c235750506020866006927f2c44ddc69b8b0966ef9e8edb873b850fb2eff06ca6e2bc37c9adf3551ec39e549697985190550151910155339251a390565b92509392955050201603612c35575090565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b5080fd5b509051903d90823e3d90fd5b60ff604051917fc523c75e00000000000000000000000000000000000000000000000000000000602084015216602482015260248152612cb48161209a565b90565b3563ffffffff811681036102295790565b356fffffffffffffffffffffffffffffffff811681036102295790565b60405190612cf28261207e565b610206546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff8080941691160191821161240957565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b80358252602081013573ffffffffffffffffffffffffffffffffffffffff811680910361022957602083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156102295701906020823592019167ffffffffffffffff811161022957803603831361022957601f81601f1992608095606060408701528160608701528686013760008582860101520116010190565b9061261960887fffffffff0000000000000000000000000000000000000000000000000000000092612e596020612e5287612cc8565b9601612cb7565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612e87602083016126e7565b91612e956040820182612678565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f0000000000000000000000000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b1660748501528484013781016000838201520360688101845201826120b6565b3360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff612f52612f5b565b51169081151590565b60006020604051612f6b8161207e565b8281520152604051612f7c8161207e565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612faf612ce5565b926020840191612fcd8184511661ffff6102055460081c1690612d1b565b81804216911611159283612fef575b505050600014612fea575090565b905090565b81929350511691511610388080612fdc565b906101008110156104c35760011b806005019061301d826126c9565b908151602083015117613039575b50505050613037613091565b565b60066000918260409555015573ffffffffffffffffffffffffffffffffffffffff918291201691167f9ec05f17908406877637da36ab29c002620cb544d0049837bfa095e62726455c6000604051a33880808061302b565b303b15613037576130a0612ce5565b6020810163ffffffff906130c28282511661ffff6102055460081c1690612d1b565b828042169116118015806131d8575b613178575b61312a575b5050506000610206556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a23880806130db565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b16921617176004556130d6565b50828251168360045460801c16106130d1565b610207544781811115612fea575090565b919091811580156133b4575b6133ab57602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a0908908380090361339f577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641419283861015613392577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff8611176104f257601b6000968580600160ff82613363987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa1561145f5773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff84161561320856fea264697066735822122058eb3196663533740eac1ac1dce894cf746a36c12ee4d5efc3ebfd4596beecd664736f6c63430008100033000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b4554482f55534400000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608080604052600436101561001d575b50361561001b57600080fd5b005b60003560e01c908115611ab757508060821461189b5780630760861b14611a045780630e5a6c70146115ef5780630fce3415146119ba57806310b07b7114611969578063224242ca146118a95780632f529d731461189b5780632fba4aa914611818578063313ce567146117fc578063393e5ede1461179c5780633bee58f9146117525780633ea0c15e146117375780634ca29923146116fc5780634fce7a2a146116b557806350d25bcd1461166b57806357de26a41461161a57806359e02dd7146115ef578063646edb68146115c957806365c4ce7a146115a857806365fae35e146115725780636712af9e14611068578063789d819114610fc65780638928a1f814610c145780638b0b044c14610bfb5780639954b0dc14610b465780639c52a7f114610b105780639dadc88614610a4d5780639fd001f614610a2e578063ab06ee16146109c3578063acf40b6f14610968578063b259da5c1461088f578063b9ee3fc11461086a578063bf353dbb14610823578063bfe5861f14610602578063c83c6334146105cb578063ceed3ef214610544578063d0a5882a14610521578063d63605b8146103ea578063dac42ad81461039d578063f29c29c41461037c578063fe663495146102b0578063feaf968c1461022e5763febb0f7e14610207575b3861000f565b3461022957600060031936011261022957602060ff6102055416604051908152f35b600080fd5b34610229576000600319360112610229573360005260026020526040600020541561029e5760a061025d612f5b565b63ffffffff60206fffffffffffffffffffffffffffffffff835116920151166040519160018352602083015260006040830152606082015260016080820152f35b63d957b595600052336020526024601cfd5b34610229576020600319360112610229576102c96120d9565b3360005260006020526040600020541561036a5760ff811680156102295761020591825460ff8116838103610301575b61001b613091565b6040805160ff92831681529390911660208401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009233917f95623b9931156d6d5cb43881a13f223ae416fb199e5edf776efb38766f38cbea91a21617905580808080806102f9565b634a0bfec1600052336020526024601cfd5b346102295760206003193601126102295761001b6103986120f7565b61244c565b3461022957600319604081360112610229576024359067ffffffffffffffff8211610229576060908236030112610229576103df602091600401600435612708565b506040519015158152f35b3461022957600060031936011261022957604051612020810181811067ffffffffffffffff8211176104f25760405261010090818152602061200036828401376000816040516104398161207e565b828152015260009060005b848110610460578284526040518061045c868261211a565b0390f35b8460ff821610156104c3576001906104806101fe82841b166005016126c9565b80518482015117610493575b5001610444565b9373ffffffffffffffffffffffffffffffffffffffff604084939620166104ba8288612438565b5201928661048c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3461022957600060031936011261022957602061053c6121b1565b604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576060610573612f5b565b80516fffffffffffffffffffffffffffffffff9081169182156105b8579063ffffffff60208193015116906001935b6040519415158552166020840152166040820152f35b506000915063ffffffff908290816105a2565b34610229576020600319360112610229576105e46120d9565b3360005260006020526040600020541561036a5761001b9033613001565b346102295760406003193601126102295767ffffffffffffffff600435818111610229573660238201121561022957806004013561063f81612199565b9061064d60405192836120b6565b808252602092838301906024809360061b82010190368211610229578301915b8183106107f3575050508035938411610229573660238501121561022957836004013561069981612199565b946106a760405196876120b6565b81865284860183606080940283010191368311610229578401905b8282106107b357505050503360005260008352604060002054156107a45750805190835182036102295761070e6106f883612199565b9261070660405194856120b6565b808452612199565b92601f19818401940136853760005b825181101561075e578061074861073660019386612438565b51610741838a612438565b5190612b38565b60ff6107548388612438565b911690520161071d565b508284604051928392818401908285525180915260408401929160005b82811061078a57505050500390f35b835160ff168552869550938101939281019260010161077b565b634a0bfec1600052338352601cfd5b8382360312610229578684916040516107cb8161209a565b6107d4856120e9565b81528285013583820152604085013560408201528152019101906106c2565b6040833603126102295785604091825161080c8161207e565b85358152828601358382015281520192019161066d565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6108516120f7565b1660005260006020526020604060002054604051908152f35b3461022957600060031936011261022957602060ff6102055460181c16604051908152f35b34610229576020806003193601126102295760043567ffffffffffffffff81116102295736602382011215610229578060040135906108cd82612199565b916108db60405193846120b6565b80835260248484019160051b8301019136831161022957602401905b828210610951578385336000526000815260406000205415610940575060005b815181101561001b578061093a60ff61093260019486612438565b511633613001565b01610917565b634a0bfec16000523390526024601cfd5b84809161095d846120e9565b8152019101906108f7565b34610229576040600319360112610229576040516109858161207e565b6004356fffffffffffffffffffffffffffffffff8116810361022957815260243563ffffffff81168103610229578161053c91602080940152612585565b34610229576020600319360112610229576004353360005260006020526040600020541561036a5761020780548281036109f957005b6040519081528260208201527ffccefdc521d919e1c7e09025203c5542ec6e99eb409c750d4fc386cdb7feee3760403392a255005b3461022957600060031936011261022957602061020754604051908152f35b346102295760206003193601126102295760043561ffff80821691828103610229573360005260006020526040600020541561036a57821561022957610205928354928360081c1691818303610aa55761001b613091565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff9262ffff009260405191825260208201527f06eaf85dccf95ffb2040720d23d296ebf45c6b193a5f373a553300fa640a111860403392a260081b16911617905580808080806102f9565b3461022957602060031936011261022957610b296120f7565b3360005260006020526040600020541561036a5761001b9061234f565b34610229576000600319360112610229576003610b6381546123ab565b9060009160005b8151811015610bea5780610b80610bbb9261224c565b9073ffffffffffffffffffffffffffffffffffffffff9182915490871b1c166000526002602052600160406000205414610bc0575b506123dc565b610b6a565b610bc98261224c565b905490861b1c16610be3610bdc876123dc565b9685612438565b5285610bb5565b8382526040518061045c848261211a565b3461022957602061053c610c0e36611ee8565b90612e1c565b3461022957602060031981813601126102295767ffffffffffffffff906004358281116102295760608160040192823603011261022957610c53612ce5565b84810163ffffffff928382511695610c77610205549761ffff8960081c1690612d1b565b8580421691161115610f9c57610c9c6044610c94602485016126e7565b930187612678565b9290610cb36040519485928c8401948b3586612d33565b0392610cc7601f19948581018352826120b6565b51902073ffffffffffffffffffffffffffffffffffffffff90811690888a1c16808203610f65575050610d2f86610d2a6fffffffffffffffffffffffffffffffff8097511660405190610d198261207e565b8152888b60c01c168c820152612585565b612708565b91909788600014610e365750505050829051168260045460801c1610610d95575b50507f3f2cf79ad39c64280638c6fc485670c10ed4dad0a35ecd2857255ee7a130ba2060405184815280610d88339487830190612d74565b0390a25b60405190158152f35b610e2e600092610de2610206938454166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b825460801c167fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b558380610d50565b90935060ff919550610e50929796945060181c1630613001565b610e586131eb565b906000808060405185335af1903d15610f5f573d9081116104f25760405190610e888989601f84011601836120b6565b81526000883d92013e5b610f19575b5084610eae60405192604084526040840190612d74565b948286038284015283519384875260005b858110610f06575050858391600084877f2984f6d7ff4df266745fc0283d83a02c5125069524c8eeb85887de137e8f890c98999a010152601f3397011601030190a2610d8c565b8181018401518882018501528301610ebf565b7f7e4d8c9da9421f78d986605928e77f0920f2c995d1cbba819c8915d2b209aa4b60405160408152610f4e6040820185612d74565b92888201528033930390a285610e97565b50610e92565b60449250604051917f73ceee3000000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60046040517fcff75876000000000000000000000000000000000000000000000000000000008152fd5b3461022957602060031936011261022957610fdf6120d9565b6101008110156104c357610ff89060011b6005016126c9565b805160208201511761103057506040600073ffffffffffffffffffffffffffffffffffffffff60005b83519215158352166020820152f35b73ffffffffffffffffffffffffffffffffffffffff60408092201673ffffffffffffffffffffffffffffffffffffffff600191611021565b346102295761107636611e7e565b9160ff6110866040840184612678565b9050116115345763ffffffff9161109b612ce5565b83602082015116946110b9610205549661ffff8860081c1690612d1b565b854216958691161161150a5763ffffffff60208301511663ffffffff60045460801c168082116000146115025750945b6110f560208601612cb7565b63ffffffff808816911611156114b6578063ffffffff61111760208801612cb7565b161161146b57506111288385612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519373ffffffffffffffffffffffffffffffffffffffff948581169561010060ff8360981c1610156104c357869060406111b26101fe8560971c166005016126c9565b20160361142e577fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639495967fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008761125161125f61122f602084016126e7565b61123c6040850185612678565b60409491945194859360208501973588612d33565b03601f1981018352826120b6565b51902060201b169360801c16911617176102055563ffffffff806020840151169116811461138a575b505063ffffffff61137c60206fffffffffffffffffffffffffffffffff95866112b082612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611304828201612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff000000000000000000000000000000000000000000000000610205549260c01b169116176102055561136760405195606087526060870190612d74565b966113718261216b565b168286015201612188565b1660408201528033930390a3005b6113e16fffffffffffffffffffffffffffffffff6114279351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000006004549260801b16911617600455565b8480611288565b602486604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b6040513d6000823e3d90fd5b61147760208601612cb7565b6040517feea80f5200000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b856114c360208701612cb7565b6040517f76f4b87800000000000000000000000000000000000000000000000000000000815263ffffffff918216600482015291166024820152604490fd5b9050946110e9565b60046040517f8855b9e8000000000000000000000000000000000000000000000000000000008152fd5b604460ff6102055416604051907fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b346102295760206003193601126102295761158b6120f7565b3360005260006020526040600020541561036a5761001b90612283565b346102295760206003193601126102295761001b6115c46120f7565b612508565b3461022957600060031936011261022957602061ffff6102055460081c16604051908152f35b3461022957600060031936011261022957611608612f25565b60408051928352901515602083015290f35b34610229576000600319360112610229573360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff611658612f5b565b5116801561022957602090604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e5760206fffffffffffffffffffffffffffffffff6116ab612f5b565b5116604051908152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6116e36120f7565b1660005260026020526020604060002054604051908152f35b346102295760006003193601126102295760206040517f4554482f555344000000000000000000000000000000000000000000000000008152f35b3461022957600060031936011261022957602061053c6131eb565b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6117806120f7565b1660005260026020526020600160406000205414604051908152f35b34610229576000600319360112610229573360005260026020526040600020541561029e576117c9612f5b565b6fffffffffffffffffffffffffffffffff81511680156102295763ffffffff602060409301511682519182526020820152f35b3461022957600060031936011261022957602060405160128152f35b34610229576020600319360112610229576118316120f7565b61010060ff8260981c1610156104c357806118576101fe60209360971c166005016126c9565b8051838201511715159182611873575b50506040519015158152f35b909150604073ffffffffffffffffffffffffffffffffffffffff809216922016148280611867565b506118a4611f24565b610201565b346102295760006003193601126102295760016118c681546123ab565b60009182815b6118e2575b8383526040518061045c858261211a565b825181101561196457611934816118f98493612215565b73ffffffffffffffffffffffffffffffffffffffff809254600392831b1c166000526000602052846040600020541461193a575b50506123dc565b906118cc565b61194383612215565b9054911b1c1661195c611955886123dc565b9787612438565b52868061192d565b6118d1565b34610229576000600319360112610229573360005260026020526040600020541561029e5760406fffffffffffffffffffffffffffffffff6119a9612f5b565b511681519080151582526020820152f35b346102295760206003193601126102295773ffffffffffffffffffffffffffffffffffffffff6119e86120f7565b1660005260006020526020600160406000205414604051908152f35b3461022957600319360160a081126102295760401361022957604051611a298161207e565b6004358152602435602082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126102295760405190611a6d8261209a565b60443560ff81168103610229578252606435602083015260843560408301523360005260006020526040600020541561036a57602091611aac91612b38565b60ff60405191168152f35b611ac036611e7e565b91604082019360ff611ad28685612678565b905011611e435750611ae2612ce5565b63ffffffff8060208301511661020595611b0687549261ffff8460081c1690612d1b565b834216938491161161150a5763ffffffff92836020860151168460045460801c16808211600014611e3c57505b611b3f60208801612cb7565b858216958691161115611e3057508063ffffffff611b5f60208901612cb7565b1611611e245750611b708686612e1c565b9060ff8135168135036102295760408051928352813560ff16602080850191909152808301358483015291013560608301526000808052909160809060015afa1561145f576000519073ffffffffffffffffffffffffffffffffffffffff978883169861010060ff8560981c1610156104c35789906040611bfa6101fe8760971c166005016126c9565b201603611df3579361137c936020938897937fffffffffffffffff000000000000000000000000000000000000000000ffffff63ff00000077ffffffffffffffffffffffffffffffffffffffff000000008b611251611ca57fb9dc937c5e394d0c8f76e0e324500b88251b4c909ddc56232df10e2ea42b3c639f9d611c9063ffffffff9f8e611c8991016126e7565b9185612678565b604094919451948f9386948501973588612d33565b519020891b169360801c16911617178955858483015116908114611d95575b50506fffffffffffffffffffffffffffffffff9687611ce283612cc8565b16610206908154907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b16921617179055611d36838301612cb7565b7fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff00000000000000000000000000000000000000000000000083549260c01b16911617905561136760405195606087526060870190612d74565b6113e16fffffffffffffffffffffffffffffffff611dec9351166fffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffff000000000000000000000000000000006004541617600455565b8880611cc4565b602489604051907f56d7d2e80000000000000000000000000000000000000000000000000000000082526004820152fd5b61147760208701612cb7565b6114c360208801612cb7565b9050611b33565b60449060ff61020554167fce818a2400000000000000000000000000000000000000000000000000000000825260ff60048301526024820152fd5b6003199182820160c0811261022957604013610229576004926044359067ffffffffffffffff8211610229576060908285030112610229577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c606091850193011261022957606490565b906003198083016060811261022957604013610229576004926044359167ffffffffffffffff8311610229578260609203011261022957820190565b503461022957611f3336611ee8565b9063ffffffff90816020611f45612f5b565b0151169260208201938084611f5987612cb7565b161115612075575082611f6b85612cb7565b814216918291161161206c575060408236031261022957611fb390610d2a604051611f958161207e565b611f9e8561216b565b8152611fa987612188565b6020820152612585565b9015612064575061202e6120286fffffffffffffffffffffffffffffffff9283611fdc82612cc8565b16600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff000000000000000000000000000000004260801b1692161717600455612cc8565b93612cb7565b90604051931683521660208201527f7045db5134e0c7ca4bff0c0e096616ef4ebb36b719cd00c5f0ba4f4475ecceee60403392a2005b602081519101fd5b61147785612cb7565b6114c385612cb7565b6040810190811067ffffffffffffffff8211176104f257604052565b6060810190811067ffffffffffffffff8211176104f257604052565b90601f601f19910116810190811067ffffffffffffffff8211176104f257604052565b6004359060ff8216820361022957565b359060ff8216820361022957565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361022957565b6020908160408183019282815285518094520193019160005b828110612141575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101612133565b35906fffffffffffffffffffffffffffffffff8216820361022957565b359063ffffffff8216820361022957565b67ffffffffffffffff81116104f25760051b60200190565b60405160208101907f19457468657265756d205369676e6564204d6573736167653a0a33320000000082527ff3acba882491058ea715223a1463b7d7e8610fbbb588100fb1e69a89099384a0603c820152603c815261220f8161209a565b51902090565b6001548110156104c35760016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60190600090565b6003548110156104c35760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b73ffffffffffffffffffffffffffffffffffffffff8091169060009082825281602052600160408320541461234a57600160408320556001546801000000000000000081101561231d576122de816001869301600155612215565b909283549160031b90811b9283911b169119161790557fe31c10b0adbedd0c6e5d024286c6eeead7761e65a67608dcf0b67604c0da7e2f3391604051a3565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff166000818152806020526040812054156123a7578060408120557f58466e5837b54e559819c9ba8a5d7c77c97c985d1aabf4bdc5f41069fa5d65a03391604051a3565b5050565b906123b582612199565b6123c260405191826120b6565b828152601f196123d28294612199565b0190602036910137565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124095760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80518210156104c35760209160051b010190565b6000338152806020526040812054156124f75773ffffffffffffffffffffffffffffffffffffffff809216918282526002602052600160408320541461234a57600160408320556003546801000000000000000081101561231d576124b881600186930160035561224c565b909283549160031b90811b9283911b169119161790557f75d30ca40c7bcd48e685894b82b864808b9cb566090efc53444a2e61742f18a33391604051a3565b634a0bfec19052336020526024601cfd5b600090338252816020526040822054156125745773ffffffffffffffffffffffffffffffffffffffff169081815260026020526040812054156123a7578060408120557fdadd1471db1ea2f303654fb1bdcc010e5a664214ab41934c0f752aabca88a4913391604051a3565b634a0bfec18252336020526024601cfd5b602081519101517fffffffff00000000000000000000000000000000000000000000000000000000604051917fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208401947f4554482f55534400000000000000000000000000000000000000000000000000865260801b16604084015260e01b166050820152603481526126198161209a565b51902060405160208101917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008352603c820152603c815261220f8161209a565b604051906126668261209a565b60006040838281528260208201520152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610229570180359067ffffffffffffffff82116102295760200191813603831361022957565b906040516126d68161207e565b602060018294805484520154910152565b3573ffffffffffffffffffffffffffffffffffffffff811681036102295790565b90600060206040516127198161207e565b8281520152612726612659565b506127346040820182612678565b93905060ff6102055416808503612ae957506127536040830183612678565b156104c35735916101008360f81c10156104c35761277a6101fe8460f71c166005016126c9565b92835160208501511715612ad75760019060f81c1b90612798612659565b5060208451940151604051946127ad8661209a565b855260208501526001604085015260015b60ff8116928784101561295b576127d86040840184612678565b85919510156104c3576101008186013560f81c10156104c3576128076101fe8287013560f71c166005016126c9565b9485516020870151171561293e5760018282013560f81c1b83166128ed5791600160ff94928194013560f81c1b179487805160208201516020828460408101517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809881808099818099818099818a81808280099781898d510990820390089d8e936040848087800981808d8180856004099b8c9a089382039082030891800908910152099b09950151816002988d82039409900908840996828080888709810384820308818a800908809a5209900982039482039008900908602088015201166127be565b96509750505050509150604051927f1280090c000000000000000000000000000000000000000000000000000000006020850152602491013560f81c8184015282526129388261209a565b60009190565b9598509550505050506129569250013560f81c612c75565b600091565b50509294505091600060206040516129728161207e565b8281520152604051906129848261207e565b600082526020820160008152604086015195600096600190807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f915b612a9a57505050867ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f809260208284612a199b9c0991838382510989520151920990099052612a11602084016126e7565b9235916131fc565b15612a4c57604051906020820182811067ffffffffffffffff8211176104f2578060009160405281845236903760019190565b604051907fbd2a556b000000000000000000000000000000000000000000000000000000006020830152600482526040820182811067ffffffffffffffff8211176104f25760405260009190565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183949b930492818c85098203900892909182029003806129c0565b9250509250612956915060f81c612c75565b6040517fce818a2400000000000000000000000000000000000000000000000000000000602082015260ff958616602482015294166044808601919091528452915061293890506064836120b6565b6040918282209273ffffffffffffffffffffffffffffffffffffffff91828516926020612b96612b666121b1565b84518386015195870151875192835260ff9091166020830152604082019590955260608101949094526080840190565b836000948592838052039060015afa15612c6957808251168403612c655760ff8660981c1695610100871015612c385760971c6101fe16906005820190612bdc826126c9565b8051602082015117612c235750506020866006927f2c44ddc69b8b0966ef9e8edb873b850fb2eff06ca6e2bc37c9adf3551ec39e549697985190550151910155339251a390565b92509392955050201603612c35575090565b80fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b5080fd5b509051903d90823e3d90fd5b60ff604051917fc523c75e00000000000000000000000000000000000000000000000000000000602084015216602482015260248152612cb48161209a565b90565b3563ffffffff811681036102295790565b356fffffffffffffffffffffffffffffffff811681036102295790565b60405190612cf28261207e565b610206546fffffffffffffffffffffffffffffffff8116835260801c63ffffffff166020830152565b91909163ffffffff8080941691160191821161240957565b91927fffffffffffffffffffffffffffffffffffffffff000000000000000000000000859460349693855260601b1660208401528483013701016000815290565b80358252602081013573ffffffffffffffffffffffffffffffffffffffff811680910361022957602083015260408101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1823603018112156102295701906020823592019167ffffffffffffffff811161022957803603831361022957601f81601f1992608095606060408701528160608701528686013760008582860101520116010190565b9061261960887fffffffff0000000000000000000000000000000000000000000000000000000092612e596020612e5287612cc8565b9601612cb7565b907fffffffffffffffffffffffffffffffffffffffff000000000000000000000000612e87602083016126e7565b91612e956040820182612678565b9384939192604051998a977fffffffffffffffffffffffffffffffff0000000000000000000000000000000060208a019d8e7f4554482f55534400000000000000000000000000000000000000000000000000905260801b1660408a015260e01b16605088015235605487015260601b1660748501528484013781016000838201520360688101845201826120b6565b3360005260026020526040600020541561029e576fffffffffffffffffffffffffffffffff612f52612f5b565b51169081151590565b60006020604051612f6b8161207e565b8281520152604051612f7c8161207e565b600454906fffffffffffffffffffffffffffffffff82168152602081019163ffffffff809160801c168352612faf612ce5565b926020840191612fcd8184511661ffff6102055460081c1690612d1b565b81804216911611159283612fef575b505050600014612fea575090565b905090565b81929350511691511610388080612fdc565b906101008110156104c35760011b806005019061301d826126c9565b908151602083015117613039575b50505050613037613091565b565b60066000918260409555015573ffffffffffffffffffffffffffffffffffffffff918291201691167f9ec05f17908406877637da36ab29c002620cb544d0049837bfa095e62726455c6000604051a33880808061302b565b303b15613037576130a0612ce5565b6020810163ffffffff906130c28282511661ffff6102055460081c1690612d1b565b828042169116118015806131d8575b613178575b61312a575b5050506000610206556004547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff73ffffffff000000000000000000000000000000004260801b16911617600455565b6fffffffffffffffffffffffffffffffff6040519351168352511660208201527f20246a67cf0cfc89415e0ea0b3293fcc5138acc3c3b2111012dd06eb742b899460403392a23880806130db565b6fffffffffffffffffffffffffffffffff845116600454907fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffff00000000000000000000000000000000855160801b16921617176004556130d6565b50828251168360045460801c16106130d1565b610207544781811115612fea575090565b919091811580156133b4575b6133ab57602081019283518251947ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80600781808a80098a0908908380090361339f577ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641419283861015613392577f0100000000000000000000000000000000000000000000000000000000000000604051936020850198895260f81b16604084015260418301527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008760601b16606183015260558252608082019382851067ffffffffffffffff8611176104f257601b6000968580600160ff82613363987fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809b60209f8e6040528c519020069851978894859251160198098303961693099003875260ff166020870152604086015260608501526080840190565b84805203019060015afa1561145f5773ffffffffffffffffffffffffffffffffffffffff806000511691161490565b5050505050505050600090565b50505050505050600090565b50505050600090565b5073ffffffffffffffffffffffffffffffffffffffff84161561320856fea264697066735822122058eb3196663533740eac1ac1dce894cf746a36c12ee4d5efc3ebfd4596beecd664736f6c63430008100033

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

000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b4554482f55534400000000000000000000000000000000000000000000000000

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

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000c50dfedb7e93ef7a3daccad7987d0960c4e2cd4b
Arg [1] : 4554482f55534400000000000000000000000000000000000000000000000000


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.