Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Initialize | 19416678 | 732 days ago | IN | 0 ETH | 0.02047905 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
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
Contract ABI
API[{"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):
Arg [1] : _target (address): 0xeA1A6307D9b18F8d1cbf1c3Dd6aad8416C06a221
Arg [2] : _heartbeat (uint64): 86400
Arg [3] : _deviationTrigger (uint64): 50
Arg [4] : _gracePeriod (uint64): 28800
Arg [5] : _observationsToUse (uint16): 6
Arg [6] : _automationRegistry (address): 0x6593c7De001fC8542bB1703532EE1E5aA0D458fD
Arg [7] : _automationRegistrar (address): 0x6B0B234fB2f380309D47A7E9391E29E9a179395a
Arg [8] : _automationAdmin (address): 0x2322ba43eFF1542b6A7bAeD35e66099Ea0d12Bd1
Arg [9] : _link (address): 0x514910771AF9Ca656af840dff83E8264EcF986CA
Arg [10] : _startingAnswer (uint216): 1000000000000000000
Arg [11] : _allowedAnswerChangeLower (uint256): 7500
Arg [12] : _allowedAnswerChangeUpper (uint256): 12500
Arg [13] : _sequencerUptimeFeed (address): 0x0000000000000000000000000000000000000000
Arg [14] : _sequencerGracePeriod (uint64): 0
-----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
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.