编译器
0.8.24+commit.e11b9ed9
文件 1 的 22:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
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();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
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);
}
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);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 22:Clones.sol
pragma solidity ^0.8.20;
library Clones {
error ERC1167FailedCreateClone();
function clone(address implementation) internal returns (address instance) {
assembly {
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
assembly {
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
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)
}
}
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}
文件 3 的 22:Governance.sol
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";
contract Governance is MultiDelegateCall, UserProxyFactory, ReentrancyGuard, Ownable, IGovernance {
using SafeERC20 for IERC20;
uint256 constant MIN_GAS_TO_HOOK = 350_000;
ILQTYStaking public immutable stakingV1;
IERC20 public immutable lqty;
IERC20 public immutable bold;
uint256 public immutable EPOCH_START;
uint256 public immutable EPOCH_DURATION;
uint256 public immutable EPOCH_VOTING_CUTOFF;
uint256 public immutable MIN_CLAIM;
uint256 public immutable MIN_ACCRUAL;
uint256 public immutable REGISTRATION_FEE;
uint256 public immutable REGISTRATION_THRESHOLD_FACTOR;
uint256 public immutable UNREGISTRATION_THRESHOLD_FACTOR;
uint256 public immutable UNREGISTRATION_AFTER_EPOCHS;
uint256 public immutable VOTING_THRESHOLD_FACTOR;
uint256 public boldAccrued;
VoteSnapshot public votesSnapshot;
mapping(address => InitiativeVoteSnapshot) public votesForInitiativeSnapshot;
GlobalState public globalState;
mapping(address => UserState) public userStates;
mapping(address => InitiativeState) public initiativeStates;
mapping(address => mapping(address => Allocation)) public lqtyAllocatedByUserToInitiative;
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;
require(_config.registrationThresholdFactor < WAD, "Gov: registration-config");
REGISTRATION_THRESHOLD_FACTOR = _config.registrationThresholdFactor;
require(_config.unregistrationThresholdFactor > WAD, "Gov: unregistration-config");
UNREGISTRATION_THRESHOLD_FACTOR = _config.unregistrationThresholdFactor;
UNREGISTRATION_AFTER_EPOCHS = _config.unregistrationAfterEpochs;
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++) {
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();
}
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));
userStates[msg.sender].unallocatedLQTY += _lqtyAmount;
userStates[msg.sender].unallocatedOffset += block.timestamp * _lqtyAmount;
return userProxy;
}
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);
}
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);
}
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");
require(_lqtyAmount <= userState.unallocatedLQTY, "Governance: insufficient-unallocated-lqty");
if (_lqtyAmount < userState.unallocatedLQTY) {
uint256 offsetDecrease = _lqtyAmount * userState.unallocatedOffset / userState.unallocatedLQTY;
userState.unallocatedOffset -= offsetDecrease;
} else {
userState.unallocatedOffset = 0;
}
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);
}
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
);
}
function epoch() public view returns (uint256) {
return ((block.timestamp - EPOCH_START) / EPOCH_DURATION) + 1;
}
function epochStart() public view returns (uint256) {
return EPOCH_START + (epoch() - 1) * EPOCH_DURATION;
}
function secondsWithinEpoch() public view returns (uint256) {
return (block.timestamp - EPOCH_START) % EPOCH_DURATION;
}
function lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) public pure returns (uint256) {
return _lqtyToVotes(_lqtyAmount, _timestamp, _offset);
}
function getLatestVotingThreshold() public view returns (uint256) {
uint256 snapshotVotes = votesSnapshot.votes;
return calculateVotingThreshold(snapshotVotes);
}
function calculateVotingThreshold() public returns (uint256) {
(VoteSnapshot memory snapshot,) = _snapshotVotes();
return calculateVotingThreshold(snapshot.votes);
}
function calculateVotingThreshold(uint256 _votes) public view returns (uint256) {
if (_votes == 0) return 0;
uint256 minVotes;
uint256 payoutPerVote = boldAccrued * WAD / _votes;
if (payoutPerVote != 0) {
minVotes = MIN_CLAIM * WAD / payoutPerVote;
}
return max(_votes * VOTING_THRESHOLD_FACTOR / WAD, minVotes);
}
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);
}
}
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;
}
}
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
);
}
}
function getInitiativeSnapshotAndState(address _initiative)
public
view
returns (
InitiativeVoteSnapshot memory initiativeSnapshot,
InitiativeState memory initiativeState,
bool shouldUpdate
)
{
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;
}
}
function snapshotVotesForInitiative(address _initiative)
external
nonReentrant
returns (VoteSnapshot memory voteSnapshot, InitiativeVoteSnapshot memory initiativeVoteSnapshot)
{
(voteSnapshot,) = _snapshotVotes();
(initiativeVoteSnapshot,) = _snapshotVotesForInitiative(_initiative);
}
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);
}
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];
if (initiativeRegistrationEpoch == 0) {
return (InitiativeStatus.NONEXISTENT, 0, 0);
}
uint256 currentEpoch = epoch();
if (initiativeRegistrationEpoch == currentEpoch) {
return (InitiativeStatus.WARM_UP, 0, 0);
}
lastEpochClaim = initiativeStates[_initiative].lastEpochClaim;
if (initiativeRegistrationEpoch == UNREGISTERED_INITIATIVE) {
return (InitiativeStatus.DISABLED, lastEpochClaim, 0);
}
if (lastEpochClaim >= currentEpoch - 1) {
return (InitiativeStatus.CLAIMED, lastEpochClaim, claimableAmount);
}
uint256 votingTheshold = calculateVotingThreshold(_votesSnapshot.votes);
if (
_votesForInitiativeSnapshot.votes > votingTheshold
&& _votesForInitiativeSnapshot.votes > _votesForInitiativeSnapshot.vetos
) {
uint256 claim = _votesForInitiativeSnapshot.votes * boldAccrued / _votesSnapshot.votes;
return (InitiativeStatus.CLAIMABLE, lastEpochClaim, claim);
}
if (
(_initiativeState.lastEpochClaim + UNREGISTRATION_AFTER_EPOCHS < currentEpoch - 1)
|| _votesForInitiativeSnapshot.vetos > _votesForInitiativeSnapshot.votes
&& _votesForInitiativeSnapshot.vetos > votingTheshold * UNREGISTRATION_THRESHOLD_FACTOR / WAD
) {
return (InitiativeStatus.UNREGISTERABLE, lastEpochClaim, 0);
}
return (InitiativeStatus.SKIP, lastEpochClaim, 0);
}
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);
uint256 upscaledSnapshotVotes = snapshot.votes;
uint256 totalUserOffset = userState.allocatedOffset + userState.unallocatedOffset;
require(
lqtyToVotes(stakingV1.stakes(userProxyAddress), epochStart(), totalUserOffset)
>= upscaledSnapshotVotes * REGISTRATION_THRESHOLD_FACTOR / WAD,
"Governance: insufficient-lqty"
);
registeredInitiatives[_initiative] = currentEpoch;
initiativeStates[_initiative].lastEpochClaim = currentEpoch - 1;
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;
}
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);
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");
cachedData[i] = ResetInitiativeData({
initiative: _initiativesToReset[i],
LQTYVotes: int256(alloc.voteLQTY),
LQTYVetos: int256(alloc.vetoLQTY),
OffsetVotes: int256(alloc.voteOffset),
OffsetVetos: int256(alloc.vetoOffset)
});
deltaLQTYVotes[i] = -(cachedData[i].LQTYVotes);
deltaLQTYVetos[i] = -(cachedData[i].LQTYVetos);
deltaOffsetVotes[i] = -(cachedData[i].OffsetVotes);
deltaOffsetVetos[i] = -(cachedData[i].OffsetVetos);
}
_allocateLQTY(_initiativesToReset, deltaLQTYVotes, deltaLQTYVetos, deltaOffsetVotes, deltaOffsetVetos);
return cachedData;
}
function resetAllocations(address[] calldata _initiativesToReset, bool checkAll) external nonReentrant {
_requireNoDuplicates(_initiativesToReset);
_resetInitiatives(_initiativesToReset);
if (checkAll) {
require(userStates[msg.sender].allocatedLQTY == 0, "Governance: must be a reset");
}
}
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"
);
_requireNoDuplicates(_initiativesToReset);
_requireNoDuplicates(_initiatives);
_requireNoNegatives(_absoluteLQTYVotes);
_requireNoNegatives(_absoluteLQTYVetos);
_requireNoNOP(_absoluteLQTYVotes, _absoluteLQTYVetos);
_requireNoSimultaneousVoteAndVeto(_absoluteLQTYVotes, _absoluteLQTYVetos);
ResetInitiativeData[] memory cachedData = _resetInitiatives(_initiativesToReset);
UserState memory userState = userStates[msg.sender];
require(userState.allocatedLQTY == 0, "must be a reset");
require(userState.unallocatedLQTY != 0, "Governance: insufficient-or-allocated-lqty");
if (secondsWithinEpoch() > EPOCH_VOTING_CUTOFF) {
for (uint256 x; x < _initiatives.length; x++) {
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;
}
}
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);
for (uint256 x; x < _initiatives.length; x++) {
(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);
}
_allocateLQTY(_initiatives, _absoluteLQTYVotes, _absoluteLQTYVetos, absoluteOffsetVotes, absoluteOffsetVetos);
}
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;
}
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];
(vars.votesForInitiativeSnapshot_, vars.initiativeState) = _snapshotVotesForInitiative(initiative);
(InitiativeStatus status,,) = getInitiativeState(
initiative, vars.votesSnapshot_, vars.votesForInitiativeSnapshot_, vars.initiativeState
);
if (vars.deltaLQTYVotes > 0 || vars.deltaLQTYVetos > 0) {
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");
}
vars.prevInitiativeState = InitiativeState(
vars.initiativeState.voteLQTY,
vars.initiativeState.voteOffset,
vars.initiativeState.vetoLQTY,
vars.initiativeState.vetoOffset,
vars.initiativeState.lastEpochClaim
);
vars.initiativeState.voteLQTY = add(vars.initiativeState.voteLQTY, vars.deltaLQTYVotes);
vars.initiativeState.vetoLQTY = add(vars.initiativeState.vetoLQTY, vars.deltaLQTYVetos);
vars.initiativeState.voteOffset = add(vars.initiativeState.voteOffset, vars.deltaOffsetVotes);
vars.initiativeState.vetoOffset = add(vars.initiativeState.vetoOffset, vars.deltaOffsetVetos);
initiativeStates[initiative] = vars.initiativeState;
if (status != InitiativeStatus.DISABLED) {
assert(vars.state.countedVoteLQTY >= vars.prevInitiativeState.voteLQTY);
vars.state.countedVoteLQTY -= vars.prevInitiativeState.voteLQTY;
vars.state.countedVoteOffset -= vars.prevInitiativeState.voteOffset;
vars.state.countedVoteLQTY += vars.initiativeState.voteLQTY;
vars.state.countedVoteOffset += vars.initiativeState.voteOffset;
}
vars.allocation = lqtyAllocatedByUserToInitiative[msg.sender][initiative];
vars.allocation.voteOffset = add(vars.allocation.voteOffset, vars.deltaOffsetVotes);
vars.allocation.vetoOffset = add(vars.allocation.vetoOffset, vars.deltaOffsetVetos);
vars.allocation.voteLQTY = add(vars.allocation.voteLQTY, vars.deltaLQTYVotes);
vars.allocation.vetoLQTY = add(vars.allocation.vetoLQTY, vars.deltaLQTYVetos);
vars.allocation.atEpoch = vars.currentEpoch;
assert(vars.allocation.voteLQTY * block.timestamp >= vars.allocation.voteOffset);
assert(vars.allocation.vetoLQTY * block.timestamp >= vars.allocation.vetoOffset);
lqtyAllocatedByUserToInitiative[msg.sender][initiative] = vars.allocation;
vars.userState.unallocatedLQTY =
sub(vars.userState.unallocatedLQTY, (vars.deltaLQTYVotes + vars.deltaLQTYVetos));
vars.userState.unallocatedOffset =
sub(vars.userState.unallocatedOffset, (vars.deltaOffsetVotes + vars.deltaOffsetVetos));
vars.userState.allocatedLQTY =
add(vars.userState.allocatedLQTY, (vars.deltaLQTYVotes + vars.deltaLQTYVetos));
vars.userState.allocatedOffset =
add(vars.userState.allocatedOffset, (vars.deltaOffsetVotes + vars.deltaOffsetVetos));
HookStatus hookStatus;
if (vars.allocation.vetoLQTY == 0) {
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;
}
function unregisterInitiative(address _initiative) external nonReentrant {
(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");
uint256 currentEpoch = epoch();
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;
registeredInitiatives[_initiative] = UNREGISTERED_INITIATIVE;
bool success = safeCallWithMinGas(
_initiative, MIN_GAS_TO_HOOK, 0, abi.encodeCall(IInitiative.onUnregisterInitiative, (currentEpoch))
);
emit UnregisterInitiative(_initiative, currentEpoch, success ? HookStatus.Succeeded : HookStatus.Failed);
}
function claimForInitiative(address _initiative) external nonReentrant returns (uint256) {
(VoteSnapshot memory votesSnapshot_,) = _snapshotVotes();
(InitiativeVoteSnapshot memory votesForInitiativeSnapshot_, InitiativeState memory initiativeState) =
_snapshotVotesForInitiative(_initiative);
(InitiativeStatus status,, uint256 claimableAmount) =
getInitiativeState(_initiative, votesSnapshot_, votesForInitiativeSnapshot_, initiativeState);
if (status != InitiativeStatus.CLAIMABLE) {
return 0;
}
assert(votesSnapshot_.forEpoch == epoch() - 1);
initiativeStates[_initiative].lastEpochClaim = epoch() - 1;
uint256 available = bold.balanceOf(address(this));
if (claimableAmount > available) {
claimableAmount = available;
}
bold.safeTransfer(_initiative, claimableAmount);
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");
}
}
}
文件 4 的 22:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 5 的 22:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 6 的 22:IGovernance.sol
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
}
event DepositLQTY(
address indexed user,
address rewardRecipient,
uint256 lqtyAmount,
uint256 lusdReceived,
uint256 lusdSent,
uint256 ethReceived,
uint256 ethSent
);
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;
function stakingV1() external view returns (ILQTYStaking stakingV1);
function lqty() external view returns (IERC20 lqty);
function bold() external view returns (IERC20 bold);
function EPOCH_START() external view returns (uint256 epochStart);
function EPOCH_DURATION() external view returns (uint256 epochDuration);
function EPOCH_VOTING_CUTOFF() external view returns (uint256 epochVotingCutoff);
function MIN_CLAIM() external view returns (uint256 minClaim);
function MIN_ACCRUAL() external view returns (uint256 minAccrual);
function REGISTRATION_FEE() external view returns (uint256 registrationFee);
function REGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 registrationThresholdFactor);
function UNREGISTRATION_THRESHOLD_FACTOR() external view returns (uint256 unregistrationThresholdFactor);
function UNREGISTRATION_AFTER_EPOCHS() external view returns (uint256 unregistrationAfterEpochs);
function VOTING_THRESHOLD_FACTOR() external view returns (uint256 votingThresholdFactor);
function boldAccrued() external view returns (uint256 boldAccrued);
struct VoteSnapshot {
uint256 votes;
uint256 forEpoch;
}
struct InitiativeVoteSnapshot {
uint256 votes;
uint256 forEpoch;
uint256 lastCountedEpoch;
uint256 vetos;
}
function votesSnapshot() external view returns (uint256 votes, uint256 forEpoch);
function votesForInitiativeSnapshot(address _initiative)
external
view
returns (uint256 votes, uint256 forEpoch, uint256 lastCountedEpoch, uint256 vetos);
struct Allocation {
uint256 voteLQTY;
uint256 voteOffset;
uint256 vetoLQTY;
uint256 vetoOffset;
uint256 atEpoch;
}
struct UserState {
uint256 unallocatedLQTY;
uint256 unallocatedOffset;
uint256 allocatedLQTY;
uint256 allocatedOffset;
}
struct InitiativeState {
uint256 voteLQTY;
uint256 voteOffset;
uint256 vetoLQTY;
uint256 vetoOffset;
uint256 lastEpochClaim;
}
struct GlobalState {
uint256 countedVoteLQTY;
uint256 countedVoteOffset;
}
function userStates(address _user)
external
view
returns (uint256 unallocatedLQTY, uint256 unallocatedOffset, uint256 allocatedLQTY, uint256 allocatedOffset);
function initiativeStates(address _initiative)
external
view
returns (uint256 voteLQTY, uint256 voteOffset, uint256 vetoLQTY, uint256 vetoOffset, uint256 lastEpochClaim);
function globalState() external view returns (uint256 countedVoteLQTY, uint256 countedVoteOffset);
function lqtyAllocatedByUserToInitiative(address _user, address _initiative)
external
view
returns (uint256 voteLQTY, uint256 voteOffset, uint256 vetoLQTY, uint256 vetoOffset, uint256 atEpoch);
function registeredInitiatives(address _initiative) external view returns (uint256 atEpoch);
function depositLQTY(uint256 _lqtyAmount) external;
function depositLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) external;
function depositLQTYViaPermit(uint256 _lqtyAmount, PermitParams calldata _permitParams) external;
function depositLQTYViaPermit(
uint256 _lqtyAmount,
PermitParams calldata _permitParams,
bool _doSendRewards,
address _recipient
) external;
function withdrawLQTY(uint256 _lqtyAmount) external;
function withdrawLQTY(uint256 _lqtyAmount, bool _doSendRewards, address _recipient) external;
function claimFromStakingV1(address _rewardRecipient) external returns (uint256 lusdSent, uint256 ethSent);
function epoch() external view returns (uint256 epoch);
function epochStart() external view returns (uint256 epochStart);
function secondsWithinEpoch() external view returns (uint256 secondsWithinEpoch);
function lqtyToVotes(uint256 _lqtyAmount, uint256 _timestamp, uint256 _offset) external pure returns (uint256);
function calculateVotingThreshold() external returns (uint256);
function calculateVotingThreshold(uint256 _votes) external view returns (uint256);
function getTotalVotesAndState()
external
view
returns (VoteSnapshot memory snapshot, GlobalState memory state, bool shouldUpdate);
function getInitiativeSnapshotAndState(address _initiative)
external
view
returns (
InitiativeVoteSnapshot memory initiativeSnapshot,
InitiativeState memory initiativeState,
bool shouldUpdate
);
function getLatestVotingThreshold() external view returns (uint256 votingThreshold);
function snapshotVotesForInitiative(address _initiative)
external
returns (VoteSnapshot memory voteSnapshot, InitiativeVoteSnapshot memory initiativeVoteSnapshot);
enum InitiativeStatus {
NONEXISTENT,
WARM_UP,
SKIP,
CLAIMABLE,
CLAIMED,
UNREGISTERABLE,
DISABLED
}
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);
function registerInitiative(address _initiative) external;
function unregisterInitiative(address _initiative) external;
function allocateLQTY(
address[] calldata _initiativesToReset,
address[] memory _initiatives,
int256[] memory _absoluteLQTYVotes,
int256[] memory _absoluteLQTYVetos
) external;
function resetAllocations(address[] calldata _initiativesToReset, bool _checkAll) external;
function claimForInitiative(address _initiative) external returns (uint256 claimed);
}
文件 7 的 22:IInitiative.sol
pragma solidity ^0.8.24;
import {IGovernance} from "./IGovernance.sol";
interface IInitiative {
function onRegisterInitiative(uint256 _atEpoch) external;
function onUnregisterInitiative(uint256 _atEpoch) external;
function onAfterAllocateLQTY(
uint256 _currentEpoch,
address _user,
IGovernance.UserState calldata _userState,
IGovernance.Allocation calldata _allocation,
IGovernance.InitiativeState calldata _initiativeState
) external;
function onClaimForInitiative(uint256 _claimEpoch, uint256 _bold) external;
}
文件 8 的 22:ILQTYStaking.sol
pragma solidity ^0.8.24;
interface ILQTYStaking {
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);
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);
}
文件 9 的 22:IMultiDelegateCall.sol
pragma solidity ^0.8.24;
interface IMultiDelegateCall {
function multiDelegateCall(bytes[] calldata inputs) external returns (bytes[] memory returnValues);
}
文件 10 的 22:IUserProxy.sol
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 {
function lqty() external view returns (IERC20 lqty);
function lusd() external view returns (IERC20 lusd);
function stakingV1() external view returns (ILQTYStaking stakingV1);
function stakingV2() external view returns (address stakingV2);
function stake(uint256 _amount, address _lqtyFrom, bool _doSendRewards, address _recipient)
external
returns (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent);
function stakeViaPermit(
uint256 _amount,
address _lqtyFrom,
PermitParams calldata _permitParams,
bool _doSendRewards,
address _recipient
) external returns (uint256 lusdReceived, uint256 lusdSent, uint256 ethReceived, uint256 ethSent);
function unstake(uint256 _amount, bool _doSendRewards, address _recipient)
external
returns (
uint256 lqtyReceived,
uint256 lqtySent,
uint256 lusdReceived,
uint256 lusdSent,
uint256 ethReceived,
uint256 ethSent
);
function staked() external view returns (uint256);
}
文件 11 的 22:IUserProxyFactory.sol
pragma solidity ^0.8.24;
interface IUserProxyFactory {
event DeployUserProxy(address indexed user, address indexed userProxy);
function userProxyImplementation() external view returns (address implementation);
function deriveUserProxyAddress(address _user) external view returns (address userProxyAddress);
function deployUserProxy() external returns (address userProxyAddress);
}
文件 12 的 22:Math.sol
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);
}
文件 13 的 22:MultiDelegateCall.sol
pragma solidity ^0.8.24;
import {IMultiDelegateCall} from "../interfaces/IMultiDelegateCall.sol";
contract MultiDelegateCall is 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) {
assembly {
revert(
add(32, returnData),
mload(returnData)
)
}
}
returnValues[i] = returnData;
}
}
}
文件 14 的 22:Ownable.sol
pragma solidity 0.8.24;
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
_owner = initialOwner;
emit OwnershipTransferred(address(0), initialOwner);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
function _renounceOwnership() internal {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
}
文件 15 的 22:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 16 的 22:SafeCallMinGas.sol
pragma solidity ^0.8.24;
function hasMinGas(uint256 _minGas, uint256 _reservedGas) view returns (bool) {
bool _hasMinGas;
assembly {
_hasMinGas := iszero(lt(mul(gas(), 63), add(mul(_minGas, 64), mul(add(40000, _reservedGas), 63))))
}
return _hasMinGas;
}
function safeCallWithMinGas(address _target, uint256 _gas, uint256 _value, bytes memory _calldata)
returns (bool success)
{
require(hasMinGas(_gas, 1_000), "Must have minGas");
assembly {
success :=
call(
_gas,
_target,
_value,
add(_calldata, 0x20),
mload(_calldata),
0,
0
)
}
return (success);
}
文件 17 的 22:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
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);
}
}
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);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 18 的 22:Types.sol
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;
文件 19 的 22:UniqueArray.sol
pragma solidity ^0.8.24;
function _requireNoDuplicates(address[] calldata arr) pure {
uint256 arrLength = arr.length;
if (arrLength == 0) return;
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");
}
}
文件 20 的 22:UserProxy.sol
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 {
IERC20 public immutable lqty;
IERC20 public immutable lusd;
ILQTYStaking public immutable stakingV1;
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");
_;
}
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);
}
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);
}
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);
}
function staked() external view returns (uint256) {
return stakingV1.stakes(address(this));
}
receive() external payable {}
}
文件 21 的 22:UserProxyFactory.sol
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 {
address public immutable userProxyImplementation;
constructor(address _lqty, address _lusd, address _stakingV1) {
userProxyImplementation = address(new UserProxy(_lqty, _lusd, _stakingV1));
}
function deriveUserProxyAddress(address _user) public view returns (address) {
return Clones.predictDeterministicAddress(userProxyImplementation, bytes32(uint256(uint160(_user))));
}
function deployUserProxy() public returns (address) {
address userProxy = Clones.cloneDeterministic(userProxyImplementation, bytes32(uint256(uint160(msg.sender))));
emit DeployUserProxy(msg.sender, userProxy);
return userProxy;
}
}
文件 22 的 22:VotingPower.sol
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;
}
{
"compilationTarget": {
"lib/V2-gov/src/Governance.sol": "Governance"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@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/",
":openzeppelin/=lib/V2-gov/lib/openzeppelin-contracts/",
":v4-core/=lib/V2-gov/lib/v4-core/"
]
}
[{"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"}]