Latest 25 from a total of 1,899 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Multi Delegate C... | 23992225 | 4 days ago | IN | 0 ETH | 0.00038557 | ||||
| Multi Delegate C... | 23798491 | 32 days ago | IN | 0 ETH | 0.0002044 | ||||
| Multi Delegate C... | 23793841 | 32 days ago | IN | 0 ETH | 0.00012887 | ||||
| Multi Delegate C... | 23641459 | 53 days ago | IN | 0 ETH | 0.00004778 | ||||
| Multi Delegate C... | 23555731 | 66 days ago | IN | 0 ETH | 0.00039943 | ||||
| Multi Delegate C... | 23552109 | 66 days ago | IN | 0 ETH | 0.00111689 | ||||
| Multi Delegate C... | 23441734 | 81 days ago | IN | 0 ETH | 0.00175909 | ||||
| Multi Delegate C... | 23413900 | 85 days ago | IN | 0 ETH | 0.00049175 | ||||
| Multi Delegate C... | 23342886 | 95 days ago | IN | 0 ETH | 0.00003413 | ||||
| Multi Delegate C... | 23312447 | 100 days ago | IN | 0 ETH | 0.00019092 | ||||
| Multi Delegate C... | 23275705 | 105 days ago | IN | 0 ETH | 0.00079365 | ||||
| Claim From Staki... | 23275694 | 105 days ago | IN | 0 ETH | 0.00006165 | ||||
| Multi Delegate C... | 23259725 | 107 days ago | IN | 0 ETH | 0.00002309 | ||||
| Multi Delegate C... | 23259153 | 107 days ago | IN | 0 ETH | 0.00010983 | ||||
| Multi Delegate C... | 23256060 | 107 days ago | IN | 0 ETH | 0.00020343 | ||||
| Multi Delegate C... | 23133602 | 124 days ago | IN | 0 ETH | 0.00181824 | ||||
| Multi Delegate C... | 23085473 | 131 days ago | IN | 0 ETH | 0.00014418 | ||||
| Multi Delegate C... | 23063284 | 134 days ago | IN | 0 ETH | 0.00003447 | ||||
| Multi Delegate C... | 23061213 | 135 days ago | IN | 0 ETH | 0.00018776 | ||||
| Multi Delegate C... | 23021705 | 140 days ago | IN | 0 ETH | 0.00027058 | ||||
| Multi Delegate C... | 23012018 | 141 days ago | IN | 0 ETH | 0.00007671 | ||||
| Multi Delegate C... | 22968595 | 148 days ago | IN | 0 ETH | 0.00418052 | ||||
| Multi Delegate C... | 22958883 | 149 days ago | IN | 0 ETH | 0.0013866 | ||||
| Multi Delegate C... | 22951643 | 150 days ago | IN | 0 ETH | 0.00020089 | ||||
| Deploy User Prox... | 22938792 | 152 days ago | IN | 0 ETH | 0.00022503 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x3d602d80 | 22938792 | 152 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22518210 | 211 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22517488 | 211 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22493454 | 214 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22477104 | 216 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22419696 | 224 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22418236 | 225 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22416917 | 225 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22410049 | 226 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22388341 | 229 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22363882 | 232 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22354823 | 233 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22351465 | 234 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22350408 | 234 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22345936 | 235 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22339845 | 236 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22296302 | 242 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22269213 | 245 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22250555 | 248 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22242875 | 249 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22232765 | 250 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22214315 | 253 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22197153 | 255 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22181667 | 258 days ago | Contract Creation | 0 ETH | |||
| 0x3d602d80 | 22174832 | 259 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Cross-Chain Transactions
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, UNREGISTERED_INITIATIVE} 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, sub, max} from "./utils/Math.sol";
import {_requireNoDuplicates, _requireNoNegatives} from "./utils/UniqueArray.sol";
import {MultiDelegateCall} from "./utils/MultiDelegateCall.sol";
import {WAD, PermitParams} from "./utils/Types.sol";
import {safeCallWithMinGas} from "./utils/SafeCallMinGas.sol";
import {Ownable} from "./utils/Ownable.sol";
import {_lqtyToVotes} from "./utils/VotingPower.sol";
/// @title Governance: Modular Initiative based Governance
contract Governance is MultiDelegateCall, 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 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 => uint256) public override registeredInitiatives;
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;
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;
require(_config.epochStart <= block.timestamp, "Gov: cannot-start-in-future");
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;
bool success = safeCallWithMinGas(
_initiatives[i], MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onRegisterInitiative, (1))
);
emit RegisterInitiative(_initiatives[i], msg.sender, 1, success ? HookStatus.Succeeded : HookStatus.Failed);
}
_renounceOwnership();
}
/*//////////////////////////////////////////////////////////////
STAKING
//////////////////////////////////////////////////////////////*/
function _increaseUserVoteTrackers(uint256 _lqtyAmount) private returns (UserProxy) {
require(_lqtyAmount > 0, "Governance: zero-lqty-amount");
address userProxyAddress = deriveUserProxyAddress(msg.sender);
if (userProxyAddress.code.length == 0) {
deployUserProxy();
}
UserProxy userProxy = UserProxy(payable(userProxyAddress));
// update the vote power trackers
userStates[msg.sender].unallocatedLQTY += _lqtyAmount;
userStates[msg.sender].unallocatedOffset += block.timestamp * _lqtyAmount;
return userProxy;
}
/// @inheritdoc IGovernance
function depositLQTY(uint256 _lqtyAmount) external {
depositLQTY(_lqtyAmount, false, msg.sender);
}
function depositLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant {
UserProxy userProxy = _increaseUserVoteTrackers(_lqtyAmount);
(uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent) =
userProxy.stake(_lqtyAmount, msg.sender, _doSendRewards, _recipient);
emit DepositLQTY(msg.sender, _recipient, _lqtyAmount, lusdReceived, lusdSent, ethReceived, ethSent);
}
/// @inheritdoc IGovernance
function depositLQTYViaPermit(uint256 _lqtyAmount, PermitParams calldata _permitParams) external {
depositLQTYViaPermit(_lqtyAmount, _permitParams, false, msg.sender);
}
function depositLQTYViaPermit(
uint256 _lqtyAmount,
PermitParams calldata _permitParams,
bool _doSendRewards,
address _recipient
) public nonReentrant {
UserProxy userProxy = _increaseUserVoteTrackers(_lqtyAmount);
(uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent) =
userProxy.stakeViaPermit(_lqtyAmount, msg.sender, _permitParams, _doSendRewards, _recipient);
emit DepositLQTY(msg.sender, _recipient, _lqtyAmount, lusdReceived, lusdSent, ethReceived, ethSent);
}
/// @inheritdoc IGovernance
function withdrawLQTY(uint256 _lqtyAmount) external {
withdrawLQTY(_lqtyAmount, true, msg.sender);
}
function withdrawLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) public nonReentrant {
UserState storage userState = userStates[msg.sender];
UserProxy userProxy = UserProxy(payable(deriveUserProxyAddress(msg.sender)));
require(address(userProxy).code.length != 0, "Governance: user-proxy-not-deployed");
// check if user has enough unallocated lqty
require(_lqtyAmount <= userState.unallocatedLQTY, "Governance: insufficient-unallocated-lqty");
// Update the offset tracker
if (_lqtyAmount < userState.unallocatedLQTY) {
// The offset decrease is proportional to the partial lqty decrease
uint256 offsetDecrease = _lqtyAmount * userState.unallocatedOffset / userState.unallocatedLQTY;
userState.unallocatedOffset -= offsetDecrease;
} else {
// if _lqtyAmount == userState.unallocatedLqty, zero the offset tracker
userState.unallocatedOffset = 0;
}
// Update the user's LQTY tracker
userState.unallocatedLQTY -= _lqtyAmount;
(
uint256 lqtyReceived,
uint256 lqtySent,
uint256 lusdReceived,
uint256 lusdSent,
uint256 ethReceived,
uint256 ethSent
) = userProxy.unstake(_lqtyAmount, _doSendRewards, _recipient);
emit WithdrawLQTY(msg.sender, _recipient, lqtyReceived, lqtySent, lusdReceived, lusdSent, ethReceived, ethSent);
}
/// @inheritdoc IGovernance
function claimFromStakingV1(address _rewardRecipient) external returns (uint256 lusdSent, uint256 ethSent) {
address payable userProxyAddress = payable(deriveUserProxyAddress(msg.sender));
require(userProxyAddress.code.length != 0, "Governance: user-proxy-not-deployed");
uint256 lqtyReceived;
uint256 lqtySent;
uint256 lusdReceived;
uint256 ethReceived;
(lqtyReceived, lqtySent, lusdReceived, lusdSent, ethReceived, ethSent) =
UserProxy(userProxyAddress).unstake(0, true, _rewardRecipient);
emit WithdrawLQTY(
msg.sender, _rewardRecipient, lqtyReceived, lqtySent, lusdReceived, lusdSent, ethReceived, ethSent
);
}
/*//////////////////////////////////////////////////////////////
VOTING
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IGovernance
function epoch() public view returns (uint256) {
return ((block.timestamp - EPOCH_START) / EPOCH_DURATION) + 1;
}
/// @inheritdoc IGovernance
function epochStart() public view returns (uint256) {
return EPOCH_START + (epoch() - 1) * EPOCH_DURATION;
}
/// @inheritdoc IGovernance
function secondsWithinEpoch() public view returns (uint256) {
return (block.timestamp - EPOCH_START) % EPOCH_DURATION;
}
/// @inheritdoc IGovernance
function lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) public pure returns (uint256) {
return _lqtyToVotes(_lqtyAmount, _timestamp, _offset);
}
/*//////////////////////////////////////////////////////////////
SNAPSHOTS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IGovernance
function getLatestVotingThreshold() public view returns (uint256) {
uint256 snapshotVotes = votesSnapshot.votes;
return calculateVotingThreshold(snapshotVotes);
}
/// @inheritdoc IGovernance
function calculateVotingThreshold() public returns (uint256) {
(VoteSnapshot memory snapshot,) = _snapshotVotes();
return calculateVotingThreshold(snapshot.votes);
}
/// @inheritdoc IGovernance
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, boldAccrued);
}
}
/// @inheritdoc IGovernance
function getTotalVotesAndState()
public
view
returns (VoteSnapshot memory snapshot, GlobalState memory state, bool shouldUpdate)
{
uint256 currentEpoch = epoch();
snapshot = votesSnapshot;
state = globalState;
if (snapshot.forEpoch < currentEpoch - 1) {
shouldUpdate = true;
snapshot.votes = lqtyToVotes(state.countedVoteLQTY, epochStart(), state.countedVoteOffset);
snapshot.forEpoch = currentEpoch - 1;
}
}
// Snapshots votes for an initiative for the previous epoch
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.vetos, initiativeSnapshot.forEpoch
);
}
}
/// @inheritdoc IGovernance
function getInitiativeSnapshotAndState(address _initiative)
public
view
returns (
InitiativeVoteSnapshot memory initiativeSnapshot,
InitiativeState memory initiativeState,
bool shouldUpdate
)
{
// Get the storage data
uint256 currentEpoch = epoch();
initiativeSnapshot = votesForInitiativeSnapshot[_initiative];
initiativeState = initiativeStates[_initiative];
if (initiativeSnapshot.forEpoch < currentEpoch - 1) {
shouldUpdate = true;
uint256 start = epochStart();
uint256 votes = lqtyToVotes(initiativeState.voteLQTY, start, initiativeState.voteOffset);
uint256 vetos = lqtyToVotes(initiativeState.vetoLQTY, start, initiativeState.vetoOffset);
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
//////////////////////////////////////////////////////////////*/
/// @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, uint256 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, uint256 lastEpochClaim, uint256 claimableAmount) {
uint256 initiativeRegistrationEpoch = registeredInitiatives[_initiative];
// == Non existent Condition == //
if (initiativeRegistrationEpoch == 0) {
return (InitiativeStatus.NONEXISTENT, 0, 0);
/// By definition it has zero rewards
}
uint256 currentEpoch = epoch();
// == Just Registered Condition == //
if (initiativeRegistrationEpoch == currentEpoch) {
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 (initiativeRegistrationEpoch == UNREGISTERED_INITIATIVE) {
return (InitiativeStatus.DISABLED, lastEpochClaim, 0);
/// By definition it has zero rewards
}
// == Already Claimed Condition == //
if (lastEpochClaim >= currentEpoch - 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
if (
_votesForInitiativeSnapshot.votes > votingTheshold
&& _votesForInitiativeSnapshot.votes > _votesForInitiativeSnapshot.vetos
) {
uint256 claim = _votesForInitiativeSnapshot.votes * boldAccrued / _votesSnapshot.votes;
return (InitiativeStatus.CLAIMABLE, lastEpochClaim, claim);
}
// == Unregister Condition == //
// e.g. if `UNREGISTRATION_AFTER_EPOCHS` is 4, the initiative will become unregisterable after spending 4 epochs
// while being in one of the following conditions:
// - in `SKIP` state (not having received enough votes to cross the voting threshold)
// - in `CLAIMABLE` state (having received enough votes to cross the voting threshold) but never being claimed
if (
(_initiativeState.lastEpochClaim + UNREGISTRATION_AFTER_EPOCHS < currentEpoch - 1)
|| _votesForInitiativeSnapshot.vetos > _votesForInitiativeSnapshot.votes
&& _votesForInitiativeSnapshot.vetos > 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 {
uint256 currentEpoch = epoch();
require(currentEpoch > 2, "Governance: registration-not-yet-enabled");
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];
bold.safeTransferFrom(msg.sender, address(this), REGISTRATION_FEE);
// 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 = snapshot.votes;
uint256 totalUserOffset = userState.allocatedOffset + userState.unallocatedOffset;
require(
// Check against the user's total voting power, so include both allocated and unallocated LQTY
lqtyToVotes(stakingV1.stakes(userProxyAddress), epochStart(), totalUserOffset)
>= upscaledSnapshotVotes * REGISTRATION_THRESHOLD_FACTOR / WAD,
"Governance: insufficient-lqty"
);
registeredInitiatives[_initiative] = currentEpoch;
/// This ensures that the initiatives has UNREGISTRATION_AFTER_EPOCHS even after the first epoch
initiativeStates[_initiative].lastEpochClaim = currentEpoch - 1;
// Replaces try / catch | Enforces sufficient gas is passed
bool success = safeCallWithMinGas(
_initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onRegisterInitiative, (currentEpoch))
);
emit RegisterInitiative(
_initiative, msg.sender, currentEpoch, success ? HookStatus.Succeeded : HookStatus.Failed
);
}
struct ResetInitiativeData {
address initiative;
int256 LQTYVotes;
int256 LQTYVetos;
int256 OffsetVotes;
int256 OffsetVetos;
}
/// @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);
int256[] memory deltaLQTYVotes = new int256[](_initiativesToReset.length);
int256[] memory deltaLQTYVetos = new int256[](_initiativesToReset.length);
int256[] memory deltaOffsetVotes = new int256[](_initiativesToReset.length);
int256[] memory deltaOffsetVetos = new int256[](_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");
// Cache, used to enforce limits later
cachedData[i] = ResetInitiativeData({
initiative: _initiativesToReset[i],
LQTYVotes: int256(alloc.voteLQTY),
LQTYVetos: int256(alloc.vetoLQTY),
OffsetVotes: int256(alloc.voteOffset),
OffsetVetos: int256(alloc.vetoOffset)
});
// -0 is still 0, so its fine to flip both
deltaLQTYVotes[i] = -(cachedData[i].LQTYVotes);
deltaLQTYVetos[i] = -(cachedData[i].LQTYVetos);
deltaOffsetVotes[i] = -(cachedData[i].OffsetVotes);
deltaOffsetVetos[i] = -(cachedData[i].OffsetVetos);
}
// RESET HERE || All initiatives will receive most updated data and 0 votes / vetos
_allocateLQTY(_initiativesToReset, deltaLQTYVotes, deltaLQTYVetos, deltaOffsetVotes, deltaOffsetVetos);
return cachedData;
}
/// @inheritdoc IGovernance
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,
int256[] calldata _absoluteLQTYVotes,
int256[] calldata _absoluteLQTYVetos
) external nonReentrant {
require(
_initiatives.length == _absoluteLQTYVotes.length && _absoluteLQTYVotes.length == _absoluteLQTYVetos.length,
"Governance: array-length-mismatch"
);
// 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);
_requireNoSimultaneousVoteAndVeto(_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");
require(userState.unallocatedLQTY != 0, "Governance: insufficient-or-allocated-lqty"); // avoid div-by-zero
// 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");
}
}
}
int256[] memory absoluteOffsetVotes = new int256[](_initiatives.length);
int256[] memory absoluteOffsetVetos = new int256[](_initiatives.length);
// Calculate the offset portions that correspond to each LQTY vote and veto portion
// By recalculating `unallocatedLQTY` & `unallocatedOffset` after each step, we ensure that rounding error
// doesn't accumulate in `unallocatedOffset`.
// However, it should be noted that this makes the exact offset allocations dependent on the ordering of the
// `_initiatives` array.
for (uint256 x; x < _initiatives.length; x++) {
// Either _absoluteLQTYVotes[x] or _absoluteLQTYVetos[x] is guaranteed to be zero
(int256[] calldata lqtyAmounts, int256[] memory offsets) = _absoluteLQTYVotes[x] > 0
? (_absoluteLQTYVotes, absoluteOffsetVotes)
: (_absoluteLQTYVetos, absoluteOffsetVetos);
uint256 lqtyAmount = uint256(lqtyAmounts[x]);
uint256 offset = userState.unallocatedOffset * lqtyAmount / userState.unallocatedLQTY;
userState.unallocatedLQTY -= lqtyAmount;
userState.unallocatedOffset -= offset;
offsets[x] = int256(offset);
}
// Vote here, all values are now absolute changes
_allocateLQTY(_initiatives, _absoluteLQTYVotes, _absoluteLQTYVetos, absoluteOffsetVotes, absoluteOffsetVetos);
}
// Avoid "stack too deep" by placing these variables in memory
struct AllocateLQTYMemory {
VoteSnapshot votesSnapshot_;
GlobalState state;
UserState userState;
InitiativeVoteSnapshot votesForInitiativeSnapshot_;
InitiativeState initiativeState;
InitiativeState prevInitiativeState;
Allocation allocation;
uint256 currentEpoch;
int256 deltaLQTYVotes;
int256 deltaLQTYVetos;
int256 deltaOffsetVotes;
int256 deltaOffsetVetos;
}
/// @dev For each given initiative applies relative changes to the allocation
/// @dev Assumes that all the input arrays are of equal length
/// @dev 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,
int256[] memory _deltaLQTYVotes,
int256[] memory _deltaLQTYVetos,
int256[] memory _deltaOffsetVotes,
int256[] memory _deltaOffsetVetos
) internal {
AllocateLQTYMemory memory vars;
(vars.votesSnapshot_, vars.state) = _snapshotVotes();
vars.currentEpoch = epoch();
vars.userState = userStates[msg.sender];
for (uint256 i = 0; i < _initiatives.length; i++) {
address initiative = _initiatives[i];
vars.deltaLQTYVotes = _deltaLQTYVotes[i];
vars.deltaLQTYVetos = _deltaLQTYVetos[i];
assert(vars.deltaLQTYVotes != 0 || vars.deltaLQTYVetos != 0);
vars.deltaOffsetVotes = _deltaOffsetVotes[i];
vars.deltaOffsetVetos = _deltaOffsetVetos[i];
/// === Check FSM === ///
// Can vote positively in SKIP, CLAIMABLE and CLAIMED states
// Force to remove votes if disabled
// Can remove votes and vetos in every stage
(vars.votesForInitiativeSnapshot_, vars.initiativeState) = _snapshotVotesForInitiative(initiative);
(InitiativeStatus status,,) = getInitiativeState(
initiative, vars.votesSnapshot_, vars.votesForInitiativeSnapshot_, vars.initiativeState
);
if (vars.deltaLQTYVotes > 0 || vars.deltaLQTYVetos > 0) {
/// 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(vars.deltaLQTYVotes <= 0 && vars.deltaLQTYVetos <= 0, "Must be a withdrawal");
}
/// === UPDATE ACCOUNTING === ///
// == INITIATIVE STATE == //
// deep copy of the initiative's state before the allocation
vars.prevInitiativeState = InitiativeState(
vars.initiativeState.voteLQTY,
vars.initiativeState.voteOffset,
vars.initiativeState.vetoLQTY,
vars.initiativeState.vetoOffset,
vars.initiativeState.lastEpochClaim
);
// allocate the voting and vetoing LQTY to the initiative
vars.initiativeState.voteLQTY = add(vars.initiativeState.voteLQTY, vars.deltaLQTYVotes);
vars.initiativeState.vetoLQTY = add(vars.initiativeState.vetoLQTY, vars.deltaLQTYVetos);
// Update the initiative's vote and veto offsets
vars.initiativeState.voteOffset = add(vars.initiativeState.voteOffset, vars.deltaOffsetVotes);
vars.initiativeState.vetoOffset = add(vars.initiativeState.vetoOffset, vars.deltaOffsetVetos);
// update the initiative's state
initiativeStates[initiative] = vars.initiativeState;
// == GLOBAL STATE == //
/// We update the state only for non-disabled initiatives
/// Disabled initiatves have had their totals subtracted already
if (status != InitiativeStatus.DISABLED) {
assert(vars.state.countedVoteLQTY >= vars.prevInitiativeState.voteLQTY);
// Remove old initative LQTY and offset from global count
vars.state.countedVoteLQTY -= vars.prevInitiativeState.voteLQTY;
vars.state.countedVoteOffset -= vars.prevInitiativeState.voteOffset;
// Add new initative LQTY and offset to global count
vars.state.countedVoteLQTY += vars.initiativeState.voteLQTY;
vars.state.countedVoteOffset += vars.initiativeState.voteOffset;
}
// == USER ALLOCATION TO INITIATIVE == //
// Record the vote and veto LQTY and offsets by user to initative
vars.allocation = lqtyAllocatedByUserToInitiative[msg.sender][initiative];
// Update offsets
vars.allocation.voteOffset = add(vars.allocation.voteOffset, vars.deltaOffsetVotes);
vars.allocation.vetoOffset = add(vars.allocation.vetoOffset, vars.deltaOffsetVetos);
// Update votes and vetos
vars.allocation.voteLQTY = add(vars.allocation.voteLQTY, vars.deltaLQTYVotes);
vars.allocation.vetoLQTY = add(vars.allocation.vetoLQTY, vars.deltaLQTYVetos);
vars.allocation.atEpoch = vars.currentEpoch;
// Voting power allocated to initiatives should never be negative, else it might break reward allocation
// schemes such as `BribeInitiative` which distribute rewards in proportion to voting power allocated.
assert(vars.allocation.voteLQTY * block.timestamp >= vars.allocation.voteOffset);
assert(vars.allocation.vetoLQTY * block.timestamp >= vars.allocation.vetoOffset);
lqtyAllocatedByUserToInitiative[msg.sender][initiative] = vars.allocation;
// == USER STATE == //
// Remove from the user's unallocated LQTY and offset
vars.userState.unallocatedLQTY =
sub(vars.userState.unallocatedLQTY, (vars.deltaLQTYVotes + vars.deltaLQTYVetos));
vars.userState.unallocatedOffset =
sub(vars.userState.unallocatedOffset, (vars.deltaOffsetVotes + vars.deltaOffsetVetos));
// Add to the user's allocated LQTY and offset
vars.userState.allocatedLQTY =
add(vars.userState.allocatedLQTY, (vars.deltaLQTYVotes + vars.deltaLQTYVetos));
vars.userState.allocatedOffset =
add(vars.userState.allocatedOffset, (vars.deltaOffsetVotes + vars.deltaOffsetVetos));
HookStatus hookStatus;
// See https://github.com/liquity/V2-gov/issues/125
// A malicious initiative could try to dissuade voters from casting vetos by consuming as much gas as
// possible in the `onAfterAllocateLQTY` hook when detecting vetos.
// We deem that the risks of calling into malicous initiatives upon veto allocation far outweigh the
// benefits of notifying benevolent initiatives of vetos.
if (vars.allocation.vetoLQTY == 0) {
// Replaces try / catch | Enforces sufficient gas is passed
hookStatus = safeCallWithMinGas(
initiative,
MIN_GAS_TO_HOOK,
0,
abi.encodeCall(
IInitiative.onAfterAllocateLQTY,
(vars.currentEpoch, msg.sender, vars.userState, vars.allocation, vars.initiativeState)
)
) ? HookStatus.Succeeded : HookStatus.Failed;
} else {
hookStatus = HookStatus.NotCalled;
}
emit AllocateLQTY(
msg.sender, initiative, vars.deltaLQTYVotes, vars.deltaLQTYVetos, vars.currentEpoch, hookStatus
);
}
require(
vars.userState.allocatedLQTY <= stakingV1.stakes(deriveUserProxyAddress(msg.sender)),
"Governance: insufficient-or-allocated-lqty"
);
globalState = vars.state;
userStates[msg.sender] = vars.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.UNREGISTERABLE, "Governance: cannot-unregister-initiative");
// Remove weight from current state
uint256 currentEpoch = epoch();
// NOTE: Safe to remove | See `check_claim_soundness`
assert(initiativeState.lastEpochClaim < currentEpoch - 1);
assert(state.countedVoteLQTY >= initiativeState.voteLQTY);
assert(state.countedVoteOffset >= initiativeState.voteOffset);
state.countedVoteLQTY -= initiativeState.voteLQTY;
state.countedVoteOffset -= initiativeState.voteOffset;
globalState = state;
/// Epoch will never reach 2^256 - 1
registeredInitiatives[_initiative] = UNREGISTERED_INITIATIVE;
// Replaces try / catch | Enforces sufficient gas is passed
bool success = safeCallWithMinGas(
_initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onUnregisterInitiative, (currentEpoch))
);
emit UnregisterInitiative(_initiative, currentEpoch, success ? HookStatus.Succeeded : HookStatus.Failed);
}
/// @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;
}
/// 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;
/// 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);
// Replaces try / catch | Enforces sufficient gas is passed
bool success = safeCallWithMinGas(
_initiative,
MIN_GAS_TO_HOOK,
0,
abi.encodeCall(IInitiative.onClaimForInitiative, (votesSnapshot_.forEpoch, claimableAmount))
);
emit ClaimForInitiative(
_initiative, claimableAmount, votesSnapshot_.forEpoch, success ? HookStatus.Succeeded : HookStatus.Failed
);
return claimableAmount;
}
function _requireNoNOP(int256[] memory _absoluteLQTYVotes, int256[] memory _absoluteLQTYVetos) internal pure {
for (uint256 i; i < _absoluteLQTYVotes.length; i++) {
require(_absoluteLQTYVotes[i] > 0 || _absoluteLQTYVetos[i] > 0, "Governance: voting nothing");
}
}
function _requireNoSimultaneousVoteAndVeto(int256[] memory _absoluteLQTYVotes, int256[] memory _absoluteLQTYVetos)
internal
pure
{
for (uint256 i; i < _absoluteLQTYVotes.length; i++) {
require(_absoluteLQTYVotes[i] == 0 || _absoluteLQTYVetos[i] == 0, "Governance: vote-and-veto");
}
}
}// 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";
uint256 constant UNREGISTERED_INITIATIVE = type(uint256).max;
interface IGovernance {
enum HookStatus {
Failed,
Succeeded,
NotCalled
}
/// @notice Emitted when a user deposits LQTY
/// @param user The account depositing LQTY
/// @param rewardRecipient The account receiving the LUSD/ETH rewards earned from staking in V1, if claimed
/// @param lqtyAmount The amount of LQTY being deposited
/// @return lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
/// @return lusdSent Amount of LUSD tokens sent to `rewardRecipient` (may include previously received LUSD)
/// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
/// @return ethSent Amount of ETH sent to `rewardRecipient` (may include previously received ETH)
event DepositLQTY(
address indexed user,
address rewardRecipient,
uint256 lqtyAmount,
uint256 lusdReceived,
uint256 lusdSent,
uint256 ethReceived,
uint256 ethSent
);
/// @notice Emitted when a user withdraws LQTY or claims V1 staking rewards
/// @param user The account withdrawing LQTY or claiming V1 staking rewards
/// @param recipient The account receiving the LQTY withdrawn, and if claimed, the LUSD/ETH rewards earned from staking in V1
/// @return lqtyReceived Amount of LQTY tokens actually withdrawn (may be lower than the `_lqtyAmount` passed to `withdrawLQTY`)
/// @return lqtySent Amount of LQTY tokens sent to `recipient` (may include LQTY sent to the user's proxy from sources other than V1 staking)
/// @return lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
/// @return lusdSent Amount of LUSD tokens sent to `recipient` (may include previously received LUSD)
/// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
/// @return ethSent Amount of ETH sent to `recipient` (may include previously received ETH)
event WithdrawLQTY(
address indexed user,
address recipient,
uint256 lqtyReceived,
uint256 lqtySent,
uint256 lusdReceived,
uint256 lusdSent,
uint256 ethReceived,
uint256 ethSent
);
event SnapshotVotes(uint256 votes, uint256 forEpoch, uint256 boldAccrued);
event SnapshotVotesForInitiative(address indexed initiative, uint256 votes, uint256 vetos, uint256 forEpoch);
event RegisterInitiative(address initiative, address registrant, uint256 atEpoch, HookStatus hookStatus);
event UnregisterInitiative(address initiative, uint256 atEpoch, HookStatus hookStatus);
event AllocateLQTY(
address indexed user,
address indexed initiative,
int256 deltaVoteLQTY,
int256 deltaVetoLQTY,
uint256 atEpoch,
HookStatus hookStatus
);
event ClaimForInitiative(address indexed initiative, uint256 bold, uint256 forEpoch, HookStatus hookStatus);
struct Configuration {
uint256 registrationFee;
uint256 registrationThresholdFactor;
uint256 unregistrationThresholdFactor;
uint256 unregistrationAfterEpochs;
uint256 votingThresholdFactor;
uint256 minClaim;
uint256 minAccrual;
uint256 epochStart;
uint256 epochDuration;
uint256 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 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 {
uint256 votes; // Votes at epoch transition
uint256 forEpoch; // Epoch for which the votes are counted
}
struct InitiativeVoteSnapshot {
uint256 votes; // Votes at epoch transition
uint256 forEpoch; // Epoch for which the votes are counted
uint256 lastCountedEpoch; // Epoch at which which the votes where counted last in the global snapshot
uint256 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 (uint256 votes, uint256 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 (uint256 votes, uint256 forEpoch, uint256 lastCountedEpoch, uint256 vetos);
struct Allocation {
uint256 voteLQTY; // LQTY allocated vouching for the initiative
uint256 voteOffset; // Offset associated with LQTY vouching for the initiative
uint256 vetoLQTY; // LQTY vetoing the initiative
uint256 vetoOffset; // Offset associated with LQTY vetoing the initiative
uint256 atEpoch; // Epoch at which the allocation was last updated
}
struct UserState {
uint256 unallocatedLQTY; // LQTY deposited and unallocated
uint256 unallocatedOffset; // The offset sum corresponding to the unallocated LQTY
uint256 allocatedLQTY; // LQTY allocated by the user to initatives
uint256 allocatedOffset; // The offset sum corresponding to the allocated LQTY
}
struct InitiativeState {
uint256 voteLQTY; // LQTY allocated vouching for the initiative
uint256 voteOffset; // Offset associated with LQTY vouching for to the initative
uint256 vetoLQTY; // LQTY allocated vetoing the initiative
uint256 vetoOffset; // Offset associated with LQTY veoting the initative
uint256 lastEpochClaim;
}
struct GlobalState {
uint256 countedVoteLQTY; // Total LQTY that is included in vote counting
uint256 countedVoteOffset; // Offset associated with the counted vote LQTY
}
/// @notice Returns the user's state
/// @return unallocatedLQTY LQTY deposited and unallocated
/// @return unallocatedOffset Offset associated with unallocated LQTY
/// @return allocatedLQTY allocated by the user to initatives
/// @return allocatedOffset Offset associated with allocated LQTY
function userStates(address _user)
external
view
returns (uint256 unallocatedLQTY, uint256 unallocatedOffset, uint256 allocatedLQTY, uint256 allocatedOffset);
/// @notice Returns the initiative's state
/// @param _initiative Address of the initiative
/// @return voteLQTY LQTY allocated vouching for the initiative
/// @return voteOffset Offset associated with voteLQTY
/// @return vetoLQTY LQTY allocated vetoing the initiative
/// @return vetoOffset Offset associated with vetoLQTY
/// @return lastEpochClaim // Last epoch at which rewards were claimed
function initiativeStates(address _initiative)
external
view
returns (uint256 voteLQTY, uint256 voteOffset, uint256 vetoLQTY, uint256 vetoOffset, uint256 lastEpochClaim);
/// @notice Returns the global state
/// @return countedVoteLQTY Total LQTY that is included in vote counting
/// @return countedVoteOffset Offset associated with countedVoteLQTY
function globalState() external view returns (uint256 countedVoteLQTY, uint256 countedVoteOffset);
/// @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 voteOffset The offset associated with voteLQTY
/// @return vetoLQTY allocated vetoing the initiative
/// @return vetoOffset the offset associated with vetoLQTY
/// @return atEpoch Epoch at which the allocation was last updated
function lqtyAllocatedByUserToInitiative(address _user, address _initiative)
external
view
returns (uint256 voteLQTY, uint256 voteOffset, uint256 vetoLQTY, uint256 vetoOffset, uint256 atEpoch);
/// @notice Returns when an initiative was registered
/// @param _initiative Address of the initiative
/// @return atEpoch If `_initiative` is an active initiative, returns the epoch at which it was registered.
/// If `_initiative` hasn't been registered, returns 0.
/// If `_initiative` has been unregistered, returns `UNREGISTERED_INITIATIVE`.
function registeredInitiatives(address _initiative) external view returns (uint256 atEpoch);
/*//////////////////////////////////////////////////////////////
STAKING
//////////////////////////////////////////////////////////////*/
/// @notice Deposits LQTY
/// @dev The caller has to approve their `UserProxy` address to spend the LQTY tokens
/// @param _lqtyAmount Amount of LQTY to deposit
function depositLQTY(uint256 _lqtyAmount) external;
/// @notice Deposits LQTY
/// @dev The caller has to approve their `UserProxy` address to spend the LQTY tokens
/// @param _lqtyAmount Amount of LQTY to deposit
/// @param _doSendRewards If true, send rewards claimed from LQTY staking
/// @param _recipient Address to which the tokens should be sent
function depositLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) external;
/// @notice Deposits LQTY via Permit
/// @param _lqtyAmount Amount of LQTY to deposit
/// @param _permitParams Permit parameters
function depositLQTYViaPermit(uint256 _lqtyAmount, PermitParams calldata _permitParams) external;
/// @notice Deposits LQTY via Permit
/// @param _lqtyAmount Amount of LQTY to deposit
/// @param _permitParams Permit parameters
/// @param _doSendRewards If true, send rewards claimed from LQTY staking
/// @param _recipient Address to which the tokens should be sent
function depositLQTYViaPermit(
uint256 _lqtyAmount,
PermitParams calldata _permitParams,
bool _doSendRewards,
address _recipient
) external;
/// @notice Withdraws LQTY and claims any accrued LUSD and ETH rewards from StakingV1
/// @param _lqtyAmount Amount of LQTY to withdraw
function withdrawLQTY(uint256 _lqtyAmount) external;
/// @notice Withdraws LQTY and claims any accrued LUSD and ETH rewards from StakingV1
/// @param _lqtyAmount Amount of LQTY to withdraw
/// @param _doSendRewards If true, send rewards claimed from LQTY staking
/// @param _recipient Address to which the tokens should be sent
function withdrawLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) external;
/// @notice Claims staking rewards from StakingV1 without unstaking
/// @dev Note: in the unlikely event that the caller's `UserProxy` holds any LQTY tokens, they will also be sent to `_rewardRecipient`
/// @param _rewardRecipient Address that will receive the rewards
/// @return lusdSent Amount of LUSD tokens sent to `_rewardRecipient` (may include previously received LUSD)
/// @return ethSent Amount of ETH sent to `_rewardRecipient` (may include previously received ETH)
function claimFromStakingV1(address _rewardRecipient) external returns (uint256 lusdSent, uint256 ethSent);
/*//////////////////////////////////////////////////////////////
VOTING
//////////////////////////////////////////////////////////////*/
/// @notice Returns the current epoch number
/// @return epoch Current epoch
function epoch() external view returns (uint256 epoch);
/// @notice Returns the timestamp at which the current epoch started
/// @return epochStart Epoch start of the current epoch
function epochStart() external view returns (uint256 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 (uint256 secondsWithinEpoch);
/// @notice Returns the voting power for an entity (i.e. user or initiative) at a given timestamp
/// @param _lqtyAmount Amount of LQTY associated with the entity
/// @param _timestamp Timestamp at which to calculate voting power
/// @param _offset The entity's offset sum
/// @return votes Number of votes
function lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) external pure returns (uint256);
/// @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() external returns (uint256);
/// @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) external view returns (uint256);
/// @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()
external
view
returns (VoteSnapshot memory snapshot, GlobalState memory state, bool shouldUpdate);
/// @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)
external
view
returns (
InitiativeVoteSnapshot memory initiativeSnapshot,
InitiativeState memory initiativeState,
bool shouldUpdate
);
/// @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);
/*//////////////////////////////////////////////////////////////
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
}
function getInitiativeState(address _initiative)
external
returns (InitiativeStatus status, uint256 lastEpochClaim, uint256 claimableAmount);
function getInitiativeState(
address _initiative,
VoteSnapshot memory _votesSnapshot,
InitiativeVoteSnapshot memory _votesForInitiativeSnapshot,
InitiativeState memory _initiativeState
) external view returns (InitiativeStatus status, uint256 lastEpochClaim, uint256 claimableAmount);
/// @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 _initiativesToReset 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 LQTY to allocate to the initiatives as votes
/// @param _absoluteLQTYVetos LQTY to allocate to the initiatives as vetos
function allocateLQTY(
address[] calldata _initiativesToReset,
address[] memory _initiatives,
int256[] memory _absoluteLQTYVotes,
int256[] memory _absoluteLQTYVetos
) external;
/// @notice Deallocates the user's LQTY from initiatives
/// @param _initiativesToReset Addresses of initiatives to deallocate LQTY from
/// @param _checkAll When true, the call will revert if there is still some allocated LQTY left after deallocating
/// from all the addresses in `_initiativesToReset`
function resetAllocations(address[] calldata _initiativesToReset, bool _checkAll) 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(uint256 _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(uint256 _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(
uint256 _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(uint256 _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 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent)
{
uint256 initialLUSDAmount = lusd.balanceOf(address(this));
uint256 initialETHAmount = address(this).balance;
lqty.transferFrom(_lqtyFrom, address(this), _amount);
stakingV1.stake(_amount);
uint256 lusdAmount = lusd.balanceOf(address(this));
uint256 ethAmount = address(this).balance;
lusdReceived = lusdAmount - initialLUSDAmount;
ethReceived = ethAmount - initialETHAmount;
if (_doSendRewards) (lusdSent, ethSent) = _sendRewards(_recipient, lusdAmount, ethAmount);
}
/// @inheritdoc IUserProxy
function stakeViaPermit(
uint256 _amount,
address _lqtyFrom,
PermitParams calldata _permitParams,
bool _doSendRewards,
address _recipient
) external onlyStakingV2 returns (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent) {
require(_lqtyFrom == _permitParams.owner, "UserProxy: owner-not-sender");
try IERC20Permit(address(lqty)).permit(
_permitParams.owner,
_permitParams.spender,
_permitParams.value,
_permitParams.deadline,
_permitParams.v,
_permitParams.r,
_permitParams.s
) {} catch {}
return stake(_amount, _lqtyFrom, _doSendRewards, _recipient);
}
/// @inheritdoc IUserProxy
function unstake(uint256 _amount, bool _doSendRewards, address _recipient)
external
onlyStakingV2
returns (
uint256 lqtyReceived,
uint256 lqtySent,
uint256 lusdReceived,
uint256 lusdSent,
uint256 ethReceived,
uint256 ethSent
)
{
uint256 initialLQTYAmount = lqty.balanceOf(address(this));
uint256 initialLUSDAmount = lusd.balanceOf(address(this));
uint256 initialETHAmount = address(this).balance;
stakingV1.unstake(_amount);
lqtySent = lqty.balanceOf(address(this));
uint256 lusdAmount = lusd.balanceOf(address(this));
uint256 ethAmount = address(this).balance;
lqtyReceived = lqtySent - initialLQTYAmount;
lusdReceived = lusdAmount - initialLUSDAmount;
ethReceived = ethAmount - initialETHAmount;
if (lqtySent > 0) lqty.transfer(_recipient, lqtySent);
if (_doSendRewards) (lusdSent, ethSent) = _sendRewards(_recipient, lusdAmount, ethAmount);
}
function _sendRewards(address _recipient, uint256 _lusdAmount, uint256 _ethAmount)
internal
returns (uint256 lusdSent, uint256 ethSent)
{
if (_lusdAmount > 0) lusd.transfer(_recipient, _lusdAmount);
if (_ethAmount > 0) {
(bool success,) = payable(_recipient).call{value: _ethAmount}("");
require(success, "UserProxy: eth-fail");
}
return (_lusdAmount, _ethAmount);
}
/// @inheritdoc IUserProxy
function staked() external view returns (uint256) {
return 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(uint256 a, int256 b) pure returns (uint256) {
if (b < 0) {
return a - abs(b);
}
return a + uint256(b);
}
function sub(uint256 a, int256 b) pure returns (uint256) {
if (b < 0) {
return a + abs(b);
}
return a - uint256(b);
}
function max(uint256 a, uint256 b) pure returns (uint256) {
return a > b ? a : b;
}
function abs(int256 a) pure returns (uint256) {
return a < 0 ? uint256(-int256(a)) : uint256(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(int256[] memory vals) pure {
uint256 arrLength = vals.length;
for (uint i; i < arrLength; i++) {
require(vals[i] >= 0, "Cannot be negative");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {IMultiDelegateCall} from "../interfaces/IMultiDelegateCall.sol";
contract MultiDelegateCall is IMultiDelegateCall {
/// @inheritdoc IMultiDelegateCall
function multiDelegateCall(bytes[] calldata inputs) external returns (bytes[] memory returnValues) {
returnValues = new bytes[](inputs.length);
for (uint256 i; i < inputs.length; ++i) {
(bool success, bytes memory returnData) = address(this).delegatecall(inputs[i]);
if (!success) {
// Bubble up the revert
assembly {
revert(
add(32, returnData), // offset (skip first 32 bytes, where the size of the array is stored)
mload(returnData) // size
)
}
}
returnValues[i] = returnData;
}
}
}// 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)
{
/// 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
pragma solidity ^0.8.24;
function _lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) pure returns (uint256) {
uint256 prod = _lqtyAmount * _timestamp;
return prod > _offset ? prod - _offset : 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 {
/// @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 lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
/// @return lusdSent Amount of LUSD tokens sent to `_recipient` (may include previously received LUSD)
/// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
/// @return ethSent Amount of ETH sent to `_recipient` (may include previously received ETH)
function stake(uint256 _amount, address _lqtyFrom, bool _doSendRewards, address _recipient)
external
returns (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent);
/// @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 lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
/// @return lusdSent Amount of LUSD tokens sent to `_recipient` (may include previously received LUSD)
/// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
/// @return ethSent Amount of ETH sent to `_recipient` (may include previously received ETH)
function stakeViaPermit(
uint256 _amount,
address _lqtyFrom,
PermitParams calldata _permitParams,
bool _doSendRewards,
address _recipient
) external returns (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent);
/// @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 lqtyReceived Amount of LQTY tokens actually unstaked (may be lower than `_amount`)
/// @return lqtySent Amount of LQTY tokens sent to `_recipient` (may include LQTY sent to the proxy from sources other than V1 staking)
/// @return lusdReceived Amount of LUSD tokens received as a side-effect of staking new LQTY
/// @return lusdSent Amount of LUSD tokens claimed (may include previously received LUSD)
/// @return ethReceived Amount of ETH received as a side-effect of staking new LQTY
/// @return ethSent Amount of ETH claimed (may include previously received ETH)
function unstake(uint256 _amount, bool _doSendRewards, address _recipient)
external
returns (
uint256 lqtyReceived,
uint256 lqtySent,
uint256 lusdReceived,
uint256 lusdSent,
uint256 ethReceived,
uint256 ethSent
);
/// @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 (uint256);
}// 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;
interface IMultiDelegateCall {
/// @notice Call multiple functions of the contract while preserving `msg.sender`
/// @param inputs Function calls to perform, encoded using `abi.encodeCall()` or equivalent
/// @return returnValues Raw data returned by each call
function multiDelegateCall(bytes[] calldata inputs) external returns (bytes[] memory returnValues);
}{
"remappings": [
"openzeppelin/=lib/V2-gov/lib/openzeppelin-contracts/",
"@chimera/=lib/V2-gov/lib/chimera/src/",
"@openzeppelin/contracts/=lib/V2-gov/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-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"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": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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":"uint256","name":"registrationFee","type":"uint256"},{"internalType":"uint256","name":"registrationThresholdFactor","type":"uint256"},{"internalType":"uint256","name":"unregistrationThresholdFactor","type":"uint256"},{"internalType":"uint256","name":"unregistrationAfterEpochs","type":"uint256"},{"internalType":"uint256","name":"votingThresholdFactor","type":"uint256"},{"internalType":"uint256","name":"minClaim","type":"uint256"},{"internalType":"uint256","name":"minAccrual","type":"uint256"},{"internalType":"uint256","name":"epochStart","type":"uint256"},{"internalType":"uint256","name":"epochDuration","type":"uint256"},{"internalType":"uint256","name":"epochVotingCutoff","type":"uint256"}],"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":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"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":"uint256","name":"atEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"name":"AllocateLQTY","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"bold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"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":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"rewardRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"lqtyAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethSent","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":"uint256","name":"atEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"name":"RegisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"votes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"boldAccrued","type":"uint256"}],"name":"SnapshotVotes","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"votes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vetos","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"forEpoch","type":"uint256"}],"name":"SnapshotVotesForInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"initiative","type":"address"},{"indexed":false,"internalType":"uint256","name":"atEpoch","type":"uint256"},{"indexed":false,"internalType":"enum IGovernance.HookStatus","name":"hookStatus","type":"uint8"}],"name":"UnregisterInitiative","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"lqtyReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lqtySent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lusdSent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethSent","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":"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":"int256[]","name":"_absoluteLQTYVotes","type":"int256[]"},{"internalType":"int256[]","name":"_absoluteLQTYVetos","type":"int256[]"}],"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":"lusdSent","type":"uint256"},{"internalType":"uint256","name":"ethSent","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deployUserProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"depositLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"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":"uint256","name":"_lqtyAmount","type":"uint256"},{"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":"address","name":"_user","type":"address"}],"name":"deriveUserProxyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeSnapshotAndState","outputs":[{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"initiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"}],"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":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"internalType":"struct IGovernance.VoteSnapshot","name":"_votesSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"internalType":"struct IGovernance.InitiativeVoteSnapshot","name":"_votesForInitiativeSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"}],"internalType":"struct IGovernance.InitiativeState","name":"_initiativeState","type":"tuple"}],"name":"getInitiativeState","outputs":[{"internalType":"enum IGovernance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"},{"internalType":"uint256","name":"claimableAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"getInitiativeState","outputs":[{"internalType":"enum IGovernance.InitiativeStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"},{"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":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"internalType":"struct IGovernance.VoteSnapshot","name":"snapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"countedVoteLQTY","type":"uint256"},{"internalType":"uint256","name":"countedVoteOffset","type":"uint256"}],"internalType":"struct IGovernance.GlobalState","name":"state","type":"tuple"},{"internalType":"bool","name":"shouldUpdate","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalState","outputs":[{"internalType":"uint256","name":"countedVoteLQTY","type":"uint256"},{"internalType":"uint256","name":"countedVoteOffset","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initiativeStates","outputs":[{"internalType":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"lastEpochClaim","type":"uint256"}],"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":"uint256","name":"voteLQTY","type":"uint256"},{"internalType":"uint256","name":"voteOffset","type":"uint256"},{"internalType":"uint256","name":"vetoLQTY","type":"uint256"},{"internalType":"uint256","name":"vetoOffset","type":"uint256"},{"internalType":"uint256","name":"atEpoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"},{"internalType":"uint256","name":"_offset","type":"uint256"}],"name":"lqtyToVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"multiDelegateCall","outputs":[{"internalType":"bytes[]","name":"returnValues","type":"bytes[]"}],"stateMutability":"nonpayable","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":"uint256","name":"","type":"uint256"}],"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":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiative","type":"address"}],"name":"snapshotVotesForInitiative","outputs":[{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"internalType":"struct IGovernance.VoteSnapshot","name":"voteSnapshot","type":"tuple"},{"components":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"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":"uint256","name":"unallocatedLQTY","type":"uint256"},{"internalType":"uint256","name":"unallocatedOffset","type":"uint256"},{"internalType":"uint256","name":"allocatedLQTY","type":"uint256"},{"internalType":"uint256","name":"allocatedOffset","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votesForInitiativeSnapshot","outputs":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"},{"internalType":"uint256","name":"lastCountedEpoch","type":"uint256"},{"internalType":"uint256","name":"vetos","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votesSnapshot","outputs":[{"internalType":"uint256","name":"votes","type":"uint256"},{"internalType":"uint256","name":"forEpoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lqtyAmount","type":"uint256"},{"internalType":"bool","name":"_doSendRewards","type":"bool"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawLQTY","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
61024060405234801562000011575f80fd5b506040516200655538038062006555833981016040819052620000349162000756565b8187878782828260405162000049906200063f565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103905ff08015801562000083573d5f803e3d5ffd5b506001600160a01b0390811660805260015f81815581546001600160a01b03191692871692831790915560405191945092505f805160206200653583398151915291508290a3506001600160a01b0380861660a090815288821660c090815291861660e05290840151908401511115620001445760405162461bcd60e51b815260206004820152601d60248201527f476f763a206d696e2d636c61696d2d67742d6d696e2d6163637275616c00000060448201526064015b60405180910390fd5b82516101a0526020830151670de0b6b3a764000011620001a75760405162461bcd60e51b815260206004820152601860248201527f476f763a20726567697374726174696f6e2d636f6e666967000000000000000060448201526064016200013b565b60208301516101c0526040830151670de0b6b3a7640000106200020d5760405162461bcd60e51b815260206004820152601a60248201527f476f763a20756e726567697374726174696f6e2d636f6e66696700000000000060448201526064016200013b565b60408301516101e0526060830151610200526080830151670de0b6b3a764000011620002715760405162461bcd60e51b8152602060048201526012602482015271476f763a20766f74696e672d636f6e66696760701b60448201526064016200013b565b60808301516102205260a08301516101605260c08301516101805260e0830151421015620002e25760405162461bcd60e51b815260206004820152601b60248201527f476f763a2063616e6e6f742d73746172742d696e2d667574757265000000000060448201526064016200013b565b60e08301516101009081528301516200033e5760405162461bcd60e51b815260206004820152601860248201527f476f763a2065706f63682d6475726174696f6e2d7a65726f000000000000000060448201526064016200013b565b6101008301805161012090815290519084015110620003b35760405162461bcd60e51b815260206004820152602a60248201527f476f763a2065706f63682d766f74696e672d6375746f66662d67742d65706f636044820152693416b23ab930ba34b7b760b11b60648201526084016200013b565b61012083015161014052805115620003d057620003d081620003dd565b50505050505050620008e0565b620003f26001546001600160a01b0316331490565b620004405760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016200013b565b5f5b815181101562000578576001600b5f84848151811062000466576200046662000881565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055505f62000501838381518110620004ab57620004ab62000881565b6020026020010151620557305f6001604051602401620004cd91815260200190565b60408051601f198184030181529190526020810180516001600160e01b03908116632695d74d60e01b179091526200058616565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c83838151811062000539576200053962000881565b60200260200101513360018462000551575f62000554565b60015b60405162000566949392919062000895565b60405180910390a15060010162000442565b5062000583620005eb565b50565b5f62000595846103e862000622565b620005d65760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b60448201526064016200013b565b5f80835160208501868989f195945050505050565b6001546040515f916001600160a01b0316905f8051602062006535833981519152908390a3600180546001600160a01b0319169055565b5f80603f83619c4001026040850201603f5a021015949350505050565b610fc2806200557383390190565b80516001600160a01b038116811462000664575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b60405161014081016001600160401b0381118282101715620006a357620006a362000669565b60405290565b5f82601f830112620006b9575f80fd5b815160206001600160401b0380831115620006d857620006d862000669565b8260051b604051601f19603f8301168101818110848211171562000700576200070062000669565b604052938452602081870181019490810192508785111562000720575f80fd5b6020870191505b848210156200074b576200073b826200064d565b8352918301919083019062000727565b979650505050505050565b5f805f805f805f8789036102008112156200076f575f80fd5b6200077a896200064d565b97506200078a60208a016200064d565b96506200079a60408a016200064d565b9550620007aa60608a016200064d565b945061014080607f1983011215620007c0575f80fd5b620007ca6200067d565b915060808a0151825260a08a0151602083015260c08a0151604083015260e08a01516060830152610100808b01516080840152610120808c015160a0850152828c015160c08501526101608c015160e08501526101808c0151828501526101a08c01518185015250505080935050620008476101c089016200064d565b6101e08901519092506001600160401b0381111562000864575f80fd5b620008728a828b01620006a9565b91505092959891949750929550565b634e487b7160e01b5f52603260045260245ffd5b6001600160a01b03858116825284166020820152604081018390526080810160038310620008d157634e487b7160e01b5f52602160045260245ffd5b82606083015295945050505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e0516102005161022051614b6162000a125f395f818161067d0152611ce201525f81816108f30152610f8501525f81816105a90152610fd701525f81816103ed01526112ce01525f8181610582015261128301525f818161092801526128cf01525f81816103c60152611ca001525f818161031d01526118a001525f81816107a901528181610ba201528181610e290152611e8001525f818161046601528181610be501528181610e4d0152611ea401525f81816105d0015281816112570152818161238e01528181612414015261285a01525f61060a01525f8181610975015281816113200152613ae801525f81816106ac01528181611049015261250a0152614b615ff3fe608060405234801561000f575f80fd5b50600436106102ff575f3560e01c80638a00b67a11610195578063adfcdb9d116100e4578063cf84a88c1161009e578063e772b76511610079578063e772b76514610923578063ecf22e321461094a578063ee1c71721461095d578063f556a79c14610970575f80fd5b8063cf84a88c14610876578063e4a61d95146108ee578063e76c01e414610915575f80fd5b8063adfcdb9d146107f6578063b1d8f27414610809578063b285a8701461082a578063bfd79cae1461083d578063c20fb59e14610850578063cc9baa1214610863575f80fd5b8063900cf0cf1161014f578063a5e8455d1161012a578063a5e8455d1461079b578063a70b9f0c146107a4578063a9952a0c146107cb578063ac85912e146107ee575f80fd5b8063900cf0cf1461073957806391050f4014610741578063972e6d6614610788575f80fd5b80638a00b67a146106785780638b7d38a11461069f5780638d4f0b6c146106a75780638da5cb5b146106ce5780638eb9399e146106df5780638f32d59b1461071b575f80fd5b8063524fcc8011610251578063727d0f351161020b5780638113630d116101e65780638113630d1461062c57806382a15be11461063f5780638777e0951461065257806388edf9be14610665575f80fd5b8063727d0f35146105cb57806378a1bdd4146105f25780637f6ec45514610605575f80fd5b8063524fcc80146104b257806352d420b91461051657806353905402146105295780635c79696c1461055d57806364b4f7511461057d5780636bca7c55146105a4575f80fd5b80632591003a116102bc578063415c2d9611610297578063415c2d961461044157806346d62a631461046157806350283275146104885780635245b1ea14610490575f80fd5b80632591003a146103e857806338f3e2851461040f578063394ff4061461042e575f80fd5b8063070f418a146103035780630df9049b146103185780630ecc535f146103525780630f2f6aa5146103a657806315e5a1e5146103b95780631cd2a05d146103c1575b5f80fd5b61031661031136600461400f565b610997565b005b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61038661036036600461403c565b60086020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610349565b6103166103b436600461403c565b6109a6565b61033f610b9f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f61041d36600461403c565b600b6020525f908152604090205481565b61031661043c366004614078565b610c0e565b61045461044f36600461410f565b610d19565b604051610349919061416f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f610e26565b6104a361049e3660046142e2565b610e7c565b60405161034993929190614390565b6104ee6104c036600461403c565b60096020525f9081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a001610349565b61031661052436600461400f565b611037565b61038661053736600461403c565b60056020525f908152604090208054600182015460028301546003909301549192909184565b610565611042565b6040516001600160a01b039091168152602001610349565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b61031661060036600461403c565b6110ab565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b61031661063a3660046143b8565b6114d1565b61031661064d36600461445f565b611627565b61033f61066036600461400f565b611c57565b610316610673366004614519565b611d1f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f611daf565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b6001546001600160a01b0316610565565b6106e7611dce565b604080518451815260209485015185820152835191810191909152929091015160608301521515608082015260a001610349565b6001546001600160a01b031633146040519015158152602001610349565b61033f611e7d565b6104ee61074f36600461456b565b600a60209081525f9283526040808420909152908252902080546001820154600283015460038401546004909401549293919290919085565b6104a361079636600461403c565b611ede565b61033f60025481565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6003546004546107d9919082565b60408051928352602083019190915201610349565b61033f611f18565b61033f61080436600461459c565b611f26565b61081c61081736600461403c565b611f3c565b6040516103499291906145c5565b610316610838366004614605565b611fa3565b6107d961084b36600461403c565b6121a5565b61033f61085e36600461403c565b6122bc565b61056561087136600461403c565b612504565b61088961088436600461403c565b61253e565b60408051845181526020808601518183015285830151828401526060958601518683015284516080808401919091529085015160a08301529184015160c08201529383015160e085015290910151610100830152151561012082015261014001610349565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b6006546007546107d9919082565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b610316610958366004614605565b6126a9565b61031661096b366004614640565b6127be565b6105657f000000000000000000000000000000000000000000000000000000000000000081565b6109a381600133611fa3565b50565b6109ae6127ce565b5f806109b86127f6565b915091505f806109c78561294e565b915091505f6109d886868585610e7c565b50909150600590508160068111156109f2576109f261437c565b14610a555760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b60648201526084015b60405180910390fd5b5f610a5e611e7d565b9050610a6b600182614677565b836080015110610a7d57610a7d61468a565b825185511015610a8f57610a8f61468a565b826020015185602001511015610aa757610aa761468a565b825185518690610ab8908390614677565b9052506020808401519086018051610ad1908390614677565b90525084516006556020808601516007556001600160a01b0388165f908152600b90915260408082205f1990555160248101839052610b459089906205573090849060440160408051601f198184030181529190526020810180516001600160e01b031663955161cd60e01b179052612a18565b90507f4b2a06fb31ab5670eed8302066f58582b640ec5a17d06ddd383d30e3e11f1b64888383610b75575f610b78565b60015b604051610b87939291906146b2565b60405180910390a1505050505050506109a360015f55565b5f7f00000000000000000000000000000000000000000000000000000000000000006001610bcb611e7d565b610bd59190614677565b610bdf91906146d6565b610c09907f00000000000000000000000000000000000000000000000000000000000000006146ed565b905090565b610c166127ce565b5f610c2085612a79565b90505f805f80846001600160a01b031663eb876bf78a338b8b8b6040518663ffffffff1660e01b8152600401610c5a959493929190614700565b6080604051808303815f875af1158015610c76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9a91906147a4565b604080516001600160a01b038c168152602081018f9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050610d1360015f55565b50505050565b6060816001600160401b03811115610d3357610d336141e7565b604051908082528060200260200182016040528015610d6657816020015b6060815260200190600190039081610d515790505b5090505f5b82811015610e1f575f8030868685818110610d8857610d886147d7565b9050602002810190610d9a91906147eb565b604051610da892919061482d565b5f60405180830381855af49150503d805f8114610de0576040519150601f19603f3d011682016040523d82523d5f602084013e610de5565b606091505b509150915081610df757805181602001fd5b80848481518110610e0a57610e0a6147d7565b60209081029190910101525050600101610d6b565b5092915050565b5f7f0000000000000000000000000000000000000000000000000000000000000000610e727f000000000000000000000000000000000000000000000000000000000000000042614677565b610c099190614850565b6001600160a01b0384165f908152600b602052604081205481908190808203610eae575f805f9350935093505061102d565b5f610eb7611e7d565b9050808203610ed15760015f80945094509450505061102d565b6001600160a01b0389165f90815260096020526040902060040154935060018201610f055750600693505f915061102d9050565b610f10600182614677565b8410610f215760049450505061102d565b5f610f2e895f0151611c57565b905080885f0151118015610f46575060608801518851115b15610f7857885160025489515f9291610f5e916146d6565b610f689190614863565b60039750945061102d9350505050565b610f83600183614677565b7f00000000000000000000000000000000000000000000000000000000000000008860800151610fb391906146ed565b108061100d57508751606089015111801561100d5750670de0b6b3a7640000610ffc7f0000000000000000000000000000000000000000000000000000000000000000836146d6565b6110069190614863565b8860600151115b156110225750600594505f925061102d915050565b50600294505f925050505b9450945094915050565b6109a3815f336126a9565b5f8061106e7f000000000000000000000000000000000000000000000000000000000000000033612b4e565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6110b36127ce565b5f6110bc611e7d565b90506002811161111f5760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a20726567697374726174696f6e2d6e6f742d7965746044820152670b595b98589b195960c21b6064820152608401610a4c565b6001600160a01b0382166111755760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610a4c565b5f61117f83611ede565b509091505f90508160068111156111985761119861437c565b146111f75760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610a4c565b5f61120133612504565b90505f61120c6127f6565b50335f8181526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460608201529192506112a7907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690307f0000000000000000000000000000000000000000000000000000000000000000612bb9565b8151602082015160608301515f916112be916146ed565b9050670de0b6b3a76400006112f37f0000000000000000000000000000000000000000000000000000000000000000846146d6565b6112fd9190614863565b6040516305a4d3f160e21b81526001600160a01b038781166004830152611399917f0000000000000000000000000000000000000000000000000000000000000000909116906316934fc490602401602060405180830381865afa158015611367573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061138b9190614876565b611393610b9f565b84611f26565b10156113e75760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610a4c565b6001600160a01b0388165f908152600b6020526040902087905561140c600188614677565b6001600160a01b0389165f9081526009602052604080822060040192909255905160248101899052611474908a90620557309084906044015b60408051601f198184030181529190526020810180516001600160e01b0316632695d74d60e01b179052612a18565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c89338a846114a5575f6114a8565b60015b6040516114b8949392919061488d565b60405180910390a150505050505050506109a360015f55565b6001546001600160a01b0316331461152b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610a4c565b5f5b815181101561161e576001600b5f84848151811061154d5761154d6147d7565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055505f6115af83838151811061158e5761158e6147d7565b6020026020010151620557305f600160405160240161144591815260200190565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c8383815181106115e4576115e46147d7565b6020026020010151336001846115fa575f6115fd565b60015b60405161160d949392919061488d565b60405180910390a15060010161152d565b506109a3612c20565b61162f6127ce565b848314801561163d57508281145b6116935760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610a4c565b61169d8888612c69565b6116a78686612c69565b6116e28484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b61171d8282808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b6117888484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612db892505050565b6117f38484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612e5692505050565b5f6117fe8989612ef4565b335f9081526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282018390526003015460608201529192501561187e5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610a4c565b80515f0361189e5760405162461bcd60e51b8152600401610a4c906148b9565b7f00000000000000000000000000000000000000000000000000000000000000006118c7610e26565b1115611a3e575f5b87811015611a3c575f805b84518110156119c6578a8a848181106118f5576118f56147d7565b905060200201602081019061190a919061403c565b6001600160a01b0316858281518110611925576119256147d7565b60200260200101515f01516001600160a01b0316036119be5760019150848181518110611954576119546147d7565b602002602001015160200151898985818110611972576119726147d7565b9050602002013513156119b95760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610a4c565b6119c6565b6001016118da565b5080611a33578787838181106119de576119de6147d7565b905060200201355f14611a335760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610a4c565b506001016118cf565b505b5f876001600160401b03811115611a5757611a576141e7565b604051908082528060200260200182016040528015611a80578160200160208202803683370190505b5090505f886001600160401b03811115611a9c57611a9c6141e7565b604051908082528060200260200182016040528015611ac5578160200160208202803683370190505b5090505f5b89811015611ba057365f805f8c8c86818110611ae857611ae86147d7565b9050602002013513611afc57898986611b00565b8b8b875b9250925092505f838386818110611b1957611b196147d7565b9050602002013590505f885f0151828a60200151611b3791906146d6565b611b419190614863565b905081895f01818151611b549190614677565b905250602089018051829190611b6b908390614677565b90525082518190849088908110611b8457611b846147d7565b6020908102919091010152505060019093019250611aca915050565b50611c408a8a808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284375f9201919091525050604080516020808d0282810182019093528c82529093508c92508b9182918501908490808284375f9201919091525088925087915061336a9050565b50505050611c4d60015f55565b5050505050505050565b5f815f03611c6657505f919050565b5f8083670de0b6b3a7640000600254611c7f91906146d6565b611c899190614863565b90508015611cd15780611cc4670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006146d6565b611cce9190614863565b91505b611d17670de0b6b3a7640000611d077f0000000000000000000000000000000000000000000000000000000000000000876146d6565b611d119190614863565b83613bf7565b949350505050565b611d276127ce565b611d318383612c69565b611d3b8383612ef4565b508015611da157335f9081526008602052604090206002015415611da15760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610a4c565b611daa60015f55565b505050565b5f80611db96127f6565b509050611dc8815f0151611c57565b91505090565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f80611dfe611e7d565b60408051808201825260035481526004546020808301919091528251808401909352600654835260075490830152955093509050611e3d600182614677565b84602001511015611e7757825160019250611e6490611e5a610b9f565b8560200151611f26565b8452611e71600182614677565b60208501525b50909192565b5f7f0000000000000000000000000000000000000000000000000000000000000000611ec97f000000000000000000000000000000000000000000000000000000000000000042614677565b611ed39190614863565b610c099060016146ed565b5f805f80611eea6127f6565b5090505f80611ef88761294e565b91509150611f0887848484610e7c565b9550955095505050509193909250565b6003545f90611dc881611c57565b5f611f32848484613c0c565b90505b9392505050565b604080518082019091525f8082526020820152611f7660405180608001604052805f81526020015f81526020015f81526020015f81525090565b611f7e6127ce565b611f866127f6565b509150611f928361294e565b509050611f9e60015f55565b915091565b611fab6127ce565b335f81815260086020526040812091611fc390612504565b9050806001600160a01b03163b5f03611fee5760405162461bcd60e51b8152600401610a4c90614903565b81548511156120515760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e73756666696369656e742d756e616c6c6f63604482015268617465642d6c71747960b81b6064820152608401610a4c565b815485101561209957815460018301545f919061206e90886146d6565b6120789190614863565b905080836001015f82825461208d9190614677565b909155506120a0915050565b5f60018301555b84825f015f8282546120b29190614677565b9091555050604051633fdf42e360e01b81526004810186905284151560248201526001600160a01b0384811660448301525f9182918291829182918291881690633fdf42e39060640160c0604051808303815f875af1158015612117573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061213b9190614946565b955095509550955095509550336001600160a01b03167f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f3398a88888888888860405161218c979695949392919061498c565b60405180910390a25050505050505050611daa60015f55565b5f805f6121b133612504565b9050806001600160a01b03163b5f036121dc5760405162461bcd60e51b8152600401610a4c90614903565b604051633fdf42e360e01b81525f60048201819052600160248301526001600160a01b0386811660448401529091829182918291861690633fdf42e39060640160c0604051808303815f875af1158015612238573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061225c9190614946565b604051929c509a50939750919550935090915033907f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f339906122aa908b908890889088908e9089908f9061498c565b60405180910390a25050505050915091565b5f6122c56127ce565b5f6122ce6127f6565b5090505f806122dc8561294e565b915091505f806122ee87868686610e7c565b9193509091506003905082600681111561230a5761230a61437c565b1461231c575f955050505050506124f6565b6001612326611e7d565b6123309190614677565b8560200151146123425761234261468a565b600161234c611e7d565b6123569190614677565b6001600160a01b038881165f90815260096020526040808220600490810194909455516370a0823160e01b81523093810193909352917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa1580156123d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123f99190614876565b905080821115612407578091505b61243b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168984613c3a565b5f61249489620557305f8a6020015187604051602401612465929190918252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b0316630f0c6f0d60e11b179052612a18565b9050886001600160a01b03167f023acf63ebe0add874abd78d1a9b96f3384a3128bfdb97bf6c23e91f3d2e6e5a848960200151846124d2575f6124d5565b60015b6040516124e4939291906149c7565b60405180910390a25090955050505050505b6124ff60015f55565b919050565b5f6125387f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038416613c6b565b92915050565b61256560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61256d613edd565b5f80612577611e7d565b905060055f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060800160405290815f82015481526020016001820154815260200160028201548152602001600382015481525050935060095f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060a00160405290815f8201548152602001600182015481526020016002820154815260200160038201548152602001600482015481525050925060018161263e9190614677565b846020015110156126a157600191505f612656610b9f565b90505f61266b855f0151838760200151611f26565b90505f6126818660400151848860600151611f26565b828852606088018190529050612698600185614677565b60208801525050505b509193909250565b6126b16127ce565b5f6126bb84612a79565b6040516338fb359960e01b81526004810186905233602482015284151560448201526001600160a01b0384811660648301529192505f91829182918291908616906338fb3599906084016080604051808303815f875af1158015612721573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061274591906147a4565b604080516001600160a01b038c168152602081018e9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050611daa60015f55565b6127ca82825f33610c0e565b5050565b60025f54036127f057604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612825611dce565b919450925090508015612949578251600355602083015160049081556040516370a0823160e01b815230918101919091525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156128a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128cb9190614876565b90507f000000000000000000000000000000000000000000000000000000000000000081106128fa57806128fc565b5f5b6002819055845160208087015160408051938452918301528101919091527f977a95eb227039fce4f8fe452d2d625ab67187ac5363991816ed06f4e5eed5019060600160405180910390a1505b509091565b61297560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61297d613edd565b5f6129878461253e565b919450925090508015612a12576001600160a01b0384165f81815260056020908152604091829020865180825587830151600183018190558885015160028401556060808a01516003909401849055855192835293820192909252928301527f27b60fbfc71b45e0696ef86e3c31ac7979d464c139f6de0709de61b6b78093c5910160405180910390a25b50915091565b5f612a25846103e8613cca565b612a645760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610a4c565b5f80835160208501868989f195945050505050565b5f808211612ac95760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610a4c565b5f612ad333612504565b9050806001600160a01b03163b5f03612af057612aee611042565b505b335f90815260086020526040812080548392869291612b109084906146ed565b90915550612b20905084426146d6565b335f9081526008602052604081206001018054909190612b419084906146ed565b9091555090949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612538576040516330be1a3d60e21b815260040160405180910390fd5b6040516001600160a01b038481166024830152838116604483015260648201839052610d139186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613ce7565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b805f819003612c7757505050565b5f5b612c84600183614677565b811015610d13575f612c978260016146ed565b90505b82811015612d3d57848482818110612cb457612cb46147d7565b9050602002016020810190612cc9919061403c565b6001600160a01b0316858584818110612ce457612ce46147d7565b9050602002016020810190612cf9919061403c565b6001600160a01b031603612d355760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610a4c565b600101612c9a565b50600101612c79565b80515f5b81811015611daa575f838281518110612d6557612d656147d7565b60200260200101511215612db05760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610a4c565b600101612d4a565b5f5b8251811015611daa575f838281518110612dd657612dd66147d7565b60200260200101511380612e0257505f828281518110612df857612df86147d7565b6020026020010151135b612e4e5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610a4c565b600101612dba565b5f5b8251811015611daa57828181518110612e7357612e736147d7565b60200260200101515f1480612ea05750818181518110612e9557612e956147d7565b60200260200101515f145b612eec5760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610a4c565b600101612e58565b60605f826001600160401b03811115612f0f57612f0f6141e7565b604051908082528060200260200182016040528015612f7657816020015b612f636040518060a001604052805f6001600160a01b031681526020015f81526020015f81526020015f81526020015f81525090565b815260200190600190039081612f2d5790505b5090505f836001600160401b03811115612f9257612f926141e7565b604051908082528060200260200182016040528015612fbb578160200160208202803683370190505b5090505f846001600160401b03811115612fd757612fd76141e7565b604051908082528060200260200182016040528015613000578160200160208202803683370190505b5090505f856001600160401b0381111561301c5761301c6141e7565b604051908082528060200260200182016040528015613045578160200160208202803683370190505b5090505f866001600160401b03811115613061576130616141e7565b60405190808252806020026020018201604052801561308a578160200160208202803683370190505b5090505f5b8781101561331c57335f908152600a60205260408120818b8b858181106130b8576130b86147d7565b90506020020160208101906130cd919061403c565b6001600160a01b0316815260208082019290925260409081015f20815160a081018352815480825260018301549482019490945260028201549281019290925260038101546060830152600401546080820152915015158061313257505f8160400151115b61317e5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610a4c565b6040518060a001604052808b8b8581811061319b5761319b6147d7565b90506020020160208101906131b0919061403c565b6001600160a01b03168152602001825f01518152602001826040015181526020018260200151815260200182606001518152508783815181106131f5576131f56147d7565b6020026020010181905250868281518110613212576132126147d7565b602002602001015160200151613227906149e2565b868381518110613239576132396147d7565b602002602001018181525050868281518110613257576132576147d7565b60200260200101516040015161326c906149e2565b85838151811061327e5761327e6147d7565b60200260200101818152505086828151811061329c5761329c6147d7565b6020026020010151606001516132b1906149e2565b8483815181106132c3576132c36147d7565b6020026020010181815250508682815181106132e1576132e16147d7565b6020026020010151608001516132f6906149e2565b838381518110613308576133086147d7565b60209081029190910101525060010161308f565b5061335e8888808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508892508791508690508561336a565b50929695505050505050565b613372613f07565b61337a6127f6565b60208301528152613389611e7d565b60e0820152335f9081526008602090815260408083208151608081018352815481526001820154938101939093526002810154838301526003015460608301528301525b8651811015613ae5575f8782815181106133e9576133e96147d7565b60200260200101519050868281518110613405576134056147d7565b602002602001015183610100018181525050858281518110613429576134296147d7565b6020908102919091010151610120840152610100830151151580613451575061012083015115155b61345d5761345d61468a565b84828151811061346f5761346f6147d7565b602002602001015183610140018181525050838281518110613493576134936147d7565b6020026020010151836101600181815250506134ae8161294e565b608085018190526060850182905284515f926134cd9285929190610e7c565b505090505f84610100015113806134e857505f846101200151135b156135865760028160068111156135015761350161437c565b148061351e5750600381600681111561351c5761351c61437c565b145b8061353a575060048160068111156135385761353861437c565b145b6135865760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610a4c565b600681600681111561359a5761359a61437c565b036135fc575f846101000151131580156135b957505f84610120015113155b6135fc5760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610a4c565b6040805160a080820183526080808801805151845280516020908101519085015280518501519484019490945283516060908101519084015283518101519083015286015251516101008501516136539190613d48565b6080850180519190915251604001516101208501516136729190613d48565b6080850180516040019190915251602001516101408501516136949190613d48565b6080850180516020019190915251606001516101608501516136b69190613d48565b60808086018051606090810193909352516001600160a01b0385165f90815260096020908152604091829020835181559083015160018201559082015160028201559281015160038401550151600490910155600681600681111561371d5761371d61437c565b146137b75760a084015151602085015151101561373c5761373c61468a565b60a08401515160208501518051613754908390614677565b90525060a0840151602090810151818601519091018051613776908390614677565b905250608084015151602085015180516137919083906146ed565b90525060808401516020908101518186015190910180516137b39083906146ed565b9052505b335f908152600a602090815260408083206001600160a01b0386168452825291829020825160a0810184528154815260018201549281019283526002820154938101939093526003810154606084015260040154608083015260c0860191909152516101408501516138299190613d48565b60c08501805160200191909152516060015161016085015161384b9190613d48565b60c08501805160600191909152515161010085015161386a9190613d48565b60c0850180519190915251604001516101208501516138899190613d48565b60c0850180516040019190915260e085015181516080015251602081015190516138b49042906146d6565b10156138c2576138c261468a565b60c084015160608101516040909101516138dd9042906146d6565b10156138eb576138eb61468a565b60c0840151335f908152600a602090815260408083206001600160a01b03871684528252918290208351815590830151600182015581830151600282015560608301516003820155608090920151600490920191909155840151516101208501516101008601516139659291613960916149fc565b613d75565b60408501805191909152516020015161016085015161014086015161398e9291613960916149fc565b6040808601805160200192909252905101516101208501516101008601516139bf92916139ba916149fc565b613d48565b846040015160400181815250506139ee8460400151606001518561016001518661014001516139ba91906149fc565b846040015160600181815250505f8460c00151604001515f03613a8057613a6c83620557305f8860e00151338a604001518b60c001518c60800151604051602401613a3d959493929190614a23565b60408051601f198184030181529190526020810180516001600160e01b0316633064cba960e21b179052612a18565b613a76575f613a79565b60015b9050613a84565b5060025b61010085015161012086015160e08701516040516001600160a01b0387169333937f361311c5372a81d4ab4bce0bf1b7dbfc61da467667c2097deb69b4b24bc12d2893613ad2938890614ad3565b60405180910390a35050506001016133cd565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166316934fc4613b1e33612504565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015613b60573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b849190614876565b8160400151604001511115613bab5760405162461bcd60e51b8152600401610a4c906148b9565b6020808201518051600655810151600755604091820151335f90815260088352839020815181559181015160018301559182015160028201556060909101516003909101555050505050565b5f818311613c055781611f35565b5090919050565b5f80613c1884866146d6565b9050828111613c27575f613c31565b613c318382614677565b95945050505050565b6040516001600160a01b03838116602483015260448201839052611daa91859182169063a9059cbb90606401612bee565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90611f35565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f613cfb6001600160a01b03841683613d9b565b905080515f14158015613d1f575080806020019051810190613d1d9190614af5565b155b15611daa57604051635274afe760e01b81526001600160a01b0384166004820152602401610a4c565b5f80821215613d6b57613d5a82613da8565b613d649084614677565b9050612538565b611f3582846146ed565b5f80821215613d9157613d8782613da8565b613d6490846146ed565b611f358284614677565b6060611f3583835f613dbf565b5f808212613db65781612538565b612538826149e2565b606081471015613de45760405163cd78605960e01b8152306004820152602401610a4c565b5f80856001600160a01b03168486604051613dff9190614b10565b5f6040518083038185875af1925050503d805f8114613e39576040519150601f19603f3d011682016040523d82523d5f602084013e613e3e565b606091505b5091509150613e4e868383613e58565b9695505050505050565b606082613e6d57613e6882613eb4565b611f35565b8151158015613e8457506001600160a01b0384163b155b15613ead57604051639996b31560e01b81526001600160a01b0385166004820152602401610a4c565b5080611f35565b805115613ec45780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b604080516101c081019091525f61018082018181526101a083019190915281908152602001613f4760405180604001604052805f81526020015f81525090565b8152602001613f7360405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613f9f60405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613fac613edd565b8152602001613fb9613edd565b8152602001613feb6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6020828403121561401f575f80fd5b5035919050565b80356001600160a01b03811681146124ff575f80fd5b5f6020828403121561404c575f80fd5b611f3582614026565b5f60e08284031215614065575f80fd5b50919050565b80151581146109a3575f80fd5b5f805f80610140858703121561408c575f80fd5b8435935061409d8660208701614055565b92506101008501356140ae8161406b565b91506140bd6101208601614026565b905092959194509250565b5f8083601f8401126140d8575f80fd5b5081356001600160401b038111156140ee575f80fd5b6020830191508360208260051b8501011115614108575f80fd5b9250929050565b5f8060208385031215614120575f80fd5b82356001600160401b03811115614135575f80fd5b614141858286016140c8565b90969095509350505050565b5f5b8381101561416757818101518382015260200161414f565b50505f910152565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b828110156141da57878503603f19018452815180518087526141bb818989018a850161414d565b601f01601f191695909501860194509285019290850190600101614194565b5092979650505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b038111828210171561421d5761421d6141e7565b60405290565b604051608081016001600160401b038111828210171561421d5761421d6141e7565b604051601f8201601f191681016001600160401b038111828210171561426d5761426d6141e7565b604052919050565b5f60a08284031215614285575f80fd5b60405160a081018181106001600160401b03821117156142a7576142a76141e7565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b5f805f808486036101808112156142f7575f80fd5b61430086614026565b94506040601f1982011215614313575f80fd5b61431b6141fb565b602087810135825260408801359082015293506080605f198201121561433f575f80fd5b50614348614223565b60608681013582526080870135602083015260a0870135604083015260c08701359082015291506140bd8660e08701614275565b634e487b7160e01b5f52602160045260245ffd5b60608101600785106143a4576143a461437c565b938152602081019290925260409091015290565b5f60208083850312156143c9575f80fd5b82356001600160401b03808211156143df575f80fd5b818501915085601f8301126143f2575f80fd5b813581811115614404576144046141e7565b8060051b9150614415848301614245565b818152918301840191848101908884111561442e575f80fd5b938501935b838510156144535761444485614026565b82529385019390850190614433565b98975050505050505050565b5f805f805f805f806080898b031215614476575f80fd5b88356001600160401b038082111561448c575f80fd5b6144988c838d016140c8565b909a50985060208b01359150808211156144b0575f80fd5b6144bc8c838d016140c8565b909850965060408b01359150808211156144d4575f80fd5b6144e08c838d016140c8565b909650945060608b01359150808211156144f8575f80fd5b506145058b828c016140c8565b999c989b5096995094979396929594505050565b5f805f6040848603121561452b575f80fd5b83356001600160401b03811115614540575f80fd5b61454c868287016140c8565b90945092505060208401356145608161406b565b809150509250925092565b5f806040838503121561457c575f80fd5b61458583614026565b915061459360208401614026565b90509250929050565b5f805f606084860312156145ae575f80fd5b505081359360208301359350604090920135919050565b825181526020808401519082015260c08101611f356040830184805182526020810151602083015260408101516040830152606081015160608301525050565b5f805f60608486031215614617575f80fd5b8335925060208401356146298161406b565b915061463760408501614026565b90509250925092565b5f806101008385031215614652575f80fd5b823591506145938460208501614055565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561253857612538614663565b634e487b7160e01b5f52600160045260245ffd5b600381106146ae576146ae61437c565b9052565b6001600160a01b03841681526020810183905260608101611d17604083018461469e565b808202811582820484141761253857612538614663565b8082018082111561253857612538614663565b8581526001600160a01b0385811660208301526101608201908061472387614026565b1660408401528061473660208801614026565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614764575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f805f80608085870312156147b7575f80fd5b505082516020840151604085015160609095015191969095509092509050565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e19843603018112614800575f80fd5b8301803591506001600160401b03821115614819575f80fd5b602001915036819003821315614108575f80fd5b818382375f9101908152919050565b634e487b7160e01b5f52601260045260245ffd5b5f8261485e5761485e61483c565b500690565b5f826148715761487161483c565b500490565b5f60208284031215614886575f80fd5b5051919050565b6001600160a01b038581168252841660208201526040810183905260808101613c31606083018461469e565b6020808252602a908201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60408201526963617465642d6c71747960b01b606082015260800190565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b5f805f805f8060c0878903121561495b575f80fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6001600160a01b03979097168752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b8381526020810183905260608101611d17604083018461469e565b5f600160ff1b82016149f6576149f6614663565b505f0390565b8082018281125f831280158216821582161715614a1b57614a1b614663565b505092915050565b8581526001600160a01b03851660208201526102008101614a686040830186805182526020810151602083015260408101516040830152606081015160608301525050565b835160c0830152602084015160e08301526040840151610100830152606084015161012083015260808401516101408301528251610160830152602083015161018083015260408301516101a083015260608301516101c083015260808301516101e0830152613e4e565b848152602081018490526040810183905260808101613c31606083018461469e565b5f60208284031215614b05575f80fd5b8151611f358161406b565b5f8251614b2181846020870161414d565b919091019291505056fea264697066735822122068679bfa2c2eee9124643e597f74c2abf6b9406391adb4f8cd43a688cc2b27df64736f6c63430008180033610100604052348015610010575f80fd5b50604051610fc2380380610fc283398101604081905261002f9161006b565b6001600160a01b0392831660805290821660a0521660c0523360e0526100ab565b80516001600160a01b0381168114610066575f80fd5b919050565b5f805f6060848603121561007d575f80fd5b61008684610050565b925061009460208501610050565b91506100a260408501610050565b90509250925092565b60805160a05160c05160e051610e7461014e5f395f8181610195015281816102d801528181610591015261096201525f818161021a015281816102530152818161045801526106fc01525f81816101c801528181610335015281816104cd01528181610678015281816108030152610b4101525f818161014a015281816103d7015281816105e50152818161076e015281816108bf0152610a160152610e745ff3fe60806040526004361061007c575f3560e01c8063886117361161004c578063886117361461018457806399ad68a7146101b7578063eb876bf7146101ea578063f556a79c14610209575f80fd5b80630b76619b1461008757806338fb3599146100ae5780633fdf42e3146100ed5780637f6ec45514610139575f80fd5b3661008357005b5f80fd5b348015610092575f80fd5b5061009b61023c565b6040519081526020015b60405180910390f35b3480156100b9575f80fd5b506100cd6100c8366004610c7f565b6102c9565b6040805194855260208501939093529183015260608201526080016100a5565b3480156100f8575f80fd5b5061010c610107366004610ccb565b610580565b604080519687526020870195909552938501929092526060840152608083015260a082015260c0016100a5565b348015610144575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100a5565b34801561018f575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101c2575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101f5575f80fd5b506100cd610204366004610d06565b610953565b348015610214575f80fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516305a4d3f160e21b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906316934fc490602401602060405180830381865afa1580156102a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102c49190610d70565b905090565b5f808080336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461031e5760405162461bcd60e51b815260040161031590610d87565b60405180910390fd5b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610382573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a69190610d70565b6040516323b872dd60e01b81526001600160a01b038a81166004830152306024830152604482018c905291925047917f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303815f875af115801561041d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104419190610dbe565b5060405163534a7e1d60e11b8152600481018b90527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a694fc3a906024015f604051808303815f87803b1580156104a1575f80fd5b505af11580156104b3573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201525f92507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a0823190602401602060405180830381865afa15801561051b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061053f9190610d70565b90504761054c8483610de0565b97506105588382610de0565b955089156105715761056b898383610b13565b90975094505b50505050945094509450949050565b5f8080808080336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105ce5760405162461bcd60e51b815260040161031590610d87565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610632573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106569190610d70565b6040516370a0823160e01b81523060048201529091505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156106bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106e19190610d70565b6040516305c2fbcf60e31b8152600481018d905290915047907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e17de78906024015f604051808303815f87803b158015610745575f80fd5b505af1158015610757573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506370a082319150602401602060405180830381865afa1580156107bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107e19190610d70565b6040516370a0823160e01b81523060048201529098505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610848573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061086c9190610d70565b905047610879858b610de0565b9a506108858483610de0565b98506108918382610de0565b9650891561092b5760405163a9059cbb60e01b81526001600160a01b038d81166004830152602482018c90527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af1158015610905573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109299190610dbe565b505b8c156109425761093c8c8383610b13565b90985095505b505050505093975093979195509350565b5f808080336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461099f5760405162461bcd60e51b815260040161031590610d87565b6109ac6020880188610e05565b6001600160a01b0316886001600160a01b031614610a0c5760405162461bcd60e51b815260206004820152601b60248201527f5573657250726f78793a206f776e65722d6e6f742d73656e64657200000000006044820152606401610315565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663d505accf610a4860208a018a610e05565b610a5860408b0160208c01610e05565b60408b013560608c0135610a7260a08e0160808f01610e1e565b6040516001600160e01b031960e088901b1681526001600160a01b0395861660048201529490931660248501526044840191909152606483015260ff16608482015260a08a013560a482015260c08a013560c482015260e4015f604051808303815f87803b158015610ae2575f80fd5b505af1925050508015610af3575060015b50610b00898988886102c9565b929c919b50995090975095505050505050565b5f808315610bad5760405163a9059cbb60e01b81526001600160a01b038681166004830152602482018690527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303815f875af1158015610b87573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bab9190610dbe565b505b8215610c4a575f856001600160a01b0316846040515f6040518083038185875af1925050503d805f8114610bfc576040519150601f19603f3d011682016040523d82523d5f602084013e610c01565b606091505b5050905080610c485760405162461bcd60e51b8152602060048201526013602482015272155cd95c941c9bde1e4e88195d1a0b59985a5b606a1b6044820152606401610315565b505b5091939092509050565b80356001600160a01b0381168114610c6a575f80fd5b919050565b8015158114610c7c575f80fd5b50565b5f805f8060808587031215610c92575f80fd5b84359350610ca260208601610c54565b92506040850135610cb281610c6f565b9150610cc060608601610c54565b905092959194509250565b5f805f60608486031215610cdd575f80fd5b833592506020840135610cef81610c6f565b9150610cfd60408501610c54565b90509250925092565b5f805f805f858703610160811215610d1c575f80fd5b86359550610d2c60208801610c54565b945060e0603f1982011215610d3f575f80fd5b50604086019250610120860135610d5581610c6f565b9150610d646101408701610c54565b90509295509295909350565b5f60208284031215610d80575f80fd5b5051919050565b6020808252601f908201527f5573657250726f78793a2063616c6c65722d6e6f742d7374616b696e67563200604082015260600190565b5f60208284031215610dce575f80fd5b8151610dd981610c6f565b9392505050565b81810381811115610dff57634e487b7160e01b5f52601160045260245ffd5b92915050565b5f60208284031215610e15575f80fd5b610dd982610c54565b5f60208284031215610e2e575f80fd5b813560ff81168114610dd9575f80fdfea2646970667358221220d1fd88ecf95b01ee889170e70150b51cc382704fb757065b94b6a610e516a1e364736f6c634300081800338be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e00000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab9800000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000005af3107a40000000000000000000000000000000000000000000000000000de0b6b3a7640001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067884c000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000007e900000000000000000000000000bec25c5590e89596bde2dfcdc71579e66858772c00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106102ff575f3560e01c80638a00b67a11610195578063adfcdb9d116100e4578063cf84a88c1161009e578063e772b76511610079578063e772b76514610923578063ecf22e321461094a578063ee1c71721461095d578063f556a79c14610970575f80fd5b8063cf84a88c14610876578063e4a61d95146108ee578063e76c01e414610915575f80fd5b8063adfcdb9d146107f6578063b1d8f27414610809578063b285a8701461082a578063bfd79cae1461083d578063c20fb59e14610850578063cc9baa1214610863575f80fd5b8063900cf0cf1161014f578063a5e8455d1161012a578063a5e8455d1461079b578063a70b9f0c146107a4578063a9952a0c146107cb578063ac85912e146107ee575f80fd5b8063900cf0cf1461073957806391050f4014610741578063972e6d6614610788575f80fd5b80638a00b67a146106785780638b7d38a11461069f5780638d4f0b6c146106a75780638da5cb5b146106ce5780638eb9399e146106df5780638f32d59b1461071b575f80fd5b8063524fcc8011610251578063727d0f351161020b5780638113630d116101e65780638113630d1461062c57806382a15be11461063f5780638777e0951461065257806388edf9be14610665575f80fd5b8063727d0f35146105cb57806378a1bdd4146105f25780637f6ec45514610605575f80fd5b8063524fcc80146104b257806352d420b91461051657806353905402146105295780635c79696c1461055d57806364b4f7511461057d5780636bca7c55146105a4575f80fd5b80632591003a116102bc578063415c2d9611610297578063415c2d961461044157806346d62a631461046157806350283275146104885780635245b1ea14610490575f80fd5b80632591003a146103e857806338f3e2851461040f578063394ff4061461042e575f80fd5b8063070f418a146103035780630df9049b146103185780630ecc535f146103525780630f2f6aa5146103a657806315e5a1e5146103b95780631cd2a05d146103c1575b5f80fd5b61031661031136600461400f565b610997565b005b61033f7f000000000000000000000000000000000000000000000000000000000007e90081565b6040519081526020015b60405180910390f35b61038661036036600461403c565b60086020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610349565b6103166103b436600461403c565b6109a6565b61033f610b9f565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b61033f7f00000000000000000000000000000000000000000000000000005af3107a400081565b61033f61041d36600461403c565b600b6020525f908152604090205481565b61031661043c366004614078565b610c0e565b61045461044f36600461410f565b610d19565b604051610349919061416f565b61033f7f0000000000000000000000000000000000000000000000000000000067884c0081565b61033f610e26565b6104a361049e3660046142e2565b610e7c565b60405161034993929190614390565b6104ee6104c036600461403c565b60096020525f9081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a001610349565b61031661052436600461400f565b611037565b61038661053736600461403c565b60056020525f908152604090208054600182015460028301546003909301549192909184565b610565611042565b6040516001600160a01b039091168152602001610349565b61033f7f00000000000000000000000000000000000000000000003635c9adc5dea0000081565b61033f7f0000000000000000000000000000000000000000000000000de0b6b3a764000181565b6105657f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab9881565b61031661060036600461403c565b6110ab565b6105657f0000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d81565b61031661063a3660046143b8565b6114d1565b61031661064d36600461445f565b611627565b61033f61066036600461400f565b611c57565b610316610673366004614519565b611d1f565b61033f7f00000000000000000000000000000000000000000000000000470de4df82000081565b61033f611daf565b6105657f0000000000000000000000005e17a49ec5efea6e54dab5468eb649b1d3bb572881565b6001546001600160a01b0316610565565b6106e7611dce565b604080518451815260209485015185820152835191810191909152929091015160608301521515608082015260a001610349565b6001546001600160a01b031633146040519015158152602001610349565b61033f611e7d565b6104ee61074f36600461456b565b600a60209081525f9283526040808420909152908252902080546001820154600283015460038401546004909401549293919290919085565b6104a361079636600461403c565b611ede565b61033f60025481565b61033f7f0000000000000000000000000000000000000000000000000000000000093a8081565b6003546004546107d9919082565b60408051928352602083019190915201610349565b61033f611f18565b61033f61080436600461459c565b611f26565b61081c61081736600461403c565b611f3c565b6040516103499291906145c5565b610316610838366004614605565b611fa3565b6107d961084b36600461403c565b6121a5565b61033f61085e36600461403c565b6122bc565b61056561087136600461403c565b612504565b61088961088436600461403c565b61253e565b60408051845181526020808601518183015285830151828401526060958601518683015284516080808401919091529085015160a08301529184015160c08201529383015160e085015290910151610100830152151561012082015261014001610349565b61033f7f000000000000000000000000000000000000000000000000000000000000000481565b6006546007546107d9919082565b61033f7f000000000000000000000000000000000000000000000000000000000000000081565b610316610958366004614605565b6126a9565b61031661096b366004614640565b6127be565b6105657f0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d81565b6109a381600133611fa3565b50565b6109ae6127ce565b5f806109b86127f6565b915091505f806109c78561294e565b915091505f6109d886868585610e7c565b50909150600590508160068111156109f2576109f261437c565b14610a555760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a2063616e6e6f742d756e72656769737465722d696e604482015267697469617469766560c01b60648201526084015b60405180910390fd5b5f610a5e611e7d565b9050610a6b600182614677565b836080015110610a7d57610a7d61468a565b825185511015610a8f57610a8f61468a565b826020015185602001511015610aa757610aa761468a565b825185518690610ab8908390614677565b9052506020808401519086018051610ad1908390614677565b90525084516006556020808601516007556001600160a01b0388165f908152600b90915260408082205f1990555160248101839052610b459089906205573090849060440160408051601f198184030181529190526020810180516001600160e01b031663955161cd60e01b179052612a18565b90507f4b2a06fb31ab5670eed8302066f58582b640ec5a17d06ddd383d30e3e11f1b64888383610b75575f610b78565b60015b604051610b87939291906146b2565b60405180910390a1505050505050506109a360015f55565b5f7f0000000000000000000000000000000000000000000000000000000000093a806001610bcb611e7d565b610bd59190614677565b610bdf91906146d6565b610c09907f0000000000000000000000000000000000000000000000000000000067884c006146ed565b905090565b610c166127ce565b5f610c2085612a79565b90505f805f80846001600160a01b031663eb876bf78a338b8b8b6040518663ffffffff1660e01b8152600401610c5a959493929190614700565b6080604051808303815f875af1158015610c76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9a91906147a4565b604080516001600160a01b038c168152602081018f9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050610d1360015f55565b50505050565b6060816001600160401b03811115610d3357610d336141e7565b604051908082528060200260200182016040528015610d6657816020015b6060815260200190600190039081610d515790505b5090505f5b82811015610e1f575f8030868685818110610d8857610d886147d7565b9050602002810190610d9a91906147eb565b604051610da892919061482d565b5f60405180830381855af49150503d805f8114610de0576040519150601f19603f3d011682016040523d82523d5f602084013e610de5565b606091505b509150915081610df757805181602001fd5b80848481518110610e0a57610e0a6147d7565b60209081029190910101525050600101610d6b565b5092915050565b5f7f0000000000000000000000000000000000000000000000000000000000093a80610e727f0000000000000000000000000000000000000000000000000000000067884c0042614677565b610c099190614850565b6001600160a01b0384165f908152600b602052604081205481908190808203610eae575f805f9350935093505061102d565b5f610eb7611e7d565b9050808203610ed15760015f80945094509450505061102d565b6001600160a01b0389165f90815260096020526040902060040154935060018201610f055750600693505f915061102d9050565b610f10600182614677565b8410610f215760049450505061102d565b5f610f2e895f0151611c57565b905080885f0151118015610f46575060608801518851115b15610f7857885160025489515f9291610f5e916146d6565b610f689190614863565b60039750945061102d9350505050565b610f83600183614677565b7f00000000000000000000000000000000000000000000000000000000000000048860800151610fb391906146ed565b108061100d57508751606089015111801561100d5750670de0b6b3a7640000610ffc7f0000000000000000000000000000000000000000000000000de0b6b3a7640001836146d6565b6110069190614863565b8860600151115b156110225750600594505f925061102d915050565b50600294505f925050505b9450945094915050565b6109a3815f336126a9565b5f8061106e7f0000000000000000000000005e17a49ec5efea6e54dab5468eb649b1d3bb572833612b4e565b6040519091506001600160a01b0382169033907fda66ba232f4fb8c122b7026f55eeff1d0b9cf2560b7873b2bba6eaab4c3d5989905f90a3919050565b6110b36127ce565b5f6110bc611e7d565b90506002811161111f5760405162461bcd60e51b815260206004820152602860248201527f476f7665726e616e63653a20726567697374726174696f6e2d6e6f742d7965746044820152670b595b98589b195960c21b6064820152608401610a4c565b6001600160a01b0382166111755760405162461bcd60e51b815260206004820152601860248201527f476f7665726e616e63653a207a65726f2d6164647265737300000000000000006044820152606401610a4c565b5f61117f83611ede565b509091505f90508160068111156111985761119861437c565b146111f75760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e69746961746976652d616c72656164792d726044820152681959da5cdd195c995960ba1b6064820152608401610a4c565b5f61120133612504565b90505f61120c6127f6565b50335f8181526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460608201529192506112a7907f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab986001600160a01b031690307f00000000000000000000000000000000000000000000003635c9adc5dea00000612bb9565b8151602082015160608301515f916112be916146ed565b9050670de0b6b3a76400006112f37f00000000000000000000000000000000000000000000000000005af3107a4000846146d6565b6112fd9190614863565b6040516305a4d3f160e21b81526001600160a01b038781166004830152611399917f0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d909116906316934fc490602401602060405180830381865afa158015611367573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061138b9190614876565b611393610b9f565b84611f26565b10156113e75760405162461bcd60e51b815260206004820152601d60248201527f476f7665726e616e63653a20696e73756666696369656e742d6c7174790000006044820152606401610a4c565b6001600160a01b0388165f908152600b6020526040902087905561140c600188614677565b6001600160a01b0389165f9081526009602052604080822060040192909255905160248101899052611474908a90620557309084906044015b60408051601f198184030181529190526020810180516001600160e01b0316632695d74d60e01b179052612a18565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c89338a846114a5575f6114a8565b60015b6040516114b8949392919061488d565b60405180910390a150505050505050506109a360015f55565b6001546001600160a01b0316331461152b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610a4c565b5f5b815181101561161e576001600b5f84848151811061154d5761154d6147d7565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f20819055505f6115af83838151811061158e5761158e6147d7565b6020026020010151620557305f600160405160240161144591815260200190565b90507f40318a3f9214cb8917bf252f11a84a919bf9acee5e9b12049e1c99cb52b3b71c8383815181106115e4576115e46147d7565b6020026020010151336001846115fa575f6115fd565b60015b60405161160d949392919061488d565b60405180910390a15060010161152d565b506109a3612c20565b61162f6127ce565b848314801561163d57508281145b6116935760405162461bcd60e51b815260206004820152602160248201527f476f7665726e616e63653a2061727261792d6c656e6774682d6d69736d6174636044820152600d60fb1b6064820152608401610a4c565b61169d8888612c69565b6116a78686612c69565b6116e28484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b61171d8282808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250612d4692505050565b6117888484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612db892505050565b6117f38484808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250506040805160208088028281018201909352878252909350879250869182918501908490808284375f92019190915250612e5692505050565b5f6117fe8989612ef4565b335f9081526008602090815260409182902082516080810184528154815260018201549281019290925260028101549282018390526003015460608201529192501561187e5760405162461bcd60e51b815260206004820152600f60248201526e1b5d5cdd0818994818481c995cd95d608a1b6044820152606401610a4c565b80515f0361189e5760405162461bcd60e51b8152600401610a4c906148b9565b7f000000000000000000000000000000000000000000000000000000000007e9006118c7610e26565b1115611a3e575f5b87811015611a3c575f805b84518110156119c6578a8a848181106118f5576118f56147d7565b905060200201602081019061190a919061403c565b6001600160a01b0316858281518110611925576119256147d7565b60200260200101515f01516001600160a01b0316036119be5760019150848181518110611954576119546147d7565b602002602001015160200151898985818110611972576119726147d7565b9050602002013513156119b95760405162461bcd60e51b815260206004820152600f60248201526e43616e6e6f7420696e63726561736560881b6044820152606401610a4c565b6119c6565b6001016118da565b5080611a33578787838181106119de576119de6147d7565b905060200201355f14611a335760405162461bcd60e51b815260206004820181905260248201527f4d757374206265207a65726f20666f72206e657720696e6974696174697665736044820152606401610a4c565b506001016118cf565b505b5f876001600160401b03811115611a5757611a576141e7565b604051908082528060200260200182016040528015611a80578160200160208202803683370190505b5090505f886001600160401b03811115611a9c57611a9c6141e7565b604051908082528060200260200182016040528015611ac5578160200160208202803683370190505b5090505f5b89811015611ba057365f805f8c8c86818110611ae857611ae86147d7565b9050602002013513611afc57898986611b00565b8b8b875b9250925092505f838386818110611b1957611b196147d7565b9050602002013590505f885f0151828a60200151611b3791906146d6565b611b419190614863565b905081895f01818151611b549190614677565b905250602089018051829190611b6b908390614677565b90525082518190849088908110611b8457611b846147d7565b6020908102919091010152505060019093019250611aca915050565b50611c408a8a808060200260200160405190810160405280939291908181526020018383602002808284375f9201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284375f9201919091525050604080516020808d0282810182019093528c82529093508c92508b9182918501908490808284375f9201919091525088925087915061336a9050565b50505050611c4d60015f55565b5050505050505050565b5f815f03611c6657505f919050565b5f8083670de0b6b3a7640000600254611c7f91906146d6565b611c899190614863565b90508015611cd15780611cc4670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006146d6565b611cce9190614863565b91505b611d17670de0b6b3a7640000611d077f00000000000000000000000000000000000000000000000000470de4df820000876146d6565b611d119190614863565b83613bf7565b949350505050565b611d276127ce565b611d318383612c69565b611d3b8383612ef4565b508015611da157335f9081526008602052604090206002015415611da15760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206d757374206265206120726573657400000000006044820152606401610a4c565b611daa60015f55565b505050565b5f80611db96127f6565b509050611dc8815f0151611c57565b91505090565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f80611dfe611e7d565b60408051808201825260035481526004546020808301919091528251808401909352600654835260075490830152955093509050611e3d600182614677565b84602001511015611e7757825160019250611e6490611e5a610b9f565b8560200151611f26565b8452611e71600182614677565b60208501525b50909192565b5f7f0000000000000000000000000000000000000000000000000000000000093a80611ec97f0000000000000000000000000000000000000000000000000000000067884c0042614677565b611ed39190614863565b610c099060016146ed565b5f805f80611eea6127f6565b5090505f80611ef88761294e565b91509150611f0887848484610e7c565b9550955095505050509193909250565b6003545f90611dc881611c57565b5f611f32848484613c0c565b90505b9392505050565b604080518082019091525f8082526020820152611f7660405180608001604052805f81526020015f81526020015f81526020015f81525090565b611f7e6127ce565b611f866127f6565b509150611f928361294e565b509050611f9e60015f55565b915091565b611fab6127ce565b335f81815260086020526040812091611fc390612504565b9050806001600160a01b03163b5f03611fee5760405162461bcd60e51b8152600401610a4c90614903565b81548511156120515760405162461bcd60e51b815260206004820152602960248201527f476f7665726e616e63653a20696e73756666696369656e742d756e616c6c6f63604482015268617465642d6c71747960b81b6064820152608401610a4c565b815485101561209957815460018301545f919061206e90886146d6565b6120789190614863565b905080836001015f82825461208d9190614677565b909155506120a0915050565b5f60018301555b84825f015f8282546120b29190614677565b9091555050604051633fdf42e360e01b81526004810186905284151560248201526001600160a01b0384811660448301525f9182918291829182918291881690633fdf42e39060640160c0604051808303815f875af1158015612117573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061213b9190614946565b955095509550955095509550336001600160a01b03167f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f3398a88888888888860405161218c979695949392919061498c565b60405180910390a25050505050505050611daa60015f55565b5f805f6121b133612504565b9050806001600160a01b03163b5f036121dc5760405162461bcd60e51b8152600401610a4c90614903565b604051633fdf42e360e01b81525f60048201819052600160248301526001600160a01b0386811660448401529091829182918291861690633fdf42e39060640160c0604051808303815f875af1158015612238573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061225c9190614946565b604051929c509a50939750919550935090915033907f2870fe177552976b0dbc3166ded2169d5d73275fc565df30894320fd3608f339906122aa908b908890889088908e9089908f9061498c565b60405180910390a25050505050915091565b5f6122c56127ce565b5f6122ce6127f6565b5090505f806122dc8561294e565b915091505f806122ee87868686610e7c565b9193509091506003905082600681111561230a5761230a61437c565b1461231c575f955050505050506124f6565b6001612326611e7d565b6123309190614677565b8560200151146123425761234261468a565b600161234c611e7d565b6123569190614677565b6001600160a01b038881165f90815260096020526040808220600490810194909455516370a0823160e01b81523093810193909352917f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab98909116906370a0823190602401602060405180830381865afa1580156123d5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123f99190614876565b905080821115612407578091505b61243b6001600160a01b037f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab98168984613c3a565b5f61249489620557305f8a6020015187604051602401612465929190918252602082015260400190565b60408051601f198184030181529190526020810180516001600160e01b0316630f0c6f0d60e11b179052612a18565b9050886001600160a01b03167f023acf63ebe0add874abd78d1a9b96f3384a3128bfdb97bf6c23e91f3d2e6e5a848960200151846124d2575f6124d5565b60015b6040516124e4939291906149c7565b60405180910390a25090955050505050505b6124ff60015f55565b919050565b5f6125387f0000000000000000000000005e17a49ec5efea6e54dab5468eb649b1d3bb57286001600160a01b038416613c6b565b92915050565b61256560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61256d613edd565b5f80612577611e7d565b905060055f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060800160405290815f82015481526020016001820154815260200160028201548152602001600382015481525050935060095f866001600160a01b03166001600160a01b031681526020019081526020015f206040518060a00160405290815f8201548152602001600182015481526020016002820154815260200160038201548152602001600482015481525050925060018161263e9190614677565b846020015110156126a157600191505f612656610b9f565b90505f61266b855f0151838760200151611f26565b90505f6126818660400151848860600151611f26565b828852606088018190529050612698600185614677565b60208801525050505b509193909250565b6126b16127ce565b5f6126bb84612a79565b6040516338fb359960e01b81526004810186905233602482015284151560448201526001600160a01b0384811660648301529192505f91829182918291908616906338fb3599906084016080604051808303815f875af1158015612721573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061274591906147a4565b604080516001600160a01b038c168152602081018e9052908101859052606081018490526080810183905260a081018290529397509195509350915033907f5cd37895bb9287e542a426d6abbc93d7daca0979bd43264d320d4fd224a5fc5e9060c00160405180910390a25050505050611daa60015f55565b6127ca82825f33610c0e565b5050565b60025f54036127f057604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f612825611dce565b919450925090508015612949578251600355602083015160049081556040516370a0823160e01b815230918101919091525f907f000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab986001600160a01b0316906370a0823190602401602060405180830381865afa1580156128a7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128cb9190614876565b90507f000000000000000000000000000000000000000000000000000000000000000081106128fa57806128fc565b5f5b6002819055845160208087015160408051938452918301528101919091527f977a95eb227039fce4f8fe452d2d625ab67187ac5363991816ed06f4e5eed5019060600160405180910390a1505b509091565b61297560405180608001604052805f81526020015f81526020015f81526020015f81525090565b61297d613edd565b5f6129878461253e565b919450925090508015612a12576001600160a01b0384165f81815260056020908152604091829020865180825587830151600183018190558885015160028401556060808a01516003909401849055855192835293820192909252928301527f27b60fbfc71b45e0696ef86e3c31ac7979d464c139f6de0709de61b6b78093c5910160405180910390a25b50915091565b5f612a25846103e8613cca565b612a645760405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665206d696e47617360801b6044820152606401610a4c565b5f80835160208501868989f195945050505050565b5f808211612ac95760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a207a65726f2d6c7174792d616d6f756e74000000006044820152606401610a4c565b5f612ad333612504565b9050806001600160a01b03163b5f03612af057612aee611042565b505b335f90815260086020526040812080548392869291612b109084906146ed565b90915550612b20905084426146d6565b335f9081526008602052604081206001018054909190612b419084906146ed565b9091555090949350505050565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760095ff590506001600160a01b038116612538576040516330be1a3d60e21b815260040160405180910390fd5b6040516001600160a01b038481166024830152838116604483015260648201839052610d139186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613ce7565b6001546040515f916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b805f819003612c7757505050565b5f5b612c84600183614677565b811015610d13575f612c978260016146ed565b90505b82811015612d3d57848482818110612cb457612cb46147d7565b9050602002016020810190612cc9919061403c565b6001600160a01b0316858584818110612ce457612ce46147d7565b9050602002016020810190612cf9919061403c565b6001600160a01b031603612d355760405162461bcd60e51b815260206004820152600360248201526206475760ec1b6044820152606401610a4c565b600101612c9a565b50600101612c79565b80515f5b81811015611daa575f838281518110612d6557612d656147d7565b60200260200101511215612db05760405162461bcd60e51b815260206004820152601260248201527143616e6e6f74206265206e6567617469766560701b6044820152606401610a4c565b600101612d4a565b5f5b8251811015611daa575f838281518110612dd657612dd66147d7565b60200260200101511380612e0257505f828281518110612df857612df86147d7565b6020026020010151135b612e4e5760405162461bcd60e51b815260206004820152601a60248201527f476f7665726e616e63653a20766f74696e67206e6f7468696e670000000000006044820152606401610a4c565b600101612dba565b5f5b8251811015611daa57828181518110612e7357612e736147d7565b60200260200101515f1480612ea05750818181518110612e9557612e956147d7565b60200260200101515f145b612eec5760405162461bcd60e51b815260206004820152601960248201527f476f7665726e616e63653a20766f74652d616e642d7665746f000000000000006044820152606401610a4c565b600101612e58565b60605f826001600160401b03811115612f0f57612f0f6141e7565b604051908082528060200260200182016040528015612f7657816020015b612f636040518060a001604052805f6001600160a01b031681526020015f81526020015f81526020015f81526020015f81525090565b815260200190600190039081612f2d5790505b5090505f836001600160401b03811115612f9257612f926141e7565b604051908082528060200260200182016040528015612fbb578160200160208202803683370190505b5090505f846001600160401b03811115612fd757612fd76141e7565b604051908082528060200260200182016040528015613000578160200160208202803683370190505b5090505f856001600160401b0381111561301c5761301c6141e7565b604051908082528060200260200182016040528015613045578160200160208202803683370190505b5090505f866001600160401b03811115613061576130616141e7565b60405190808252806020026020018201604052801561308a578160200160208202803683370190505b5090505f5b8781101561331c57335f908152600a60205260408120818b8b858181106130b8576130b86147d7565b90506020020160208101906130cd919061403c565b6001600160a01b0316815260208082019290925260409081015f20815160a081018352815480825260018301549482019490945260028201549281019290925260038101546060830152600401546080820152915015158061313257505f8160400151115b61317e5760405162461bcd60e51b815260206004820152601c60248201527f476f7665726e616e63653a206e6f7468696e6720746f207265736574000000006044820152606401610a4c565b6040518060a001604052808b8b8581811061319b5761319b6147d7565b90506020020160208101906131b0919061403c565b6001600160a01b03168152602001825f01518152602001826040015181526020018260200151815260200182606001518152508783815181106131f5576131f56147d7565b6020026020010181905250868281518110613212576132126147d7565b602002602001015160200151613227906149e2565b868381518110613239576132396147d7565b602002602001018181525050868281518110613257576132576147d7565b60200260200101516040015161326c906149e2565b85838151811061327e5761327e6147d7565b60200260200101818152505086828151811061329c5761329c6147d7565b6020026020010151606001516132b1906149e2565b8483815181106132c3576132c36147d7565b6020026020010181815250508682815181106132e1576132e16147d7565b6020026020010151608001516132f6906149e2565b838381518110613308576133086147d7565b60209081029190910101525060010161308f565b5061335e8888808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152508892508791508690508561336a565b50929695505050505050565b613372613f07565b61337a6127f6565b60208301528152613389611e7d565b60e0820152335f9081526008602090815260408083208151608081018352815481526001820154938101939093526002810154838301526003015460608301528301525b8651811015613ae5575f8782815181106133e9576133e96147d7565b60200260200101519050868281518110613405576134056147d7565b602002602001015183610100018181525050858281518110613429576134296147d7565b6020908102919091010151610120840152610100830151151580613451575061012083015115155b61345d5761345d61468a565b84828151811061346f5761346f6147d7565b602002602001015183610140018181525050838281518110613493576134936147d7565b6020026020010151836101600181815250506134ae8161294e565b608085018190526060850182905284515f926134cd9285929190610e7c565b505090505f84610100015113806134e857505f846101200151135b156135865760028160068111156135015761350161437c565b148061351e5750600381600681111561351c5761351c61437c565b145b8061353a575060048160068111156135385761353861437c565b145b6135865760405162461bcd60e51b815260206004820152601b60248201527f476f7665726e616e63653a206163746976652d766f74652d66736d00000000006044820152606401610a4c565b600681600681111561359a5761359a61437c565b036135fc575f846101000151131580156135b957505f84610120015113155b6135fc5760405162461bcd60e51b8152602060048201526014602482015273135d5cdd0818994818481dda5d1a191c985dd85b60621b6044820152606401610a4c565b6040805160a080820183526080808801805151845280516020908101519085015280518501519484019490945283516060908101519084015283518101519083015286015251516101008501516136539190613d48565b6080850180519190915251604001516101208501516136729190613d48565b6080850180516040019190915251602001516101408501516136949190613d48565b6080850180516020019190915251606001516101608501516136b69190613d48565b60808086018051606090810193909352516001600160a01b0385165f90815260096020908152604091829020835181559083015160018201559082015160028201559281015160038401550151600490910155600681600681111561371d5761371d61437c565b146137b75760a084015151602085015151101561373c5761373c61468a565b60a08401515160208501518051613754908390614677565b90525060a0840151602090810151818601519091018051613776908390614677565b905250608084015151602085015180516137919083906146ed565b90525060808401516020908101518186015190910180516137b39083906146ed565b9052505b335f908152600a602090815260408083206001600160a01b0386168452825291829020825160a0810184528154815260018201549281019283526002820154938101939093526003810154606084015260040154608083015260c0860191909152516101408501516138299190613d48565b60c08501805160200191909152516060015161016085015161384b9190613d48565b60c08501805160600191909152515161010085015161386a9190613d48565b60c0850180519190915251604001516101208501516138899190613d48565b60c0850180516040019190915260e085015181516080015251602081015190516138b49042906146d6565b10156138c2576138c261468a565b60c084015160608101516040909101516138dd9042906146d6565b10156138eb576138eb61468a565b60c0840151335f908152600a602090815260408083206001600160a01b03871684528252918290208351815590830151600182015581830151600282015560608301516003820155608090920151600490920191909155840151516101208501516101008601516139659291613960916149fc565b613d75565b60408501805191909152516020015161016085015161014086015161398e9291613960916149fc565b6040808601805160200192909252905101516101208501516101008601516139bf92916139ba916149fc565b613d48565b846040015160400181815250506139ee8460400151606001518561016001518661014001516139ba91906149fc565b846040015160600181815250505f8460c00151604001515f03613a8057613a6c83620557305f8860e00151338a604001518b60c001518c60800151604051602401613a3d959493929190614a23565b60408051601f198184030181529190526020810180516001600160e01b0316633064cba960e21b179052612a18565b613a76575f613a79565b60015b9050613a84565b5060025b61010085015161012086015160e08701516040516001600160a01b0387169333937f361311c5372a81d4ab4bce0bf1b7dbfc61da467667c2097deb69b4b24bc12d2893613ad2938890614ad3565b60405180910390a35050506001016133cd565b507f0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d6001600160a01b03166316934fc4613b1e33612504565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015613b60573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b849190614876565b8160400151604001511115613bab5760405162461bcd60e51b8152600401610a4c906148b9565b6020808201518051600655810151600755604091820151335f90815260088352839020815181559181015160018301559182015160028201556060909101516003909101555050505050565b5f818311613c055781611f35565b5090919050565b5f80613c1884866146d6565b9050828111613c27575f613c31565b613c318382614677565b95945050505050565b6040516001600160a01b03838116602483015260448201839052611daa91859182169063a9059cbb90606401612bee565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c820120607882015260556043909101205f90611f35565b5f80603f83619c4001026040850201603f5a021015949350505050565b5f613cfb6001600160a01b03841683613d9b565b905080515f14158015613d1f575080806020019051810190613d1d9190614af5565b155b15611daa57604051635274afe760e01b81526001600160a01b0384166004820152602401610a4c565b5f80821215613d6b57613d5a82613da8565b613d649084614677565b9050612538565b611f3582846146ed565b5f80821215613d9157613d8782613da8565b613d6490846146ed565b611f358284614677565b6060611f3583835f613dbf565b5f808212613db65781612538565b612538826149e2565b606081471015613de45760405163cd78605960e01b8152306004820152602401610a4c565b5f80856001600160a01b03168486604051613dff9190614b10565b5f6040518083038185875af1925050503d805f8114613e39576040519150601f19603f3d011682016040523d82523d5f602084013e613e3e565b606091505b5091509150613e4e868383613e58565b9695505050505050565b606082613e6d57613e6882613eb4565b611f35565b8151158015613e8457506001600160a01b0384163b155b15613ead57604051639996b31560e01b81526001600160a01b0385166004820152602401610a4c565b5080611f35565b805115613ec45780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b604080516101c081019091525f61018082018181526101a083019190915281908152602001613f4760405180604001604052805f81526020015f81525090565b8152602001613f7360405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613f9f60405180608001604052805f81526020015f81526020015f81526020015f81525090565b8152602001613fac613edd565b8152602001613fb9613edd565b8152602001613feb6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f81525090565b81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f6020828403121561401f575f80fd5b5035919050565b80356001600160a01b03811681146124ff575f80fd5b5f6020828403121561404c575f80fd5b611f3582614026565b5f60e08284031215614065575f80fd5b50919050565b80151581146109a3575f80fd5b5f805f80610140858703121561408c575f80fd5b8435935061409d8660208701614055565b92506101008501356140ae8161406b565b91506140bd6101208601614026565b905092959194509250565b5f8083601f8401126140d8575f80fd5b5081356001600160401b038111156140ee575f80fd5b6020830191508360208260051b8501011115614108575f80fd5b9250929050565b5f8060208385031215614120575f80fd5b82356001600160401b03811115614135575f80fd5b614141858286016140c8565b90969095509350505050565b5f5b8381101561416757818101518382015260200161414f565b50505f910152565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b828110156141da57878503603f19018452815180518087526141bb818989018a850161414d565b601f01601f191695909501860194509285019290850190600101614194565b5092979650505050505050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b038111828210171561421d5761421d6141e7565b60405290565b604051608081016001600160401b038111828210171561421d5761421d6141e7565b604051601f8201601f191681016001600160401b038111828210171561426d5761426d6141e7565b604052919050565b5f60a08284031215614285575f80fd5b60405160a081018181106001600160401b03821117156142a7576142a76141e7565b806040525080915082358152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b5f805f808486036101808112156142f7575f80fd5b61430086614026565b94506040601f1982011215614313575f80fd5b61431b6141fb565b602087810135825260408801359082015293506080605f198201121561433f575f80fd5b50614348614223565b60608681013582526080870135602083015260a0870135604083015260c08701359082015291506140bd8660e08701614275565b634e487b7160e01b5f52602160045260245ffd5b60608101600785106143a4576143a461437c565b938152602081019290925260409091015290565b5f60208083850312156143c9575f80fd5b82356001600160401b03808211156143df575f80fd5b818501915085601f8301126143f2575f80fd5b813581811115614404576144046141e7565b8060051b9150614415848301614245565b818152918301840191848101908884111561442e575f80fd5b938501935b838510156144535761444485614026565b82529385019390850190614433565b98975050505050505050565b5f805f805f805f806080898b031215614476575f80fd5b88356001600160401b038082111561448c575f80fd5b6144988c838d016140c8565b909a50985060208b01359150808211156144b0575f80fd5b6144bc8c838d016140c8565b909850965060408b01359150808211156144d4575f80fd5b6144e08c838d016140c8565b909650945060608b01359150808211156144f8575f80fd5b506145058b828c016140c8565b999c989b5096995094979396929594505050565b5f805f6040848603121561452b575f80fd5b83356001600160401b03811115614540575f80fd5b61454c868287016140c8565b90945092505060208401356145608161406b565b809150509250925092565b5f806040838503121561457c575f80fd5b61458583614026565b915061459360208401614026565b90509250929050565b5f805f606084860312156145ae575f80fd5b505081359360208301359350604090920135919050565b825181526020808401519082015260c08101611f356040830184805182526020810151602083015260408101516040830152606081015160608301525050565b5f805f60608486031215614617575f80fd5b8335925060208401356146298161406b565b915061463760408501614026565b90509250925092565b5f806101008385031215614652575f80fd5b823591506145938460208501614055565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561253857612538614663565b634e487b7160e01b5f52600160045260245ffd5b600381106146ae576146ae61437c565b9052565b6001600160a01b03841681526020810183905260608101611d17604083018461469e565b808202811582820484141761253857612538614663565b8082018082111561253857612538614663565b8581526001600160a01b0385811660208301526101608201908061472387614026565b1660408401528061473660208801614026565b1660608401525060408501356080830152606085013560a0830152608085013560ff81168114614764575f80fd5b60ff1660c08381019190915260a086013560e084015294909401356101008201529115156101208301526001600160a01b03166101409091015292915050565b5f805f80608085870312156147b7575f80fd5b505082516020840151604085015160609095015191969095509092509050565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e19843603018112614800575f80fd5b8301803591506001600160401b03821115614819575f80fd5b602001915036819003821315614108575f80fd5b818382375f9101908152919050565b634e487b7160e01b5f52601260045260245ffd5b5f8261485e5761485e61483c565b500690565b5f826148715761487161483c565b500490565b5f60208284031215614886575f80fd5b5051919050565b6001600160a01b038581168252841660208201526040810183905260808101613c31606083018461469e565b6020808252602a908201527f476f7665726e616e63653a20696e73756666696369656e742d6f722d616c6c6f60408201526963617465642d6c71747960b01b606082015260800190565b60208082526023908201527f476f7665726e616e63653a20757365722d70726f78792d6e6f742d6465706c6f6040820152621e595960ea1b606082015260800190565b5f805f805f8060c0878903121561495b575f80fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6001600160a01b03979097168752602087019590955260408601939093526060850191909152608084015260a083015260c082015260e00190565b8381526020810183905260608101611d17604083018461469e565b5f600160ff1b82016149f6576149f6614663565b505f0390565b8082018281125f831280158216821582161715614a1b57614a1b614663565b505092915050565b8581526001600160a01b03851660208201526102008101614a686040830186805182526020810151602083015260408101516040830152606081015160608301525050565b835160c0830152602084015160e08301526040840151610100830152606084015161012083015260808401516101408301528251610160830152602083015161018083015260408301516101a083015260608301516101c083015260808301516101e0830152613e4e565b848152602081018490526040810183905260808101613c31606083018461469e565b5f60208284031215614b05575f80fd5b8151611f358161406b565b5f8251614b2181846020870161414d565b919091019291505056fea264697066735822122068679bfa2c2eee9124643e597f74c2abf6b9406391adb4f8cd43a688cc2b27df64736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba00000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab9800000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000000000005af3107a40000000000000000000000000000000000000000000000000000de0b6b3a7640001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067884c000000000000000000000000000000000000000000000000000000000000093a80000000000000000000000000000000000000000000000000000000000007e900000000000000000000000000bec25c5590e89596bde2dfcdc71579e66858772c00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _lqty (address): 0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D
Arg [1] : _lusd (address): 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0
Arg [2] : _stakingV1 (address): 0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d
Arg [3] : _bold (address): 0xb01dd87B29d187F3E3a4Bf6cdAebfb97F3D9aB98
Arg [4] : _config (tuple):
Arg [1] : registrationFee (uint256): 1000000000000000000000
Arg [2] : registrationThresholdFactor (uint256): 100000000000000
Arg [3] : unregistrationThresholdFactor (uint256): 1000000000000000001
Arg [4] : unregistrationAfterEpochs (uint256): 4
Arg [5] : votingThresholdFactor (uint256): 20000000000000000
Arg [6] : minClaim (uint256): 0
Arg [7] : minAccrual (uint256): 0
Arg [8] : epochStart (uint256): 1736985600
Arg [9] : epochDuration (uint256): 604800
Arg [10] : epochVotingCutoff (uint256): 518400
Arg [5] : _owner (address): 0xbEC25C5590e89596BDE2DfCdc71579E66858772c
Arg [6] : _initiatives (address[]):
-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 0000000000000000000000006dea81c8171d0ba574754ef6f8b412f2ed88c54d
Arg [1] : 0000000000000000000000005f98805a4e8be255a32880fdec7f6728c6568ba0
Arg [2] : 0000000000000000000000004f9fbb3f1e99b56e0fe2892e623ed36a76fc605d
Arg [3] : 000000000000000000000000b01dd87b29d187f3e3a4bf6cdaebfb97f3d9ab98
Arg [4] : 00000000000000000000000000000000000000000000003635c9adc5dea00000
Arg [5] : 00000000000000000000000000000000000000000000000000005af3107a4000
Arg [6] : 0000000000000000000000000000000000000000000000000de0b6b3a7640001
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 00000000000000000000000000000000000000000000000000470de4df820000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000067884c00
Arg [12] : 0000000000000000000000000000000000000000000000000000000000093a80
Arg [13] : 000000000000000000000000000000000000000000000000000000000007e900
Arg [14] : 000000000000000000000000bec25c5590e89596bde2dfcdc71579e66858772c
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.