ETH Price: $2,501.10 (+3.81%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Initialize186894992023-12-01 5:32:23340 days ago1701408743IN
0x4d229DD7...1A540c079
0 ETH0.0146827529.57793829

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
186836732023-11-30 9:56:59341 days ago1701338219  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x84785287...73B32d9CB
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
ERC4626SharePriceOracle

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 10 : ERC4626SharePriceOracle.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

import { ERC4626 } from "@solmate/mixins/ERC4626.sol";
import { SafeTransferLib } from "@solmate/utils/SafeTransferLib.sol";
import { ERC20 } from "@solmate/tokens/ERC20.sol";
import { Math } from "src/utils/Math.sol";
import { Owned } from "@solmate/auth/Owned.sol";
import { AutomationCompatibleInterface } from "@chainlink/contracts/src/v0.8/interfaces/AutomationCompatibleInterface.sol";
import { IRegistrar } from "src/interfaces/external/Chainlink/IRegistrar.sol";
import { IRegistry } from "src/interfaces/external/Chainlink/IRegistry.sol";

contract ERC4626SharePriceOracle is AutomationCompatibleInterface {
    using Math for uint256;
    using SafeTransferLib for ERC20;

    // ========================================= STRUCTS =========================================

    struct Observation {
        uint64 timestamp;
        uint192 cumulative;
    }

    // ========================================= CONSTANTS =========================================
    /**
     * @notice Gas Limit to use for Upkeep created in `initialize`.
     * @dev Should be fairly constant between networks, but 50_000 is a safe limit in
     *      most situations.
     */
    uint32 public constant UPKEEP_GAS_LIMIT = 50_000;

    /**
     * @notice Decimals used to scale share price for internal calculations.
     */
    uint8 public constant decimals = 18;

    // ========================================= GLOBAL STATE =========================================
    /**
     * @notice The latest stored onchain answer.
     */
    uint216 public answer;

    /**
     * @notice Stores the index of observations with the pending Observation.
     */
    uint16 public currentIndex;

    /**
     * @notice The length of the observations array.
     * @dev `observations` will never change its length once set in the constructor.
     *      By saving this value here, we can take advantage of variable packing to make reads cheaper.
     * @dev This is not immutable to make it easier in the future to create oracles that can expand their observations.
     */
    uint16 public observationsLength;

    /**
     * @notice Triggered when answer provided by Chainlink Automation is extreme.
     * @dev true: No further upkeeps are allowed, `getLatest` and `getLatestAnswer` will return true error bools.
     *      false: Continue as normal.
     */
    bool public killSwitch;

    /**
     * @notice Stores the observations this contract uses to derive a
     *         time weighted average answer.
     */
    Observation[] public observations;

    /**
     * @notice The Automation V2 Forwarder address for this contract.
     */
    address public automationForwarder;

    /**
     * @notice keccak256 hash of the parameters used to create this upkeep.
     * @dev Only set if `initialize` leads to a pending upkeep.
     */
    bytes32 public pendingUpkeepParamHash;

    //============================== ERRORS ===============================

    error ERC4626SharePriceOracle__OnlyCallableByAutomationForwarder();
    error ERC4626SharePriceOracle__StalePerformData();
    error ERC4626SharePriceOracle__CumulativeTooLarge();
    error ERC4626SharePriceOracle__NoUpkeepConditionMet();
    error ERC4626SharePriceOracle__SharePriceTooLarge();
    error ERC4626SharePriceOracle__FuturePerformData();
    error ERC4626SharePriceOracle__ContractKillSwitch();
    error ERC4626SharePriceOracle__AlreadyInitialized();
    error ERC4626SharePriceOracle__ParamHashDiffers();
    error ERC4626SharePriceOracle__NoPendingUpkeepToHandle();

    //============================== EVENTS ===============================

    /**
     * @notice Emitted when performUpkeep is ran.
     * @param timeUpdated the time the answer was updated on chain
     * @param timeAnswerCalculated the time the answer was calculated in checkUpkeep
     * @param latestAnswer the new answer
     * @param timeWeightedAverageAnswer the new time weighted average answer
     * @param isNotSafeToUse bool
     *                       if true: `timeWeightedAverageAnswer` is illogical, use `latestAnswer`
     *                       if false: use `timeWeightedAverageAnswer`
     */
    event OracleUpdated(
        uint256 timeUpdated,
        uint256 timeAnswerCalculated,
        uint256 latestAnswer,
        uint256 timeWeightedAverageAnswer,
        bool isNotSafeToUse
    );

    /**
     * @notice Emitted when the oracles kill switch is activated.
     * @dev If this happens, then the proposed performData lead to extremely volatile share price,
     *      so we need to investigate why that happened, mitigate it, then launch a new share price oracle.
     */
    event KillSwitchActivated(uint256 reportedAnswer, uint256 minAnswer, uint256 maxAnswer);

    event UpkeepRegistered(uint256 upkeepId, address forwarder);
    event UpkeepPending(bytes32 upkeepParamHash);

    //============================== IMMUTABLES ===============================

    /**
     * @notice Determines the minimum time for each observation, and is used to determine if an
     *         answer is stale.
     */
    uint64 public immutable heartbeat;

    /**
     * @notice Used to enforce that the summation of each observations delay used in
     *         a time weighed average calculation is less than the gracePeriod.
     * @dev Example: Using a 3 day TWAA with 1 hour grace period.
     *      When calculating the TWAA, the total time delta for completed observations must be greater than 3 days but less than
     *      3 days + 1hr. So one observation could be delayed 1 hr, or two observations could be
     *      delayed 30 min each.
     */
    uint64 public immutable gracePeriod;

    /**
     * @notice Number between 0 -> 10_000 that determines how far off the last saved answer
     *         can deviate from the current answer.
     * @dev This value should be reflective of the vaults expected maximum percent share
     *      price change during a heartbeat duration.
     * @dev
     *    -1_000 == 10%
     *    -100 == 1%
     *    -10 == 0.1%
     *    -1 == 0.01% or 1 bps
     */
    uint64 public immutable deviationTrigger;

    /**
     * @notice One share of target vault.
     */
    uint256 public immutable ONE_SHARE;

    /**
     * @notice The admin address for the Automation Upkeep.
     */
    address public immutable automationAdmin;

    /**
     * @notice Chainlink's Automation Registry contract address.
     * @notice For mainnet use 0x6593c7De001fC8542bB1703532EE1E5aA0D458fD.
     */
    address public immutable automationRegistry;

    /**
     * @notice Chainlink's Automation Registrar contract address.
     * @notice For mainnet use 0x6B0B234fB2f380309D47A7E9391E29E9a179395a.
     */
    address public immutable automationRegistrar;

    /**
     * @notice Link Token.
     * @notice For mainnet use 0x514910771AF9Ca656af840dff83E8264EcF986CA.
     */
    ERC20 public immutable link;

    /**
     * @notice ERC4626 target vault this contract is an oracle for.
     */
    ERC4626 public immutable target;

    /**
     * @notice Target vault decimals.
     */
    uint8 public immutable targetDecimals;

    /**
     * @notice Multiplier with 4 decimals that determines the acceptable lower band
     *         for a performUpkeep answer.
     */
    uint256 public immutable allowedAnswerChangeLower;

    /**
     * @notice Multiplier with 4 decimals that determines the acceptable upper band
     *         for a performUpkeep answer.
     */
    uint256 public immutable allowedAnswerChangeUpper;

    /**
     * @notice TWAA Minimum Duration = `_observationsToUse` * `_heartbeat`.
     * @notice TWAA Maximum Duration = `_observationsToUse` * `_heartbeat` + `gracePeriod` + `_heartbeat`.
     * @notice TWAA calculations will use the current pending observation, and then `_observationsToUse` observations.
     */
    constructor(
        ERC4626 _target,
        uint64 _heartbeat,
        uint64 _deviationTrigger,
        uint64 _gracePeriod,
        uint16 _observationsToUse,
        address _automationRegistry,
        address _automationRegistrar,
        address _automationAdmin,
        address _link,
        uint216 _startingAnswer,
        uint256 _allowedAnswerChangeLower,
        uint256 _allowedAnswerChangeUpper
    ) {
        target = _target;
        targetDecimals = target.decimals();
        ONE_SHARE = 10 ** targetDecimals;
        heartbeat = _heartbeat;
        deviationTrigger = _deviationTrigger;
        gracePeriod = _gracePeriod;
        // Add 1 to observations to use.
        _observationsToUse = _observationsToUse + 1;
        observationsLength = _observationsToUse;

        // Grow Observations array to required length, and fill it with observations that use 1 for timestamp and cumulative.
        // That way the initial upkeeps won't need to change state from 0 which is more expensive.
        for (uint256 i; i < _observationsToUse; ++i) observations.push(Observation({ timestamp: 1, cumulative: 1 }));

        // Set to _startingAnswer so slot is dirty for first upkeep, and does not trigger kill switch.
        answer = _startingAnswer;

        if (_allowedAnswerChangeLower > 1e4) revert("Illogical Lower");
        allowedAnswerChangeLower = _allowedAnswerChangeLower;
        if (_allowedAnswerChangeUpper < 1e4) revert("Illogical Upper");
        allowedAnswerChangeUpper = _allowedAnswerChangeUpper;

        automationRegistry = _automationRegistry;
        automationRegistrar = _automationRegistrar;
        automationAdmin = _automationAdmin;
        link = ERC20(_link);
    }

    //============================== INITIALIZATION ===============================

    /**
     * @notice Should be called after contract creation.
     * @dev Creates a Chainlink Automation Upkeep, and set the `automationForwarder` address.
     */
    function initialize(uint96 initialUpkeepFunds) external {
        // This function is only callable once.
        if (automationForwarder != address(0) || pendingUpkeepParamHash != bytes32(0))
            revert ERC4626SharePriceOracle__AlreadyInitialized();

        link.safeTransferFrom(msg.sender, address(this), initialUpkeepFunds);

        // Create the upkeep.
        IRegistrar registrar = IRegistrar(automationRegistrar);
        IRegistry registry = IRegistry(automationRegistry);
        IRegistrar.RegistrationParams memory params = IRegistrar.RegistrationParams({
            name: string.concat(target.name(), " Share Price Oracle"),
            encryptedEmail: hex"",
            upkeepContract: address(this),
            gasLimit: UPKEEP_GAS_LIMIT,
            adminAddress: automationAdmin,
            triggerType: 0,
            checkData: hex"",
            triggerConfig: hex"",
            offchainConfig: hex"",
            amount: initialUpkeepFunds
        });

        link.safeApprove(automationRegistrar, initialUpkeepFunds);
        uint256 upkeepID = registrar.registerUpkeep(params);
        if (upkeepID > 0) {
            // Upkeep was successfully registered.
            address forwarder = registry.getForwarder(upkeepID);
            automationForwarder = forwarder;
            emit UpkeepRegistered(upkeepID, forwarder);
        } else {
            // Upkeep is pending.
            bytes32 paramHash = keccak256(
                abi.encode(
                    params.upkeepContract,
                    params.gasLimit,
                    params.adminAddress,
                    params.triggerType,
                    params.checkData,
                    params.offchainConfig
                )
            );
            pendingUpkeepParamHash = paramHash;
            emit UpkeepPending(paramHash);
        }
    }

    /**
     * @notice Finish setting forwarder address if `initialize` did not get an auto-approved upkeep.
     */
    function handlePendingUpkeep(uint256 _upkeepId) external {
        if (pendingUpkeepParamHash == bytes32(0) || automationForwarder != address(0))
            revert ERC4626SharePriceOracle__NoPendingUpkeepToHandle();

        IRegistry registry = IRegistry(automationRegistry);

        IRegistry.UpkeepInfo memory upkeepInfo = registry.getUpkeep(_upkeepId);
        // Build the param hash using upkeepInfo.
        // The upkeep id has 16 bytes of entropy, that need to be shifted out(16*8=128).
        // Then take the resulting number and only take the last byte of it to get the trigger type.
        uint8 triggerType = uint8(_upkeepId >> 128);
        bytes32 proposedParamHash = keccak256(
            abi.encode(
                upkeepInfo.target,
                upkeepInfo.executeGas,
                upkeepInfo.admin,
                triggerType,
                upkeepInfo.checkData,
                upkeepInfo.offchainConfig
            )
        );
        if (pendingUpkeepParamHash != proposedParamHash) revert ERC4626SharePriceOracle__ParamHashDiffers();

        // Hashes match, so finish initialization.
        address forwarder = registry.getForwarder(_upkeepId);
        automationForwarder = forwarder;
        emit UpkeepRegistered(_upkeepId, forwarder);
    }

    //============================== CHAINLINK AUTOMATION ===============================

    /**
     * @notice Leverages Automation V2 secure offchain computation to run expensive share price calculations offchain,
     *         then inject them onchain using `performUpkeep`.
     */
    function checkUpkeep(bytes calldata) external view returns (bool upkeepNeeded, bytes memory performData) {
        // Get target share price.
        uint216 sharePrice = _getTargetSharePrice();
        // Read state from one slot.
        uint256 _answer = answer;
        uint16 _currentIndex = currentIndex;
        uint16 _observationsLength = observationsLength;
        bool _killSwitch = killSwitch;

        if (!_killSwitch) {
            // See if we need to update because answer is stale or outside deviation.
            // Time since answer was last updated.
            uint256 timeDeltaCurrentAnswer = block.timestamp - observations[_currentIndex].timestamp;
            uint256 timeDeltaSincePreviousObservation = block.timestamp -
                observations[_getPreviousIndex(_currentIndex, _observationsLength)].timestamp;
            uint64 _heartbeat = heartbeat;

            if (
                timeDeltaCurrentAnswer >= _heartbeat ||
                timeDeltaSincePreviousObservation >= _heartbeat ||
                sharePrice > _answer.mulDivDown(1e4 + deviationTrigger, 1e4) ||
                sharePrice < _answer.mulDivDown(1e4 - deviationTrigger, 1e4)
            ) {
                // We need to update answer.
                upkeepNeeded = true;
                performData = abi.encode(sharePrice, uint64(block.timestamp));
            }
        } // else no upkeep is needed
    }

    /**
     * @notice Save answer on chain, and update observations if needed.
     */
    function performUpkeep(bytes calldata performData) external {
        if (msg.sender != automationForwarder) revert ERC4626SharePriceOracle__OnlyCallableByAutomationForwarder();
        (uint216 sharePrice, uint64 currentTime) = abi.decode(performData, (uint216, uint64));

        // Verify atleast one of the upkeep conditions was met.
        bool upkeepConditionMet;

        // Read state from one slot.
        uint256 _answer = answer;
        uint16 _currentIndex = currentIndex;
        uint16 _observationsLength = observationsLength;
        bool _killSwitch = killSwitch;

        if (_killSwitch) revert ERC4626SharePriceOracle__ContractKillSwitch();

        // See if kill switch should be activated based on change between answers.
        if (_checkIfKillSwitchShouldBeTriggered(sharePrice, _answer)) return;

        // See if we are upkeeping because of deviation.
        if (
            sharePrice > uint256(_answer).mulDivDown(1e4 + deviationTrigger, 1e4) ||
            sharePrice < uint256(_answer).mulDivDown(1e4 - deviationTrigger, 1e4)
        ) upkeepConditionMet = true;

        // Update answer.
        answer = sharePrice;

        // Update current observation.
        Observation storage currentObservation = observations[_currentIndex];
        // Make sure time is larger than previous time.
        if (currentTime <= currentObservation.timestamp) revert ERC4626SharePriceOracle__StalePerformData();

        // Make sure time is not in the future.
        if (currentTime > block.timestamp) revert ERC4626SharePriceOracle__FuturePerformData();

        // See if we are updating because of staleness.
        uint256 timeDelta = currentTime - currentObservation.timestamp;
        if (timeDelta >= heartbeat) upkeepConditionMet = true;

        // Use the old answer to calculate cumulative.
        uint256 currentCumulative = currentObservation.cumulative + (_answer * timeDelta);
        if (currentCumulative > type(uint192).max) revert ERC4626SharePriceOracle__CumulativeTooLarge();
        currentObservation.cumulative = uint192(currentCumulative);
        currentObservation.timestamp = currentTime;

        uint256 timeDeltaSincePreviousObservation = currentTime -
            observations[_getPreviousIndex(_currentIndex, _observationsLength)].timestamp;
        // See if we need to advance to the next cumulative.
        if (timeDeltaSincePreviousObservation >= heartbeat) {
            uint16 nextIndex = _getNextIndex(_currentIndex, _observationsLength);
            currentIndex = nextIndex;
            // Update memory variable for event.
            _currentIndex = nextIndex;
            // Update newest cumulative.
            Observation storage newObservation = observations[nextIndex];
            newObservation.cumulative = uint192(currentCumulative);
            newObservation.timestamp = currentTime;
            upkeepConditionMet = true;
        }

        if (!upkeepConditionMet) revert ERC4626SharePriceOracle__NoUpkeepConditionMet();

        (uint256 timeWeightedAverageAnswer, bool isNotSafeToUse) = _getTimeWeightedAverageAnswer(
            sharePrice,
            _currentIndex,
            _observationsLength
        );

        // See if kill switch should be activated based on change between proposed answer and time weighted average answer.
        if (!isNotSafeToUse && _checkIfKillSwitchShouldBeTriggered(sharePrice, timeWeightedAverageAnswer)) return;
        emit OracleUpdated(block.timestamp, currentTime, sharePrice, timeWeightedAverageAnswer, isNotSafeToUse);
    }

    //============================== ORACLE VIEW FUNCTIONS ===============================

    /**
     * @notice Get the latest answer, time weighted average answer, and bool indicating whether they can be safely used.
     */
    function getLatest() external view returns (uint256 ans, uint256 timeWeightedAverageAnswer, bool notSafeToUse) {
        // Read state from one slot.
        ans = answer;
        uint16 _currentIndex = currentIndex;
        uint16 _observationsLength = observationsLength;
        bool _killSwitch = killSwitch;

        if (_killSwitch) return (0, 0, true);

        // Check if answer is stale, if so set notSafeToUse to true, and return.
        uint256 timeDeltaSinceLastUpdated = block.timestamp - observations[currentIndex].timestamp;
        // Note add in the grace period here, because it can take time for the upkeep TX to go through.
        if (timeDeltaSinceLastUpdated > (heartbeat + gracePeriod)) return (0, 0, true);

        (timeWeightedAverageAnswer, notSafeToUse) = _getTimeWeightedAverageAnswer(
            ans,
            _currentIndex,
            _observationsLength
        );
        if (notSafeToUse) return (0, 0, true);
    }

    /**
     * @notice Get the latest answer, and bool indicating whether answer is safe to use or not.
     */
    function getLatestAnswer() external view returns (uint256, bool) {
        uint256 _answer = answer;
        bool _killSwitch = killSwitch;

        if (_killSwitch) return (0, true);

        // Check if answer is stale, if so set notSafeToUse to true, and return.
        uint256 timeDeltaSinceLastUpdated = block.timestamp - observations[currentIndex].timestamp;
        // Note add in the grace period here, because it can take time for the upkeep TX to go through.
        if (timeDeltaSinceLastUpdated > (heartbeat + gracePeriod)) return (0, true);

        return (_answer, false);
    }

    //============================== INTERNAL HELPER FUNCTIONS ===============================

    /**
     * @notice Get the next index of observations array.
     */
    function _getNextIndex(uint16 _currentIndex, uint16 _length) internal pure returns (uint16 nextIndex) {
        nextIndex = (_currentIndex == _length - 1) ? 0 : _currentIndex + 1;
    }

    /**
     * @notice Get the previous index of observations array.
     */
    function _getPreviousIndex(uint16 _currentIndex, uint16 _length) internal pure returns (uint16 previousIndex) {
        previousIndex = (_currentIndex == 0) ? _length - 1 : _currentIndex - 1;
    }

    /**
     * @notice Use observations to get the time weighted average answer.
     */
    function _getTimeWeightedAverageAnswer(
        uint256 _answer,
        uint16 _currentIndex,
        uint16 _observationsLength
    ) internal view returns (uint256 timeWeightedAverageAnswer, bool notSafeToUse) {
        // Read observations from storage.
        Observation memory mostRecentlyCompletedObservation = observations[
            _getPreviousIndex(_currentIndex, _observationsLength)
        ];
        Observation memory oldestObservation = observations[_getNextIndex(_currentIndex, _observationsLength)];

        // Data is not set.
        if (oldestObservation.timestamp == 1) return (0, true);

        // Make sure that the old observations we are using are not too stale.
        uint256 timeDelta = mostRecentlyCompletedObservation.timestamp - oldestObservation.timestamp;
        /// @dev use _length - 2 because
        /// remove 1 because observations array stores the current pending observation.
        /// remove 1 because we are really interested in the time between observations.
        uint256 minDuration = heartbeat * (_observationsLength - 2);
        uint256 maxDuration = minDuration + gracePeriod;
        // Data is too new
        if (timeDelta < minDuration) return (0, true);
        // Data is too old
        if (timeDelta > maxDuration) return (0, true);

        Observation memory latestObservation = observations[_currentIndex];
        uint192 latestCumulative = latestObservation.cumulative +
            uint192((_answer * (block.timestamp - latestObservation.timestamp)));

        timeWeightedAverageAnswer =
            (latestCumulative - oldestObservation.cumulative) /
            (block.timestamp - oldestObservation.timestamp);
    }

    /**
     * @notice Get the target ERC4626's share price using totalAssets, and totalSupply.
     */
    function _getTargetSharePrice() internal view returns (uint216 sharePrice) {
        uint256 totalShares = target.totalSupply();
        // Get total Assets but scale it up to decimals decimals of precision.
        uint256 totalAssets = target.totalAssets().changeDecimals(targetDecimals, decimals);

        if (totalShares == 0) return 0;

        uint256 _sharePrice = ONE_SHARE.mulDivDown(totalAssets, totalShares);

        if (_sharePrice > type(uint216).max) revert ERC4626SharePriceOracle__SharePriceTooLarge();
        sharePrice = uint216(_sharePrice);
    }

    /**
     * @notice Activate the kill switch if `proposedAnswer` is extreme when compared to `answerToCompareAgainst`
     * @return bool indicating whether calling function should immediately exit or not.
     */
    function _checkIfKillSwitchShouldBeTriggered(
        uint256 proposedAnswer,
        uint256 answerToCompareAgainst
    ) internal returns (bool) {
        if (
            proposedAnswer < answerToCompareAgainst.mulDivDown(allowedAnswerChangeLower, 1e4) ||
            proposedAnswer > answerToCompareAgainst.mulDivDown(allowedAnswerChangeUpper, 1e4)
        ) {
            killSwitch = true;
            emit KillSwitchActivated(
                proposedAnswer,
                answerToCompareAgainst.mulDivDown(allowedAnswerChangeLower, 1e4),
                answerToCompareAgainst.mulDivDown(allowedAnswerChangeUpper, 1e4)
            );
            return true;
        }
        return false;
    }
}

File 2 of 10 : ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function totalAssets() public view virtual returns (uint256);

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}

