Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Initialize | 19416678 | 311 days ago | IN | 0 ETH | 0.02047905 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
ERC4626SharePriceOracle
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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"; import { IChainlinkAggregator } from "src/interfaces/external/IChainlinkAggregator.sol"; contract ERC4626SharePriceOracle is AutomationCompatibleInterface { using Math for uint256; using SafeTransferLib for ERC20; // ========================================= STRUCTS ========================================= struct Observation { uint64 timestamp; uint192 cumulative; } /** * @notice Use a struct for constructor args so we do not encounter stack too deep errors. */ struct ConstructorArgs { ERC4626 _target; uint64 _heartbeat; uint64 _deviationTrigger; uint64 _gracePeriod; uint16 _observationsToUse; address _automationRegistry; address _automationRegistrar; address _automationAdmin; address _link; uint216 _startingAnswer; uint256 _allowedAnswerChangeLower; uint256 _allowedAnswerChangeUpper; address _sequencerUptimeFeed; uint64 _sequencerGracePeriod; } // ========================================= 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); /** * @notice Emitted when the upkeep is registered. */ event UpkeepRegistered(uint256 upkeepId, address forwarder); /** * @notice Emitted when a upkeep registration is left pending. */ 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 Address for the networks sequencer uptime feed. * @dev For oracles that do not rely on a sequencer being up, use address(0). */ IChainlinkAggregator internal immutable sequencerUptimeFeed; /** * @notice The grace period to enforce after a sequencer comes back online. * @dev Calls to `getLatest` and `getLatestAnswer` will return a true for * `isNotSafeToUse` if the sequencer is down, or if the time since the * sequencer went back online is less than `sequencerGracePeriod`. */ uint64 public immutable sequencerGracePeriod; /** * @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(ConstructorArgs memory args) { target = args._target; targetDecimals = target.decimals(); ONE_SHARE = 10 ** targetDecimals; heartbeat = args._heartbeat; deviationTrigger = args._deviationTrigger; gracePeriod = args._gracePeriod; // Add 1 to observations to use. args._observationsToUse = args._observationsToUse + 1; observationsLength = args._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 < args._observationsToUse; ++i) observations.push(Observation({ timestamp: 1, cumulative: 1 })); // Set to args._startingAnswer so slot is dirty for first upkeep, and does not trigger kill switch. answer = args._startingAnswer; if (args._allowedAnswerChangeLower > 1e4) revert("Illogical Lower"); allowedAnswerChangeLower = args._allowedAnswerChangeLower; if (args._allowedAnswerChangeUpper < 1e4) revert("Illogical Upper"); allowedAnswerChangeUpper = args._allowedAnswerChangeUpper; automationRegistry = args._automationRegistry; automationRegistrar = args._automationRegistrar; automationAdmin = args._automationAdmin; link = ERC20(args._link); sequencerUptimeFeed = IChainlinkAggregator(args._sequencerUptimeFeed); sequencerGracePeriod = args._sequencerGracePeriod; } //============================== 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); if (_checkSequencer()) 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); if (_checkSequencer()) 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; } /** * @notice Checks if the sequencer is down, or if the grace period is not met. * @return bool indicating if the sequencer has a problem */ function _checkSequencer() internal view returns (bool) { if (address(sequencerUptimeFeed) != address(0)) { (, int256 sequencerAnswer, uint256 startedAt, , ) = sequencerUptimeFeed.latestRoundData(); // This check should make TXs from L1 to L2 revert if someone tried interacting with the cellar while the sequencer is down. // Answer == 0: Sequencer is up // Answer == 1: Sequencer is down if (sequencerAnswer == 1) { return true; } // Make sure the grace period has passed after the // sequencer is back up. uint256 timeSinceUp = block.timestamp - startedAt; if (timeSinceUp <= sequencerGracePeriod) { return true; } } // If we made it this far, the sequencer is fine, and or it is not set. return false; } }
// 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 {} }
// 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"); } }
// 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); } }
// 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)) } } }
// 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); } }
// 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; }
// 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; }
// 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); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol"; interface IChainlinkAggregator is AggregatorV2V3Interface { function maxAnswer() external view returns (int192); function minAnswer() external view returns (int192); function aggregator() external view returns (address); }
// 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)) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./AggregatorInterface.sol"; import "./AggregatorV3Interface.sol"; interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorInterface { function latestAnswer() external view returns (int256); function latestTimestamp() external view returns (uint256); function latestRound() external view returns (uint256); function getAnswer(uint256 roundId) external view returns (int256); function getTimestamp(uint256 roundId) external view returns (uint256); event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
{ "remappings": [ "@solmate/=lib/solmate/src/", "@forge-std/=lib/forge-std/src/", "@ds-test/=lib/forge-std/lib/ds-test/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "@openzeppelin/=lib/openzeppelin-contracts/", "@uniswap/v3-periphery/=lib/v3-periphery/", "@uniswap/v3-core/=lib/v3-core/", "@chainlink/=lib/chainlink/", "@uniswapV3P/=lib/v3-periphery/contracts/", "@uniswapV3C/=lib/v3-core/contracts/", "@balancer/=lib/balancer-v2-monorepo/pkg/", "@ccip/=lib/ccip/", "@balancer-labs/=lib/balancer-v2-monorepo/../../node_modules/@balancer-labs/", "axelar-gmp-sdk-solidity/=lib/axelar-gmp-sdk-solidity/contracts/", "balancer-v2-monorepo/=lib/balancer-v2-monorepo/", "ccip/=lib/ccip/contracts/", "chainlink/=lib/chainlink/integration-tests/contracts/ethereum/src/", "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": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"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"},{"internalType":"address","name":"_sequencerUptimeFeed","type":"address"},{"internalType":"uint64","name":"_sequencerGracePeriod","type":"uint64"}],"internalType":"struct ERC4626SharePriceOracle.ConstructorArgs","name":"args","type":"tuple"}],"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":"sequencerGracePeriod","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","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"}]
Contract Creation Code
61024060405234801562000011575f80fd5b5060405162002d2138038062002d2183398101604081905262000034916200036d565b80516001600160a01b03166101808190526040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa1580156200007f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620000a5919062000488565b60ff166101a0819052620000bb90600a620005c0565b60e05260208101516001600160401b0390811660809081526040830151821660c052606083015190911660a052810151620000f8906001620005d0565b61ffff16608082018190525f805461ffff60e81b1916600160e81b9092029190911781555b816080015161ffff16811015620001b05760408051808201909152600180825260208201818152815480830183555f92909252915191516001600160c01b031668010000000000000000026001600160401b0392909216919091177fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf690910155620001a881620005f5565b90506200011d565b506101208101515f80546001600160d81b0319166001600160d81b039092169190911790556101408101516127101015620002245760405162461bcd60e51b815260206004820152600f60248201526e24b63637b3b4b1b0b6102637bbb2b960891b60448201526064015b60405180910390fd5b6101408101516101c0526101608101516127101115620002795760405162461bcd60e51b815260206004820152600f60248201526e24b63637b3b4b1b0b6102ab83832b960891b60448201526064016200021b565b610160808201516101e05260a08201516001600160a01b039081166101205260c083015181166101405260e08301518116610100908152830151811690915261018082015116610200526101a001516001600160401b03166102205262000610565b6040516101c081016001600160401b03811182821017156200030b57634e487b7160e01b5f52604160045260245ffd5b60405290565b80516001600160a01b038116811462000328575f80fd5b919050565b80516001600160401b038116811462000328575f80fd5b805161ffff8116811462000328575f80fd5b80516001600160d81b038116811462000328575f80fd5b5f6101c082840312156200037f575f80fd5b62000389620002db565b620003948362000311565b8152620003a4602084016200032d565b6020820152620003b7604084016200032d565b6040820152620003ca606084016200032d565b6060820152620003dd6080840162000344565b6080820152620003f060a0840162000311565b60a08201526200040360c0840162000311565b60c08201526200041660e0840162000311565b60e08201526101006200042b81850162000311565b908201526101206200043f84820162000356565b90820152610140838101519082015261016080840151908201526101806200046981850162000311565b908201526101a06200047d8482016200032d565b908201529392505050565b5f6020828403121562000499575f80fd5b815160ff81168114620004aa575f80fd5b9392505050565b634e487b7160e01b5f52601160045260245ffd5b600181815b808511156200050557815f1904821115620004e957620004e9620004b1565b80851615620004f757918102915b93841c9390800290620004ca565b509250929050565b5f826200051d57506001620005ba565b816200052b57505f620005ba565b81600181146200054457600281146200054f576200056f565b6001915050620005ba565b60ff841115620005635762000563620004b1565b50506001821b620005ba565b5060208310610133831016604e8410600b841016171562000594575081810a620005ba565b620005a08383620004c5565b805f1904821115620005b657620005b6620004b1565b0290505b92915050565b5f620004aa60ff8416836200050d565b61ffff818116838216019080821115620005ee57620005ee620004b1565b5092915050565b5f60018201620006095762000609620004b1565b5060010190565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516125a0620007815f395f81816102bc0152611c1c01525f8181611b460152611b7701525f8181610416015281816114f3015261158b01525f81816103ef015281816114be015261155e01525f8181610315015261193701525f818161056801528181610f23015281816118b1015261195a01525f818161023401528181610e9b015261106901525f81816101e001528181610ecf015261108c01525f8181610389015281816105fe0152610ef301525f81816104be0152610ff101525f818161050801526119f601525f818161058f015281816108aa015281816108fd01528181610d810152610dd501525f81816104970152818161130501528181611421015261178101525f818161034f015281816109f501528181610af001528181610d330152818161132601528181611442015261174f01526125a05ff3fe608060405234801561000f575f80fd5b50600436106101bb575f3560e01c80636e04ff0d116100f3578063a69e972c11610093578063c36af4601161006e578063c36af4601461052a578063cd7f5ce21461054f578063d4b8399214610563578063feabaa021461058a575f80fd5b8063a69e972c146104b9578063ada14698146104e0578063b7d122b514610503575f80fd5b806385bb7d69116100ce57806385bb7d6914610438578063909f1cad1461046257806396237c0214610475578063a06db7dc14610492575f80fd5b80636e04ff0d146103c95780637167adbc146103ea5780637d4cdb4f14610411575f80fd5b8063313ce5671161015e5780633defb962116101395780633defb9621461034a5780634585e33b146103715780635dc228a014610384578063643917f5146103ab575f80fd5b8063313ce567146102f657806336c1387e146103105780633b2786cb14610337575f80fd5b80631c4695f4116101995780631c4695f41461022f578063252c09d71461025657806326987b601461029057806326a97b94146102b7575f80fd5b8063025c67cb146101bf5780630c381873146101db57806318da78821461021a575b5f80fd5b6101c860035481565b6040519081526020015b60405180910390f35b6102027f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101d2565b61022d610228366004611cc5565b6105b1565b005b6102027f000000000000000000000000000000000000000000000000000000000000000081565b610269610264366004611cc5565b6107b6565b604080516001600160401b0390931683526001600160c01b039091166020830152016101d2565b5f546102a490600160d81b900461ffff1681565b60405161ffff90911681526020016101d2565b6102de7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160401b0390911681526020016101d2565b6102fe601281565b60405160ff90911681526020016101d2565b6102fe7f000000000000000000000000000000000000000000000000000000000000000081565b600254610202906001600160a01b031681565b6102de7f000000000000000000000000000000000000000000000000000000000000000081565b61022d61037f366004611cdc565b6107ef565b6102027f000000000000000000000000000000000000000000000000000000000000000081565b6103b461c35081565b60405163ffffffff90911681526020016101d2565b6103dc6103d7366004611cdc565b610c74565b6040516101d2929190611d94565b6101c87f000000000000000000000000000000000000000000000000000000000000000081565b6101c87f000000000000000000000000000000000000000000000000000000000000000081565b5f5461044a906001600160d81b031681565b6040516001600160d81b0390911681526020016101d2565b61022d610470366004611dcd565b610e56565b61047d611278565b604080519283529015156020830152016101d2565b6102de7f000000000000000000000000000000000000000000000000000000000000000081565b6102027f000000000000000000000000000000000000000000000000000000000000000081565b5f546104f390600160f81b900460ff1681565b60405190151581526020016101d2565b6101c87f000000000000000000000000000000000000000000000000000000000000000081565b610532611372565b6040805193845260208401929092521515908201526060016101d2565b5f546102a490600160e81b900461ffff1681565b6102027f000000000000000000000000000000000000000000000000000000000000000081565b6102de7f000000000000000000000000000000000000000000000000000000000000000081565b60035415806105ca57506002546001600160a01b031615155b156105e857604051637213905b60e11b815260040160405180910390fd5b6040516363e1d0cd60e11b8152600481018290527f0000000000000000000000000000000000000000000000000000000000000000905f906001600160a01b0383169063c7c3a19a906024015f60405180830381865afa15801561064e573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526106759190810190611f1b565b90505f608084901c90505f825f0151836020015184608001518486604001518761012001516040516020016106af96959493929190612033565b60405160208183030381529060405280519060200120905080600354146106e957604051632bec54e160e11b815260040160405180910390fd5b6040516379ea994360e01b8152600481018690525f906001600160a01b038616906379ea994390602401602060405180830381865afa15801561072e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107529190612092565b600280546001600160a01b0319166001600160a01b0383169081179091556040805189815260208101929092529192507fb2b260d8dc5d3268f11a016e359df280f36fa8e36b1550c67cd956bed4ae775d91015b60405180910390a1505050505050565b600181815481106107c5575f80fd5b5f918252602090912001546001600160401b0381169150600160401b90046001600160c01b031682565b6002546001600160a01b0316331461081a5760405163749bdb8760e01b815260040160405180910390fd5b5f80610828838501856120ab565b5f8054929450909250906001600160d81b0381169061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b90910416801561087f576040516316f70cd160e01b815260040160405180910390fd5b610892876001600160d81b0316856114b7565b156108a257505050505050505050565b6108e46108d17f0000000000000000000000000000000000000000000000000000000000000000612710612101565b85906001600160401b03166127106115e1565b876001600160d81b0316118061093057506109246108d17f0000000000000000000000000000000000000000000000000000000000000000612710612128565b876001600160d81b0316105b1561093a57600194505b5f80546001600160d81b0319166001600160d81b0389161781556001805461ffff861690811061096c5761096c612148565b5f91825260209091200180549091506001600160401b03908116908816116109a7576040516306635bcb60e51b815260040160405180910390fd5b42876001600160401b031611156109d1576040516307987ab960e31b815260040160405180910390fd5b80545f906109e8906001600160401b031689612128565b6001600160401b031690507f00000000000000000000000000000000000000000000000000000000000000006001600160401b03168110610a2857600196505b5f610a33828861215c565b8354610a4f9190600160401b90046001600160c01b0316612173565b90506001600160c01b03811115610a7c576040516001626ee14160e01b0319815260040160405180910390fd5b6001600160401b0389166001600160c01b038216600160401b0267ffffffffffffffff19161783555f6001610ab188886115ff565b61ffff1681548110610ac557610ac5612148565b5f91825260209091200154610ae3906001600160401b03168b612128565b6001600160401b031690507f00000000000000000000000000000000000000000000000000000000000000006001600160401b03168110610b9a575f610b29888861162c565b5f805461ffff60d81b1916600160d81b61ffff841690810291909117825560018054939b508b945091928110610b6157610b61612148565b5f91825260209091206001600160401b038e166001600160c01b038716600160401b0267ffffffffffffffff1916179101555060019950505b88610bb8576040516309f9f55d60e01b815260040160405180910390fd5b5f80610bce8d6001600160d81b03168a8a611651565b9150915080158015610bee5750610bee8d6001600160d81b0316836114b7565b15610c0457505050505050505050505050505050565b604080514281526001600160401b038e1660208201526001600160d81b038f168183015260608101849052821515608082015290517f8c2ecabee4ec920ce555679e0efd8aa525aee64dc39f4a13a093f9fc72893fdf9181900360a00190a1505050505050505050505050505050565b5f60605f610c806118ad565b5f549091506001600160d81b0381169061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b9091041680610e4a575f60018461ffff1681548110610cd057610cd0612148565b5f91825260209091200154610cee906001600160401b031642612186565b90505f6001610cfd86866115ff565b61ffff1681548110610d1157610d11612148565b5f91825260209091200154610d2f906001600160401b031642612186565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160401b03811683101580610d735750806001600160401b03168210155b80610dc75750610dbb610da87f0000000000000000000000000000000000000000000000000000000000000000612710612101565b88906001600160401b03166127106115e1565b886001600160d81b0316115b80610e085750610dfc610da87f0000000000000000000000000000000000000000000000000000000000000000612710612128565b886001600160d81b0316105b15610e4657604080516001600160d81b038a166020820152426001600160401b031681830152815180820383018152606090910190915260019a5098505b5050505b50505050509250929050565b6002546001600160a01b0316151580610e70575060035415155b15610e8e5760405163268763a360e21b815260040160405180910390fd5b610ecc6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633306001600160601b038516611a46565b5f7f000000000000000000000000000000000000000000000000000000000000000090505f7f000000000000000000000000000000000000000000000000000000000000000090505f6040518061014001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166306fdde036040518163ffffffff1660e01b81526004015f60405180830381865afa158015610f7c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610fa39190810190612199565b604051602001610fb391906121dd565b60408051808303601f190181529181529082528051602080820183525f80835281850192909252308484015261c35060608501526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116608086015260a085018390528351808301855283815260c08601528351808301855283815260e086015283519182019093529081526101008301526001600160601b0387166101209092018290529192506110b1917f000000000000000000000000000000000000000000000000000000000000000016907f000000000000000000000000000000000000000000000000000000000000000090611aca565b604051633f678e1160e01b81525f906001600160a01b03851690633f678e11906110df908590600401612213565b6020604051808303815f875af11580156110fb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111f9190612327565b905080156111f1576040516379ea994360e01b8152600481018290525f906001600160a01b038516906379ea994390602401602060405180830381865afa15801561116c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111909190612092565b600280546001600160a01b0319166001600160a01b0383169081179091556040805185815260208101929092529192507fb2b260d8dc5d3268f11a016e359df280f36fa8e36b1550c67cd956bed4ae775d910160405180910390a150611271565b5f8260400151836060015184608001518560a001518660c0015187610100015160405160200161122696959493929190612033565b60408051601f19818403018152908290528051602091820120600381905580835292507f87b8902f760f4a16ca050d02d4ae2fe155fba5e58ed9b48a5d832d73796a3f2a91016107a6565b5050505050565b5f805481906001600160d81b03811690600160f81b900460ff1680156112a457505f9360019350915050565b6112ac611b43565b156112bd57505f9360019350915050565b5f8054600180549091600160d81b900461ffff169081106112e0576112e0612148565b5f918252602090912001546112fe906001600160401b031642612186565b905061134a7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612101565b6001600160401b031681111561136757505f946001945092505050565b5090935f9350915050565b5f80546001600160d81b0381169190819061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b9091041680156113bc57505f958695506001945092505050565b6113c4611b43565b156113d957505f958695506001945092505050565b5f8054600180549091600160d81b900461ffff169081106113fc576113fc612148565b5f9182526020909120015461141a906001600160401b031642612186565b90506114667f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612101565b6001600160401b031681111561148757505f96879650600195509350505050565b611492878585611651565b909650945084156114ae57505f96879650600195509350505050565b50505050909192565b5f6114e5827f00000000000000000000000000000000000000000000000000000000000000006127106115e1565b83108061151d575061151a827f00000000000000000000000000000000000000000000000000000000000000006127106115e1565b83115b156115d8575f80546001600160f81b0316600160f81b1790557f472eb7b5f33f38b3f139fef7fc88820178d1cf9d0abffee988953791a84d66da83611585847f00000000000000000000000000000000000000000000000000000000000000006127106115e1565b6115b2857f00000000000000000000000000000000000000000000000000000000000000006127106115e1565b6040805193845260208401929092529082015260600160405180910390a15060016115db565b505f5b92915050565b8282028115158415858304851417166115f8575f80fd5b0492915050565b5f61ffff83161561161a5761161560018461233e565b611625565b61162560018361233e565b9392505050565b5f61163860018361233e565b61ffff168361ffff16146115d857611615836001612359565b5f805f600161166086866115ff565b61ffff168154811061167457611674612148565b5f9182526020808320604080518082019091529201546001600160401b0381168352600160401b90046001600160c01b031690820152915060016116b8878761162c565b61ffff16815481106116cc576116cc612148565b5f918252602091829020604080518082019091529101546001600160401b038116808352600160401b9091046001600160c01b031692820192909252915060010361171f575f60019350935050506118a5565b805182515f9161172e91612128565b6001600160401b031690505f61174560028861233e565b6117739061ffff167f0000000000000000000000000000000000000000000000000000000000000000612374565b6001600160401b031690505f7f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316826117b49190612173565b9050818310156117cf575f60019650965050505050506118a5565b808311156117e8575f60019650965050505050506118a5565b5f60018a61ffff168154811061180057611800612148565b5f9182526020808320604080518082019091529201546001600160401b038116808452600160401b9091046001600160c01b0316918301919091529092506118489042612186565b611852908d61215c565b8260200151611861919061239f565b8651909150611879906001600160401b031642612186565b602087015161188890836123bf565b6001600160c01b031661189b91906123df565b9850505050505050505b935093915050565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561190b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061192f9190612327565b90505f6119df7f000000000000000000000000000000000000000000000000000000000000000060127f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119d89190612327565b9190611c5d565b9050815f036119f0575f9250505090565b5f611a1c7f000000000000000000000000000000000000000000000000000000000000000083856115e1565b90506001600160d81b0381111561162557604051637c5c0ad360e01b815260040160405180910390fd5b5f6040516323b872dd60e01b815284600482015283602482015282604482015260205f6064835f8a5af13d15601f3d1160015f5114161716915050806112715760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064015b60405180910390fd5b5f60405163095ea7b360e01b815283600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080611b3d5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401611ac1565b50505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615611c58575f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611bd1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bf59190612417565b5050925092505081600103611c0d5760019250505090565b5f611c188242612186565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160401b03168111611c54576001935050505090565b5050505b505f90565b5f8160ff168360ff1603611c72575082611625565b8160ff168360ff161015611ca657611c8a8383612463565b611c9590600a61255c565b611c9f908561215c565b9050611625565b611cb08284612463565b611cbb90600a61255c565b611c9f90856123df565b5f60208284031215611cd5575f80fd5b5035919050565b5f8060208385031215611ced575f80fd5b82356001600160401b0380821115611d03575f80fd5b818501915085601f830112611d16575f80fd5b813581811115611d24575f80fd5b866020828501011115611d35575f80fd5b60209290920196919550909350505050565b5f5b83811015611d61578181015183820152602001611d49565b50505f910152565b5f8151808452611d80816020860160208601611d47565b601f01601f19169290920160200192915050565b8215158152604060208201525f611dae6040830184611d69565b949350505050565b6001600160601b0381168114611dca575f80fd5b50565b5f60208284031215611ddd575f80fd5b813561162581611db6565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b0381118282101715611e1f57611e1f611de8565b60405290565b80516001600160a01b0381168114611e3b575f80fd5b919050565b805163ffffffff81168114611e3b575f80fd5b5f6001600160401b0380841115611e6c57611e6c611de8565b604051601f8501601f19908116603f01168101908282118183101715611e9457611e94611de8565b81604052809350858152868686011115611eac575f80fd5b611eba866020830187611d47565b5050509392505050565b5f82601f830112611ed3575f80fd5b61162583835160208501611e53565b8051611e3b81611db6565b6001600160401b0381168114611dca575f80fd5b8051611e3b81611eed565b80518015158114611e3b575f80fd5b5f60208284031215611f2b575f80fd5b81516001600160401b0380821115611f41575f80fd5b908301906101408286031215611f55575f80fd5b611f5d611dfc565b611f6683611e25565b8152611f7460208401611e40565b6020820152604083015182811115611f8a575f80fd5b611f9687828601611ec4565b604083015250611fa860608401611ee2565b6060820152611fb960808401611e25565b6080820152611fca60a08401611f01565b60a0820152611fdb60c08401611e40565b60c0820152611fec60e08401611ee2565b60e0820152610100611fff818501611f0c565b908201526101208381015183811115612016575f80fd5b61202288828701611ec4565b918301919091525095945050505050565b6001600160a01b03878116825263ffffffff871660208301528516604082015260ff8416606082015260c0608082018190525f9061207390830185611d69565b82810360a08401526120858185611d69565b9998505050505050505050565b5f602082840312156120a2575f80fd5b61162582611e25565b5f80604083850312156120bc575f80fd5b82356001600160d81b03811681146120d2575f80fd5b915060208301356120e281611eed565b809150509250929050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b03818116838216019080821115612121576121216120ed565b5092915050565b6001600160401b03828116828216039080821115612121576121216120ed565b634e487b7160e01b5f52603260045260245ffd5b80820281158282048414176115db576115db6120ed565b808201808211156115db576115db6120ed565b818103818111156115db576115db6120ed565b5f602082840312156121a9575f80fd5b81516001600160401b038111156121be575f80fd5b8201601f810184136121ce575f80fd5b611dae84825160208401611e53565b5f82516121ee818460208701611d47565b72205368617265205072696365204f7261636c6560681b920191825250601301919050565b602081525f8251610140806020850152612231610160850183611d69565b91506020850151601f198086850301604087015261224f8483611d69565b93506040870151915061226d60608701836001600160a01b03169052565b606087015163ffffffff81166080880152915060808701516001600160a01b03811660a0880152915060a087015160ff811660c0880152915060c08701519150808685030160e08701526122c18483611d69565b935060e087015191506101008187860301818801526122e08584611d69565b9450808801519250506101208187860301818801526122ff8584611d69565b9450808801519250505061231d828601826001600160601b03169052565b5090949350505050565b5f60208284031215612337575f80fd5b5051919050565b61ffff828116828216039080821115612121576121216120ed565b61ffff818116838216019080821115612121576121216120ed565b6001600160401b03818116838216028082169190828114612397576123976120ed565b505092915050565b6001600160c01b03818116838216019080821115612121576121216120ed565b6001600160c01b03828116828216039080821115612121576121216120ed565b5f826123f957634e487b7160e01b5f52601260045260245ffd5b500490565b805169ffffffffffffffffffff81168114611e3b575f80fd5b5f805f805f60a0868803121561242b575f80fd5b612434866123fe565b9450602086015193506040860151925060608601519150612457608087016123fe565b90509295509295909350565b60ff82811682821603908111156115db576115db6120ed565b600181815b808511156124b657815f190482111561249c5761249c6120ed565b808516156124a957918102915b93841c9390800290612481565b509250929050565b5f826124cc575060016115db565b816124d857505f6115db565b81600181146124ee57600281146124f857612514565b60019150506115db565b60ff841115612509576125096120ed565b50506001821b6115db565b5060208310610133831016604e8410600b8410161715612537575081810a6115db565b612541838361247c565b805f1904821115612554576125546120ed565b029392505050565b5f61162560ff8416836124be56fea26469706673582212200b927b53419b5df309dbfd07bc47719d7f4ad39012483a8886ddb3ecdce6b5cf64736f6c63430008150033000000000000000000000000ea1a6307d9b18f8d1cbf1c3dd6aad8416c06a22100000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000000060000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a0000000000000000000000002322ba43eff1542b6a7baed35e66099ea0d12bd1000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000001d4c00000000000000000000000000000000000000000000000000000000000030d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106101bb575f3560e01c80636e04ff0d116100f3578063a69e972c11610093578063c36af4601161006e578063c36af4601461052a578063cd7f5ce21461054f578063d4b8399214610563578063feabaa021461058a575f80fd5b8063a69e972c146104b9578063ada14698146104e0578063b7d122b514610503575f80fd5b806385bb7d69116100ce57806385bb7d6914610438578063909f1cad1461046257806396237c0214610475578063a06db7dc14610492575f80fd5b80636e04ff0d146103c95780637167adbc146103ea5780637d4cdb4f14610411575f80fd5b8063313ce5671161015e5780633defb962116101395780633defb9621461034a5780634585e33b146103715780635dc228a014610384578063643917f5146103ab575f80fd5b8063313ce567146102f657806336c1387e146103105780633b2786cb14610337575f80fd5b80631c4695f4116101995780631c4695f41461022f578063252c09d71461025657806326987b601461029057806326a97b94146102b7575f80fd5b8063025c67cb146101bf5780630c381873146101db57806318da78821461021a575b5f80fd5b6101c860035481565b6040519081526020015b60405180910390f35b6102027f0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a81565b6040516001600160a01b0390911681526020016101d2565b61022d610228366004611cc5565b6105b1565b005b6102027f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca81565b610269610264366004611cc5565b6107b6565b604080516001600160401b0390931683526001600160c01b039091166020830152016101d2565b5f546102a490600160d81b900461ffff1681565b60405161ffff90911681526020016101d2565b6102de7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160401b0390911681526020016101d2565b6102fe601281565b60405160ff90911681526020016101d2565b6102fe7f000000000000000000000000000000000000000000000000000000000000001281565b600254610202906001600160a01b031681565b6102de7f000000000000000000000000000000000000000000000000000000000001518081565b61022d61037f366004611cdc565b6107ef565b6102027f0000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd81565b6103b461c35081565b60405163ffffffff90911681526020016101d2565b6103dc6103d7366004611cdc565b610c74565b6040516101d2929190611d94565b6101c87f0000000000000000000000000000000000000000000000000000000000001d4c81565b6101c87f00000000000000000000000000000000000000000000000000000000000030d481565b5f5461044a906001600160d81b031681565b6040516001600160d81b0390911681526020016101d2565b61022d610470366004611dcd565b610e56565b61047d611278565b604080519283529015156020830152016101d2565b6102de7f000000000000000000000000000000000000000000000000000000000000708081565b6102027f0000000000000000000000002322ba43eff1542b6a7baed35e66099ea0d12bd181565b5f546104f390600160f81b900460ff1681565b60405190151581526020016101d2565b6101c87f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b610532611372565b6040805193845260208401929092521515908201526060016101d2565b5f546102a490600160e81b900461ffff1681565b6102027f000000000000000000000000ea1a6307d9b18f8d1cbf1c3dd6aad8416c06a22181565b6102de7f000000000000000000000000000000000000000000000000000000000000003281565b60035415806105ca57506002546001600160a01b031615155b156105e857604051637213905b60e11b815260040160405180910390fd5b6040516363e1d0cd60e11b8152600481018290527f0000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd905f906001600160a01b0383169063c7c3a19a906024015f60405180830381865afa15801561064e573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526106759190810190611f1b565b90505f608084901c90505f825f0151836020015184608001518486604001518761012001516040516020016106af96959493929190612033565b60405160208183030381529060405280519060200120905080600354146106e957604051632bec54e160e11b815260040160405180910390fd5b6040516379ea994360e01b8152600481018690525f906001600160a01b038616906379ea994390602401602060405180830381865afa15801561072e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107529190612092565b600280546001600160a01b0319166001600160a01b0383169081179091556040805189815260208101929092529192507fb2b260d8dc5d3268f11a016e359df280f36fa8e36b1550c67cd956bed4ae775d91015b60405180910390a1505050505050565b600181815481106107c5575f80fd5b5f918252602090912001546001600160401b0381169150600160401b90046001600160c01b031682565b6002546001600160a01b0316331461081a5760405163749bdb8760e01b815260040160405180910390fd5b5f80610828838501856120ab565b5f8054929450909250906001600160d81b0381169061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b90910416801561087f576040516316f70cd160e01b815260040160405180910390fd5b610892876001600160d81b0316856114b7565b156108a257505050505050505050565b6108e46108d17f0000000000000000000000000000000000000000000000000000000000000032612710612101565b85906001600160401b03166127106115e1565b876001600160d81b0316118061093057506109246108d17f0000000000000000000000000000000000000000000000000000000000000032612710612128565b876001600160d81b0316105b1561093a57600194505b5f80546001600160d81b0319166001600160d81b0389161781556001805461ffff861690811061096c5761096c612148565b5f91825260209091200180549091506001600160401b03908116908816116109a7576040516306635bcb60e51b815260040160405180910390fd5b42876001600160401b031611156109d1576040516307987ab960e31b815260040160405180910390fd5b80545f906109e8906001600160401b031689612128565b6001600160401b031690507f00000000000000000000000000000000000000000000000000000000000151806001600160401b03168110610a2857600196505b5f610a33828861215c565b8354610a4f9190600160401b90046001600160c01b0316612173565b90506001600160c01b03811115610a7c576040516001626ee14160e01b0319815260040160405180910390fd5b6001600160401b0389166001600160c01b038216600160401b0267ffffffffffffffff19161783555f6001610ab188886115ff565b61ffff1681548110610ac557610ac5612148565b5f91825260209091200154610ae3906001600160401b03168b612128565b6001600160401b031690507f00000000000000000000000000000000000000000000000000000000000151806001600160401b03168110610b9a575f610b29888861162c565b5f805461ffff60d81b1916600160d81b61ffff841690810291909117825560018054939b508b945091928110610b6157610b61612148565b5f91825260209091206001600160401b038e166001600160c01b038716600160401b0267ffffffffffffffff1916179101555060019950505b88610bb8576040516309f9f55d60e01b815260040160405180910390fd5b5f80610bce8d6001600160d81b03168a8a611651565b9150915080158015610bee5750610bee8d6001600160d81b0316836114b7565b15610c0457505050505050505050505050505050565b604080514281526001600160401b038e1660208201526001600160d81b038f168183015260608101849052821515608082015290517f8c2ecabee4ec920ce555679e0efd8aa525aee64dc39f4a13a093f9fc72893fdf9181900360a00190a1505050505050505050505050505050565b5f60605f610c806118ad565b5f549091506001600160d81b0381169061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b9091041680610e4a575f60018461ffff1681548110610cd057610cd0612148565b5f91825260209091200154610cee906001600160401b031642612186565b90505f6001610cfd86866115ff565b61ffff1681548110610d1157610d11612148565b5f91825260209091200154610d2f906001600160401b031642612186565b90507f00000000000000000000000000000000000000000000000000000000000151806001600160401b03811683101580610d735750806001600160401b03168210155b80610dc75750610dbb610da87f0000000000000000000000000000000000000000000000000000000000000032612710612101565b88906001600160401b03166127106115e1565b886001600160d81b0316115b80610e085750610dfc610da87f0000000000000000000000000000000000000000000000000000000000000032612710612128565b886001600160d81b0316105b15610e4657604080516001600160d81b038a166020820152426001600160401b031681830152815180820383018152606090910190915260019a5098505b5050505b50505050509250929050565b6002546001600160a01b0316151580610e70575060035415155b15610e8e5760405163268763a360e21b815260040160405180910390fd5b610ecc6001600160a01b037f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca1633306001600160601b038516611a46565b5f7f0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a90505f7f0000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd90505f6040518061014001604052807f000000000000000000000000ea1a6307d9b18f8d1cbf1c3dd6aad8416c06a2216001600160a01b03166306fdde036040518163ffffffff1660e01b81526004015f60405180830381865afa158015610f7c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610fa39190810190612199565b604051602001610fb391906121dd565b60408051808303601f190181529181529082528051602080820183525f80835281850192909252308484015261c35060608501526001600160a01b037f0000000000000000000000002322ba43eff1542b6a7baed35e66099ea0d12bd18116608086015260a085018390528351808301855283815260c08601528351808301855283815260e086015283519182019093529081526101008301526001600160601b0387166101209092018290529192506110b1917f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca16907f0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a90611aca565b604051633f678e1160e01b81525f906001600160a01b03851690633f678e11906110df908590600401612213565b6020604051808303815f875af11580156110fb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111f9190612327565b905080156111f1576040516379ea994360e01b8152600481018290525f906001600160a01b038516906379ea994390602401602060405180830381865afa15801561116c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111909190612092565b600280546001600160a01b0319166001600160a01b0383169081179091556040805185815260208101929092529192507fb2b260d8dc5d3268f11a016e359df280f36fa8e36b1550c67cd956bed4ae775d910160405180910390a150611271565b5f8260400151836060015184608001518560a001518660c0015187610100015160405160200161122696959493929190612033565b60408051601f19818403018152908290528051602091820120600381905580835292507f87b8902f760f4a16ca050d02d4ae2fe155fba5e58ed9b48a5d832d73796a3f2a91016107a6565b5050505050565b5f805481906001600160d81b03811690600160f81b900460ff1680156112a457505f9360019350915050565b6112ac611b43565b156112bd57505f9360019350915050565b5f8054600180549091600160d81b900461ffff169081106112e0576112e0612148565b5f918252602090912001546112fe906001600160401b031642612186565b905061134a7f00000000000000000000000000000000000000000000000000000000000070807f0000000000000000000000000000000000000000000000000000000000015180612101565b6001600160401b031681111561136757505f946001945092505050565b5090935f9350915050565b5f80546001600160d81b0381169190819061ffff600160d81b8204811691600160e81b81049091169060ff600160f81b9091041680156113bc57505f958695506001945092505050565b6113c4611b43565b156113d957505f958695506001945092505050565b5f8054600180549091600160d81b900461ffff169081106113fc576113fc612148565b5f9182526020909120015461141a906001600160401b031642612186565b90506114667f00000000000000000000000000000000000000000000000000000000000070807f0000000000000000000000000000000000000000000000000000000000015180612101565b6001600160401b031681111561148757505f96879650600195509350505050565b611492878585611651565b909650945084156114ae57505f96879650600195509350505050565b50505050909192565b5f6114e5827f0000000000000000000000000000000000000000000000000000000000001d4c6127106115e1565b83108061151d575061151a827f00000000000000000000000000000000000000000000000000000000000030d46127106115e1565b83115b156115d8575f80546001600160f81b0316600160f81b1790557f472eb7b5f33f38b3f139fef7fc88820178d1cf9d0abffee988953791a84d66da83611585847f0000000000000000000000000000000000000000000000000000000000001d4c6127106115e1565b6115b2857f00000000000000000000000000000000000000000000000000000000000030d46127106115e1565b6040805193845260208401929092529082015260600160405180910390a15060016115db565b505f5b92915050565b8282028115158415858304851417166115f8575f80fd5b0492915050565b5f61ffff83161561161a5761161560018461233e565b611625565b61162560018361233e565b9392505050565b5f61163860018361233e565b61ffff168361ffff16146115d857611615836001612359565b5f805f600161166086866115ff565b61ffff168154811061167457611674612148565b5f9182526020808320604080518082019091529201546001600160401b0381168352600160401b90046001600160c01b031690820152915060016116b8878761162c565b61ffff16815481106116cc576116cc612148565b5f918252602091829020604080518082019091529101546001600160401b038116808352600160401b9091046001600160c01b031692820192909252915060010361171f575f60019350935050506118a5565b805182515f9161172e91612128565b6001600160401b031690505f61174560028861233e565b6117739061ffff167f0000000000000000000000000000000000000000000000000000000000015180612374565b6001600160401b031690505f7f00000000000000000000000000000000000000000000000000000000000070806001600160401b0316826117b49190612173565b9050818310156117cf575f60019650965050505050506118a5565b808311156117e8575f60019650965050505050506118a5565b5f60018a61ffff168154811061180057611800612148565b5f9182526020808320604080518082019091529201546001600160401b038116808452600160401b9091046001600160c01b0316918301919091529092506118489042612186565b611852908d61215c565b8260200151611861919061239f565b8651909150611879906001600160401b031642612186565b602087015161188890836123bf565b6001600160c01b031661189b91906123df565b9850505050505050505b935093915050565b5f807f000000000000000000000000ea1a6307d9b18f8d1cbf1c3dd6aad8416c06a2216001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561190b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061192f9190612327565b90505f6119df7f000000000000000000000000000000000000000000000000000000000000001260127f000000000000000000000000ea1a6307d9b18f8d1cbf1c3dd6aad8416c06a2216001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119b4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119d89190612327565b9190611c5d565b9050815f036119f0575f9250505090565b5f611a1c7f0000000000000000000000000000000000000000000000000de0b6b3a764000083856115e1565b90506001600160d81b0381111561162557604051637c5c0ad360e01b815260040160405180910390fd5b5f6040516323b872dd60e01b815284600482015283602482015282604482015260205f6064835f8a5af13d15601f3d1160015f5114161716915050806112715760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064015b60405180910390fd5b5f60405163095ea7b360e01b815283600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080611b3d5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401611ac1565b50505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615611c58575f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611bd1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bf59190612417565b5050925092505081600103611c0d5760019250505090565b5f611c188242612186565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160401b03168111611c54576001935050505090565b5050505b505f90565b5f8160ff168360ff1603611c72575082611625565b8160ff168360ff161015611ca657611c8a8383612463565b611c9590600a61255c565b611c9f908561215c565b9050611625565b611cb08284612463565b611cbb90600a61255c565b611c9f90856123df565b5f60208284031215611cd5575f80fd5b5035919050565b5f8060208385031215611ced575f80fd5b82356001600160401b0380821115611d03575f80fd5b818501915085601f830112611d16575f80fd5b813581811115611d24575f80fd5b866020828501011115611d35575f80fd5b60209290920196919550909350505050565b5f5b83811015611d61578181015183820152602001611d49565b50505f910152565b5f8151808452611d80816020860160208601611d47565b601f01601f19169290920160200192915050565b8215158152604060208201525f611dae6040830184611d69565b949350505050565b6001600160601b0381168114611dca575f80fd5b50565b5f60208284031215611ddd575f80fd5b813561162581611db6565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b0381118282101715611e1f57611e1f611de8565b60405290565b80516001600160a01b0381168114611e3b575f80fd5b919050565b805163ffffffff81168114611e3b575f80fd5b5f6001600160401b0380841115611e6c57611e6c611de8565b604051601f8501601f19908116603f01168101908282118183101715611e9457611e94611de8565b81604052809350858152868686011115611eac575f80fd5b611eba866020830187611d47565b5050509392505050565b5f82601f830112611ed3575f80fd5b61162583835160208501611e53565b8051611e3b81611db6565b6001600160401b0381168114611dca575f80fd5b8051611e3b81611eed565b80518015158114611e3b575f80fd5b5f60208284031215611f2b575f80fd5b81516001600160401b0380821115611f41575f80fd5b908301906101408286031215611f55575f80fd5b611f5d611dfc565b611f6683611e25565b8152611f7460208401611e40565b6020820152604083015182811115611f8a575f80fd5b611f9687828601611ec4565b604083015250611fa860608401611ee2565b6060820152611fb960808401611e25565b6080820152611fca60a08401611f01565b60a0820152611fdb60c08401611e40565b60c0820152611fec60e08401611ee2565b60e0820152610100611fff818501611f0c565b908201526101208381015183811115612016575f80fd5b61202288828701611ec4565b918301919091525095945050505050565b6001600160a01b03878116825263ffffffff871660208301528516604082015260ff8416606082015260c0608082018190525f9061207390830185611d69565b82810360a08401526120858185611d69565b9998505050505050505050565b5f602082840312156120a2575f80fd5b61162582611e25565b5f80604083850312156120bc575f80fd5b82356001600160d81b03811681146120d2575f80fd5b915060208301356120e281611eed565b809150509250929050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b03818116838216019080821115612121576121216120ed565b5092915050565b6001600160401b03828116828216039080821115612121576121216120ed565b634e487b7160e01b5f52603260045260245ffd5b80820281158282048414176115db576115db6120ed565b808201808211156115db576115db6120ed565b818103818111156115db576115db6120ed565b5f602082840312156121a9575f80fd5b81516001600160401b038111156121be575f80fd5b8201601f810184136121ce575f80fd5b611dae84825160208401611e53565b5f82516121ee818460208701611d47565b72205368617265205072696365204f7261636c6560681b920191825250601301919050565b602081525f8251610140806020850152612231610160850183611d69565b91506020850151601f198086850301604087015261224f8483611d69565b93506040870151915061226d60608701836001600160a01b03169052565b606087015163ffffffff81166080880152915060808701516001600160a01b03811660a0880152915060a087015160ff811660c0880152915060c08701519150808685030160e08701526122c18483611d69565b935060e087015191506101008187860301818801526122e08584611d69565b9450808801519250506101208187860301818801526122ff8584611d69565b9450808801519250505061231d828601826001600160601b03169052565b5090949350505050565b5f60208284031215612337575f80fd5b5051919050565b61ffff828116828216039080821115612121576121216120ed565b61ffff818116838216019080821115612121576121216120ed565b6001600160401b03818116838216028082169190828114612397576123976120ed565b505092915050565b6001600160c01b03818116838216019080821115612121576121216120ed565b6001600160c01b03828116828216039080821115612121576121216120ed565b5f826123f957634e487b7160e01b5f52601260045260245ffd5b500490565b805169ffffffffffffffffffff81168114611e3b575f80fd5b5f805f805f60a0868803121561242b575f80fd5b612434866123fe565b9450602086015193506040860151925060608601519150612457608087016123fe565b90509295509295909350565b60ff82811682821603908111156115db576115db6120ed565b600181815b808511156124b657815f190482111561249c5761249c6120ed565b808516156124a957918102915b93841c9390800290612481565b509250929050565b5f826124cc575060016115db565b816124d857505f6115db565b81600181146124ee57600281146124f857612514565b60019150506115db565b60ff841115612509576125096120ed565b50506001821b6115db565b5060208310610133831016604e8410600b8410161715612537575081810a6115db565b612541838361247c565b805f1904821115612554576125546120ed565b029392505050565b5f61162560ff8416836124be56fea26469706673582212200b927b53419b5df309dbfd07bc47719d7f4ad39012483a8886ddb3ecdce6b5cf64736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ea1a6307d9b18f8d1cbf1c3dd6aad8416c06a22100000000000000000000000000000000000000000000000000000000000151800000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000000060000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a0000000000000000000000002322ba43eff1542b6a7baed35e66099ea0d12bd1000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000001d4c00000000000000000000000000000000000000000000000000000000000030d400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : args (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
14 Constructor Arguments found :
Arg [0] : 000000000000000000000000ea1a6307d9b18f8d1cbf1c3dd6aad8416c06a221
Arg [1] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [3] : 0000000000000000000000000000000000000000000000000000000000007080
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [5] : 0000000000000000000000006593c7de001fc8542bb1703532ee1e5aa0d458fd
Arg [6] : 0000000000000000000000006b0b234fb2f380309d47a7e9391e29e9a179395a
Arg [7] : 0000000000000000000000002322ba43eff1542b6a7baed35e66099ea0d12bd1
Arg [8] : 000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca
Arg [9] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000001d4c
Arg [11] : 00000000000000000000000000000000000000000000000000000000000030d4
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ 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.