Source Code
Overview
S Balance
0 S
Token Holdings
More Info
ContractCreator
Latest 2 internal transactions
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
4374972 | 6 days ago | Contract Creation | 0 S | |||
4374972 | 6 days ago | Contract Creation | 0 S |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
Governance
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IERC20} from "openzeppelin/contracts/interfaces/IERC20.sol"; import {SafeERC20} from "openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ReentrancyGuard} from "openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {IGovernance} from "./interfaces/IGovernance.sol"; import {IInitiative} from "./interfaces/IInitiative.sol"; import {ILQTYStaking} from "./interfaces/ILQTYStaking.sol"; import {UserProxy} from "./UserProxy.sol"; import {UserProxyFactory} from "./UserProxyFactory.sol"; import {add, max} from "./utils/Math.sol"; import {_requireNoDuplicates, _requireNoNegatives} from "./utils/UniqueArray.sol"; import {Multicall} from "./utils/Multicall.sol"; import {WAD, PermitParams} from "./utils/Types.sol"; import {safeCallWithMinGas} from "./utils/SafeCallMinGas.sol"; import {Ownable} from "./utils/Ownable.sol"; /// @title Governance: Modular Initiative based Governance contract Governance is Multicall, UserProxyFactory, ReentrancyGuard, Ownable, IGovernance { using SafeERC20 for IERC20; uint256 constant MIN_GAS_TO_HOOK = 350_000; /// Replace this to ensure hooks have sufficient gas /// @inheritdoc IGovernance ILQTYStaking public immutable stakingV1; /// @inheritdoc IGovernance IERC20 public immutable lqty; /// @inheritdoc IGovernance IERC20 public immutable bold; /// @inheritdoc IGovernance uint256 public immutable EPOCH_START; /// @inheritdoc IGovernance uint256 public immutable EPOCH_DURATION; /// @inheritdoc IGovernance uint256 public immutable EPOCH_VOTING_CUTOFF; /// @inheritdoc IGovernance uint256 public immutable MIN_CLAIM; /// @inheritdoc IGovernance uint256 public immutable MIN_ACCRUAL; /// @inheritdoc IGovernance uint256 public immutable REGISTRATION_FEE; /// @inheritdoc IGovernance uint256 public immutable REGISTRATION_THRESHOLD_FACTOR; /// @inheritdoc IGovernance uint256 public immutable UNREGISTRATION_THRESHOLD_FACTOR; /// @inheritdoc IGovernance uint256 public immutable REGISTRATION_WARM_UP_PERIOD; /// @inheritdoc IGovernance uint256 public immutable UNREGISTRATION_AFTER_EPOCHS; /// @inheritdoc IGovernance uint256 public immutable VOTING_THRESHOLD_FACTOR; /// @inheritdoc IGovernance uint256 public boldAccrued; /// @inheritdoc IGovernance VoteSnapshot public votesSnapshot; /// @inheritdoc IGovernance mapping(address => InitiativeVoteSnapshot) public votesForInitiativeSnapshot; /// @inheritdoc IGovernance GlobalState public globalState; /// @inheritdoc IGovernance mapping(address => UserState) public userStates; /// @inheritdoc IGovernance mapping(address => InitiativeState) public initiativeStates; /// @inheritdoc IGovernance mapping(address => mapping(address => Allocation)) public lqtyAllocatedByUserToInitiative; /// @inheritdoc IGovernance mapping(address => uint16) public override registeredInitiatives; uint16 constant UNREGISTERED_INITIATIVE = type(uint16).max; // 100 Million LQTY will be necessary to make the rounding error cause 1 second of loss per operation uint120 public constant TIMESTAMP_PRECISION = 1e26; constructor( address _lqty, address _lusd, address _stakingV1, address _bold, Configuration memory _config, address _owner, address[] memory _initiatives ) UserProxyFactory(_lqty, _lusd, _stakingV1) Ownable(_owner) { stakingV1 = ILQTYStaking(_stakingV1); lqty = IERC20(_lqty); bold = IERC20(_bold); require(_config.minClaim <= _config.minAccrual, "Gov: min-claim-gt-min-accrual"); REGISTRATION_FEE = _config.registrationFee; // Registration threshold must be below 100% of votes require(_config.registrationThresholdFactor < WAD, "Gov: registration-config"); REGISTRATION_THRESHOLD_FACTOR = _config.registrationThresholdFactor; // Unregistration must be X times above the `votingThreshold` require(_config.unregistrationThresholdFactor > WAD, "Gov: unregistration-config"); UNREGISTRATION_THRESHOLD_FACTOR = _config.unregistrationThresholdFactor; REGISTRATION_WARM_UP_PERIOD = _config.registrationWarmUpPeriod; UNREGISTRATION_AFTER_EPOCHS = _config.unregistrationAfterEpochs; // Voting threshold must be below 100% of votes require(_config.votingThresholdFactor < WAD, "Gov: voting-config"); VOTING_THRESHOLD_FACTOR = _config.votingThresholdFactor; MIN_CLAIM = _config.minClaim; MIN_ACCRUAL = _config.minAccrual; EPOCH_START = _config.epochStart; require(_config.epochDuration > 0, "Gov: epoch-duration-zero"); EPOCH_DURATION = _config.epochDuration; require(_config.epochVotingCutoff < _config.epochDuration, "Gov: epoch-voting-cutoff-gt-epoch-duration"); EPOCH_VOTING_CUTOFF = _config.epochVotingCutoff; if (_initiatives.length > 0) { registerInitialInitiatives(_initiatives); } } function registerInitialInitiatives(address[] memory _initiatives) public onlyOwner { for (uint256 i = 0; i < _initiatives.length; i++) { // Register initial initiatives in the earliest possible epoch, which lets us make them votable immediately // post-deployment if we so choose, by backdating the first epoch at least EPOCH_DURATION in the past. registeredInitiatives[_initiatives[i]] = 1; emit RegisterInitiative(_initiatives[i], msg.sender, 1); } _renounceOwnership(); } function _averageAge(uint120 _currentTimestamp, uint120 _averageTimestamp) internal pure returns (uint120) { if (_averageTimestamp == 0 || _currentTimestamp < _averageTimestamp) return 0; return _currentTimestamp - _averageTimestamp; } function _calculateAverageTimestamp( uint120 _prevOuterAverageTimestamp, uint120 _newInnerAverageTimestamp, uint88 _prevLQTYBalance, uint88 _newLQTYBalance ) internal view returns (uint120) { if (_newLQTYBalance == 0) return 0; // NOTE: Truncation // NOTE: u32 -> u120 // While we upscale the Timestamp, the system will stop working at type(uint32).max // Because the rest of the type is used for precision uint120 currentTime = uint120(uint32(block.timestamp)) * uint120(TIMESTAMP_PRECISION); uint120 prevOuterAverageAge = _averageAge(currentTime, _prevOuterAverageTimestamp); uint120 newInnerAverageAge = _averageAge(currentTime, _newInnerAverageTimestamp); // 120 for timestamps = 2^32 * 1e18 | 2^32 * 1e26 // 208 for voting power = 2^120 * 2^88 // NOTE: 208 / X can go past u120! // Therefore we keep `newOuterAverageAge` as u208 uint208 newOuterAverageAge; if (_prevLQTYBalance <= _newLQTYBalance) { uint88 deltaLQTY = _newLQTYBalance - _prevLQTYBalance; uint208 prevVotes = uint208(_prevLQTYBalance) * uint208(prevOuterAverageAge); uint208 newVotes = uint208(deltaLQTY) * uint208(newInnerAverageAge); uint208 votes = prevVotes + newVotes; newOuterAverageAge = votes / _newLQTYBalance; } else { uint88 deltaLQTY = _prevLQTYBalance - _newLQTYBalance; uint208 prevVotes = uint208(_prevLQTYBalance) * uint208(prevOuterAverageAge); uint208 newVotes = uint208(deltaLQTY) * uint208(newInnerAverageAge); uint208 votes = (prevVotes >= newVotes) ? prevVotes - newVotes : 0; newOuterAverageAge = votes / _newLQTYBalance; } if (newOuterAverageAge > currentTime) return 0; return uint120(currentTime - newOuterAverageAge); } /*////////////////////////////////////////////////////////////// STAKING //////////////////////////////////////////////////////////////*/ function _updateUserTimestamp(uint88 _lqtyAmount) private returns (UserProxy) { require(_lqtyAmount > 0, "Governance: zero-lqty-amount"); // Assert that we have resetted here UserState memory userState = userStates[msg.sender]; require(userState.allocatedLQTY == 0, "Governance: must-be-zero-allocation"); address userProxyAddress = deriveUserProxyAddress(msg.sender); if (userProxyAddress.code.length == 0) { deployUserProxy(); } UserProxy userProxy = UserProxy(payable(userProxyAddress)); uint88 lqtyStaked = uint88(stakingV1.stakes(userProxyAddress)); // update the average staked timestamp for LQTY staked by the user // NOTE: Upscale user TS by `TIMESTAMP_PRECISION` userState.averageStakingTimestamp = _calculateAverageTimestamp( userState.averageStakingTimestamp, uint120(block.timestamp) * uint120(TIMESTAMP_PRECISION), lqtyStaked, lqtyStaked + _lqtyAmount ); userStates[msg.sender] = userState; emit DepositLQTY(msg.sender, _lqtyAmount); return userProxy; } /// @inheritdoc IGovernance function depositLQTY(uint88 _lqtyAmount) external { depositLQTY(_lqtyAmount, false, msg.sender); } function depositLQTY(uint88 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant { UserProxy userProxy = _updateUserTimestamp(_lqtyAmount); userProxy.stake(_lqtyAmount, msg.sender, _doSendRewards, _recipient); } /// @inheritdoc IGovernance function depositLQTYViaPermit(uint88 _lqtyAmount, PermitParams calldata _permitParams) external { depositLQTYViaPermit(_lqtyAmount, _permitParams, false, msg.sender); } function depositLQTYViaPermit( uint88 _lqtyAmount, PermitParams calldata _permitParams, bool _doSendRewards, address _recipient ) public nonReentrant { UserProxy userProxy = _updateUserTimestamp(_lqtyAmount); userProxy.stakeViaPermit(_lqtyAmount, msg.sender, _permitParams, _doSendRewards, _recipient); } /// @inheritdoc IGovernance function withdrawLQTY(uint88 _lqtyAmount) external { withdrawLQTY(_lqtyAmount, true, msg.sender); } function withdrawLQTY(uint88 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant { // check that user has reset before changing lqty balance UserState storage userState = userStates[msg.sender]; require(userState.allocatedLQTY == 0, "Governance: must-allocate-zero"); UserProxy userProxy = UserProxy(payable(deriveUserProxyAddress(msg.sender))); require(address(userProxy).code.length != 0, "Governance: user-proxy-not-deployed"); uint88 lqtyStaked = uint88(stakingV1.stakes(address(userProxy))); (uint256 accruedLUSD, uint256 accruedETH) = userProxy.unstake(_lqtyAmount, _doSendRewards, _recipient); emit WithdrawLQTY(msg.sender, _lqtyAmount, accruedLUSD, accruedETH); } /// @inheritdoc IGovernance function claimFromStakingV1(address _rewardRecipient) external returns (uint256 accruedLUSD, uint256 accruedETH) { address payable userProxyAddress = payable(deriveUserProxyAddress(msg.sender)); require(userProxyAddress.code.length != 0, "Governance: user-proxy-not-deployed"); return UserProxy(userProxyAddress).unstake(0, true, _rewardRecipient); } /*////////////////////////////////////////////////////////////// VOTING //////////////////////////////////////////////////////////////*/ /// @inheritdoc IGovernance function epoch() public view returns (uint16) { if (block.timestamp < EPOCH_START) { return 0; } return uint16(((block.timestamp - EPOCH_START) / EPOCH_DURATION) + 1); } /// @inheritdoc IGovernance function epochStart() public view returns (uint32) { uint16 currentEpoch = epoch(); if (currentEpoch == 0) return 0; return uint32(EPOCH_START + (currentEpoch - 1) * EPOCH_DURATION); } /// @inheritdoc IGovernance function secondsWithinEpoch() public view returns (uint32) { if (block.timestamp < EPOCH_START) return 0; return uint32((block.timestamp - EPOCH_START) % EPOCH_DURATION); } /// @inheritdoc IGovernance function lqtyToVotes(uint88 _lqtyAmount, uint120 _currentTimestamp, uint120 _averageTimestamp) public pure returns (uint208) { return uint208(_lqtyAmount) * uint208(_averageAge(_currentTimestamp, _averageTimestamp)); } /*////////////////////////////////////////////////////////////// SNAPSHOTS //////////////////////////////////////////////////////////////*/ /// @inheritdoc IGovernance function getLatestVotingThreshold() public view returns (uint256) { uint256 snapshotVotes = votesSnapshot.votes; /// @audit technically can be out of synch return calculateVotingThreshold(snapshotVotes); } /// @dev Returns the most up to date voting threshold /// In contrast to `getLatestVotingThreshold` this function updates the snapshot /// This ensures that the value returned is always the latest function calculateVotingThreshold() public returns (uint256) { (VoteSnapshot memory snapshot,) = _snapshotVotes(); return calculateVotingThreshold(snapshot.votes); } /// @dev Utility function to compute the threshold votes without recomputing the snapshot /// Note that `boldAccrued` is a cached value, this function works correctly only when called after an accrual function calculateVotingThreshold(uint256 _votes) public view returns (uint256) { if (_votes == 0) return 0; uint256 minVotes; // to reach MIN_CLAIM: snapshotVotes * MIN_CLAIM / boldAccrued uint256 payoutPerVote = boldAccrued * WAD / _votes; if (payoutPerVote != 0) { minVotes = MIN_CLAIM * WAD / payoutPerVote; } return max(_votes * VOTING_THRESHOLD_FACTOR / WAD, minVotes); } // Snapshots votes at the end of the previous epoch // Accrues funds until the first activity of the current epoch, which are valid throughout all of the current epoch function _snapshotVotes() internal returns (VoteSnapshot memory snapshot, GlobalState memory state) { bool shouldUpdate; (snapshot, state, shouldUpdate) = getTotalVotesAndState(); if (shouldUpdate) { votesSnapshot = snapshot; uint256 boldBalance = bold.balanceOf(address(this)); boldAccrued = (boldBalance < MIN_ACCRUAL) ? 0 : boldBalance; emit SnapshotVotes(snapshot.votes, snapshot.forEpoch); } } /// @notice Return the most up to date global snapshot and state as well as a flag to notify whether the state can be updated /// This is a convenience function to always retrieve the most up to date state values function getTotalVotesAndState() public view returns (VoteSnapshot memory snapshot, GlobalState memory state, bool shouldUpdate) { uint16 currentEpoch = epoch(); snapshot = votesSnapshot; state = globalState; if (snapshot.forEpoch < currentEpoch - 1) { shouldUpdate = true; snapshot.votes = lqtyToVotes( state.countedVoteLQTY, uint120(epochStart()) * uint120(TIMESTAMP_PRECISION), state.countedVoteLQTYAverageTimestamp ); snapshot.forEpoch = currentEpoch - 1; } } // Snapshots votes for an initiative for the previous epoch but only count the votes // if the received votes meet the voting threshold function _snapshotVotesForInitiative(address _initiative) internal returns (InitiativeVoteSnapshot memory initiativeSnapshot, InitiativeState memory initiativeState) { bool shouldUpdate; (initiativeSnapshot, initiativeState, shouldUpdate) = getInitiativeSnapshotAndState(_initiative); if (shouldUpdate) { votesForInitiativeSnapshot[_initiative] = initiativeSnapshot; emit SnapshotVotesForInitiative(_initiative, initiativeSnapshot.votes, initiativeSnapshot.forEpoch); } } /// @dev Given an initiative address, return it's most up to date snapshot and state as well as a flag to notify whether the state can be updated /// This is a convenience function to always retrieve the most up to date state values function getInitiativeSnapshotAndState(address _initiative) public view returns ( InitiativeVoteSnapshot memory initiativeSnapshot, InitiativeState memory initiativeState, bool shouldUpdate ) { // Get the storage data uint16 currentEpoch = epoch(); initiativeSnapshot = votesForInitiativeSnapshot[_initiative]; initiativeState = initiativeStates[_initiative]; if (initiativeSnapshot.forEpoch < currentEpoch - 1) { shouldUpdate = true; uint120 start = uint120(epochStart()) * uint120(TIMESTAMP_PRECISION); uint208 votes = lqtyToVotes(initiativeState.voteLQTY, start, initiativeState.averageStakingTimestampVoteLQTY); uint208 vetos = lqtyToVotes(initiativeState.vetoLQTY, start, initiativeState.averageStakingTimestampVetoLQTY); // NOTE: Upscaling to u224 is safe initiativeSnapshot.votes = votes; initiativeSnapshot.vetos = vetos; initiativeSnapshot.forEpoch = currentEpoch - 1; } } /// @inheritdoc IGovernance function snapshotVotesForInitiative(address _initiative) external nonReentrant returns (VoteSnapshot memory voteSnapshot, InitiativeVoteSnapshot memory initiativeVoteSnapshot) { (voteSnapshot,) = _snapshotVotes(); (initiativeVoteSnapshot,) = _snapshotVotesForInitiative(_initiative); } /*////////////////////////////////////////////////////////////// FSM //////////////////////////////////////////////////////////////*/ enum InitiativeStatus { NONEXISTENT, /// This Initiative Doesn't exist | This is never returned WARM_UP, /// This epoch was just registered SKIP, /// This epoch will result in no rewards and no unregistering CLAIMABLE, /// This epoch will result in claiming rewards CLAIMED, /// The rewards for this epoch have been claimed UNREGISTERABLE, /// Can be unregistered DISABLED // It was already Unregistered } /// @notice Given an inititive address, updates all snapshots and return the initiative state /// See the view version of `getInitiativeState` for the underlying logic on Initatives FSM function getInitiativeState(address _initiative) public returns (InitiativeStatus status, uint16 lastEpochClaim, uint256 claimableAmount) { (VoteSnapshot memory votesSnapshot_,) = _snapshotVotes(); (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) = _snapshotVotesForInitiative(_initiative); return getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState); } /// @dev Given an initiative address and its snapshot, determines the current state for an initiative function getInitiativeState( address _initiative, VoteSnapshot memory _votesSnapshot, InitiativeVoteSnapshot memory _votesForInitiativeSnapshot, InitiativeState memory _initiativeState ) public view returns (InitiativeStatus status, uint16 lastEpochClaim, uint256 claimableAmount) { // == Non existent Condition == // if (registeredInitiatives[_initiative] == 0) { return (InitiativeStatus.NONEXISTENT, 0, 0); /// By definition it has zero rewards } // == Just Registered Condition == // if (registeredInitiatives[_initiative] == epoch()) { return (InitiativeStatus.WARM_UP, 0, 0); /// Was registered this week, cannot have rewards } // Fetch last epoch at which we claimed lastEpochClaim = initiativeStates[_initiative].lastEpochClaim; // == Disabled Condition == // if (registeredInitiatives[_initiative] == UNREGISTERED_INITIATIVE) { return (InitiativeStatus.DISABLED, lastEpochClaim, 0); /// By definition it has zero rewards } // == Already Claimed Condition == // if (lastEpochClaim >= epoch() - 1) { // early return, we have already claimed return (InitiativeStatus.CLAIMED, lastEpochClaim, claimableAmount); } // NOTE: Pass the snapshot value so we get accurate result uint256 votingTheshold = calculateVotingThreshold(_votesSnapshot.votes); // If it's voted and can get rewards // Votes > calculateVotingThreshold // == Rewards Conditions (votes can be zero, logic is the same) == // // By definition if _votesForInitiativeSnapshot.votes > 0 then _votesSnapshot.votes > 0 uint256 upscaledInitiativeVotes = uint256(_votesForInitiativeSnapshot.votes); uint256 upscaledInitiativeVetos = uint256(_votesForInitiativeSnapshot.vetos); uint256 upscaledTotalVotes = uint256(_votesSnapshot.votes); if (upscaledInitiativeVotes > votingTheshold && !(upscaledInitiativeVetos >= upscaledInitiativeVotes)) { /// @audit 2^208 means we only have 2^48 left /// Therefore we need to scale the value down by 4 orders of magnitude to make it fit assert(upscaledInitiativeVotes * 1e14 / (VOTING_THRESHOLD_FACTOR / 1e4) > upscaledTotalVotes); // 34 times when using 0.03e18 -> 33.3 + 1-> 33 + 1 = 34 uint256 CUSTOM_PRECISION = WAD / VOTING_THRESHOLD_FACTOR + 1; /// @audit Because of the updated timestamp, we can run into overflows if we multiply by `boldAccrued` /// We use `CUSTOM_PRECISION` for this reason, a smaller multiplicative value /// The change SHOULD be safe because we already check for `threshold` before getting into these lines /// As an alternative, this line could be replaced by https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol uint256 claim = upscaledInitiativeVotes * CUSTOM_PRECISION / upscaledTotalVotes * boldAccrued / CUSTOM_PRECISION; return (InitiativeStatus.CLAIMABLE, lastEpochClaim, claim); } // == Unregister Condition == // // e.g. if `UNREGISTRATION_AFTER_EPOCHS` is 4, the 4th epoch flip that would result in SKIP, will result in the initiative being `UNREGISTERABLE` if ( (_initiativeState.lastEpochClaim + UNREGISTRATION_AFTER_EPOCHS < epoch() - 1) || upscaledInitiativeVetos > upscaledInitiativeVotes && upscaledInitiativeVetos > votingTheshold * UNREGISTRATION_THRESHOLD_FACTOR / WAD ) { return (InitiativeStatus.UNREGISTERABLE, lastEpochClaim, 0); } // == Not meeting threshold Condition == // return (InitiativeStatus.SKIP, lastEpochClaim, 0); } /// @inheritdoc IGovernance function registerInitiative(address _initiative) external nonReentrant { bold.safeTransferFrom(msg.sender, address(this), REGISTRATION_FEE); require(_initiative != address(0), "Governance: zero-address"); (InitiativeStatus status,,) = getInitiativeState(_initiative); require(status == InitiativeStatus.NONEXISTENT, "Governance: initiative-already-registered"); address userProxyAddress = deriveUserProxyAddress(msg.sender); (VoteSnapshot memory snapshot,) = _snapshotVotes(); UserState memory userState = userStates[msg.sender]; // an initiative can be registered if the registrant has more voting power (LQTY * age) // than the registration threshold derived from the previous epoch's total global votes uint256 upscaledSnapshotVotes = uint256(snapshot.votes); require( lqtyToVotes( uint88(stakingV1.stakes(userProxyAddress)), uint120(epochStart()) * uint120(TIMESTAMP_PRECISION), userState.averageStakingTimestamp ) >= upscaledSnapshotVotes * REGISTRATION_THRESHOLD_FACTOR / WAD, "Governance: insufficient-lqty" ); uint16 currentEpoch = epoch(); registeredInitiatives[_initiative] = currentEpoch; /// @audit This ensures that the initiatives has UNREGISTRATION_AFTER_EPOCHS even after the first epoch initiativeStates[_initiative].lastEpochClaim = currentEpoch - 1; emit RegisterInitiative(_initiative, msg.sender, currentEpoch); // Replaces try / catch | Enforces sufficient gas is passed safeCallWithMinGas( _initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onRegisterInitiative, (currentEpoch)) ); } struct ResetInitiativeData { address initiative; int88 LQTYVotes; int88 LQTYVetos; } /// @dev Resets an initiative and return the previous votes /// NOTE: Technically we don't need vetos /// NOTE: Technically we want to populate the `ResetInitiativeData` only when `secondsWithinEpoch() > EPOCH_VOTING_CUTOFF` function _resetInitiatives(address[] calldata _initiativesToReset) internal returns (ResetInitiativeData[] memory) { ResetInitiativeData[] memory cachedData = new ResetInitiativeData[](_initiativesToReset.length); int88[] memory deltaLQTYVotes = new int88[](_initiativesToReset.length); int88[] memory deltaLQTYVetos = new int88[](_initiativesToReset.length); // Prepare reset data for (uint256 i; i < _initiativesToReset.length; i++) { Allocation memory alloc = lqtyAllocatedByUserToInitiative[msg.sender][_initiativesToReset[i]]; require(alloc.voteLQTY > 0 || alloc.vetoLQTY > 0, "Governance: nothing to reset"); // Must be below, else we cannot reset" // Makes cast safe /// @audit Check INVARIANT: property_ensure_user_alloc_cannot_dos assert(alloc.voteLQTY <= uint88(type(int88).max)); assert(alloc.vetoLQTY <= uint88(type(int88).max)); // Cache, used to enforce limits later cachedData[i] = ResetInitiativeData({ initiative: _initiativesToReset[i], LQTYVotes: int88(alloc.voteLQTY), LQTYVetos: int88(alloc.vetoLQTY) }); // -0 is still 0, so its fine to flip both deltaLQTYVotes[i] = -int88(cachedData[i].LQTYVotes); deltaLQTYVetos[i] = -int88(cachedData[i].LQTYVetos); } // RESET HERE || All initiatives will receive most updated data and 0 votes / vetos _allocateLQTY(_initiativesToReset, deltaLQTYVotes, deltaLQTYVetos); return cachedData; } /// @notice Reset the allocations for the initiatives being passed, must pass all initiatives else it will revert /// NOTE: If you reset at the last day of the epoch, you won't be able to vote again /// Use `allocateLQTY` to reset and vote function resetAllocations(address[] calldata _initiativesToReset, bool checkAll) external nonReentrant { _requireNoDuplicates(_initiativesToReset); _resetInitiatives(_initiativesToReset); // NOTE: In most cases, the check will pass // But if you allocate too many initiatives, we may run OOG // As such the check is optional here // All other calls to the system enforce this // So it's recommended that your last call to `resetAllocations` passes the check if (checkAll) { require(userStates[msg.sender].allocatedLQTY == 0, "Governance: must be a reset"); } } /// @inheritdoc IGovernance function allocateLQTY( address[] calldata _initiativesToReset, address[] calldata _initiatives, int88[] calldata _absoluteLQTYVotes, int88[] calldata _absoluteLQTYVetos ) external nonReentrant { require(_initiatives.length == _absoluteLQTYVotes.length, "Length"); require(_absoluteLQTYVetos.length == _absoluteLQTYVotes.length, "Length"); // To ensure the change is safe, enforce uniqueness _requireNoDuplicates(_initiativesToReset); _requireNoDuplicates(_initiatives); // Explicit >= 0 checks for all values since we reset values below _requireNoNegatives(_absoluteLQTYVotes); _requireNoNegatives(_absoluteLQTYVetos); // If the goal is to remove all votes from an initiative, including in _initiativesToReset is enough _requireNoNOP(_absoluteLQTYVotes, _absoluteLQTYVetos); // You MUST always reset ResetInitiativeData[] memory cachedData = _resetInitiatives(_initiativesToReset); /// Invariant, 0 allocated = 0 votes UserState memory userState = userStates[msg.sender]; require(userState.allocatedLQTY == 0, "must be a reset"); // After cutoff you can only re-apply the same vote // Or vote less // Or abstain // You can always add a veto, hence we only validate the addition of Votes // And ignore the addition of vetos // Validate the data here to ensure that the voting is capped at the amount in the other case if (secondsWithinEpoch() > EPOCH_VOTING_CUTOFF) { // Cap the max votes to the previous cache value // This means that no new votes can happen here // Removing and VETOING is always accepted for (uint256 x; x < _initiatives.length; x++) { // If we find it, we ensure it cannot be an increase bool found; for (uint256 y; y < cachedData.length; y++) { if (cachedData[y].initiative == _initiatives[x]) { found = true; require(_absoluteLQTYVotes[x] <= cachedData[y].LQTYVotes, "Cannot increase"); break; } } // Else we assert that the change is a veto, because by definition the initiatives will have received zero votes past this line if (!found) { require(_absoluteLQTYVotes[x] == 0, "Must be zero for new initiatives"); } } } // Vote here, all values are now absolute changes _allocateLQTY(_initiatives, _absoluteLQTYVotes, _absoluteLQTYVetos); } /// @dev For each given initiative applies relative changes to the allocation /// NOTE: Given the current usage the function either: Resets the value to 0, or sets the value to a new value /// Review the flows as the function could be used in many ways, but it ends up being used in just those 2 ways function _allocateLQTY( address[] memory _initiatives, int88[] memory _deltaLQTYVotes, int88[] memory _deltaLQTYVetos ) internal { require( _initiatives.length == _deltaLQTYVotes.length && _initiatives.length == _deltaLQTYVetos.length, "Governance: array-length-mismatch" ); (VoteSnapshot memory votesSnapshot_, GlobalState memory state) = _snapshotVotes(); uint16 currentEpoch = epoch(); UserState memory userState = userStates[msg.sender]; for (uint256 i = 0; i < _initiatives.length; i++) { address initiative = _initiatives[i]; int88 deltaLQTYVotes = _deltaLQTYVotes[i]; int88 deltaLQTYVetos = _deltaLQTYVetos[i]; assert(deltaLQTYVotes != 0 || deltaLQTYVetos != 0); /// === Check FSM === /// // Can vote positively in SKIP, CLAIMABLE, CLAIMED and UNREGISTERABLE states // Force to remove votes if disabled // Can remove votes and vetos in every stage (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) = _snapshotVotesForInitiative(initiative); (InitiativeStatus status,,) = getInitiativeState(initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState); if (deltaLQTYVotes > 0 || deltaLQTYVetos > 0) { /// @audit You cannot vote on `unregisterable` but a vote may have been there require( status == InitiativeStatus.SKIP || status == InitiativeStatus.CLAIMABLE || status == InitiativeStatus.CLAIMED, "Governance: active-vote-fsm" ); } if (status == InitiativeStatus.DISABLED) { require(deltaLQTYVotes <= 0 && deltaLQTYVetos <= 0, "Must be a withdrawal"); } /// === UPDATE ACCOUNTING === /// // == INITIATIVE STATE == // // deep copy of the initiative's state before the allocation InitiativeState memory prevInitiativeState = InitiativeState( initiativeState.voteLQTY, initiativeState.vetoLQTY, initiativeState.averageStakingTimestampVoteLQTY, initiativeState.averageStakingTimestampVetoLQTY, initiativeState.lastEpochClaim ); // update the average staking timestamp for the initiative based on the user's average staking timestamp initiativeState.averageStakingTimestampVoteLQTY = _calculateAverageTimestamp( initiativeState.averageStakingTimestampVoteLQTY, userState.averageStakingTimestamp, /// @audit This is wrong unless we enforce a reset on deposit and withdrawal initiativeState.voteLQTY, add(initiativeState.voteLQTY, deltaLQTYVotes) ); initiativeState.averageStakingTimestampVetoLQTY = _calculateAverageTimestamp( initiativeState.averageStakingTimestampVetoLQTY, userState.averageStakingTimestamp, /// @audit This is wrong unless we enforce a reset on deposit and withdrawal initiativeState.vetoLQTY, add(initiativeState.vetoLQTY, deltaLQTYVetos) ); // allocate the voting and vetoing LQTY to the initiative initiativeState.voteLQTY = add(initiativeState.voteLQTY, deltaLQTYVotes); initiativeState.vetoLQTY = add(initiativeState.vetoLQTY, deltaLQTYVetos); // update the initiative's state initiativeStates[initiative] = initiativeState; // == GLOBAL STATE == // // TODO: Veto reducing total votes logic change // TODO: Accounting invariants // TODO: Let's say I want to cap the votes vs weights // Then by definition, I add the effective LQTY // And the effective TS // I remove the previous one // and add the next one // Veto > Vote // Reduce down by Vote (cap min) // If Vote > Veto // Increase by Veto - Veto (reduced max) // update the average staking timestamp for all counted voting LQTY /// Discount previous only if the initiative was not unregistered /// @audit We update the state only for non-disabled initiaitives /// Disabled initiaitves have had their totals subtracted already /// Math is also non associative so we cannot easily compare values if (status != InitiativeStatus.DISABLED) { /// @audit Trophy: `test_property_sum_of_lqty_global_user_matches_0` /// Removing votes from state desynchs the state until all users remove their votes from the initiative /// The invariant that holds is: the one that removes the initiatives that have been unregistered state.countedVoteLQTYAverageTimestamp = _calculateAverageTimestamp( state.countedVoteLQTYAverageTimestamp, prevInitiativeState.averageStakingTimestampVoteLQTY, /// @audit We don't have a test that fails when this line is changed state.countedVoteLQTY, state.countedVoteLQTY - prevInitiativeState.voteLQTY ); assert(state.countedVoteLQTY >= prevInitiativeState.voteLQTY); /// @audit INVARIANT: Never overflows state.countedVoteLQTY -= prevInitiativeState.voteLQTY; state.countedVoteLQTYAverageTimestamp = _calculateAverageTimestamp( state.countedVoteLQTYAverageTimestamp, initiativeState.averageStakingTimestampVoteLQTY, state.countedVoteLQTY, state.countedVoteLQTY + initiativeState.voteLQTY ); state.countedVoteLQTY += initiativeState.voteLQTY; } // == USER ALLOCATION == // // allocate the voting and vetoing LQTY to the initiative Allocation memory allocation = lqtyAllocatedByUserToInitiative[msg.sender][initiative]; allocation.voteLQTY = add(allocation.voteLQTY, deltaLQTYVotes); allocation.vetoLQTY = add(allocation.vetoLQTY, deltaLQTYVetos); allocation.atEpoch = currentEpoch; require(!(allocation.voteLQTY != 0 && allocation.vetoLQTY != 0), "Governance: vote-and-veto"); lqtyAllocatedByUserToInitiative[msg.sender][initiative] = allocation; // == USER STATE == // userState.allocatedLQTY = add(userState.allocatedLQTY, deltaLQTYVotes + deltaLQTYVetos); emit AllocateLQTY(msg.sender, initiative, deltaLQTYVotes, deltaLQTYVetos, currentEpoch); // Replaces try / catch | Enforces sufficient gas is passed safeCallWithMinGas( initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall( IInitiative.onAfterAllocateLQTY, (currentEpoch, msg.sender, userState, allocation, initiativeState) ) ); } require( userState.allocatedLQTY == 0 || userState.allocatedLQTY <= uint88(stakingV1.stakes(deriveUserProxyAddress(msg.sender))), "Governance: insufficient-or-allocated-lqty" ); globalState = state; userStates[msg.sender] = userState; } /// @inheritdoc IGovernance function unregisterInitiative(address _initiative) external nonReentrant { /// Enforce FSM (VoteSnapshot memory votesSnapshot_, GlobalState memory state) = _snapshotVotes(); (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) = _snapshotVotesForInitiative(_initiative); (InitiativeStatus status,,) = getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState); require(status != InitiativeStatus.NONEXISTENT, "Governance: initiative-not-registered"); require(status != InitiativeStatus.WARM_UP, "Governance: initiative-in-warm-up"); require(status == InitiativeStatus.UNREGISTERABLE, "Governance: cannot-unregister-initiative"); // Remove weight from current state uint16 currentEpoch = epoch(); /// @audit Invariant: Must only claim once or unregister // NOTE: Safe to remove | See `check_claim_soundness` assert(initiativeState.lastEpochClaim < currentEpoch - 1); // recalculate the average staking timestamp for all counted voting LQTY if the initiative was counted in /// @audit Trophy: `test_property_sum_of_lqty_global_user_matches_0` // Removing votes from state desynchs the state until all users remove their votes from the initiative state.countedVoteLQTYAverageTimestamp = _calculateAverageTimestamp( state.countedVoteLQTYAverageTimestamp, initiativeState.averageStakingTimestampVoteLQTY, state.countedVoteLQTY, state.countedVoteLQTY - initiativeState.voteLQTY ); assert(state.countedVoteLQTY >= initiativeState.voteLQTY); /// RECON: Overflow state.countedVoteLQTY -= initiativeState.voteLQTY; globalState = state; /// weeks * 2^16 > u32 so the contract will stop working before this is an issue registeredInitiatives[_initiative] = UNREGISTERED_INITIATIVE; emit UnregisterInitiative(_initiative, currentEpoch); // Replaces try / catch | Enforces sufficient gas is passed safeCallWithMinGas( _initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onUnregisterInitiative, (currentEpoch)) ); } /// @inheritdoc IGovernance function claimForInitiative(address _initiative) external nonReentrant returns (uint256) { // Accrue and update state (VoteSnapshot memory votesSnapshot_,) = _snapshotVotes(); (InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) = _snapshotVotesForInitiative(_initiative); // Compute values on accrued state (InitiativeStatus status,, uint256 claimableAmount) = getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState); if (status != InitiativeStatus.CLAIMABLE) { return 0; } /// @audit INVARIANT: You can only claim for previous epoch assert(votesSnapshot_.forEpoch == epoch() - 1); /// All unclaimed rewards are always recycled /// Invariant `lastEpochClaim` is < epoch() - 1; | /// If `lastEpochClaim` is older than epoch() - 1 it means the initiative couldn't claim any rewards this epoch initiativeStates[_initiative].lastEpochClaim = epoch() - 1; // @audit INVARIANT, because of rounding errors the system can overpay /// We upscale the timestamp to reduce the impact of the loss /// However this is still possible uint256 available = bold.balanceOf(address(this)); if (claimableAmount > available) { claimableAmount = available; } bold.safeTransfer(_initiative, claimableAmount); emit ClaimForInitiative(_initiative, claimableAmount, votesSnapshot_.forEpoch); // Replaces try / catch | Enforces sufficient gas is passed safeCallWithMinGas( _initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onClaimForInitiative, (votesSnapshot_.forEpoch, claimableAmount)) ); return claimableAmount; } function _requireNoNOP(int88[] memory _absoluteLQTYVotes, int88[] memory _absoluteLQTYVetos) internal pure { for (uint256 i; i < _absoluteLQTYVotes.length; i++) { require(_absoluteLQTYVotes[i] > 0 || _absoluteLQTYVetos[i] > 0, "Governance: voting nothing"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IERC20} from "openzeppelin/contracts/interfaces/IERC20.sol"; import {ILQTYStaking} from "./ILQTYStaking.sol"; import {PermitParams} from "../utils/Types.sol"; interface IGovernance { event DepositLQTY(address user, uint256 depositedLQTY); event WithdrawLQTY(address user, uint256 withdrawnLQTY, uint256 accruedLUSD, uint256 accruedETH); event SnapshotVotes(uint240 votes, uint16 forEpoch); event SnapshotVotesForInitiative(address initiative, uint240 votes, uint16 forEpoch); event RegisterInitiative(address initiative, address registrant, uint16 atEpoch); event UnregisterInitiative(address initiative, uint16 atEpoch); event AllocateLQTY(address user, address initiative, int256 deltaVoteLQTY, int256 deltaVetoLQTY, uint16 atEpoch); event ClaimForInitiative(address initiative, uint256 bold, uint256 forEpoch); struct Configuration { uint128 registrationFee; uint128 registrationThresholdFactor; uint128 unregistrationThresholdFactor; uint16 registrationWarmUpPeriod; uint16 unregistrationAfterEpochs; uint128 votingThresholdFactor; uint88 minClaim; uint88 minAccrual; uint32 epochStart; uint32 epochDuration; uint32 epochVotingCutoff; } function registerInitialInitiatives(address[] memory _initiatives) external; /// @notice Address of the LQTY StakingV1 contract /// @return stakingV1 Address of the LQTY StakingV1 contract function stakingV1() external view returns (ILQTYStaking stakingV1); /// @notice Address of the LQTY token /// @return lqty Address of the LQTY token function lqty() external view returns (IERC20 lqty); /// @notice Address of the BOLD token /// @return bold Address of the BOLD token function bold() external view returns (IERC20 bold); /// @notice Timestamp at which the first epoch starts /// @return epochStart Timestamp at which the first epoch starts function EPOCH_START() external view returns (uint256 epochStart); /// @notice Duration of an epoch in seconds (e.g. 1 week) /// @return epochDuration Epoch duration function EPOCH_DURATION() external view returns (uint256 epochDuration); /// @notice Voting period of an epoch in seconds (e.g. 6 days) /// @return epochVotingCutoff Epoch voting cutoff function EPOCH_VOTING_CUTOFF() external view returns (uint256 epochVotingCutoff); /// @notice Minimum BOLD amount that has to be claimed, if an initiative doesn't have enough votes to meet the /// criteria then it's votes a excluded from the vote count and distribution /// @return minClaim Minimum claim amount function MIN_CLAIM() external view returns (uint256 minClaim); /// @notice Minimum amount of BOLD that have to be accrued for an epoch, otherwise accrual will be skipped for /// that epoch /// @return minAccrual Minimum amount of BOLD function MIN_ACCRUAL() external view returns (uint256 minAccrual); /// @notice Amount of BOLD to be paid in order to register a new initiative /// @return registrationFee Registration fee function REGISTRATION_FEE() external view returns (uint256 registrationFee); /// @notice Share of all votes that are necessary to register a new initiative /// @return registrationThresholdFactor Threshold factor function REGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 registrationThresholdFactor); /// @notice Multiple of the voting threshold in vetos that are necessary to unregister an initiative /// @return unregistrationThresholdFactor Unregistration threshold factor function UNREGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 unregistrationThresholdFactor); /// @notice Number of epochs an initiative has to exist before it can be unregistered /// @return registrationWarmUpPeriod Number of epochs function REGISTRATION_WARM_UP_PERIOD() external view returns (uint256 registrationWarmUpPeriod); /// @notice Number of epochs an initiative has to be inactive before it can be unregistered /// @return unregistrationAfterEpochs Number of epochs function UNREGISTRATION_AFTER_EPOCHS() external view returns (uint256 unregistrationAfterEpochs); /// @notice Share of all votes that are necessary for an initiative to be included in the vote count /// @return votingThresholdFactor Voting threshold factor function VOTING_THRESHOLD_FACTOR() external view returns (uint256 votingThresholdFactor); /// @notice Returns the amount of BOLD accrued since last epoch (last snapshot) /// @return boldAccrued BOLD accrued function boldAccrued() external view returns (uint256 boldAccrued); struct VoteSnapshot { uint240 votes; // Votes at epoch transition uint16 forEpoch; // Epoch for which the votes are counted } struct InitiativeVoteSnapshot { uint224 votes; // Votes at epoch transition uint16 forEpoch; // Epoch for which the votes are counted uint16 lastCountedEpoch; // Epoch at which which the votes where counted last in the global snapshot uint224 vetos; // Vetos at epoch transition } /// @notice Returns the vote count snapshot of the previous epoch /// @return votes Number of votes /// @return forEpoch Epoch for which the votes are counted function votesSnapshot() external view returns (uint240 votes, uint16 forEpoch); /// @notice Returns the vote count snapshot for an initiative of the previous epoch /// @param _initiative Address of the initiative /// @return votes Number of votes /// @return forEpoch Epoch for which the votes are counted /// @return lastCountedEpoch Epoch at which which the votes where counted last in the global snapshot function votesForInitiativeSnapshot(address _initiative) external view returns (uint224 votes, uint16 forEpoch, uint16 lastCountedEpoch, uint224 vetos); struct Allocation { uint88 voteLQTY; // LQTY allocated vouching for the initiative uint88 vetoLQTY; // LQTY vetoing the initiative uint16 atEpoch; // Epoch at which the allocation was last updated } struct UserState { uint88 allocatedLQTY; // LQTY allocated by the user uint120 averageStakingTimestamp; // Average timestamp at which LQTY was staked by the user } struct InitiativeState { uint88 voteLQTY; // LQTY allocated vouching for the initiative uint88 vetoLQTY; // LQTY allocated vetoing the initiative uint120 averageStakingTimestampVoteLQTY; // Average staking timestamp of the voting LQTY for the initiative uint120 averageStakingTimestampVetoLQTY; // Average staking timestamp of the vetoing LQTY for the initiative uint16 lastEpochClaim; } struct GlobalState { uint88 countedVoteLQTY; // Total LQTY that is included in vote counting uint120 countedVoteLQTYAverageTimestamp; // Average timestamp: derived initiativeAllocation.averageTimestamp } /// TODO: Bold balance? Prob cheaper /// @notice Returns the user's state /// @param _user Address of the user /// @return allocatedLQTY LQTY allocated by the user /// @return averageStakingTimestamp Average timestamp at which LQTY was staked (deposited) by the user function userStates(address _user) external view returns (uint88 allocatedLQTY, uint120 averageStakingTimestamp); /// @notice Returns the initiative's state /// @param _initiative Address of the initiative /// @return voteLQTY LQTY allocated vouching for the initiative /// @return vetoLQTY LQTY allocated vetoing the initiative /// @return averageStakingTimestampVoteLQTY // Average staking timestamp of the voting LQTY for the initiative /// @return averageStakingTimestampVetoLQTY // Average staking timestamp of the vetoing LQTY for the initiative /// @return lastEpochClaim // Last epoch at which rewards were claimed function initiativeStates(address _initiative) external view returns ( uint88 voteLQTY, uint88 vetoLQTY, uint120 averageStakingTimestampVoteLQTY, uint120 averageStakingTimestampVetoLQTY, uint16 lastEpochClaim ); /// @notice Returns the global state /// @return countedVoteLQTY Total LQTY that is included in vote counting /// @return countedVoteLQTYAverageTimestamp Average timestamp: derived initiativeAllocation.averageTimestamp function globalState() external view returns (uint88 countedVoteLQTY, uint120 countedVoteLQTYAverageTimestamp); /// @notice Returns the amount of voting and vetoing LQTY a user allocated to an initiative /// @param _user Address of the user /// @param _initiative Address of the initiative /// @return voteLQTY LQTY allocated vouching for the initiative /// @return vetoLQTY LQTY allocated vetoing the initiative /// @return atEpoch Epoch at which the allocation was last updated function lqtyAllocatedByUserToInitiative(address _user, address _initiative) external view returns (uint88 voteLQTY, uint88 vetoLQTY, uint16 atEpoch); /// @notice Returns when an initiative was registered /// @param _initiative Address of the initiative /// @return atEpoch Epoch at which the initiative was registered function registeredInitiatives(address _initiative) external view returns (uint16 atEpoch); /*////////////////////////////////////////////////////////////// STAKING //////////////////////////////////////////////////////////////*/ /// @notice Deposits LQTY /// @dev The caller has to approve this contract to spend the LQTY tokens /// @param _lqtyAmount Amount of LQTY to deposit function depositLQTY(uint88 _lqtyAmount) external; /// @notice Deposits LQTY via Permit /// @param _lqtyAmount Amount of LQTY to deposit /// @param _permitParams Permit parameters function depositLQTYViaPermit(uint88 _lqtyAmount, PermitParams memory _permitParams) external; /// @notice Withdraws LQTY and claims any accrued LUSD and ETH rewards from StakingV1 /// @param _lqtyAmount Amount of LQTY to withdraw function withdrawLQTY(uint88 _lqtyAmount) external; /// @notice Claims staking rewards from StakingV1 without unstaking /// @param _rewardRecipient Address that will receive the rewards /// @return accruedLUSD Amount of LUSD accrued /// @return accruedETH Amount of ETH accrued function claimFromStakingV1(address _rewardRecipient) external returns (uint256 accruedLUSD, uint256 accruedETH); /*////////////////////////////////////////////////////////////// VOTING //////////////////////////////////////////////////////////////*/ /// @notice Returns the current epoch number /// @return epoch Current epoch function epoch() external view returns (uint16 epoch); /// @notice Returns the timestamp at which the current epoch started /// @return epochStart Epoch start of the current epoch function epochStart() external view returns (uint32 epochStart); /// @notice Returns the number of seconds that have gone by since the current epoch started /// @return secondsWithinEpoch Seconds within the current epoch function secondsWithinEpoch() external view returns (uint32 secondsWithinEpoch); /// @notice Returns the number of votes per LQTY for a user /// @param _lqtyAmount Amount of LQTY to convert to votes /// @param _currentTimestamp Current timestamp /// @param _averageTimestamp Average timestamp at which the LQTY was staked /// @return votes Number of votes function lqtyToVotes(uint88 _lqtyAmount, uint120 _currentTimestamp, uint120 _averageTimestamp) external pure returns (uint208); /// @notice Voting threshold is the max. of either: /// - 4% of the total voting LQTY in the previous epoch /// - or the minimum number of votes necessary to claim at least MIN_CLAIM BOLD /// This value can be offsynch, use the non view `calculateVotingThreshold` to always retrieve the most up to date value /// @return votingThreshold Voting threshold function getLatestVotingThreshold() external view returns (uint256 votingThreshold); /// @notice Snapshots votes for the previous epoch and accrues funds for the current epoch /// @param _initiative Address of the initiative /// @return voteSnapshot Vote snapshot /// @return initiativeVoteSnapshot Vote snapshot of the initiative function snapshotVotesForInitiative(address _initiative) external returns (VoteSnapshot memory voteSnapshot, InitiativeVoteSnapshot memory initiativeVoteSnapshot); /// @notice Registers a new initiative /// @param _initiative Address of the initiative function registerInitiative(address _initiative) external; // /// @notice Unregisters an initiative if it didn't receive enough votes in the last 4 epochs // /// or if it received more vetos than votes and the number of vetos are greater than 3 times the voting threshold // /// @param _initiative Address of the initiative function unregisterInitiative(address _initiative) external; /// @notice Allocates the user's LQTY to initiatives /// @dev The user can only allocate to active initiatives (older than 1 epoch) and has to have enough unallocated /// LQTY available, the initiatives listed must be unique, and towards the end of the epoch a user can only maintain or reduce their votes /// @param _resetInitiatives Addresses of the initiatives the caller was previously allocated to, must be reset to prevent desynch of voting power /// @param _initiatives Addresses of the initiatives to allocate to, can match or be different from `_resetInitiatives` /// @param _absoluteLQTYVotes Delta LQTY to allocate to the initiatives as votes /// @param absoluteLQTYVetos Delta LQTY to allocate to the initiatives as vetos function allocateLQTY( address[] calldata _resetInitiatives, address[] memory _initiatives, int88[] memory _absoluteLQTYVotes, int88[] memory absoluteLQTYVetos ) external; /// @notice Splits accrued funds according to votes received between all initiatives /// @param _initiative Addresse of the initiative /// @return claimed Amount of BOLD claimed function claimForInitiative(address _initiative) external returns (uint256 claimed); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IGovernance} from "./IGovernance.sol"; interface IInitiative { /// @notice Callback hook that is called by Governance after the initiative was successfully registered /// @param _atEpoch Epoch at which the initiative is registered function onRegisterInitiative(uint16 _atEpoch) external; /// @notice Callback hook that is called by Governance after the initiative was unregistered /// @param _atEpoch Epoch at which the initiative is unregistered function onUnregisterInitiative(uint16 _atEpoch) external; /// @notice Callback hook that is called by Governance after the LQTY allocation is updated by a user /// @param _currentEpoch Epoch at which the LQTY allocation is updated /// @param _user Address of the user that updated their LQTY allocation /// @param _userState User state /// @param _allocation Allocation state from user to initiative /// @param _initiativeState Initiative state function onAfterAllocateLQTY( uint16 _currentEpoch, address _user, IGovernance.UserState calldata _userState, IGovernance.Allocation calldata _allocation, IGovernance.InitiativeState calldata _initiativeState ) external; /// @notice Callback hook that is called by Governance after the claim for the last epoch was distributed /// to the initiative /// @param _claimEpoch Epoch at which the claim was distributed /// @param _bold Amount of BOLD that was distributed function onClaimForInitiative(uint16 _claimEpoch, uint256 _bold) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface ILQTYStaking { // --- Events -- event LQTYTokenAddressSet(address _lqtyTokenAddress); event LUSDTokenAddressSet(address _lusdTokenAddress); event TroveManagerAddressSet(address _troveManager); event BorrowerOperationsAddressSet(address _borrowerOperationsAddress); event ActivePoolAddressSet(address _activePoolAddress); event StakeChanged(address indexed staker, uint256 newStake); event StakingGainsWithdrawn(address indexed staker, uint256 LUSDGain, uint256 ETHGain); event F_ETHUpdated(uint256 _F_ETH); event F_LUSDUpdated(uint256 _F_LUSD); event TotalLQTYStakedUpdated(uint256 _totalLQTYStaked); event EtherSent(address _account, uint256 _amount); event StakerSnapshotsUpdated(address _staker, uint256 _F_ETH, uint256 _F_LUSD); // --- Functions --- function setAddresses( address _lqtyTokenAddress, address _lusdTokenAddress, address _troveManagerAddress, address _borrowerOperationsAddress, address _activePoolAddress ) external; function stake(uint256 _LQTYamount) external; function unstake(uint256 _LQTYamount) external; function increaseF_ETH(uint256 _ETHFee) external; function increaseF_LUSD(uint256 _LQTYFee) external; function getPendingETHGain(address _user) external view returns (uint256); function getPendingLUSDGain(address _user) external view returns (uint256); function stakes(address _user) external view returns (uint256); function totalLQTYStaked() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IERC20} from "openzeppelin/contracts/interfaces/IERC20.sol"; import {IERC20Permit} from "openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import {IUserProxy} from "./interfaces/IUserProxy.sol"; import {ILQTYStaking} from "./interfaces/ILQTYStaking.sol"; import {PermitParams} from "./utils/Types.sol"; contract UserProxy is IUserProxy { /// @inheritdoc IUserProxy IERC20 public immutable lqty; /// @inheritdoc IUserProxy IERC20 public immutable lusd; /// @inheritdoc IUserProxy ILQTYStaking public immutable stakingV1; /// @inheritdoc IUserProxy address public immutable stakingV2; constructor(address _lqty, address _lusd, address _stakingV1) { lqty = IERC20(_lqty); lusd = IERC20(_lusd); stakingV1 = ILQTYStaking(_stakingV1); stakingV2 = msg.sender; } modifier onlyStakingV2() { require(msg.sender == stakingV2, "UserProxy: caller-not-stakingV2"); _; } /// @inheritdoc IUserProxy function stake(uint256 _amount, address _lqtyFrom, bool _doSendRewards, address _recipient) public onlyStakingV2 returns (uint256 lusdAmount, uint256 ethAmount) { uint256 initialLUSDAmount = lusd.balanceOf(address(this)); uint256 initialETHAmount = address(this).balance; lqty.transferFrom(_lqtyFrom, address(this), _amount); stakingV1.stake(_amount); emit Stake(_amount, _lqtyFrom); if (_doSendRewards) { (lusdAmount, ethAmount) = _sendRewards(_recipient, initialLUSDAmount, initialETHAmount); } } /// @inheritdoc IUserProxy function stakeViaPermit( uint256 _amount, address _lqtyFrom, PermitParams calldata _permitParams, bool _doSendRewards, address _recipient ) public onlyStakingV2 returns (uint256 lusdAmount, uint256 ethAmount) { require(_lqtyFrom == _permitParams.owner, "UserProxy: owner-not-sender"); uint256 initialLUSDAmount = lusd.balanceOf(address(this)); uint256 initialETHAmount = address(this).balance; try IERC20Permit(address(lqty)).permit( _permitParams.owner, _permitParams.spender, _permitParams.value, _permitParams.deadline, _permitParams.v, _permitParams.r, _permitParams.s ) {} catch {} stake(_amount, _lqtyFrom, _doSendRewards, _recipient); if (_doSendRewards) { (lusdAmount, ethAmount) = _sendRewards(_recipient, initialLUSDAmount, initialETHAmount); } } /// @inheritdoc IUserProxy function unstake(uint256 _amount, bool _doSendRewards, address _recipient) public onlyStakingV2 returns (uint256 lusdAmount, uint256 ethAmount) { uint256 initialLQTYAmount = lqty.balanceOf(address(this)); uint256 initialLUSDAmount = lusd.balanceOf(address(this)); uint256 initialETHAmount = address(this).balance; stakingV1.unstake(_amount); uint256 lqtyAmount = lqty.balanceOf(address(this)); if (lqtyAmount > 0) lqty.transfer(_recipient, lqtyAmount); emit Unstake(_recipient, lqtyAmount - initialLQTYAmount, lqtyAmount); if (_doSendRewards) { (lusdAmount, ethAmount) = _sendRewards(_recipient, initialLUSDAmount, initialETHAmount); } } function _sendRewards(address _recipient, uint256 _initialLUSDAmount, uint256 _initialETHAmount) internal returns (uint256 lusdAmount, uint256 ethAmount) { lusdAmount = lusd.balanceOf(address(this)); if (lusdAmount > 0) lusd.transfer(_recipient, lusdAmount); ethAmount = address(this).balance; if (ethAmount > 0) { (bool success,) = payable(_recipient).call{value: ethAmount}(""); require(success, "UserProxy: eth-fail"); } emit SendRewards( _recipient, lusdAmount - _initialLUSDAmount, lusdAmount, ethAmount - _initialETHAmount, ethAmount ); } /// @inheritdoc IUserProxy function staked() external view returns (uint88) { return uint88(stakingV1.stakes(address(this))); } receive() external payable {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {Clones} from "openzeppelin/contracts/proxy/Clones.sol"; import {IUserProxyFactory} from "./interfaces/IUserProxyFactory.sol"; import {UserProxy} from "./UserProxy.sol"; contract UserProxyFactory is IUserProxyFactory { /// @inheritdoc IUserProxyFactory address public immutable userProxyImplementation; constructor(address _lqty, address _lusd, address _stakingV1) { userProxyImplementation = address(new UserProxy(_lqty, _lusd, _stakingV1)); } /// @inheritdoc IUserProxyFactory function deriveUserProxyAddress(address _user) public view returns (address) { return Clones.predictDeterministicAddress(userProxyImplementation, bytes32(uint256(uint160(_user)))); } /// @inheritdoc IUserProxyFactory function deployUserProxy() public returns (address) { // reverts if the user already has a proxy address userProxy = Clones.cloneDeterministic(userProxyImplementation, bytes32(uint256(uint160(msg.sender)))); emit DeployUserProxy(msg.sender, userProxy); return userProxy; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; function add(uint88 a, int88 b) pure returns (uint88) { if (b < 0) { return a - abs(b); } return a + abs(b); } function max(uint256 a, uint256 b) pure returns (uint256) { return a > b ? a : b; } function abs(int88 a) pure returns (uint88) { return a < 0 ? uint88(uint256(-int256(a))) : uint88(a); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// @dev Checks that there's no duplicate addresses /// @param arr - List to check for dups function _requireNoDuplicates(address[] calldata arr) pure { uint256 arrLength = arr.length; if (arrLength == 0) return; // only up to len - 1 (no j to check if i == len - 1) for (uint i; i < arrLength - 1;) { for (uint j = i + 1; j < arrLength;) { require(arr[i] != arr[j], "dup"); unchecked { ++j; } } unchecked { ++i; } } } function _requireNoNegatives(int88[] memory vals) pure { uint256 arrLength = vals.length; for (uint i; i < arrLength; i++) { require(vals[i] >= 0, "Cannot be negative"); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.24; import {IMulticall} from "../interfaces/IMulticall.sol"; /// Copied from: https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicall.sol /// @title Multicall /// @notice Enables calling multiple methods in a single call to the contract abstract contract Multicall is IMulticall { /// @inheritdoc IMulticall function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { (bool success, bytes memory result) = address(this).delegatecall(data[i]); if (!success) { // Next 5 lines from https://ethereum.stackexchange.com/a/83577 if (result.length < 68) revert(); assembly { result := add(result, 0x04) } revert(abi.decode(result, (string))); } results[i] = result; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; struct PermitParams { address owner; address spender; uint256 value; uint256 deadline; uint8 v; bytes32 r; bytes32 s; } uint256 constant WAD = 1e18;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// @notice Given the gas requirement, ensures that the current context has sufficient gas to perform a call + a fixed buffer /// @dev Credits: https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/libraries/SafeCall.sol#L100-L107 function hasMinGas(uint256 _minGas, uint256 _reservedGas) view returns (bool) { bool _hasMinGas; assembly { // Equation: gas × 63 ≥ minGas × 64 + 63(40_000 + reservedGas) _hasMinGas := iszero(lt(mul(gas(), 63), add(mul(_minGas, 64), mul(add(40000, _reservedGas), 63)))) } return _hasMinGas; } /// @dev Performs a call ignoring the recipient existing or not, passing the exact gas value, ignoring any return value function safeCallWithMinGas(address _target, uint256 _gas, uint256 _value, bytes memory _calldata) returns (bool success) { /// @audit This is not necessary /// But this is basically a worst case estimate of mem exp cost + operations before the call require(hasMinGas(_gas, 1_000), "Must have minGas"); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { success := call( _gas, // gas _target, // recipient _value, // ether value add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // Ignore all return values } return (success); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; /** * Based on OpenZeppelin's Ownable contract: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol * * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting `initialOwner` as the initial owner. */ constructor(address initialOwner) { _owner = initialOwner; emit OwnershipTransferred(address(0), initialOwner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner(), "Ownable: caller is not the owner"); _; } /** * @dev Returns true if the caller is the current owner. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. * * NOTE: This function is not safe, as it doesn’t check owner is calling it. * Make sure you check it before calling it. */ function _renounceOwnership() internal { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IERC20} from "openzeppelin/contracts/interfaces/IERC20.sol"; import {ILQTYStaking} from "../interfaces/ILQTYStaking.sol"; import {PermitParams} from "../utils/Types.sol"; interface IUserProxy { event Stake(uint256 amount, address lqtyFrom); event Unstake(address indexed lqtyRecipient, uint256 lqtyReceived, uint256 lqtySent); event SendRewards( address indexed recipient, uint256 lusdAmountReceived, uint256 lusdAmountSent, uint256 ethAmountReceived, uint256 ethAmountSent ); /// @notice Address of the LQTY token /// @return lqty Address of the LQTY token function lqty() external view returns (IERC20 lqty); /// @notice Address of the LUSD token /// @return lusd Address of the LUSD token function lusd() external view returns (IERC20 lusd); /// @notice Address of the V1 LQTY staking contract /// @return stakingV1 Address of the V1 LQTY staking contract function stakingV1() external view returns (ILQTYStaking stakingV1); /// @notice Address of the V2 LQTY staking contract /// @return stakingV2 Address of the V2 LQTY staking contract function stakingV2() external view returns (address stakingV2); /// @notice Stakes a given amount of LQTY tokens in the V1 staking contract /// @dev The LQTY tokens must be approved for transfer by the user /// @param _amount Amount of LQTY tokens to stake /// @param _lqtyFrom Address from which to transfer the LQTY tokens /// @param _doSendRewards If true, send rewards claimed from LQTY staking /// @param _recipient Address to which the tokens should be sent /// @return lusdAmount Amount of LUSD tokens claimed /// @return ethAmount Amount of ETH claimed function stake(uint256 _amount, address _lqtyFrom, bool _doSendRewards, address _recipient) external returns (uint256 lusdAmount, uint256 ethAmount); /// @notice Stakes a given amount of LQTY tokens in the V1 staking contract using a permit /// @param _amount Amount of LQTY tokens to stake /// @param _lqtyFrom Address from which to transfer the LQTY tokens /// @param _permitParams Parameters for the permit data /// @param _doSendRewards If true, send rewards claimed from LQTY staking /// @param _recipient Address to which the tokens should be sent /// @return lusdAmount Amount of LUSD tokens claimed /// @return ethAmount Amount of ETH claimed function stakeViaPermit( uint256 _amount, address _lqtyFrom, PermitParams calldata _permitParams, bool _doSendRewards, address _recipient ) external returns (uint256 lusdAmount, uint256 ethAmount); /// @notice Unstakes a given amount of LQTY tokens from the V1 staking contract and claims the accrued rewards /// @param _amount Amount of LQTY tokens to unstake /// @param _doSendRewards If true, send rewards claimed from LQTY staking /// @param _recipient Address to which the tokens should be sent /// @return lusdAmount Amount of LUSD tokens claimed /// @return ethAmount Amount of ETH claimed function unstake(uint256 _amount, bool _doSendRewards, address _recipient) external returns (uint256 lusdAmount, uint256 ethAmount); /// @notice Returns the current amount LQTY staked by a user in the V1 staking contract /// @return staked Amount of LQTY tokens staked function staked() external view returns (uint88); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol) pragma solidity ^0.8.20; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. */ library Clones { /** * @dev A clone instance deployment failed. */ error ERC1167FailedCreateClone(); /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 0x37) } if (instance == address(0)) { revert ERC1167FailedCreateClone(); } } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 0x37, salt) } if (instance == address(0)) { revert ERC1167FailedCreateClone(); } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; interface IUserProxyFactory { event DeployUserProxy(address indexed user, address indexed userProxy); /// @notice Address of the UserProxy implementation contract /// @return implementation Address of the UserProxy implementation contract function userProxyImplementation() external view returns (address implementation); /// @notice Derive the address of a user's proxy contract /// @param _user Address of the user /// @return userProxyAddress Address of the user's proxy contract function deriveUserProxyAddress(address _user) external view returns (address userProxyAddress); /// @notice Deploy a new UserProxy contract for the sender /// @return userProxyAddress Address of the deployed UserProxy contract function deployUserProxy() external returns (address userProxyAddress); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// Copied from: https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IMulticall.sol /// @title Multicall interface /// @notice Enables calling multiple methods in a single call to the contract interface IMulticall { /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed /// @dev The `msg.value` should not be trusted for any method callable from multicall. /// @param data The encoded function data for each of the calls to make to this contract /// @return results The results from each of the calls passed in via data function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); }
{ "remappings": [ "@chimera/=lib/V2-gov/lib/chimera/src/", "@ensdomains/=lib/V2-gov/lib/v4-core/node_modules/@ensdomains/", "@openzeppelin/=lib/V2-gov/lib/v4-core/lib/openzeppelin-contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "Solady/=lib/Solady/src/", "V2-gov/=lib/V2-gov/", "chimera/=lib/V2-gov/lib/chimera/src/", "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-gas-snapshot/=lib/V2-gov/lib/v4-core/lib/forge-gas-snapshot/src/", "forge-std/=lib/forge-std/src/", "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/", "hardhat/=lib/V2-gov/lib/v4-core/node_modules/hardhat/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/V2-gov/lib/openzeppelin-contracts/", "solmate/=lib/V2-gov/lib/v4-core/lib/solmate/src/", "v4-core/=lib/V2-gov/lib/v4-core/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"_lqty","type":"address"},{"internalType":"address","name":"_lusd","type":"address"},{"internalType":"address","name":"_stakingV1","type":"address"},{"internalType":"address","name":"_bold","type":"address"},{"components":[{"internalType":"uint128","name":"registrationFee","type":"uint128"},{"internalType":"uint128","name":"registrationThresholdFactor","type":"uint128"},{"internalType":"uint128","name":"unregistrationThresholdFactor","type":"uint128"},{"internalType":"uint16","name":"registrationWarmUpPeriod","type":"uint16"},{"internalType":"uint16","name":"unregistrationAfterEpochs","type":"uint16"},{"internalType":"uint128","name":"votingThresholdFactor","type":"uint128"},{"internalType":"uint88","name":"minClaim","type":"uint88"},{"internalType":"uint88","name":"minAccrual","type":"uint88"},{"internalType":"uint32","name":"epochStart","type":"uint32"},{"internalType":"uint32","name":"epochDuration","type":"uint32"},{"internalType":"uint32","name":"epochVotingCutoff","type":"uint32"}],"internalType":"struct IGovernance.Configuration","name":"_config","type":"tuple"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address[]","name":"_initiatives","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ERC1167FailedCreateClone","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"int256","name":"deltaVoteLQTY","type":"int256"},{"indexed":false,"internalType":"int256","name":"deltaVetoLQTY","type":"int256"},{"indexed":false,"internalType":"uint16","name":"atEpoch","type":"uint16"}],"name":"AllocateLQTY","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"bold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"}],"name":"ClaimForInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"userProxy","type":"address"}],"name":"DeployUserProxy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositedLQTY","type":"uint256"}],"name":"DepositLQTY","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"address","name":"registrant","type":"address"},{"indexed":false,"internalType":"uint16","name":"atEpoch","type":"uint16"}],"name":"RegisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint240","name":"votes","type":"uint240"},{"indexed":false,"internalType":"uint16","name":"forEpoch","type":"uint16"}],"name":"SnapshotVotes","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint240","name":"votes","type":"uint240"},{"indexed":false,"internalType":"uint16","name":"forEpoch","type":"uint16"}],"name":"SnapshotVotesForInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint16","name":"atEpoch","type":"uint16"}],"name":"UnregisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawnLQTY","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accruedLUSD","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accruedETH","type":"uint256"}],"name":"WithdrawLQTY","type":"event"},{"inputs":[],"name":"EPOCH_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EPOCH_START","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EPOCH_VOTING_CUTOFF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_ACCRUAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_CLAIM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRATION_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRATION_THRESHOLD_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRATION_WARM_UP_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIMESTAMP_PRECISION","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNREGISTRATION_AFTER_EPOCHS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNREGISTRATION_THRESHOLD_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTING_THRESHOLD_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_initiativesToReset","type":"address[]"},{"internalType":"address[]","name":"_initiatives","type":"address[]"},{"internalType":"int88[]","name":"_absoluteLQTYVotes","type":"int88[]"},{"internalType":"int88[]","name":"_absoluteLQTYVetos","type":"int88[]"}],"name":"allocateLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bold","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boldAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_votes","type":"uint256"}],"name":"calculateVotingThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calculateVotingThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"claimForInitiative","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardRecipient","type":"address"}],"name":"claimFromStakingV1","outputs":[{"internalType":"uint256","name":"accruedLUSD","type":"uint256"},{"internalType":"uint256","name":"accruedETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deployUserProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct PermitParams","name":"_permitParams","type":"tuple"}],"name":"depositLQTYViaPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct PermitParams","name":"_permitParams","type":"tuple"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"depositLQTYViaPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"deriveUserProxyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochStart","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeSnapshotAndState","outputs":[{"components":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"initiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestampVoteLQTY","type":"uint120"},{"internalType":"uint120","name":"averageStakingTimestampVetoLQTY","type":"uint120"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"}],"internalType":"struct IGovernance.InitiativeState","name":"initiativeState","type":"tuple"},{"internalType":"bool","name":"shouldUpdate","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"},{"components":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"internalType":"struct IGovernance.VoteSnapshot","name":"_votesSnapshot","type":"tuple"},{"components":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"_votesForInitiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestampVoteLQTY","type":"uint120"},{"internalType":"uint120","name":"averageStakingTimestampVetoLQTY","type":"uint120"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"}],"internalType":"struct IGovernance.InitiativeState","name":"_initiativeState","type":"tuple"}],"name":"getInitiativeState","outputs":[{"internalType":"enum Governance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeState","outputs":[{"internalType":"enum Governance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLatestVotingThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalVotesAndState","outputs":[{"components":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"internalType":"struct IGovernance.VoteSnapshot","name":"snapshot","type":"tuple"},{"components":[{"internalType":"uint88","name":"countedVoteLQTY","type":"uint88"},{"internalType":"uint120","name":"countedVoteLQTYAverageTimestamp","type":"uint120"}],"internalType":"struct IGovernance.GlobalState","name":"state","type":"tuple"},{"internalType":"bool","name":"shouldUpdate","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalState","outputs":[{"internalType":"uint88","name":"countedVoteLQTY","type":"uint88"},{"internalType":"uint120","name":"countedVoteLQTYAverageTimestamp","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initiativeStates","outputs":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestampVoteLQTY","type":"uint120"},{"internalType":"uint120","name":"averageStakingTimestampVetoLQTY","type":"uint120"},{"internalType":"uint16","name":"lastEpochClaim","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lqty","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"lqtyAllocatedByUserToInitiative","outputs":[{"internalType":"uint88","name":"voteLQTY","type":"uint88"},{"internalType":"uint88","name":"vetoLQTY","type":"uint88"},{"internalType":"uint16","name":"atEpoch","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"internalType":"uint120","name":"_currentTimestamp","type":"uint120"},{"internalType":"uint120","name":"_averageTimestamp","type":"uint120"}],"name":"lqtyToVotes","outputs":[{"internalType":"uint208","name":"","type":"uint208"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_initiatives","type":"address[]"}],"name":"registerInitialInitiatives","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"registerInitiative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"registeredInitiatives","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_initiativesToReset","type":"address[]"},{"internalType":"bool","name":"checkAll","type":"bool"}],"name":"resetAllocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"secondsWithinEpoch","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"snapshotVotesForInitiative","outputs":[{"components":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"internalType":"struct IGovernance.VoteSnapshot","name":"voteSnapshot","type":"tuple"},{"components":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"initiativeVoteSnapshot","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingV1","outputs":[{"internalType":"contract ILQTYStaking","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"unregisterInitiative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userProxyImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userStates","outputs":[{"internalType":"uint88","name":"allocatedLQTY","type":"uint88"},{"internalType":"uint120","name":"averageStakingTimestamp","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votesForInitiativeSnapshot","outputs":[{"internalType":"uint224","name":"votes","type":"uint224"},{"internalType":"uint16","name":"forEpoch","type":"uint16"},{"internalType":"uint16","name":"lastCountedEpoch","type":"uint16"},{"internalType":"uint224","name":"vetos","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votesSnapshot","outputs":[{"internalType":"uint240","name":"votes","type":"uint240"},{"internalType":"uint16","name":"forEpoch","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint88","name":"_lqtyAmount","type":"uint88"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
61026060405234801562000011575f80fd5b5060405162006ddc38038062006ddc8339810160408190526200003491620006e9565b8187878782828260405162000049906200057e565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103905ff08015801562000083573d5f803e3d5ffd5b506001600160a01b0390811660805260015f81815581546001600160a01b03191692871692831790915560405191945092505f8051602062006dbc83398151915291508290a3506001600160a01b0380861660a05287811660c090815290851660e0908152840151908401516001600160581b0391821691161115620001505760405162461bcd60e51b815260206004820152601d60248201527f476f763a206d696e2d636c61696d2d67742d6d696e2d6163637275616c00000060448201526064015b60405180910390fd5b82516001600160801b039081166101a0526020840151670de0b6b3a7640000911610620001c05760405162461bcd60e51b815260206004820152601860248201527f476f763a20726567697374726174696f6e2d636f6e6669670000000000000000604482015260640162000147565b60208301516001600160801b039081166101c0526040840151670de0b6b3a7640000911611620002335760405162461bcd60e51b815260206004820152601a60248201527f476f763a20756e726567697374726174696f6e2d636f6e666967000000000000604482015260640162000147565b60408301516001600160801b039081166101e052606084015161ffff908116610200526080850151166102205260a0840151670de0b6b3a7640000911610620002b45760405162461bcd60e51b8152602060048201526012602482015271476f763a20766f74696e672d636f6e66696760701b604482015260640162000147565b60a08301516001600160801b03166102405260c08301516001600160581b039081166101605260e084015116610180526101008084015163ffffffff908116909152610120840151166200034b5760405162461bcd60e51b815260206004820152601860248201527f476f763a2065706f63682d6475726174696f6e2d7a65726f0000000000000000604482015260640162000147565b610120808401805163ffffffff90811690925251610140850151908216911610620003cc5760405162461bcd60e51b815260206004820152602a60248201527f476f763a2065706f63682d766f74696e672d6375746f66662d67742d65706f636044820152693416b23ab930ba34b7b760b11b606482015260840162000147565b6101408084015163ffffffff169052805115620003ee57620003ee81620003fb565b5050505050505062000896565b620004106001546001600160a01b0316331490565b6200045e5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000147565b5f5b81518110156200053957600160095f84848151811062000484576200048462000882565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548161ffff021916908361ffff1602179055507f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a828281518110620004fa57620004fa62000882565b602090810291909101810151604080516001600160a01b039092168252339282019290925260018183015290519081900360600190a160010162000460565b506200054462000547565b50565b6001546040515f916001600160a01b0316905f8051602062006dbc833981519152908390a3600180546001600160a01b0319169055565b6110508062005d6c83390190565b80516001600160a01b0381168114620005a3575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b60405161016081016001600160401b0381118282101715620005e257620005e2620005a8565b60405290565b80516001600160801b0381168114620005a3575f80fd5b805161ffff81168114620005a3575f80fd5b80516001600160581b0381168114620005a3575f80fd5b805163ffffffff81168114620005a3575f80fd5b5f82601f8301126200064c575f80fd5b815160206001600160401b03808311156200066b576200066b620005a8565b8260051b604051601f19603f83011681018181108482111715620006935762000693620005a8565b6040529384526020818701810194908101925087851115620006b3575f80fd5b6020870191505b84821015620006de57620006ce826200058c565b83529183019190830190620006ba565b979650505050505050565b5f805f805f805f87890361022081121562000702575f80fd5b6200070d896200058c565b97506200071d60208a016200058c565b96506200072d60408a016200058c565b95506200073d60608a016200058c565b945061016080607f198301121562000753575f80fd5b6200075d620005bc565b91506200076d60808b01620005e8565b82526200077d60a08b01620005e8565b60208301526200079060c08b01620005e8565b6040830152620007a360e08b01620005ff565b6060830152610100620007b8818c01620005ff565b6080840152610120620007cd818d01620005e8565b60a0850152610140620007e2818e0162000611565b60c0860152620007f4848e0162000611565b60e0860152620008086101808e0162000628565b838601526200081b6101a08e0162000628565b828601526200082e6101c08e0162000628565b9085015250919450620008489150506101e089016200058c565b6102008901519092506001600160401b0381111562000865575f80fd5b620008738a828b016200063c565b91505092959891949750929550565b634e487b7160e01b5f52603260045260245ffd5b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e051610200516102205161024051615370620009fc5f395f81816108d30152818161135b015281816113ac0152611ffa01525f8181610cf2015261144201525f61041001525f818161079f015261149301525f81816104be0152611bfa01525f818161076c0152611a9001525f8181610d560152612d7801525f818161048b0152611fb801525f818161033c01526117df01525f8181610b1a015281816110e901528181611521015261220901525f818161059001528181611127015281816114f401528181611545015281816121dc015261222d01525f81816107f101528181611a6c015281816126a0015281816127260152612d0b01525f61084301525f8181610d8901528181611c4c015281816128d80152818161328e015261410201525f818161091a015281816115800152612a6401526153705ff3fe608060405260043610610327575f3560e01c80638a00b67a116101a3578063a9952a0c116100f2578063cc904bec11610092578063e4a61d951161006d578063e4a61d9514610ce1578063e76c01e414610d14578063e772b76514610d45578063f556a79c14610d78575f80fd5b8063cc904bec14610c5d578063cc9baa1214610c94578063cf84a88c14610cb3575f80fd5b8063b1d8f274116100cd578063b1d8f27414610bbe578063bfd79cae14610beb578063c20fb59e14610c1f578063c3140c4914610c3e575f80fd5b8063a9952a0c14610b3c578063ac85912e14610b8a578063ac9650d814610b9e575f80fd5b8063900cf0cf1161015d578063985b11f911610138578063985b11f914610a9f578063a5c29ba514610ad5578063a5e8455d14610af4578063a70b9f0c14610b09575f80fd5b8063900cf0cf146109ea57806391050f40146109fe578063972e6d6614610a80575f80fd5b80638a00b67a146108c25780638b7d38a1146108f55780638d4f0b6c146109095780638da5cb5b1461093c5780638eb9399e146109595780638f32d59b146109c0575f80fd5b80634e0846c7116102795780636f844ae2116102195780637f6ec455116101f45780637f6ec455146108325780638113630d146108655780638777e0951461088457806388edf9be146108a3575f80fd5b80636f844ae2146107c1578063727d0f35146107e057806378a1bdd414610813575f80fd5b8063539054021161025457806353905402146106a05780635c79696c1461072f57806364b4f7511461075b5780636bca7c551461078e575f80fd5b80634e0846c7146105b257806350283275146105e0578063524fcc80146105f4575f80fd5b80631cd2a05d116102e457806338f3e285116102bf57806338f3e285146104ff5780633c12e04e146105415780633f37dc771461056057806346d62a631461057f575f80fd5b80631cd2a05d1461047a5780632591003a146104ad5780632c2cc7da146104e0575f80fd5b80630df9049b1461032b5780630ecc535f146103715780630f2f6aa5146103de5780631030e7b6146103ff57806315e5a1e5146104325780631b4ff3201461045b575b5f80fd5b348015610336575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b34801561037c575f80fd5b506103b761038b366004614602565b60066020525f90815260409020546001600160581b03811690600160581b90046001600160781b031682565b604080516001600160581b0390931683526001600160781b03909116602083015201610368565b3480156103e9575f80fd5b506103fd6103f8366004614602565b610dab565b005b34801561040a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561043d575f80fd5b506104466110c9565b60405163ffffffff9091168152602001610368565b348015610466575f80fd5b506103fd610475366004614631565b611151565b348015610485575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104b8575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104eb575f80fd5b506103fd6104fa366004614657565b611158565b34801561050a575f80fd5b5061052e610519366004614602565b60096020525f908152604090205461ffff1681565b60405161ffff9091168152602001610368565b34801561054c575f80fd5b506103fd61055b366004614631565b611204565b34801561056b575f80fd5b506103fd61057a3660046146af565b611210565b34801561058a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105bd575f80fd5b506105d16105cc36600461483b565b611220565b6040516103689392919061492b565b3480156105eb575f80fd5b506104466114f1565b3480156105ff575f80fd5b5061065d61060e366004614602565b60076020525f9081526040902080546001909101546001600160581b0380831692600160581b900416906001600160781b0380821691600160781b810490911690600160f01b900461ffff1685565b604080516001600160581b0396871681529590941660208601526001600160781b039283169385019390935216606083015261ffff16608082015260a001610368565b3480156106ab575f80fd5b506106f66106ba366004614602565b60046020525f9081526040902080546001909101546001600160e01b038083169261ffff600160e01b8204811693600160f01b90920416911684565b60405161036894939291906001600160e01b03948516815261ffff93841660208201529190921660408201529116606082015260800190565b34801561073a575f80fd5b50610743611579565b6040516001600160a01b039091168152602001610368565b348015610766575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610799575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156107cc575f80fd5b506103fd6107db3660046149aa565b6115e2565b3480156107eb575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b34801561081e575f80fd5b506103fd61082d366004614602565b611a57565b34801561083d575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b348015610870575f80fd5b506103fd61087f366004614a64565b611e39565b34801561088f575f80fd5b5061035e61089e366004614b0b565b611f6f565b3480156108ae575f80fd5b506103fd6108bd366004614b22565b612037565b3480156108cd575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610900575f80fd5b5061035e6120c8565b348015610914575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b348015610947575f80fd5b506001546001600160a01b0316610743565b348015610964575f80fd5b5061096d6120ea565b6040805184516001600160f01b0316815260209485015161ffff168582015283516001600160581b03169181019190915292909101516001600160781b031660608301521515608082015260a001610368565b3480156109cb575f80fd5b506001546001600160a01b031633146040519015158152602001610368565b3480156109f5575f80fd5b5061052e6121d9565b348015610a09575f80fd5b50610a56610a18366004614b74565b600860209081525f92835260408084209091529082529020546001600160581b0380821691600160581b810490911690600160b01b900461ffff1683565b604080516001600160581b03948516815293909216602084015261ffff1690820152606001610368565b348015610a8b575f80fd5b506105d1610a9a366004614602565b612267565b348015610aaa575f80fd5b50610abd6a52b7d2dcc80cd2e400000081565b6040516001600160781b039091168152602001610368565b348015610ae0575f80fd5b506103fd610aef366004614b9c565b6122a1565b348015610aff575f80fd5b5061035e60025481565b348015610b14575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610b47575f80fd5b50600354610b68906001600160f01b03811690600160f01b900461ffff1682565b604080516001600160f01b03909316835261ffff909116602083015201610368565b348015610b95575f80fd5b5061035e61233b565b610bb1610bac366004614be8565b612352565b6040516103689190614c73565b348015610bc9575f80fd5b50610bdd610bd8366004614602565b612498565b604051610368929190614d10565b348015610bf6575f80fd5b50610c0a610c05366004614602565b6124f7565b60408051928352602083019190915201610368565b348015610c2a575f80fd5b5061035e610c39366004614602565b6125ad565b348015610c49575f80fd5b506103fd610c58366004614657565b612814565b348015610c68575f80fd5b50610c7c610c77366004614d3c565b612a2d565b6040516001600160d01b039091168152602001610368565b348015610c9f575f80fd5b50610743610cae366004614602565b612a5e565b348015610cbe575f80fd5b50610cd2610ccd366004614602565b612a98565b60405161036893929190614dc3565b348015610cec575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610d1f575f80fd5b506005546103b7906001600160581b03811690600160581b90046001600160781b031682565b348015610d50575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b348015610d83575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b610db3612c68565b5f80610dbd612c90565b915091505f80610dcc85612e06565b915091505f610ddd86868585611220565b509091505f9050816006811115610df657610df6614917565b03610e565760405162461bcd60e51b815260206004820152602560248201527f476f7665726e616e63653a20696e69746961746976652d6e6f742d72656769736044820152641d195c995960da1b60648201526084015b60405180910390fd5b6001816006811115610e6a57610e6a614917565b03610ec15760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a20696e69746961746976652d696e2d7761726d2d756044820152600760fc1b6064820152608401610e4d565b6005816006811115610ed557610ed5614917565b14610f335760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b6064820152608401610e4d565b5f610f3c6121d9565b9050610f49600182614e04565b61ffff16836080015161ffff1610610f6357610f63614e1f565b6020850151604084015186518551610f8793929190610f829082614e33565b612f39565b6001600160781b03166020860152825185516001600160581b0391821691161015610fb457610fb4614e1f565b825185518690610fc5908390614e33565b6001600160581b039081169091528651600580546020808b01516001600160781b0316600160581b026001600160d01b031990921693909416929092179190911790556001600160a01b0389165f81815260098352604090819020805461ffff191661ffff90811790915581519283528516928201929092527f8231be6875060a0fe4f35eaa6e112ebc15b8c04993114fe41c976bcb0a8c5ad892500160405180910390a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316630a9a78f360e11b1790526130f1565b505050505050506110c660015f55565b50565b5f806110d36121d9565b90508061ffff165f036110e7575f91505090565b7f0000000000000000000000000000000000000000000000000000000000000000611113600183614e04565b61ffff166111219190614e53565b61114b907f0000000000000000000000000000000000000000000000000000000000000000614e6a565b91505090565b6110c6815f335b611160612c68565b5f61116a84613152565b6040516338fb359960e01b81526001600160581b038616600482015233602482015284151560448201526001600160a01b038481166064830152919250908216906338fb35999060840160408051808303815f875af11580156111cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111f39190614e7d565b5050506111ff60015f55565b505050565b6110c681600133612814565b61121c82825f336122a1565b5050565b6001600160a01b0384165f908152600960205260408120548190819061ffff16810361125357505f9150819050806114e7565b61125b6121d9565b6001600160a01b0388165f9081526009602052604090205461ffff91821691160361128e5750600191505f9050806114e7565b6001600160a01b0387165f9081526007602090815260408083206001015460099092529091205461ffff600160f01b9092048216935061fffe199116016112da5750600691505f6114e7565b60016112e46121d9565b6112ee9190614e04565b61ffff168261ffff161061130557600492506114e7565b5f61131b875f01516001600160f01b0316611f6f565b8651606088015189519293506001600160e01b03918216929116906001600160f01b0316838311801561134d57508282105b15611428578061137f6127107f0000000000000000000000000000000000000000000000000000000000000000614eb3565b61138f85655af3107a4000614e53565b6113999190614eb3565b116113a6576113a6614e1f565b5f6113d97f0000000000000000000000000000000000000000000000000000000000000000670de0b6b3a7640000614eb3565b6113e4906001614e6a565b90505f816002548484886113f89190614e53565b6114029190614eb3565b61140c9190614e53565b6114169190614eb3565b6003995096506114e795505050505050565b60016114326121d9565b61143c9190614e04565b61ffff167f0000000000000000000000000000000000000000000000000000000000000000896080015161ffff166114749190614e6a565b10806114c5575082821180156114c55750670de0b6b3a76400006114b87f000000000000000000000000000000000000000000000000000000000000000086614e53565b6114c29190614eb3565b82115b156114db5750600595505f93506114e792505050565b50600295505f93505050505b9450945094915050565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561151f57505f90565b7f000000000000000000000000000000000000000000000000000000000000000061156a7f000000000000000000000000000000000000000000000000000000000000000042614ec6565b6115749190614ed9565b905090565b5f806115a57f0000000000000000000000000000000000000000000000000000000000000000336133b3565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6115ea612c68565b8483146116225760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b80831461165a5760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b611664888861341e565b61166e868661341e565b6116a98484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b6116e48282808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b61174f8484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f9201919091525061357092505050565b5f61175a8989613614565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b03169183019190915291925090156117dd5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610e4d565b7f00000000000000000000000000000000000000000000000000000000000000006118066114f1565b63ffffffff1611156119a7575f5b878110156119a5575f805b845181101561191f578a8a8481811061183a5761183a614eec565b905060200201602081019061184f9190614602565b6001600160a01b031685828151811061186a5761186a614eec565b60200260200101515f01516001600160a01b031603611917576001915084818151811061189957611899614eec565b602002602001015160200151600a0b8989858181106118ba576118ba614eec565b90506020020160208101906118cf9190614f00565b600a0b13156119125760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610e4d565b61191f565b60010161181f565b508061199c5787878381811061193757611937614eec565b905060200201602081019061194c9190614f00565b600a0b1561199c5760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610e4d565b50600101611814565b505b611a428888808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284375f9201919091525050604080516020808b0282810182019093528a82529093508a9250899182918501908490808284375f920191909152506139b292505050565b5050611a4d60015f55565b5050505050505050565b611a5f612c68565b611ab46001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633307f0000000000000000000000000000000000000000000000000000000000000000614282565b6001600160a01b038116611b0a5760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610e4d565b5f611b1482612267565b509091505f9050816006811115611b2d57611b2d614917565b14611b8c5760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610e4d565b5f611b9633612a5e565b90505f611ba1612c90565b50335f908152600660209081526040918290208251808401909352546001600160581b0381168352600160581b90046001600160781b0316908201528151919250906001600160f01b0316670de0b6b3a7640000611c1f7f000000000000000000000000000000000000000000000000000000000000000083614e53565b611c299190614eb3565b6040516305a4d3f160e21b81526001600160a01b038681166004830152611ce5917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa158015611c93573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cb79190614f20565b6a52b7d2dcc80cd2e4000000611ccb6110c9565b63ffffffff16611cdb9190614f37565b8560200151612a2d565b6001600160d01b03161015611d3c5760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610e4d565b5f611d456121d9565b6001600160a01b0388165f908152600960205260409020805461ffff191661ffff83161790559050611d78600182614e04565b6001600160a01b0388165f8181526007602090815260409182902060010180546001600160f01b0316600160f01b61ffff96871602179055815192835233908301529183168183015290517f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a916060908290030190a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316628152f560e81b1790526130f1565b6001546001600160a01b03163314611e935760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610e4d565b5f5b8151811015611f6657600160095f848481518110611eb557611eb5614eec565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548161ffff021916908361ffff1602179055507f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a828281518110611f2857611f28614eec565b602090810291909101810151604080516001600160a01b039092168252339282019290925260018183015290519081900360600190a1600101611e95565b506110c66142e9565b5f815f03611f7e57505f919050565b5f8083670de0b6b3a7640000600254611f979190614e53565b611fa19190614eb3565b90508015611fe95780611fdc670de0b6b3a76400007f0000000000000000000000000000000000000000000000000000000000000000614e53565b611fe69190614eb3565b91505b61202f670de0b6b3a764000061201f7f000000000000000000000000000000000000000000000000000000000000000087614e53565b6120299190614eb3565b83614332565b949350505050565b61203f612c68565b612049838361341e565b6120538383613614565b5080156120bf57335f908152600660205260409020546001600160581b0316156120bf5760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610e4d565b6111ff60015f55565b5f806120d2612c90565b50905061114b815f01516001600160f01b0316611f6f565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f8061211a6121d9565b6040805180820182526003546001600160f01b0381168252600160f01b900461ffff1660208083019190915282518084019093526005546001600160581b0381168452600160581b90046001600160781b031690830152955093509050612182600182614e04565b61ffff16846020015161ffff1610156121d3578251600192506121b3906a52b7d2dcc80cd2e4000000611ccb6110c9565b6001600160d01b031684526121c9600182614e04565b61ffff1660208501525b50909192565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561220757505f90565b7f00000000000000000000000000000000000000000000000000000000000000006122527f000000000000000000000000000000000000000000000000000000000000000042614ec6565b61225c9190614eb3565b611574906001614e6a565b5f805f80612273612c90565b5090505f8061228187612e06565b9150915061229187848484611220565b9550955095505050509193909250565b6122a9612c68565b5f6122b385613152565b60405163eb876bf760e01b81529091506001600160a01b0382169063eb876bf7906122ea9088903390899089908990600401614f62565b60408051808303815f875af1158015612305573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123299190614e7d565b50505061233560015f55565b50505050565b6003545f906001600160f01b031661114b81611f6f565b6060816001600160401b0381111561236c5761236c6146e2565b60405190808252806020026020018201604052801561239f57816020015b606081526020019060019003908161238a5790505b5090505f5b82811015612491575f80308686858181106123c1576123c1614eec565b90506020028101906123d3919061500f565b6040516123e1929190615051565b5f60405180830381855af49150503d805f8114612419576040519150601f19603f3d011682016040523d82523d5f602084013e61241e565b606091505b50915091508161246957604481511015612436575f80fd5b600481019050808060200190518101906124509190615060565b60405162461bcd60e51b8152600401610e4d91906150ee565b8084848151811061247c5761247c614eec565b602090810291909101015250506001016123a4565b5092915050565b6040805180820182525f808252602080830182905283516080810185528281529081018290529283018190526060830152906124d2612c68565b6124da612c90565b5091506124e683612e06565b5090506124f260015f55565b915091565b5f805f61250333612a5e565b9050806001600160a01b03163b5f0361252e5760405162461bcd60e51b8152600401610e4d90615100565b604051633fdf42e360e01b81525f6004820152600160248201526001600160a01b038581166044830152821690633fdf42e39060640160408051808303815f875af115801561257f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125a39190614e7d565b9250925050915091565b5f6125b6612c68565b5f6125bf612c90565b5090505f806125cd85612e06565b915091505f806125df87868686611220565b919350909150600390508260068111156125fb576125fb614917565b1461260d575f95505050505050612806565b60016126176121d9565b6126219190614e04565b61ffff16856020015161ffff161461263b5761263b614e1f565b60016126456121d9565b61264f9190614e04565b6001600160a01b038881165f90815260076020526040808220600101805461ffff95909516600160f01b026001600160f01b039095169490941790935591516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa1580156126e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061270b9190614f20565b905080821115612719578091505b61274d6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168984614347565b602086810151604080516001600160a01b038c16815292830185905261ffff90911682820152517fa1ee42ce2e3b0e300645a781ea3e7c494f626cfeb1c2cce20e2fa4a5da249d189181900360600190a16127fc88620557305f8960200151866040516024016127cd92919061ffff929092168252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b03166313440fab60e01b1790526130f1565b5090955050505050505b61280f60015f55565b919050565b61281c612c68565b335f90815260066020526040902080546001600160581b0316156128825760405162461bcd60e51b815260206004820152601e60248201527f476f7665726e616e63653a206d7573742d616c6c6f636174652d7a65726f00006044820152606401610e4d565b5f61288c33612a5e565b9050806001600160a01b03163b5f036128b75760405162461bcd60e51b8152600401610e4d90615100565b6040516305a4d3f160e21b81526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa15801561291f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129439190614f20565b604051633fdf42e360e01b81526001600160581b038816600482015286151560248201526001600160a01b0386811660448301529192505f91829190851690633fdf42e39060640160408051808303815f875af11580156129a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129ca9190614e7d565b604080513381526001600160581b038c1660208201529081018390526060810182905291935091507f71330c97f647d5136a666c3cf5cef9ec255ec0468d9d7891929eff0f4845c4589060800160405180910390a150505050506111ff60015f55565b5f612a388383614378565b612a54906001600160781b03166001600160581b038616615143565b90505b9392505050565b5f612a927f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0384166143b6565b92915050565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f80612aef6121d9565b6001600160a01b0386165f81815260046020908152604080832081516080808201845282546001600160e01b03808216845261ffff600160e01b8304811685890152600160f01b928390048116858801526001958601549091166060808601919091529888526007875296859020855160a08101875281546001600160581b038082168352600160581b90910416978101979097528401546001600160781b0380821696880196909652600160781b8104909516978601979097529590920490931693820193909352919650909450909150612bcb9082614e04565b61ffff16846020015161ffff161015612c6057600191505f6a52b7d2dcc80cd2e4000000612bf76110c9565b63ffffffff16612c079190614f37565b90505f612c1c855f0151838760400151612a2d565b90505f612c328660200151848860600151612a2d565b6001600160d01b038084168952811660608901529050612c53600185614e04565b61ffff1660208801525050505b509193909250565b60025f5403612c8a57604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612cbf6120ea565b919450925090508015612e01578251602084015161ffff16600160f01b026001600160f01b03909116176003556040516370a0823160e01b81523060048201525f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612d50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d749190614f20565b90507f00000000000000000000000000000000000000000000000000000000000000008110612da35780612da5565b5f5b600255835160208501516040517ff8c3496a83a68868434a061a876d356df06c7ef5bd8476f13d496b966855686b92612df79290916001600160f01b0392909216825261ffff16602082015260400190565b60405180910390a1505b509091565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f612e5d84612a98565b919450925090508015612f33576001600160a01b0384165f818152600460209081526040918290208651815488840151898601516001600160e01b039384166001600160f01b03199093168317600160e01b61ffff938416908102919091176001600160f01b0316600160f01b93909216929092021784556060808b0151600190950180546001600160e01b0319169590941694909417909255845195865292850192909252918301527f6f22c33d2cf529b6a07e72fdf3fd25bb07828d8017cc4d47405c3ec9cadf6ffd910160405180910390a15b50915091565b5f816001600160581b03165f03612f5157505f61202f565b5f612f6d6a52b7d2dcc80cd2e400000063ffffffff4216614f37565b90505f612f7a8288614378565b90505f612f878388614378565b90505f856001600160581b0316876001600160581b031611613019575f612fae8888614e33565b90505f612fcd6001600160781b0386166001600160581b038b16615143565b90505f612fec6001600160781b0386166001600160581b038516615143565b90505f612ff98284615175565b905061300e6001600160581b038b1682615195565b9450505050506130ab565b5f6130248789614e33565b90505f6130436001600160781b0386166001600160581b038b16615143565b90505f6130626001600160781b0386166001600160581b038516615143565b90505f816001600160d01b0316836001600160d01b03161015613085575f61308f565b61308f82846151ba565b90506130a46001600160581b038b1682615195565b9450505050505b836001600160781b0316816001600160d01b031611156130d1575f94505050505061202f565b6130e4816001600160781b0386166151ba565b9998505050505050505050565b5f6130fe846103e8614415565b61313d5760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610e4d565b5f80835160208501868989f195945050505050565b5f80826001600160581b0316116131ab5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610e4d565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b031691830191909152156132445760405162461bcd60e51b815260206004820152602360248201527f476f7665726e616e63653a206d7573742d62652d7a65726f2d616c6c6f63617460448201526234b7b760e91b6064820152608401610e4d565b5f61324e33612a5e565b9050806001600160a01b03163b5f0361326b57613269611579565b505b6040516305a4d3f160e21b81526001600160a01b03808316600483015282915f917f000000000000000000000000000000000000000000000000000000000000000016906316934fc490602401602060405180830381865afa1580156132d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132f79190614f20565b905061332384602001516a52b7d2dcc80cd2e4000000426133189190614f37565b83610f828a826151da565b6001600160781b039081166020868101918252335f81815260068352604090819020895181549551909616600160581b026001600160d01b03199095166001600160581b0396871617949094179093558251908152928916908301527f841ac74294f07d75868ac51778dc366b1fa11e8934b969cd5b0185c0e91dabb1910160405180910390a150949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612a92576040516330be1a3d60e21b815260040160405180910390fd5b805f81900361342c57505050565b5f5b613439600183614ec6565b811015612335575f61344c826001614e6a565b90505b828110156134f25784848281811061346957613469614eec565b905060200201602081019061347e9190614602565b6001600160a01b031685858481811061349957613499614eec565b90506020020160208101906134ae9190614602565b6001600160a01b0316036134ea5760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610e4d565b60010161344f565b5060010161342e565b80515f5b818110156111ff575f83828151811061351a5761351a614eec565b6020026020010151600a0b12156135685760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610e4d565b6001016134ff565b5f5b82518110156111ff575f83828151811061358e5761358e614eec565b6020026020010151600a0b13806135c057505f8282815181106135b3576135b3614eec565b6020026020010151600a0b135b61360c5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610e4d565b600101613572565b60605f826001600160401b0381111561362f5761362f6146e2565b60405190808252806020026020018201604052801561367857816020015b604080516060810182525f80825260208083018290529282015282525f1990920191018161364d5790505b5090505f836001600160401b03811115613694576136946146e2565b6040519080825280602002602001820160405280156136bd578160200160208202803683370190505b5090505f846001600160401b038111156136d9576136d96146e2565b604051908082528060200260200182016040528015613702578160200160208202803683370190505b5090505f5b8581101561396857335f9081526008602052604081208189898581811061373057613730614eec565b90506020020160208101906137459190614602565b6001600160a01b0316815260208082019290925260409081015f20815160608101835290546001600160581b03808216808452600160581b830490911694830194909452600160b01b900461ffff169181019190915291501515806137b657505f81602001516001600160581b0316115b6138025760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610e4d565b80516001600160571b036001600160581b03909116111561382557613825614e1f565b6001600160571b036001600160581b031681602001516001600160581b0316111561385257613852614e1f565b604051806060016040528089898581811061386f5761386f614eec565b90506020020160208101906138849190614602565b6001600160a01b03168152602001825f0151600a0b81526020018260200151600a0b8152508583815181106138bb576138bb614eec565b60200260200101819052508482815181106138d8576138d8614eec565b6020026020010151602001516138ed906151fa565b8483815181106138ff576138ff614eec565b6020026020010190600a0b9081600a0b8152505084828151811061392557613925614eec565b60200260200101516040015161393a906151fa565b83838151811061394c5761394c614eec565b600a9290920b6020928302919091019091015250600101613707565b506139a88686808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508692508591506139b29050565b5090949350505050565b815183511480156139c4575080518351145b613a1a5760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610e4d565b5f80613a24612c90565b915091505f613a316121d9565b335f9081526006602090815260408083208151808301909252546001600160581b0381168252600160581b90046001600160781b0316918101919091529192505b87518110156140ed575f888281518110613a8e57613a8e614eec565b602002602001015190505f888381518110613aab57613aab614eec565b602002602001015190505f888481518110613ac857613ac8614eec565b6020026020010151905081600a0b5f141580613ae7575080600a0b5f14155b613af357613af3614e1f565b5f80613afe85612e06565b915091505f613b0f868c8585611220565b505090505f85600a0b1380613b2657505f84600a0b135b15613bc4576002816006811115613b3f57613b3f614917565b1480613b5c57506003816006811115613b5a57613b5a614917565b145b80613b7857506004816006811115613b7657613b76614917565b145b613bc45760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610e4d565b6006816006811115613bd857613bd8614917565b03613c36575f85600a0b13158015613bf357505f84600a0b13155b613c365760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610e4d565b5f6040518060a00160405280845f01516001600160581b0316815260200184602001516001600160581b0316815260200184604001516001600160781b0316815260200184606001516001600160781b03168152602001846080015161ffff168152509050613cb983604001518a60200151855f0151610f82875f01518b614432565b6001600160781b0316604084015260608301516020808b015190850151613ce6929190610f82818a614432565b6001600160781b031660608401528251613d009087614432565b6001600160581b031683526020830151613d1a9086614432565b6001600160581b0390811660208086019182526001600160a01b038a165f90815260079091526040908190208651815493518516600160581b026001600160b01b0319909416941693909317919091178255840151600190910180546060860151608087015161ffff16600160f01b026001600160f01b036001600160781b03928316600160781b026001600160f01b0319909416929095169190911791909117929092169190911790556006826006811115613dd957613dd9614917565b14613e925760208b015160408201518c518351613dfd93929190610f829082614e33565b6001600160781b031660208c015280518b516001600160581b0391821691161015613e2a57613e2a614e1f565b80518b518c90613e3b908390614e33565b6001600160581b031690525060208b015160408401518c518551613e6693929190610f8290826151da565b6001600160781b031660208c015282518b518c90613e859083906151da565b6001600160581b03169052505b335f9081526008602090815260408083206001600160a01b038b168452825291829020825160608101845290546001600160581b03808216808452600160581b830490911693830193909352600160b01b900461ffff1692810192909252613efa9088614432565b6001600160581b031681526020810151613f149087614432565b6001600160581b03908116602083015261ffff8c16604083015281511615801590613f4b575060208101516001600160581b031615155b15613f985760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610e4d565b335f9081526008602090815260408083206001600160a01b038c1684528252918290208351815492850151938501516001600160581b039182166001600160b01b031990941693909317600160581b91909416029290921761ffff60b01b1916600160b01b61ffff90921691909102179055895161401f9061401a888a61521e565b614432565b6001600160581b03168a52604080513381526001600160a01b038a166020820152600a89810b8284015288900b606082015261ffff8d16608082015290517ffc65ef0a2ea71843c9c13a7a7c0bdccb27001f82e748634c3155cba36faccc2c9181900360a00190a16140d888620557305f8e338f878b6040516024016140a995949392919061524f565b60408051601f198184030181529190526020810180516001600160e01b0316633d0fb77360e01b1790526130f1565b505060019097019650613a7295505050505050565b5080516001600160581b031615806141b757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166316934fc461413833612a5e565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa15801561417a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061419e9190614f20565b6001600160581b0316815f01516001600160581b031611155b6142165760405162461bcd60e51b815260206004820152602a60248201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60448201526963617465642d6c71747960b01b6064820152608401610e4d565b8251600580546020958601516001600160781b03908116600160581b9081026001600160d01b03199384166001600160581b039687161717909355335f908152600688526040902085518154969098015190911690920293169490911693909317179091555050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526123359186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061446b565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b5f8183116143405781612a57565b5090919050565b6040516001600160a01b038381166024830152604482018390526111ff91859182169063a9059cbb906064016142b7565b5f6001600160781b03821615806143a05750816001600160781b0316836001600160781b0316105b156143ac57505f612a92565b612a5782846152ca565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90612a57565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f8082600a0b121561445857614447826144cc565b6144519084614e33565b9050612a92565b614461826144cc565b612a5790846151da565b5f61447f6001600160a01b038416836144ea565b905080515f141580156144a35750808060200190518101906144a191906152ea565b155b156111ff57604051635274afe760e01b81526001600160a01b0384166004820152602401610e4d565b5f8082600a0b126144dd5781612a92565b81600a0b612a9290615305565b6060612a5783835f845f80856001600160a01b0316848660405161450e919061531f565b5f6040518083038185875af1925050503d805f8114614548576040519150601f19603f3d011682016040523d82523d5f602084013e61454d565b606091505b509150915061455d868383614567565b9695505050505050565b60608261457c57614577826145c3565b612a57565b815115801561459357506001600160a01b0384163b155b156145bc57604051639996b31560e01b81526001600160a01b0385166004820152602401610e4d565b5080612a57565b8051156145d35780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461280f575f80fd5b5f60208284031215614612575f80fd5b612a57826145ec565b80356001600160581b038116811461280f575f80fd5b5f60208284031215614641575f80fd5b612a578261461b565b80151581146110c6575f80fd5b5f805f60608486031215614669575f80fd5b6146728461461b565b925060208401356146828161464a565b9150614690604085016145ec565b90509250925092565b5f60e082840312156146a9575f80fd5b50919050565b5f8061010083850312156146c1575f80fd5b6146ca8361461b565b91506146d98460208501614699565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715614718576147186146e2565b60405290565b604051608081016001600160401b0381118282101715614718576147186146e2565b604051601f8201601f191681016001600160401b0381118282101715614768576147686146e2565b604052919050565b803561ffff8116811461280f575f80fd5b80356001600160e01b038116811461280f575f80fd5b80356001600160781b038116811461280f575f80fd5b5f60a082840312156147bd575f80fd5b60405160a081018181106001600160401b03821117156147df576147df6146e2565b6040529050806147ee8361461b565b81526147fc6020840161461b565b602082015261480d60408401614797565b604082015261481e60608401614797565b606082015261482f60808401614770565b60808201525092915050565b5f805f80848603610180811215614850575f80fd5b614859866145ec565b94506040601f198201121561486c575f80fd5b6148746146f6565b60208701356001600160f01b038116811461488d575f80fd5b815261489b60408801614770565b602082015293506080605f19820112156148b3575f80fd5b506148bc61471e565b6148c860608701614781565b81526148d660808701614770565b60208201526148e760a08701614770565b60408201526148f860c08701614781565b6060820152915061490c8660e087016147ad565b905092959194509250565b634e487b7160e01b5f52602160045260245ffd5b606081016007851061494b57634e487b7160e01b5f52602160045260245ffd5b93815261ffff92909216602083015260409091015290565b5f8083601f840112614973575f80fd5b5081356001600160401b03811115614989575f80fd5b6020830191508360208260051b85010111156149a3575f80fd5b9250929050565b5f805f805f805f806080898b0312156149c1575f80fd5b88356001600160401b03808211156149d7575f80fd5b6149e38c838d01614963565b909a50985060208b01359150808211156149fb575f80fd5b614a078c838d01614963565b909850965060408b0135915080821115614a1f575f80fd5b614a2b8c838d01614963565b909650945060608b0135915080821115614a43575f80fd5b50614a508b828c01614963565b999c989b5096995094979396929594505050565b5f6020808385031215614a75575f80fd5b82356001600160401b0380821115614a8b575f80fd5b818501915085601f830112614a9e575f80fd5b813581811115614ab057614ab06146e2565b8060051b9150614ac1848301614740565b8181529183018401918481019088841115614ada575f80fd5b938501935b83851015614aff57614af0856145ec565b82529385019390850190614adf565b98975050505050505050565b5f60208284031215614b1b575f80fd5b5035919050565b5f805f60408486031215614b34575f80fd5b83356001600160401b03811115614b49575f80fd5b614b5586828701614963565b9094509250506020840135614b698161464a565b809150509250925092565b5f8060408385031215614b85575f80fd5b614b8e836145ec565b91506146d9602084016145ec565b5f805f806101408587031215614bb0575f80fd5b614bb98561461b565b9350614bc88660208701614699565b9250610100850135614bd98161464a565b915061490c61012086016145ec565b5f8060208385031215614bf9575f80fd5b82356001600160401b03811115614c0e575f80fd5b614c1a85828601614963565b90969095509350505050565b5f5b83811015614c40578181015183820152602001614c28565b50505f910152565b5f8151808452614c5f816020860160208601614c26565b601f01601f19169290920160200192915050565b5f60208083016020845280855180835260408601915060408160051b8701019250602087015f5b82811015614cc857603f19888603018452614cb6858351614c48565b94509285019290850190600101614c9a565b5092979650505050505050565b80516001600160e01b03908116835260208083015161ffff908116918501919091526040808401519091169084015260609182015116910152565b82516001600160f01b0316815260208084015161ffff169082015260c08101612a576040830184614cd5565b5f805f60608486031215614d4e575f80fd5b614d578461461b565b9250614d6560208501614797565b915061469060408501614797565b6001600160581b038082511683528060208301511660208401525060408101516001600160781b038082166040850152806060840151166060850152505061ffff60808201511660808301525050565b6101408101614dd28286614cd5565b614ddf6080830185614d73565b821515610120830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b61ffff82811682821603908082111561249157612491614df0565b634e487b7160e01b5f52600160045260245ffd5b6001600160581b0382811682821603908082111561249157612491614df0565b8082028115828204841417612a9257612a92614df0565b80820180821115612a9257612a92614df0565b5f8060408385031215614e8e575f80fd5b505080516020909101519092909150565b634e487b7160e01b5f52601260045260245ffd5b5f82614ec157614ec1614e9f565b500490565b81810381811115612a9257612a92614df0565b5f82614ee757614ee7614e9f565b500690565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614f10575f80fd5b813580600a0b8114612a57575f80fd5b5f60208284031215614f30575f80fd5b5051919050565b6001600160781b03818116838216028082169190828114614f5a57614f5a614df0565b505092915050565b6001600160581b03861681526001600160a01b03858116602083015261016082019080614f8e876145ec565b16604084015280614fa1602088016145ec565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614fcf575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f808335601e19843603018112615024575f80fd5b8301803591506001600160401b0382111561503d575f80fd5b6020019150368190038213156149a3575f80fd5b818382375f9101908152919050565b5f60208284031215615070575f80fd5b81516001600160401b0380821115615086575f80fd5b818401915084601f830112615099575f80fd5b8151818111156150ab576150ab6146e2565b6150be601f8201601f1916602001614740565b91508082528560208285010111156150d4575f80fd5b6150e5816020840160208601614c26565b50949350505050565b602081525f612a576020830184614c48565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b6001600160d01b0382811682821681810283169291811582850482141761516c5761516c614df0565b50505092915050565b6001600160d01b0381811683821601908082111561249157612491614df0565b5f6001600160d01b03838116806151ae576151ae614e9f565b92169190910492915050565b6001600160d01b0382811682821603908082111561249157612491614df0565b6001600160581b0381811683821601908082111561249157612491614df0565b5f81600a0b6001600160571b0319810361521657615216614df0565b5f0392915050565b600a81810b9083900b016001600160571b0381136a7fffffffffffffffffffff1982121715612a9257612a92614df0565b61ffff86811682526001600160a01b0386166020830152610180820190615295604084018780516001600160581b031682526020908101516001600160781b0316910152565b84516001600160581b03908116608085015260208601511660a084015260408501511660c083015261455d60e0830184614d73565b6001600160781b0382811682821603908082111561249157612491614df0565b5f602082840312156152fa575f80fd5b8151612a578161464a565b5f600160ff1b820161531957615319614df0565b505f0390565b5f8251615330818460208701614c26565b919091019291505056fea26469706673582212207c8aca494c606851d6ce92ed1d9a36a4bbb9f7d57cefde59074d285e103bc4a664736f6c63430008180033610100604052348015610010575f80fd5b5060405161105038038061105083398101604081905261002f9161006b565b6001600160a01b0392831660805290821660a0521660c0523360e0526100ab565b80516001600160a01b0381168114610066575f80fd5b919050565b5f805f6060848603121561007d575f80fd5b61008684610050565b925061009460208501610050565b91506100a260408501610050565b90509250925092565b60805160a05160c05160e051610f0261014e5f395f818161016b015281816102ac015281816104fc015261086401525f81816101f0015281816102290152818161042c015261066701525f818161019e01528181610309015281816105e30152818161092501528181610ad30152610b6a01525f8181610120015281816103ab01528181610550015281816106dc0152818161077c01526109a30152610f025ff3fe60806040526004361061007c575f3560e01c8063886117361161004c578063886117361461015a57806399ad68a71461018d578063eb876bf7146101c0578063f556a79c146101df575f80fd5b80630b76619b1461008757806338fb3599146100bc5780633fdf42e3146100f05780637f6ec4551461010f575f80fd5b3661008357005b5f80fd5b348015610092575f80fd5b5061009b610212565b6040516affffffffffffffffffffff90911681526020015b60405180910390f35b3480156100c7575f80fd5b506100db6100d6366004610d0d565b61029f565b604080519283526020830191909152016100b3565b3480156100fb575f80fd5b506100db61010a366004610d59565b6104ef565b34801561011a575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100b3565b348015610165575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b348015610198575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b3480156101cb575f80fd5b506100db6101da366004610d94565b610857565b3480156101ea575f80fd5b506101427f000000000000000000000000000000000000000000000000000000000000000081565b6040516305a4d3f160e21b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906316934fc490602401602060405180830381865afa158015610276573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061029a9190610dfe565b905090565b5f80336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102f25760405162461bcd60e51b81526004016102e990610e15565b60405180910390fd5b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610356573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061037a9190610dfe565b6040516323b872dd60e01b81526001600160a01b038881166004830152306024830152604482018a905291925047917f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303815f875af11580156103f1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104159190610e4c565b5060405163534a7e1d60e11b8152600481018990527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a694fc3a906024015f604051808303815f87803b158015610475575f80fd5b505af1158015610487573d5f803e3d5ffd5b5050604080518b81526001600160a01b038b1660208201527f2bccdce62e5aec7ee273161a374088a6da4311d0e688784bde3c1cec8a3c003a935001905060405180910390a185156104e4576104de858383610ab2565b90945092505b505094509492505050565b5f80336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105395760405162461bcd60e51b81526004016102e990610e15565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561059d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105c19190610dfe565b6040516370a0823160e01b81523060048201529091505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610628573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061064c9190610dfe565b6040516305c2fbcf60e31b81526004810189905290915047907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e17de78906024015f604051808303815f87803b1580156106b0575f80fd5b505af11580156106c2573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201525f92507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a0823190602401602060405180830381865afa15801561072a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061074e9190610dfe565b905080156107e85760405163a9059cbb60e01b81526001600160a01b038881166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af11580156107c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107e69190610e4c565b505b6001600160a01b0387167ff960dbf9e5d0682f7a298ed974e33a28b4464914b7a2bfac12ae419a9afeb28061081d8684610e6e565b60408051918252602082018590520160405180910390a2871561084b57610845878484610ab2565b90965094505b50505050935093915050565b5f80336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146108a15760405162461bcd60e51b81526004016102e990610e15565b6108ae6020860186610e93565b6001600160a01b0316866001600160a01b03161461090e5760405162461bcd60e51b815260206004820152601b60248201527f5573657250726f78793a206f776e65722d6e6f742d73656e646572000000000060448201526064016102e9565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610972573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109969190610dfe565b9050476001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d505accf6109d560208a018a610e93565b6109e560408b0160208c01610e93565b60408b013560608c01356109ff60a08e0160808f01610eac565b6040516001600160e01b031960e088901b1681526001600160a01b0395861660048201529490931660248501526044840191909152606483015260ff16608482015260a08a013560a482015260c08a013560c482015260e4015f604051808303815f87803b158015610a6f575f80fd5b505af1925050508015610a80575060015b50610a8d8989888861029f565b50508515610aa657610aa0858383610ab2565b90945092505b50509550959350505050565b6040516370a0823160e01b81523060048201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610b18573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b3c9190610dfe565b91508115610bd65760405163a9059cbb60e01b81526001600160a01b038681166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af1158015610bb0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bd49190610e4c565b505b50478015610c75575f856001600160a01b0316826040515f6040518083038185875af1925050503d805f8114610c27576040519150601f19603f3d011682016040523d82523d5f602084013e610c2c565b606091505b5050905080610c735760405162461bcd60e51b8152602060048201526013602482015272155cd95c941c9bde1e4e88195d1a0b59985a5b606a1b60448201526064016102e9565b505b6001600160a01b0385167fc8706b1b13fe53cf2c02ef30ed1caa67ae1afbdaf0bf43da98358f104d9a37b0610caa8685610e6e565b84610cb58786610e6e565b604080519384526020840192909252908201526060810184905260800160405180910390a2935093915050565b80356001600160a01b0381168114610cf8575f80fd5b919050565b8015158114610d0a575f80fd5b50565b5f805f8060808587031215610d20575f80fd5b84359350610d3060208601610ce2565b92506040850135610d4081610cfd565b9150610d4e60608601610ce2565b905092959194509250565b5f805f60608486031215610d6b575f80fd5b833592506020840135610d7d81610cfd565b9150610d8b60408501610ce2565b90509250925092565b5f805f805f858703610160811215610daa575f80fd5b86359550610dba60208801610ce2565b945060e0603f1982011215610dcd575f80fd5b50604086019250610120860135610de381610cfd565b9150610df26101408701610ce2565b90509295509295909350565b5f60208284031215610e0e575f80fd5b5051919050565b6020808252601f908201527f5573657250726f78793a2063616c6c65722d6e6f742d7374616b696e67563200604082015260600190565b5f60208284031215610e5c575f80fd5b8151610e6781610cfd565b9392505050565b81810381811115610e8d57634e487b7160e01b5f52601160045260245ffd5b92915050565b5f60208284031215610ea3575f80fd5b610e6782610ce2565b5f60208284031215610ebc575f80fd5b813560ff81168114610e67575f80fdfea264697066735822122076abccf10289bcbd1e5e511c36bad58de1747880f5b6884481ba93813298c56864736f6c634300081800338be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0000000000000000000000000eeec870468b25d515f5a125c30574912f1626c98000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e9000000000000000000000000000000000000000000000000000000000000015180000000000000000000000000835e072530503ddda07b2f35e05dd3e647a0d9f200000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405260043610610327575f3560e01c80638a00b67a116101a3578063a9952a0c116100f2578063cc904bec11610092578063e4a61d951161006d578063e4a61d9514610ce1578063e76c01e414610d14578063e772b76514610d45578063f556a79c14610d78575f80fd5b8063cc904bec14610c5d578063cc9baa1214610c94578063cf84a88c14610cb3575f80fd5b8063b1d8f274116100cd578063b1d8f27414610bbe578063bfd79cae14610beb578063c20fb59e14610c1f578063c3140c4914610c3e575f80fd5b8063a9952a0c14610b3c578063ac85912e14610b8a578063ac9650d814610b9e575f80fd5b8063900cf0cf1161015d578063985b11f911610138578063985b11f914610a9f578063a5c29ba514610ad5578063a5e8455d14610af4578063a70b9f0c14610b09575f80fd5b8063900cf0cf146109ea57806391050f40146109fe578063972e6d6614610a80575f80fd5b80638a00b67a146108c25780638b7d38a1146108f55780638d4f0b6c146109095780638da5cb5b1461093c5780638eb9399e146109595780638f32d59b146109c0575f80fd5b80634e0846c7116102795780636f844ae2116102195780637f6ec455116101f45780637f6ec455146108325780638113630d146108655780638777e0951461088457806388edf9be146108a3575f80fd5b80636f844ae2146107c1578063727d0f35146107e057806378a1bdd414610813575f80fd5b8063539054021161025457806353905402146106a05780635c79696c1461072f57806364b4f7511461075b5780636bca7c551461078e575f80fd5b80634e0846c7146105b257806350283275146105e0578063524fcc80146105f4575f80fd5b80631cd2a05d116102e457806338f3e285116102bf57806338f3e285146104ff5780633c12e04e146105415780633f37dc771461056057806346d62a631461057f575f80fd5b80631cd2a05d1461047a5780632591003a146104ad5780632c2cc7da146104e0575f80fd5b80630df9049b1461032b5780630ecc535f146103715780630f2f6aa5146103de5780631030e7b6146103ff57806315e5a1e5146104325780631b4ff3201461045b575b5f80fd5b348015610336575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000001518081565b6040519081526020015b60405180910390f35b34801561037c575f80fd5b506103b761038b366004614602565b60066020525f90815260409020546001600160581b03811690600160581b90046001600160781b031682565b604080516001600160581b0390931683526001600160781b03909116602083015201610368565b3480156103e9575f80fd5b506103fd6103f8366004614602565b610dab565b005b34801561040a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000481565b34801561043d575f80fd5b506104466110c9565b60405163ffffffff9091168152602001610368565b348015610466575f80fd5b506103fd610475366004614631565b611151565b348015610485575f80fd5b5061035e7f00000000000000000000000000000000000000000000001b1ae4d6e2ef50000081565b3480156104b8575f80fd5b5061035e7f00000000000000000000000000000000000000000000000000038d7ea4c6800081565b3480156104eb575f80fd5b506103fd6104fa366004614657565b611158565b34801561050a575f80fd5b5061052e610519366004614602565b60096020525f908152604090205461ffff1681565b60405161ffff9091168152602001610368565b34801561054c575f80fd5b506103fd61055b366004614631565b611204565b34801561056b575f80fd5b506103fd61057a3660046146af565b611210565b34801561058a575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000081565b3480156105bd575f80fd5b506105d16105cc36600461483b565b611220565b6040516103689392919061492b565b3480156105eb575f80fd5b506104466114f1565b3480156105ff575f80fd5b5061065d61060e366004614602565b60076020525f9081526040902080546001909101546001600160581b0380831692600160581b900416906001600160781b0380821691600160781b810490911690600160f01b900461ffff1685565b604080516001600160581b0396871681529590941660208601526001600160781b039283169385019390935216606083015261ffff16608082015260a001610368565b3480156106ab575f80fd5b506106f66106ba366004614602565b60046020525f9081526040902080546001909101546001600160e01b038083169261ffff600160e01b8204811693600160f01b90920416911684565b60405161036894939291906001600160e01b03948516815261ffff93841660208201529190921660408201529116606082015260800190565b34801561073a575f80fd5b50610743611579565b6040516001600160a01b039091168152602001610368565b348015610766575f80fd5b5061035e7f0000000000000000000000000000000000000000000000056bc75e2d6310000081565b348015610799575f80fd5b5061035e7f00000000000000000000000000000000000000000000000029a2241af62c000081565b3480156107cc575f80fd5b506103fd6107db3660046149aa565b6115e2565b3480156107eb575f80fd5b506107437f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca646381565b34801561081e575f80fd5b506103fd61082d366004614602565b611a57565b34801561083d575f80fd5b506107437f000000000000000000000000eeec870468b25d515f5a125c30574912f1626c9881565b348015610870575f80fd5b506103fd61087f366004614a64565b611e39565b34801561088f575f80fd5b5061035e61089e366004614b0b565b611f6f565b3480156108ae575f80fd5b506103fd6108bd366004614b22565b612037565b3480156108cd575f80fd5b5061035e7f000000000000000000000000000000000000000000000000006a94d74f43000081565b348015610900575f80fd5b5061035e6120c8565b348015610914575f80fd5b506107437f0000000000000000000000007442782ab523d0ab806cb8da018561e40787aa6981565b348015610947575f80fd5b506001546001600160a01b0316610743565b348015610964575f80fd5b5061096d6120ea565b6040805184516001600160f01b0316815260209485015161ffff168582015283516001600160581b03169181019190915292909101516001600160781b031660608301521515608082015260a001610368565b3480156109cb575f80fd5b506001546001600160a01b031633146040519015158152602001610368565b3480156109f5575f80fd5b5061052e6121d9565b348015610a09575f80fd5b50610a56610a18366004614b74565b600860209081525f92835260408084209091529082529020546001600160581b0380821691600160581b810490911690600160b01b900461ffff1683565b604080516001600160581b03948516815293909216602084015261ffff1690820152606001610368565b348015610a8b575f80fd5b506105d1610a9a366004614602565b612267565b348015610aaa575f80fd5b50610abd6a52b7d2dcc80cd2e400000081565b6040516001600160781b039091168152602001610368565b348015610ae0575f80fd5b506103fd610aef366004614b9c565b6122a1565b348015610aff575f80fd5b5061035e60025481565b348015610b14575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000007e90081565b348015610b47575f80fd5b50600354610b68906001600160f01b03811690600160f01b900461ffff1682565b604080516001600160f01b03909316835261ffff909116602083015201610368565b348015610b95575f80fd5b5061035e61233b565b610bb1610bac366004614be8565b612352565b6040516103689190614c73565b348015610bc9575f80fd5b50610bdd610bd8366004614602565b612498565b604051610368929190614d10565b348015610bf6575f80fd5b50610c0a610c05366004614602565b6124f7565b60408051928352602083019190915201610368565b348015610c2a575f80fd5b5061035e610c39366004614602565b6125ad565b348015610c49575f80fd5b506103fd610c58366004614657565b612814565b348015610c68575f80fd5b50610c7c610c77366004614d3c565b612a2d565b6040516001600160d01b039091168152602001610368565b348015610c9f575f80fd5b50610743610cae366004614602565b612a5e565b348015610cbe575f80fd5b50610cd2610ccd366004614602565b612a98565b60405161036893929190614dc3565b348015610cec575f80fd5b5061035e7f000000000000000000000000000000000000000000000000000000000000000481565b348015610d1f575f80fd5b506005546103b7906001600160581b03811690600160581b90046001600160781b031682565b348015610d50575f80fd5b5061035e7f00000000000000000000000000000000000000000000003635c9adc5dea0000081565b348015610d83575f80fd5b506107437f000000000000000000000000000000000000000000000000000000000000000081565b610db3612c68565b5f80610dbd612c90565b915091505f80610dcc85612e06565b915091505f610ddd86868585611220565b509091505f9050816006811115610df657610df6614917565b03610e565760405162461bcd60e51b815260206004820152602560248201527f476f7665726e616e63653a20696e69746961746976652d6e6f742d72656769736044820152641d195c995960da1b60648201526084015b60405180910390fd5b6001816006811115610e6a57610e6a614917565b03610ec15760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a20696e69746961746976652d696e2d7761726d2d756044820152600760fc1b6064820152608401610e4d565b6005816006811115610ed557610ed5614917565b14610f335760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b6064820152608401610e4d565b5f610f3c6121d9565b9050610f49600182614e04565b61ffff16836080015161ffff1610610f6357610f63614e1f565b6020850151604084015186518551610f8793929190610f829082614e33565b612f39565b6001600160781b03166020860152825185516001600160581b0391821691161015610fb457610fb4614e1f565b825185518690610fc5908390614e33565b6001600160581b039081169091528651600580546020808b01516001600160781b0316600160581b026001600160d01b031990921693909416929092179190911790556001600160a01b0389165f81815260098352604090819020805461ffff191661ffff90811790915581519283528516928201929092527f8231be6875060a0fe4f35eaa6e112ebc15b8c04993114fe41c976bcb0a8c5ad892500160405180910390a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316630a9a78f360e11b1790526130f1565b505050505050506110c660015f55565b50565b5f806110d36121d9565b90508061ffff165f036110e7575f91505090565b7f000000000000000000000000000000000000000000000000000000000007e900611113600183614e04565b61ffff166111219190614e53565b61114b907f0000000000000000000000000000000000000000000000000000000000000000614e6a565b91505090565b6110c6815f335b611160612c68565b5f61116a84613152565b6040516338fb359960e01b81526001600160581b038616600482015233602482015284151560448201526001600160a01b038481166064830152919250908216906338fb35999060840160408051808303815f875af11580156111cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111f39190614e7d565b5050506111ff60015f55565b505050565b6110c681600133612814565b61121c82825f336122a1565b5050565b6001600160a01b0384165f908152600960205260408120548190819061ffff16810361125357505f9150819050806114e7565b61125b6121d9565b6001600160a01b0388165f9081526009602052604090205461ffff91821691160361128e5750600191505f9050806114e7565b6001600160a01b0387165f9081526007602090815260408083206001015460099092529091205461ffff600160f01b9092048216935061fffe199116016112da5750600691505f6114e7565b60016112e46121d9565b6112ee9190614e04565b61ffff168261ffff161061130557600492506114e7565b5f61131b875f01516001600160f01b0316611f6f565b8651606088015189519293506001600160e01b03918216929116906001600160f01b0316838311801561134d57508282105b15611428578061137f6127107f000000000000000000000000000000000000000000000000006a94d74f430000614eb3565b61138f85655af3107a4000614e53565b6113999190614eb3565b116113a6576113a6614e1f565b5f6113d97f000000000000000000000000000000000000000000000000006a94d74f430000670de0b6b3a7640000614eb3565b6113e4906001614e6a565b90505f816002548484886113f89190614e53565b6114029190614eb3565b61140c9190614e53565b6114169190614eb3565b6003995096506114e795505050505050565b60016114326121d9565b61143c9190614e04565b61ffff167f0000000000000000000000000000000000000000000000000000000000000004896080015161ffff166114749190614e6a565b10806114c5575082821180156114c55750670de0b6b3a76400006114b87f00000000000000000000000000000000000000000000000029a2241af62c000086614e53565b6114c29190614eb3565b82115b156114db5750600595505f93506114e792505050565b50600295505f93505050505b9450945094915050565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561151f57505f90565b7f000000000000000000000000000000000000000000000000000000000007e90061156a7f000000000000000000000000000000000000000000000000000000000000000042614ec6565b6115749190614ed9565b905090565b5f806115a57f0000000000000000000000007442782ab523d0ab806cb8da018561e40787aa69336133b3565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6115ea612c68565b8483146116225760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b80831461165a5760405162461bcd60e51b8152602060048201526006602482015265098cadccee8d60d31b6044820152606401610e4d565b611664888861341e565b61166e868661341e565b6116a98484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b6116e48282808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506134fb92505050565b61174f8484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f9201919091525061357092505050565b5f61175a8989613614565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b03169183019190915291925090156117dd5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610e4d565b7f00000000000000000000000000000000000000000000000000000000000151806118066114f1565b63ffffffff1611156119a7575f5b878110156119a5575f805b845181101561191f578a8a8481811061183a5761183a614eec565b905060200201602081019061184f9190614602565b6001600160a01b031685828151811061186a5761186a614eec565b60200260200101515f01516001600160a01b031603611917576001915084818151811061189957611899614eec565b602002602001015160200151600a0b8989858181106118ba576118ba614eec565b90506020020160208101906118cf9190614f00565b600a0b13156119125760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610e4d565b61191f565b60010161181f565b508061199c5787878381811061193757611937614eec565b905060200201602081019061194c9190614f00565b600a0b1561199c5760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610e4d565b50600101611814565b505b611a428888808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284375f9201919091525050604080516020808b0282810182019093528a82529093508a9250899182918501908490808284375f920191909152506139b292505050565b5050611a4d60015f55565b5050505050505050565b611a5f612c68565b611ab46001600160a01b037f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64631633307f0000000000000000000000000000000000000000000000056bc75e2d63100000614282565b6001600160a01b038116611b0a5760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610e4d565b5f611b1482612267565b509091505f9050816006811115611b2d57611b2d614917565b14611b8c5760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610e4d565b5f611b9633612a5e565b90505f611ba1612c90565b50335f908152600660209081526040918290208251808401909352546001600160581b0381168352600160581b90046001600160781b0316908201528151919250906001600160f01b0316670de0b6b3a7640000611c1f7f00000000000000000000000000000000000000000000000000038d7ea4c6800083614e53565b611c299190614eb3565b6040516305a4d3f160e21b81526001600160a01b038681166004830152611ce5917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa158015611c93573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cb79190614f20565b6a52b7d2dcc80cd2e4000000611ccb6110c9565b63ffffffff16611cdb9190614f37565b8560200151612a2d565b6001600160d01b03161015611d3c5760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610e4d565b5f611d456121d9565b6001600160a01b0388165f908152600960205260409020805461ffff191661ffff83161790559050611d78600182614e04565b6001600160a01b0388165f8181526007602090815260409182902060010180546001600160f01b0316600160f01b61ffff96871602179055815192835233908301529183168183015290517f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a916060908290030190a160405161ffff821660248201526110b690889062055730905f9060440160408051601f198184030181529190526020810180516001600160e01b0316628152f560e81b1790526130f1565b6001546001600160a01b03163314611e935760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610e4d565b5f5b8151811015611f6657600160095f848481518110611eb557611eb5614eec565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548161ffff021916908361ffff1602179055507f7ab17b444faba95987ecdd72a2f119eeb382788e3c01bd2abdb8ddb718ee9b3a828281518110611f2857611f28614eec565b602090810291909101810151604080516001600160a01b039092168252339282019290925260018183015290519081900360600190a1600101611e95565b506110c66142e9565b5f815f03611f7e57505f919050565b5f8083670de0b6b3a7640000600254611f979190614e53565b611fa19190614eb3565b90508015611fe95780611fdc670de0b6b3a76400007f00000000000000000000000000000000000000000000001b1ae4d6e2ef500000614e53565b611fe69190614eb3565b91505b61202f670de0b6b3a764000061201f7f000000000000000000000000000000000000000000000000006a94d74f43000087614e53565b6120299190614eb3565b83614332565b949350505050565b61203f612c68565b612049838361341e565b6120538383613614565b5080156120bf57335f908152600660205260409020546001600160581b0316156120bf5760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610e4d565b6111ff60015f55565b5f806120d2612c90565b50905061114b815f01516001600160f01b0316611f6f565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f8061211a6121d9565b6040805180820182526003546001600160f01b0381168252600160f01b900461ffff1660208083019190915282518084019093526005546001600160581b0381168452600160581b90046001600160781b031690830152955093509050612182600182614e04565b61ffff16846020015161ffff1610156121d3578251600192506121b3906a52b7d2dcc80cd2e4000000611ccb6110c9565b6001600160d01b031684526121c9600182614e04565b61ffff1660208501525b50909192565b5f7f000000000000000000000000000000000000000000000000000000000000000042101561220757505f90565b7f000000000000000000000000000000000000000000000000000000000007e9006122527f000000000000000000000000000000000000000000000000000000000000000042614ec6565b61225c9190614eb3565b611574906001614e6a565b5f805f80612273612c90565b5090505f8061228187612e06565b9150915061229187848484611220565b9550955095505050509193909250565b6122a9612c68565b5f6122b385613152565b60405163eb876bf760e01b81529091506001600160a01b0382169063eb876bf7906122ea9088903390899089908990600401614f62565b60408051808303815f875af1158015612305573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123299190614e7d565b50505061233560015f55565b50505050565b6003545f906001600160f01b031661114b81611f6f565b6060816001600160401b0381111561236c5761236c6146e2565b60405190808252806020026020018201604052801561239f57816020015b606081526020019060019003908161238a5790505b5090505f5b82811015612491575f80308686858181106123c1576123c1614eec565b90506020028101906123d3919061500f565b6040516123e1929190615051565b5f60405180830381855af49150503d805f8114612419576040519150601f19603f3d011682016040523d82523d5f602084013e61241e565b606091505b50915091508161246957604481511015612436575f80fd5b600481019050808060200190518101906124509190615060565b60405162461bcd60e51b8152600401610e4d91906150ee565b8084848151811061247c5761247c614eec565b602090810291909101015250506001016123a4565b5092915050565b6040805180820182525f808252602080830182905283516080810185528281529081018290529283018190526060830152906124d2612c68565b6124da612c90565b5091506124e683612e06565b5090506124f260015f55565b915091565b5f805f61250333612a5e565b9050806001600160a01b03163b5f0361252e5760405162461bcd60e51b8152600401610e4d90615100565b604051633fdf42e360e01b81525f6004820152600160248201526001600160a01b038581166044830152821690633fdf42e39060640160408051808303815f875af115801561257f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125a39190614e7d565b9250925050915091565b5f6125b6612c68565b5f6125bf612c90565b5090505f806125cd85612e06565b915091505f806125df87868686611220565b919350909150600390508260068111156125fb576125fb614917565b1461260d575f95505050505050612806565b60016126176121d9565b6126219190614e04565b61ffff16856020015161ffff161461263b5761263b614e1f565b60016126456121d9565b61264f9190614e04565b6001600160a01b038881165f90815260076020526040808220600101805461ffff95909516600160f01b026001600160f01b039095169490941790935591516370a0823160e01b81523060048201527f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463909116906370a0823190602401602060405180830381865afa1580156126e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061270b9190614f20565b905080821115612719578091505b61274d6001600160a01b037f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463168984614347565b602086810151604080516001600160a01b038c16815292830185905261ffff90911682820152517fa1ee42ce2e3b0e300645a781ea3e7c494f626cfeb1c2cce20e2fa4a5da249d189181900360600190a16127fc88620557305f8960200151866040516024016127cd92919061ffff929092168252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b03166313440fab60e01b1790526130f1565b5090955050505050505b61280f60015f55565b919050565b61281c612c68565b335f90815260066020526040902080546001600160581b0316156128825760405162461bcd60e51b815260206004820152601e60248201527f476f7665726e616e63653a206d7573742d616c6c6f636174652d7a65726f00006044820152606401610e4d565b5f61288c33612a5e565b9050806001600160a01b03163b5f036128b75760405162461bcd60e51b8152600401610e4d90615100565b6040516305a4d3f160e21b81526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa15801561291f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129439190614f20565b604051633fdf42e360e01b81526001600160581b038816600482015286151560248201526001600160a01b0386811660448301529192505f91829190851690633fdf42e39060640160408051808303815f875af11580156129a6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129ca9190614e7d565b604080513381526001600160581b038c1660208201529081018390526060810182905291935091507f71330c97f647d5136a666c3cf5cef9ec255ec0468d9d7891929eff0f4845c4589060800160405180910390a150505050506111ff60015f55565b5f612a388383614378565b612a54906001600160781b03166001600160581b038616615143565b90505b9392505050565b5f612a927f0000000000000000000000007442782ab523d0ab806cb8da018561e40787aa696001600160a01b0384166143b6565b92915050565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f80612aef6121d9565b6001600160a01b0386165f81815260046020908152604080832081516080808201845282546001600160e01b03808216845261ffff600160e01b8304811685890152600160f01b928390048116858801526001958601549091166060808601919091529888526007875296859020855160a08101875281546001600160581b038082168352600160581b90910416978101979097528401546001600160781b0380821696880196909652600160781b8104909516978601979097529590920490931693820193909352919650909450909150612bcb9082614e04565b61ffff16846020015161ffff161015612c6057600191505f6a52b7d2dcc80cd2e4000000612bf76110c9565b63ffffffff16612c079190614f37565b90505f612c1c855f0151838760400151612a2d565b90505f612c328660200151848860600151612a2d565b6001600160d01b038084168952811660608901529050612c53600185614e04565b61ffff1660208801525050505b509193909250565b60025f5403612c8a57604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612cbf6120ea565b919450925090508015612e01578251602084015161ffff16600160f01b026001600160f01b03909116176003556040516370a0823160e01b81523060048201525f906001600160a01b037f000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca646316906370a0823190602401602060405180830381865afa158015612d50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d749190614f20565b90507f00000000000000000000000000000000000000000000003635c9adc5dea000008110612da35780612da5565b5f5b600255835160208501516040517ff8c3496a83a68868434a061a876d356df06c7ef5bd8476f13d496b966855686b92612df79290916001600160f01b0392909216825261ffff16602082015260400190565b60405180910390a1505b509091565b604080516080810182525f8082526020820181905291810182905260608101919091526040805160a0810182525f808252602082018190529181018290526060810182905260808101919091525f612e5d84612a98565b919450925090508015612f33576001600160a01b0384165f818152600460209081526040918290208651815488840151898601516001600160e01b039384166001600160f01b03199093168317600160e01b61ffff938416908102919091176001600160f01b0316600160f01b93909216929092021784556060808b0151600190950180546001600160e01b0319169590941694909417909255845195865292850192909252918301527f6f22c33d2cf529b6a07e72fdf3fd25bb07828d8017cc4d47405c3ec9cadf6ffd910160405180910390a15b50915091565b5f816001600160581b03165f03612f5157505f61202f565b5f612f6d6a52b7d2dcc80cd2e400000063ffffffff4216614f37565b90505f612f7a8288614378565b90505f612f878388614378565b90505f856001600160581b0316876001600160581b031611613019575f612fae8888614e33565b90505f612fcd6001600160781b0386166001600160581b038b16615143565b90505f612fec6001600160781b0386166001600160581b038516615143565b90505f612ff98284615175565b905061300e6001600160581b038b1682615195565b9450505050506130ab565b5f6130248789614e33565b90505f6130436001600160781b0386166001600160581b038b16615143565b90505f6130626001600160781b0386166001600160581b038516615143565b90505f816001600160d01b0316836001600160d01b03161015613085575f61308f565b61308f82846151ba565b90506130a46001600160581b038b1682615195565b9450505050505b836001600160781b0316816001600160d01b031611156130d1575f94505050505061202f565b6130e4816001600160781b0386166151ba565b9998505050505050505050565b5f6130fe846103e8614415565b61313d5760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610e4d565b5f80835160208501868989f195945050505050565b5f80826001600160581b0316116131ab5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610e4d565b335f908152600660209081526040918290208251808401909352546001600160581b038116808452600160581b9091046001600160781b031691830191909152156132445760405162461bcd60e51b815260206004820152602360248201527f476f7665726e616e63653a206d7573742d62652d7a65726f2d616c6c6f63617460448201526234b7b760e91b6064820152608401610e4d565b5f61324e33612a5e565b9050806001600160a01b03163b5f0361326b57613269611579565b505b6040516305a4d3f160e21b81526001600160a01b03808316600483015282915f917f000000000000000000000000000000000000000000000000000000000000000016906316934fc490602401602060405180830381865afa1580156132d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132f79190614f20565b905061332384602001516a52b7d2dcc80cd2e4000000426133189190614f37565b83610f828a826151da565b6001600160781b039081166020868101918252335f81815260068352604090819020895181549551909616600160581b026001600160d01b03199095166001600160581b0396871617949094179093558251908152928916908301527f841ac74294f07d75868ac51778dc366b1fa11e8934b969cd5b0185c0e91dabb1910160405180910390a150949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612a92576040516330be1a3d60e21b815260040160405180910390fd5b805f81900361342c57505050565b5f5b613439600183614ec6565b811015612335575f61344c826001614e6a565b90505b828110156134f25784848281811061346957613469614eec565b905060200201602081019061347e9190614602565b6001600160a01b031685858481811061349957613499614eec565b90506020020160208101906134ae9190614602565b6001600160a01b0316036134ea5760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610e4d565b60010161344f565b5060010161342e565b80515f5b818110156111ff575f83828151811061351a5761351a614eec565b6020026020010151600a0b12156135685760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610e4d565b6001016134ff565b5f5b82518110156111ff575f83828151811061358e5761358e614eec565b6020026020010151600a0b13806135c057505f8282815181106135b3576135b3614eec565b6020026020010151600a0b135b61360c5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610e4d565b600101613572565b60605f826001600160401b0381111561362f5761362f6146e2565b60405190808252806020026020018201604052801561367857816020015b604080516060810182525f80825260208083018290529282015282525f1990920191018161364d5790505b5090505f836001600160401b03811115613694576136946146e2565b6040519080825280602002602001820160405280156136bd578160200160208202803683370190505b5090505f846001600160401b038111156136d9576136d96146e2565b604051908082528060200260200182016040528015613702578160200160208202803683370190505b5090505f5b8581101561396857335f9081526008602052604081208189898581811061373057613730614eec565b90506020020160208101906137459190614602565b6001600160a01b0316815260208082019290925260409081015f20815160608101835290546001600160581b03808216808452600160581b830490911694830194909452600160b01b900461ffff169181019190915291501515806137b657505f81602001516001600160581b0316115b6138025760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610e4d565b80516001600160571b036001600160581b03909116111561382557613825614e1f565b6001600160571b036001600160581b031681602001516001600160581b0316111561385257613852614e1f565b604051806060016040528089898581811061386f5761386f614eec565b90506020020160208101906138849190614602565b6001600160a01b03168152602001825f0151600a0b81526020018260200151600a0b8152508583815181106138bb576138bb614eec565b60200260200101819052508482815181106138d8576138d8614eec565b6020026020010151602001516138ed906151fa565b8483815181106138ff576138ff614eec565b6020026020010190600a0b9081600a0b8152505084828151811061392557613925614eec565b60200260200101516040015161393a906151fa565b83838151811061394c5761394c614eec565b600a9290920b6020928302919091019091015250600101613707565b506139a88686808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508692508591506139b29050565b5090949350505050565b815183511480156139c4575080518351145b613a1a5760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610e4d565b5f80613a24612c90565b915091505f613a316121d9565b335f9081526006602090815260408083208151808301909252546001600160581b0381168252600160581b90046001600160781b0316918101919091529192505b87518110156140ed575f888281518110613a8e57613a8e614eec565b602002602001015190505f888381518110613aab57613aab614eec565b602002602001015190505f888481518110613ac857613ac8614eec565b6020026020010151905081600a0b5f141580613ae7575080600a0b5f14155b613af357613af3614e1f565b5f80613afe85612e06565b915091505f613b0f868c8585611220565b505090505f85600a0b1380613b2657505f84600a0b135b15613bc4576002816006811115613b3f57613b3f614917565b1480613b5c57506003816006811115613b5a57613b5a614917565b145b80613b7857506004816006811115613b7657613b76614917565b145b613bc45760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610e4d565b6006816006811115613bd857613bd8614917565b03613c36575f85600a0b13158015613bf357505f84600a0b13155b613c365760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610e4d565b5f6040518060a00160405280845f01516001600160581b0316815260200184602001516001600160581b0316815260200184604001516001600160781b0316815260200184606001516001600160781b03168152602001846080015161ffff168152509050613cb983604001518a60200151855f0151610f82875f01518b614432565b6001600160781b0316604084015260608301516020808b015190850151613ce6929190610f82818a614432565b6001600160781b031660608401528251613d009087614432565b6001600160581b031683526020830151613d1a9086614432565b6001600160581b0390811660208086019182526001600160a01b038a165f90815260079091526040908190208651815493518516600160581b026001600160b01b0319909416941693909317919091178255840151600190910180546060860151608087015161ffff16600160f01b026001600160f01b036001600160781b03928316600160781b026001600160f01b0319909416929095169190911791909117929092169190911790556006826006811115613dd957613dd9614917565b14613e925760208b015160408201518c518351613dfd93929190610f829082614e33565b6001600160781b031660208c015280518b516001600160581b0391821691161015613e2a57613e2a614e1f565b80518b518c90613e3b908390614e33565b6001600160581b031690525060208b015160408401518c518551613e6693929190610f8290826151da565b6001600160781b031660208c015282518b518c90613e859083906151da565b6001600160581b03169052505b335f9081526008602090815260408083206001600160a01b038b168452825291829020825160608101845290546001600160581b03808216808452600160581b830490911693830193909352600160b01b900461ffff1692810192909252613efa9088614432565b6001600160581b031681526020810151613f149087614432565b6001600160581b03908116602083015261ffff8c16604083015281511615801590613f4b575060208101516001600160581b031615155b15613f985760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610e4d565b335f9081526008602090815260408083206001600160a01b038c1684528252918290208351815492850151938501516001600160581b039182166001600160b01b031990941693909317600160581b91909416029290921761ffff60b01b1916600160b01b61ffff90921691909102179055895161401f9061401a888a61521e565b614432565b6001600160581b03168a52604080513381526001600160a01b038a166020820152600a89810b8284015288900b606082015261ffff8d16608082015290517ffc65ef0a2ea71843c9c13a7a7c0bdccb27001f82e748634c3155cba36faccc2c9181900360a00190a16140d888620557305f8e338f878b6040516024016140a995949392919061524f565b60408051601f198184030181529190526020810180516001600160e01b0316633d0fb77360e01b1790526130f1565b505060019097019650613a7295505050505050565b5080516001600160581b031615806141b757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166316934fc461413833612a5e565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa15801561417a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061419e9190614f20565b6001600160581b0316815f01516001600160581b031611155b6142165760405162461bcd60e51b815260206004820152602a60248201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60448201526963617465642d6c71747960b01b6064820152608401610e4d565b8251600580546020958601516001600160781b03908116600160581b9081026001600160d01b03199384166001600160581b039687161717909355335f908152600688526040902085518154969098015190911690920293169490911693909317179091555050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526123359186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061446b565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b5f8183116143405781612a57565b5090919050565b6040516001600160a01b038381166024830152604482018390526111ff91859182169063a9059cbb906064016142b7565b5f6001600160781b03821615806143a05750816001600160781b0316836001600160781b0316105b156143ac57505f612a92565b612a5782846152ca565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90612a57565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f8082600a0b121561445857614447826144cc565b6144519084614e33565b9050612a92565b614461826144cc565b612a5790846151da565b5f61447f6001600160a01b038416836144ea565b905080515f141580156144a35750808060200190518101906144a191906152ea565b155b156111ff57604051635274afe760e01b81526001600160a01b0384166004820152602401610e4d565b5f8082600a0b126144dd5781612a92565b81600a0b612a9290615305565b6060612a5783835f845f80856001600160a01b0316848660405161450e919061531f565b5f6040518083038185875af1925050503d805f8114614548576040519150601f19603f3d011682016040523d82523d5f602084013e61454d565b606091505b509150915061455d868383614567565b9695505050505050565b60608261457c57614577826145c3565b612a57565b815115801561459357506001600160a01b0384163b155b156145bc57604051639996b31560e01b81526001600160a01b0385166004820152602401610e4d565b5080612a57565b8051156145d35780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461280f575f80fd5b5f60208284031215614612575f80fd5b612a57826145ec565b80356001600160581b038116811461280f575f80fd5b5f60208284031215614641575f80fd5b612a578261461b565b80151581146110c6575f80fd5b5f805f60608486031215614669575f80fd5b6146728461461b565b925060208401356146828161464a565b9150614690604085016145ec565b90509250925092565b5f60e082840312156146a9575f80fd5b50919050565b5f8061010083850312156146c1575f80fd5b6146ca8361461b565b91506146d98460208501614699565b90509250929050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715614718576147186146e2565b60405290565b604051608081016001600160401b0381118282101715614718576147186146e2565b604051601f8201601f191681016001600160401b0381118282101715614768576147686146e2565b604052919050565b803561ffff8116811461280f575f80fd5b80356001600160e01b038116811461280f575f80fd5b80356001600160781b038116811461280f575f80fd5b5f60a082840312156147bd575f80fd5b60405160a081018181106001600160401b03821117156147df576147df6146e2565b6040529050806147ee8361461b565b81526147fc6020840161461b565b602082015261480d60408401614797565b604082015261481e60608401614797565b606082015261482f60808401614770565b60808201525092915050565b5f805f80848603610180811215614850575f80fd5b614859866145ec565b94506040601f198201121561486c575f80fd5b6148746146f6565b60208701356001600160f01b038116811461488d575f80fd5b815261489b60408801614770565b602082015293506080605f19820112156148b3575f80fd5b506148bc61471e565b6148c860608701614781565b81526148d660808701614770565b60208201526148e760a08701614770565b60408201526148f860c08701614781565b6060820152915061490c8660e087016147ad565b905092959194509250565b634e487b7160e01b5f52602160045260245ffd5b606081016007851061494b57634e487b7160e01b5f52602160045260245ffd5b93815261ffff92909216602083015260409091015290565b5f8083601f840112614973575f80fd5b5081356001600160401b03811115614989575f80fd5b6020830191508360208260051b85010111156149a3575f80fd5b9250929050565b5f805f805f805f806080898b0312156149c1575f80fd5b88356001600160401b03808211156149d7575f80fd5b6149e38c838d01614963565b909a50985060208b01359150808211156149fb575f80fd5b614a078c838d01614963565b909850965060408b0135915080821115614a1f575f80fd5b614a2b8c838d01614963565b909650945060608b0135915080821115614a43575f80fd5b50614a508b828c01614963565b999c989b5096995094979396929594505050565b5f6020808385031215614a75575f80fd5b82356001600160401b0380821115614a8b575f80fd5b818501915085601f830112614a9e575f80fd5b813581811115614ab057614ab06146e2565b8060051b9150614ac1848301614740565b8181529183018401918481019088841115614ada575f80fd5b938501935b83851015614aff57614af0856145ec565b82529385019390850190614adf565b98975050505050505050565b5f60208284031215614b1b575f80fd5b5035919050565b5f805f60408486031215614b34575f80fd5b83356001600160401b03811115614b49575f80fd5b614b5586828701614963565b9094509250506020840135614b698161464a565b809150509250925092565b5f8060408385031215614b85575f80fd5b614b8e836145ec565b91506146d9602084016145ec565b5f805f806101408587031215614bb0575f80fd5b614bb98561461b565b9350614bc88660208701614699565b9250610100850135614bd98161464a565b915061490c61012086016145ec565b5f8060208385031215614bf9575f80fd5b82356001600160401b03811115614c0e575f80fd5b614c1a85828601614963565b90969095509350505050565b5f5b83811015614c40578181015183820152602001614c28565b50505f910152565b5f8151808452614c5f816020860160208601614c26565b601f01601f19169290920160200192915050565b5f60208083016020845280855180835260408601915060408160051b8701019250602087015f5b82811015614cc857603f19888603018452614cb6858351614c48565b94509285019290850190600101614c9a565b5092979650505050505050565b80516001600160e01b03908116835260208083015161ffff908116918501919091526040808401519091169084015260609182015116910152565b82516001600160f01b0316815260208084015161ffff169082015260c08101612a576040830184614cd5565b5f805f60608486031215614d4e575f80fd5b614d578461461b565b9250614d6560208501614797565b915061469060408501614797565b6001600160581b038082511683528060208301511660208401525060408101516001600160781b038082166040850152806060840151166060850152505061ffff60808201511660808301525050565b6101408101614dd28286614cd5565b614ddf6080830185614d73565b821515610120830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b61ffff82811682821603908082111561249157612491614df0565b634e487b7160e01b5f52600160045260245ffd5b6001600160581b0382811682821603908082111561249157612491614df0565b8082028115828204841417612a9257612a92614df0565b80820180821115612a9257612a92614df0565b5f8060408385031215614e8e575f80fd5b505080516020909101519092909150565b634e487b7160e01b5f52601260045260245ffd5b5f82614ec157614ec1614e9f565b500490565b81810381811115612a9257612a92614df0565b5f82614ee757614ee7614e9f565b500690565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215614f10575f80fd5b813580600a0b8114612a57575f80fd5b5f60208284031215614f30575f80fd5b5051919050565b6001600160781b03818116838216028082169190828114614f5a57614f5a614df0565b505092915050565b6001600160581b03861681526001600160a01b03858116602083015261016082019080614f8e876145ec565b16604084015280614fa1602088016145ec565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614fcf575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f808335601e19843603018112615024575f80fd5b8301803591506001600160401b0382111561503d575f80fd5b6020019150368190038213156149a3575f80fd5b818382375f9101908152919050565b5f60208284031215615070575f80fd5b81516001600160401b0380821115615086575f80fd5b818401915084601f830112615099575f80fd5b8151818111156150ab576150ab6146e2565b6150be601f8201601f1916602001614740565b91508082528560208285010111156150d4575f80fd5b6150e5816020840160208601614c26565b50949350505050565b602081525f612a576020830184614c48565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b6001600160d01b0382811682821681810283169291811582850482141761516c5761516c614df0565b50505092915050565b6001600160d01b0381811683821601908082111561249157612491614df0565b5f6001600160d01b03838116806151ae576151ae614e9f565b92169190910492915050565b6001600160d01b0382811682821603908082111561249157612491614df0565b6001600160581b0381811683821601908082111561249157612491614df0565b5f81600a0b6001600160571b0319810361521657615216614df0565b5f0392915050565b600a81810b9083900b016001600160571b0381136a7fffffffffffffffffffff1982121715612a9257612a92614df0565b61ffff86811682526001600160a01b0386166020830152610180820190615295604084018780516001600160581b031682526020908101516001600160781b0316910152565b84516001600160581b03908116608085015260208601511660a084015260408501511660c083015261455d60e0830184614d73565b6001600160781b0382811682821603908082111561249157612491614df0565b5f602082840312156152fa575f80fd5b8151612a578161464a565b5f600160ff1b820161531957615319614df0565b505f0390565b5f8251615330818460208701614c26565b919091019291505056fea26469706673582212207c8aca494c606851d6ce92ed1d9a36a4bbb9f7d57cefde59074d285e103bc4a664736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000eeec870468b25d515f5a125c30574912f1626c98000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca64630000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000006a94d74f43000000000000000000000000000000000000000000000000001b1ae4d6e2ef50000000000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007e9000000000000000000000000000000000000000000000000000000000000015180000000000000000000000000835e072530503ddda07b2f35e05dd3e647a0d9f200000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _lqty (address): 0xeEEc870468b25D515F5a125C30574912F1626C98
Arg [1] : _lusd (address): 0xf0076A2160247884DA5AB0A5625F192cD3cA6463
Arg [2] : _stakingV1 (address): 0x0000000000000000000000000000000000000000
Arg [3] : _bold (address): 0xf0076A2160247884DA5AB0A5625F192cD3cA6463
Arg [4] : _config (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [5] : _owner (address): 0x835E072530503DddA07B2F35e05DD3e647a0D9F2
-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 000000000000000000000000eeec870468b25d515f5a125c30574912f1626c98
Arg [1] : 000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 000000000000000000000000f0076a2160247884da5ab0a5625f192cd3ca6463
Arg [4] : 0000000000000000000000000000000000000000000000056bc75e2d63100000
Arg [5] : 00000000000000000000000000000000000000000000000000038d7ea4c68000
Arg [6] : 00000000000000000000000000000000000000000000000029a2241af62c0000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [9] : 000000000000000000000000000000000000000000000000006a94d74f430000
Arg [10] : 00000000000000000000000000000000000000000000001b1ae4d6e2ef500000
Arg [11] : 00000000000000000000000000000000000000000000003635c9adc5dea00000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 000000000000000000000000000000000000000000000000000000000007e900
Arg [14] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [15] : 000000000000000000000000835e072530503ddda07b2f35e05dd3e647a0d9f2
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000220
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ 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.