File 3 of 10 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 4 of 10 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 5 of 10 : Math.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

library Math {
    /**
     * @notice Substract with a floor of 0 for the result.
     */
    function subMinZero(uint256 x, uint256 y) internal pure returns (uint256) {
        return x > y ? x - y : 0;
    }

    /**
     * @notice Used to change the decimals of precision used for an amount.
     */
    function changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
        if (fromDecimals == toDecimals) {
            return amount;
        } else if (fromDecimals < toDecimals) {
            return amount * 10 ** (toDecimals - fromDecimals);
        } else {
            return amount / 10 ** (fromDecimals - toDecimals);
        }
    }

    // ===================================== OPENZEPPELIN'S MATH =====================================

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    // ================================= SOLMATE's FIXEDPOINTMATHLIB =================================

    uint256 public constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }

    function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // First, divide z - 1 by the denominator and add 1.
            // We allow z - 1 to underflow if z is 0, because we multiply the
            // end result by 0 if z is zero, ensuring we return 0 if z is zero.
            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
        }
    }
}

File 6 of 10 : Owned.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnershipTransferred(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

File 7 of 10 : AutomationCompatibleInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AutomationCompatibleInterface {
  /**
   * @notice method that is simulated by the keepers to see if any work actually
   * needs to be performed. This method does does not actually need to be
   * executable, and since it is only ever simulated it can consume lots of gas.
   * @dev To ensure that it is never called, you may want to add the
   * cannotExecute modifier from KeeperBase to your implementation of this
   * method.
   * @param checkData specified in the upkeep registration so it is always the
   * same for a registered upkeep. This can easily be broken down into specific
   * arguments using `abi.decode`, so multiple upkeeps can be registered on the
   * same contract and easily differentiated by the contract.
   * @return upkeepNeeded boolean to indicate whether the keeper should call
   * performUpkeep or not.
   * @return performData bytes that the keeper should call performUpkeep with, if
   * upkeep is needed. If you would like to encode data to decode later, try
   * `abi.encode`.
   */
  function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData);

  /**
   * @notice method that is actually executed by the keepers, via the registry.
   * The data returned by the checkUpkeep simulation will be passed into
   * this method to actually be executed.
   * @dev The input to this method should not be trusted, and the caller of the
   * method should not even be restricted to any single registry. Anyone should
   * be able call it, and the input should be validated, there is no guarantee
   * that the data passed in is the performData returned from checkUpkeep. This
   * could happen due to malicious keepers, racing keepers, or simply a state
   * change while the performUpkeep transaction is waiting for confirmation.
   * Always validate the data passed in.
   * @param performData is the data which was passed back from the checkData
   * simulation. If it is encoded, it can easily be decoded into other types by
   * calling `abi.decode`. This data should not be trusted, and should be
   * validated against the contract's current state.
   */
  function performUpkeep(bytes calldata performData) external;
}

File 8 of 10 : IRegistrar.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

interface IRegistrar {
    struct RegistrationParams {
        string name;
        bytes encryptedEmail;
        address upkeepContract;
        uint32 gasLimit;
        address adminAddress;
        uint8 triggerType;
        bytes checkData;
        bytes triggerConfig;
        bytes offchainConfig;
        uint96 amount;
    }

    enum AutoApproveType {
        DISABLED,
        ENABLED_SENDER_ALLOWLIST,
        ENABLED_ALL
    }

    function registerUpkeep(RegistrationParams calldata requestParams) external returns (uint256 id);

    function setTriggerConfig(
        uint8 triggerType,
        AutoApproveType autoApproveType,
        uint32 autoApproveMaxAllowed
    ) external;

    function owner() external view returns (address);

    function approve(
        string memory name,
        address upkeepContract,
        uint32 gasLimit,
        address adminAddress,
        uint8 triggerType,
        bytes calldata checkData,
        bytes memory triggerConfig,
        bytes calldata offchainConfig,
        bytes32 hash
    ) external;
}

File 9 of 10 : IRegistry.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

interface IRegistry {
    struct UpkeepInfo {
        address target;
        uint32 executeGas;
        bytes checkData;
        uint96 balance;
        address admin;
        uint64 maxValidBlocknumber;
        uint32 lastPerformBlockNumber;
        uint96 amountSpent;
        bool paused;
        bytes offchainConfig;
    }

    function getForwarder(uint256 upkeepID) external view returns (address forwarder);

    function getUpkeep(uint256 id) external view returns (UpkeepInfo memory upkeepInfo);
}

File 10 of 10 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

Settings
{
  "remappings": [
    "@balancer-labs/=lib/balancer-v2-monorepo/../../node_modules/@balancer-labs/",
    "@balancer/=lib/balancer-v2-monorepo/pkg/",
    "@chainlink/=lib/chainlink/",
    "@ds-test/=lib/forge-std/lib/ds-test/src/",
    "@forge-std/=lib/forge-std/src/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@solmate/=lib/solmate/src/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@uniswapV3C/=lib/v3-core/contracts/",
    "@uniswapV3P/=lib/v3-periphery/contracts/",
    "axelar-gmp-sdk-solidity/=lib/axelar-gmp-sdk-solidity/contracts/",
    "balancer-v2-monorepo/=lib/balancer-v2-monorepo/",
    "ccip/=lib/ccip/",
    "chainlink/=lib/chainlink/integration-tests/contracts/ethereum/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/ccip/contracts/foundry-lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "pendle-core-v2-public/=lib/pendle-core-v2-public/contracts/",
    "solmate/=lib/solmate/src/",
    "v3-core/=lib/v3-core/contracts/",
    "v3-periphery/=lib/v3-periphery/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract ERC4626","name":"_target","type":"address"},{"internalType":"uint64","name":"_heartbeat","type":"uint64"},{"internalType":"uint64","name":"_deviationTrigger","type":"uint64"},{"internalType":"uint64","name":"_gracePeriod","type":"uint64"},{"internalType":"uint16","name":"_observationsToUse","type":"uint16"},{"internalType":"address","name":"_automationRegistry","type":"address"},{"internalType":"address","name":"_automationRegistrar","type":"address"},{"internalType":"address","name":"_automationAdmin","type":"address"},{"internalType":"address","name":"_link","type":"address"},{"internalType":"uint216","name":"_startingAnswer","type":"uint216"},{"internalType":"uint256","name":"_allowedAnswerChangeLower","type":"uint256"},{"internalType":"uint256","name":"_allowedAnswerChangeUpper","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ERC4626SharePriceOracle__AlreadyInitialized","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__ContractKillSwitch","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__CumulativeTooLarge","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__FuturePerformData","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__NoPendingUpkeepToHandle","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__NoUpkeepConditionMet","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__OnlyCallableByAutomationForwarder","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__ParamHashDiffers","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__SharePriceTooLarge","type":"error"},{"inputs":[],"name":"ERC4626SharePriceOracle__StalePerformData","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reportedAnswer","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minAnswer","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxAnswer","type":"uint256"}],"name":"KillSwitchActivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timeUpdated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeAnswerCalculated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"latestAnswer","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeWeightedAverageAnswer","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isNotSafeToUse","type":"bool"}],"name":"OracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"upkeepParamHash","type":"bytes32"}],"name":"UpkeepPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"upkeepId","type":"uint256"},{"indexed":false,"internalType":"address","name":"forwarder","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"inputs":[],"name":"ONE_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPKEEP_GAS_LIMIT","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowedAnswerChangeLower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowedAnswerChangeUpper","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"answer","outputs":[{"internalType":"uint216","name":"","type":"uint216"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"automationAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"automationForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"automationRegistrar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"automationRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentIndex","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deviationTrigger","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatest","outputs":[{"internalType":"uint256","name":"ans","type":"uint256"},{"internalType":"uint256","name":"timeWeightedAverageAnswer","type":"uint256"},{"internalType":"bool","name":"notSafeToUse","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestAnswer","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gracePeriod","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_upkeepId","type":"uint256"}],"name":"handlePendingUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"heartbeat","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"initialUpkeepFunds","type":"uint96"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"killSwitch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"link","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"observations","outputs":[{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint192","name":"cumulative","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"observationsLength","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingUpkeepParamHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"performUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"target","outputs":[{"internalType":"contract ERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"targetDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101a95760003560e01c80636e04ff0d116100f9578063a69e972c11610097578063c36af46011610071578063c36af460146104f5578063cd7f5ce21461051a578063d4b839921461052f578063feabaa021461055657600080fd5b8063a69e972c14610483578063ada14698146104aa578063b7d122b5146104ce57600080fd5b806385bb7d69116100d357806385bb7d6914610401578063909f1cad1461042c57806396237c021461043f578063a06db7dc1461045c57600080fd5b80636e04ff0d146103925780637167adbc146103b35780637d4cdb4f146103da57600080fd5b8063313ce567116101665780633defb962116101405780633defb962146102fb5780634585e33b1461033a5780635dc228a01461034d578063643917f51461037457600080fd5b8063313ce567146102a757806336c1387e146102c15780633b2786cb146102e857600080fd5b8063025c67cb146101ae5780630c381873146101ca57806318da7882146102095780631c4695f41461021e578063252c09d71461024557806326987b601461027f575b600080fd5b6101b760035481565b6040519081526020015b60405180910390f35b6101f17f0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a81565b6040516001600160a01b0390911681526020016101c1565b61021c610217366004611ba1565b61057d565b005b6101f17f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca81565b610258610253366004611ba1565b61078d565b604080516001600160401b0390931683526001600160c01b039091166020830152016101c1565b60005461029490600160d81b900461ffff1681565b60405161ffff90911681526020016101c1565b6102af601281565b60405160ff90911681526020016101c1565b6102af7f000000000000000000000000000000000000000000000000000000000000001281565b6002546101f1906001600160a01b031681565b6103227f000000000000000000000000000000000000000000000000000000000001518081565b6040516001600160401b0390911681526020016101c1565b61021c610348366004611bba565b6107c8565b6101f17f0000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd81565b61037d61c35081565b60405163ffffffff90911681526020016101c1565b6103a56103a0366004611bba565b610c59565b6040516101c1929190611c7b565b6101b77f0000000000000000000000000000000000000000000000000000000000001f4081565b6101b77f00000000000000000000000000000000000000000000000000000000000186a081565b600054610414906001600160d81b031681565b6040516001600160d81b0390911681526020016101c1565b61021c61043a366004611cb6565b610e42565b610447611274565b604080519283529015156020830152016101c1565b6103227f000000000000000000000000000000000000000000000000000000000000384081565b6101f17f000000000000000000000000eef7b7205caf2bcd71437d9acde3874c3388c13881565b6000546104be90600160f81b900460ff1681565b60405190151581526020016101c1565b6101b77f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b6104fd61135b565b6040805193845260208401929092521515908201526060016101c1565b60005461029490600160e81b900461ffff1681565b6101f17f000000000000000000000000c7372ab5dd315606db799246e8aa112405abaeff81565b6103227f000000000000000000000000000000000000000000000000000000000000003281565b600354158061059657506002546001600160a01b031615155b156105b457604051637213905b60e11b815260040160405180910390fd5b6040516363e1d0cd60e11b8152600481018290527f0000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd906000906001600160a01b0383169063c7c3a19a90602401600060405180830381865afa15801561061e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106469190810190611e10565b90506000608084901c9050600082600001518360200151846080015184866040015187610120015160405160200161068396959493929190611f2e565b60405160208183030381529060405280519060200120905080600354146106bd57604051632bec54e160e11b815260040160405180910390fd5b6040516379ea994360e01b8152600481018690526000906001600160a01b038616906379ea994390602401602060405180830381865afa158015610705573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107299190611f8e565b600280546001600160a01b0319166001600160a01b0383169081179091556040805189815260208101929092529192507fb2b260d8dc5d3268f11a016e359df280f36fa8e36b1550c67cd956bed4ae775d91015b60405180910390a1505050505050565b6001818154811061079d57600080fd5b6000918252602090912001546001600160401b0381169150600160401b90046001600160c01b031682565b6002546001600160a01b031633146107f35760405163749bdb8760e01b815260040160405180910390fd5b60008061080283850185611fa9565b60008054929450909250906001600160d81b0381169061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b90910416801561085a576040516316f70cd160e01b815260040160405180910390fd5b61086d876001600160d81b031685611489565b1561087d57505050505050505050565b6108bf6108ac7f0000000000000000000000000000000000000000000000000000000000000032612710612004565b85906001600160401b03166127106115b6565b876001600160d81b0316118061090b57506108ff6108ac7f000000000000000000000000000000000000000000000000000000000000003261271061202b565b876001600160d81b0316105b1561091557600194505b600080546001600160d81b0319166001600160d81b0389161781556001805461ffff86169081106109485761094861204b565b600091825260209091200180549091506001600160401b0390811690881611610984576040516306635bcb60e51b815260040160405180910390fd5b42876001600160401b031611156109ae576040516307987ab960e31b815260040160405180910390fd5b80546000906109c6906001600160401b03168961202b565b6001600160401b031690507f00000000000000000000000000000000000000000000000000000000000151806001600160401b03168110610a0657600196505b6000610a128288612061565b8354610a2e9190600160401b90046001600160c01b0316612078565b90506001600160c01b03811115610a5b576040516001626ee14160e01b0319815260040160405180910390fd5b6001600160401b0389166001600160c01b038216600160401b0267ffffffffffffffff191617835560006001610a9188886115d5565b61ffff1681548110610aa557610aa561204b565b600091825260209091200154610ac4906001600160401b03168b61202b565b6001600160401b031690507f00000000000000000000000000000000000000000000000000000000000151806001600160401b03168110610b7e576000610b0b8888611603565b6000805461ffff60d81b1916600160d81b61ffff841690810291909117825560018054939b508b945091928110610b4457610b4461204b565b600091825260209091206001600160401b038e166001600160c01b038716600160401b0267ffffffffffffffff1916179101555060019950505b88610b9c576040516309f9f55d60e01b815260040160405180910390fd5b600080610bb38d6001600160d81b03168a8a611629565b9150915080158015610bd35750610bd38d6001600160d81b031683611489565b15610be957505050505050505050505050505050565b604080514281526001600160401b038e1660208201526001600160d81b038f168183015260608101849052821515608082015290517f8c2ecabee4ec920ce555679e0efd8aa525aee64dc39f4a13a093f9fc72893fdf9181900360a00190a1505050505050505050505050505050565b600060606000610c67611891565b6000549091506001600160d81b0381169061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b9091041680610e3657600060018461ffff1681548110610cb957610cb961204b565b600091825260209091200154610cd8906001600160401b03164261208b565b905060006001610ce886866115d5565b61ffff1681548110610cfc57610cfc61204b565b600091825260209091200154610d1b906001600160401b03164261208b565b90507f00000000000000000000000000000000000000000000000000000000000151806001600160401b03811683101580610d5f5750806001600160401b03168210155b80610db35750610da7610d947f0000000000000000000000000000000000000000000000000000000000000032612710612004565b88906001600160401b03166127106115b6565b886001600160d81b0316115b80610df45750610de8610d947f000000000000000000000000000000000000000000000000000000000000003261271061202b565b886001600160d81b0316105b15610e3257604080516001600160d81b038a166020820152426001600160401b031681830152815180820383018152606090910190915260019a5098505b5050505b50505050509250929050565b6002546001600160a01b0316151580610e5c575060035415155b15610e7a5760405163268763a360e21b815260040160405180910390fd5b610eb86001600160a01b037f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca1633306001600160601b038516611a33565b60007f0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a905060007f0000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd905060006040518061014001604052807f000000000000000000000000c7372ab5dd315606db799246e8aa112405abaeff6001600160a01b03166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa158015610f6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610f96919081019061209e565b604051602001610fa691906120e6565b60408051808303601f19018152918152908252805160208082018352600080835281850192909252308484015261c35060608501526001600160a01b037f000000000000000000000000eef7b7205caf2bcd71437d9acde3874c3388c1388116608086015260a085018390528351808301855283815260c08601528351808301855283815260e086015283519182019093529081526101008301526001600160601b0387166101209092018290529192506110a5917f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca16907f0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a90611abb565b604051633f678e1160e01b81526000906001600160a01b03851690633f678e11906110d490859060040161211d565b6020604051808303816000875af11580156110f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111179190612232565b905080156111ec576040516379ea994360e01b8152600481018290526000906001600160a01b038516906379ea994390602401602060405180830381865afa158015611167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118b9190611f8e565b600280546001600160a01b0319166001600160a01b0383169081179091556040805185815260208101929092529192507fb2b260d8dc5d3268f11a016e359df280f36fa8e36b1550c67cd956bed4ae775d910160405180910390a15061126d565b60008260400151836060015184608001518560a001518660c0015187610100015160405160200161122296959493929190611f2e565b60408051601f19818403018152908290528051602091820120600381905580835292507f87b8902f760f4a16ca050d02d4ae2fe155fba5e58ed9b48a5d832d73796a3f2a910161077d565b5050505050565b6000805481906001600160d81b03811690600160f81b900460ff1680156112a2575060009360019350915050565b60008054600180549091600160d81b900461ffff169081106112c6576112c661204b565b6000918252602090912001546112e5906001600160401b03164261208b565b90506113317f00000000000000000000000000000000000000000000000000000000000038407f0000000000000000000000000000000000000000000000000000000000015180612004565b6001600160401b031681111561134f57506000946001945092505050565b50909360009350915050565b600080546001600160d81b0381169190819061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b9091041680156113a757506000958695506001945092505050565b60008054600180549091600160d81b900461ffff169081106113cb576113cb61204b565b6000918252602090912001546113ea906001600160401b03164261208b565b90506114367f00000000000000000000000000000000000000000000000000000000000038407f0000000000000000000000000000000000000000000000000000000000015180612004565b6001600160401b03168111156114585750600096879650600195509350505050565b611463878585611629565b909650945084156114805750600096879650600195509350505050565b50505050909192565b60006114b8827f0000000000000000000000000000000000000000000000000000000000001f406127106115b6565b8310806114f057506114ed827f00000000000000000000000000000000000000000000000000000000000186a06127106115b6565b83115b156115ac57600080546001600160f81b0316600160f81b1790557f472eb7b5f33f38b3f139fef7fc88820178d1cf9d0abffee988953791a84d66da83611559847f0000000000000000000000000000000000000000000000000000000000001f406127106115b6565b611586857f00000000000000000000000000000000000000000000000000000000000186a06127106115b6565b6040805193845260208401929092529082015260600160405180910390a15060016115b0565b5060005b92915050565b8282028115158415858304851417166115ce57600080fd5b0492915050565b600061ffff8316156115f1576115ec60018461224b565b6115fc565b6115fc60018361224b565b9392505050565b600061161060018361224b565b61ffff168361ffff16146115ac576115ec836001612266565b6000806000600161163a86866115d5565b61ffff168154811061164e5761164e61204b565b60009182526020808320604080518082019091529201546001600160401b0381168352600160401b90046001600160c01b031690820152915060016116938787611603565b61ffff16815481106116a7576116a761204b565b6000918252602091829020604080518082019091529101546001600160401b038116808352600160401b9091046001600160c01b03169282019290925291506001036116fc5760006001935093505050611889565b8051825160009161170c9161202b565b6001600160401b03169050600061172460028861224b565b6117529061ffff167f0000000000000000000000000000000000000000000000000000000000015180612281565b6001600160401b0316905060007f00000000000000000000000000000000000000000000000000000000000038406001600160401b0316826117949190612078565b9050818310156117b05760006001965096505050505050611889565b808311156117ca5760006001965096505050505050611889565b600060018a61ffff16815481106117e3576117e361204b565b60009182526020808320604080518082019091529201546001600160401b038116808452600160401b9091046001600160c01b03169183019190915290925061182c904261208b565b611836908d612061565b826020015161184591906122ac565b865190915061185d906001600160401b03164261208b565b602087015161186c90836122cc565b6001600160c01b031661187f91906122ec565b9850505050505050505b935093915050565b6000807f000000000000000000000000c7372ab5dd315606db799246e8aa112405abaeff6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119169190612232565b905060006119c97f000000000000000000000000000000000000000000000000000000000000001260127f000000000000000000000000c7372ab5dd315606db799246e8aa112405abaeff6001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa15801561199e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119c29190612232565b9190611b38565b9050816000036119dc5760009250505090565b6000611a097f0000000000000000000000000000000000000000000000000de0b6b3a764000083856115b6565b90506001600160d81b038111156115fc57604051637c5c0ad360e01b815260040160405180910390fd5b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061126d5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064015b60405180910390fd5b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080611b325760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401611ab2565b50505050565b60008160ff168360ff1603611b4e5750826115fc565b8160ff168360ff161015611b8257611b66838361230e565b611b7190600a61240b565b611b7b9085612061565b90506115fc565b611b8c828461230e565b611b9790600a61240b565b611b7b90856122ec565b600060208284031215611bb357600080fd5b5035919050565b60008060208385031215611bcd57600080fd5b82356001600160401b0380821115611be457600080fd5b818501915085601f830112611bf857600080fd5b813581811115611c0757600080fd5b866020828501011115611c1957600080fd5b60209290920196919550909350505050565b60005b83811015611c46578181015183820152602001611c2e565b50506000910152565b60008151808452611c67816020860160208601611c2b565b601f01601f19169290920160200192915050565b8215158152604060208201526000611c966040830184611c4f565b949350505050565b6001600160601b0381168114611cb357600080fd5b50565b600060208284031215611cc857600080fd5b81356115fc81611c9e565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b0381118282101715611d0c57611d0c611cd3565b60405290565b80516001600160a01b0381168114611d2957600080fd5b919050565b805163ffffffff81168114611d2957600080fd5b60006001600160401b0380841115611d5c57611d5c611cd3565b604051601f8501601f19908116603f01168101908282118183101715611d8457611d84611cd3565b81604052809350858152868686011115611d9d57600080fd5b611dab866020830187611c2b565b5050509392505050565b600082601f830112611dc657600080fd5b6115fc83835160208501611d42565b8051611d2981611c9e565b6001600160401b0381168114611cb357600080fd5b8051611d2981611de0565b80518015158114611d2957600080fd5b600060208284031215611e2257600080fd5b81516001600160401b0380821115611e3957600080fd5b908301906101408286031215611e4e57600080fd5b611e56611ce9565b611e5f83611d12565b8152611e6d60208401611d2e565b6020820152604083015182811115611e8457600080fd5b611e9087828601611db5565b604083015250611ea260608401611dd5565b6060820152611eb360808401611d12565b6080820152611ec460a08401611df5565b60a0820152611ed560c08401611d2e565b60c0820152611ee660e08401611dd5565b60e0820152610100611ef9818501611e00565b908201526101208381015183811115611f1157600080fd5b611f1d88828701611db5565b918301919091525095945050505050565b6001600160a01b03878116825263ffffffff871660208301528516604082015260ff8416606082015260c060808201819052600090611f6f90830185611c4f565b82810360a0840152611f818185611c4f565b9998505050505050505050565b600060208284031215611fa057600080fd5b6115fc82611d12565b60008060408385031215611fbc57600080fd5b82356001600160d81b0381168114611fd357600080fd5b91506020830135611fe381611de0565b809150509250929050565b634e487b7160e01b600052601160045260246000fd5b6001600160401b0381811683821601908082111561202457612024611fee565b5092915050565b6001600160401b0382811682821603908082111561202457612024611fee565b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176115b0576115b0611fee565b808201808211156115b0576115b0611fee565b818103818111156115b0576115b0611fee565b6000602082840312156120b057600080fd5b81516001600160401b038111156120c657600080fd5b8201601f810184136120d757600080fd5b611c9684825160208401611d42565b600082516120f8818460208701611c2b565b72205368617265205072696365204f7261636c6560681b920191825250601301919050565b602081526000825161014080602085015261213c610160850183611c4f565b91506020850151601f198086850301604087015261215a8483611c4f565b93506040870151915061217860608701836001600160a01b03169052565b606087015163ffffffff81166080880152915060808701516001600160a01b03811660a0880152915060a087015160ff811660c0880152915060c08701519150808685030160e08701526121cc8483611c4f565b935060e087015191506101008187860301818801526121eb8584611c4f565b94508088015192505061012081878603018188015261220a8584611c4f565b94508088015192505050612228828601826001600160601b03169052565b5090949350505050565b60006020828403121561224457600080fd5b5051919050565b61ffff82811682821603908082111561202457612024611fee565b61ffff81811683821601908082111561202457612024611fee565b6001600160401b038181168382160280821691908281146122a4576122a4611fee565b505092915050565b6001600160c01b0381811683821601908082111561202457612024611fee565b6001600160c01b0382811682821603908082111561202457612024611fee565b60008261230957634e487b7160e01b600052601260045260246000fd5b500490565b60ff82811682821603908111156115b0576115b0611fee565b600181815b8085111561236257816000190482111561234857612348611fee565b8085161561235557918102915b93841c939080029061232c565b509250929050565b600082612379575060016115b0565b81612386575060006115b0565b816001811461239c57600281146123a6576123c2565b60019150506115b0565b60ff8411156123b7576123b7611fee565b50506001821b6115b0565b5060208310610133831016604e8410600b84101617156123e5575081810a6115b0565b6123ef8383612327565b806000190482111561240357612403611fee565b029392505050565b60006115fc60ff84168361236a56fea2646970667358221220c7342a1cb62cdab1915d69aea68e2613a3dccb1cc13fcb5ed5bd4b02bc243ad464736f6c63430008150033

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.