Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"lib/ve-governance/src/queue/DynamicExitQueue.sol": {
"content": "/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import {IDAO} from "@aragon/osx-commons-contracts/src/dao/IDAO.sol";
import {IDynamicExitQueue, IDynamicExitQueueFee} from "./IDynamicExitQueue.sol";
import {
IERC20Upgradeable as IERC20
} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {IVotingEscrowIncreasing as IVotingEscrow} from "@escrow/IVotingEscrowIncreasing.sol";
import {IClockUser, IClock} from "@clock/IClock.sol";
import {
SafeERC20Upgradeable as SafeERC20
} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {
DaoAuthorizableUpgradeable as DaoAuthorizable
} from "@aragon/osx-commons-contracts/src/permission/auth/DaoAuthorizableUpgradeable.sol";
/// @title DynamicExitQueue
/// @notice Token IDs associated with an NFT are given a ticket when they are queued for exit.
/// After a cooldown period, the ticket holder can exit the NFT with dynamic fee calculations.
contract DynamicExitQueue is IDynamicExitQueue, IClockUser, DaoAuthorizable, UUPSUpgradeable {
using SafeERC20 for IERC20;
/// @notice role required to manage the exit queue
bytes32 public constant QUEUE_ADMIN_ROLE = keccak256("QUEUE_ADMIN");
/// @notice role required to withdraw tokens from the escrow contract
bytes32 public constant WITHDRAW_ROLE = keccak256("WITHDRAW_ROLE");
/// @dev 10_000 = 100%
uint16 private constant MAX_FEE_PERCENT = 10_000;
/// @dev 1e18 is used for internal precision in fee calculations
uint256 private constant INTERNAL_PRECISION = 1e18;
/// @notice the highest fee someone will pay on exit
uint256 public feePercent;
/// @notice address of the escrow contract
address public escrow;
/// @notice clock contract for epoch duration
address public clock;
/// @notice time in seconds between entering queue and exiting on optimal terms
uint48 public cooldown;
/// @notice minimum time from the original lock date before one can enter the queue
uint48 public minLock;
/// @notice tokenId => TicketV2
mapping(uint256 => TicketV2) internal _queue;
/*//////////////////////////////////////////////////////////////
Dynamic Fee Params
//////////////////////////////////////////////////////////////*/
/// @notice Minimum fee percent charged after full cooldown period
uint256 public minFeePercent;
/// @notice Minimum wait time before any exit is possible
uint48 public minCooldown;
/// @notice Fee decrease per second (basis points/second) during decay period
/// @dev Set to 0 when minCooldown == cooldown to prevent division by zero
uint256 internal _slope;
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
constructor() {
_disableInitializers();
}
/// @param _escrow address of the escrow contract where tokens are stored
/// @param _cooldown time in seconds between exit and withdrawal
/// @param _dao address of the DAO that will be able to set the queue
function initialize(
address _escrow,
uint48 _cooldown,
address _dao,
uint256 _feePercent,
address _clock,
uint48 _minLock
) external initializer {
__DaoAuthorizableUpgradeable_init(IDAO(_dao));
escrow = _escrow;
clock = _clock;
_setMinLock(_minLock);
// Initialize with fixed fee system, no early exits
if (_feePercent > MAX_FEE_PERCENT) revert FeePercentTooHigh(MAX_FEE_PERCENT);
_setFixedExitFeePercent(_feePercent, _cooldown);
}
/*//////////////////////////////////////////////////////////////
Modifiers
//////////////////////////////////////////////////////////////*/
modifier onlyEscrow() {
if (msg.sender != escrow) revert OnlyEscrow();
_;
}
/*//////////////////////////////////////////////////////////////
Admin Functions
//////////////////////////////////////////////////////////////*/
/// @notice The exit queue manager can set the minimum lock time
function setMinLock(uint48 _minLock) external auth(QUEUE_ADMIN_ROLE) {
_setMinLock(_minLock);
}
function _setMinLock(uint48 _minLock) internal {
if (_minLock == 0) revert MinLockOutOfBounds();
minLock = _minLock;
emit MinLockSet(_minLock);
}
/// @inheritdoc IDynamicExitQueueFee
function setDynamicExitFeePercent(
uint256 _minFeePercent,
uint256 _maxFeePercent,
uint48 _cooldown,
uint48 _minCooldown
) external auth(QUEUE_ADMIN_ROLE) {
if (_minFeePercent > MAX_FEE_PERCENT || _maxFeePercent > MAX_FEE_PERCENT) {
revert FeePercentTooHigh(MAX_FEE_PERCENT);
}
if (_maxFeePercent <= _minFeePercent) revert InvalidFeeParameters();
// setting cooldown == minCooldown would imply a vertical slope
if (_cooldown <= _minCooldown) revert CooldownTooShort();
_setDynamicExitFeePercent(_minFeePercent, _maxFeePercent, _cooldown, _minCooldown);
}
/// @inheritdoc IDynamicExitQueueFee
function setTieredExitFeePercent(
uint256 _baseFeePercent,
uint256 _earlyFeePercent,
uint48 _cooldown,
uint48 _minCooldown
) external auth(QUEUE_ADMIN_ROLE) {
if (_baseFeePercent > MAX_FEE_PERCENT || _earlyFeePercent > MAX_FEE_PERCENT) {
revert FeePercentTooHigh(MAX_FEE_PERCENT);
}
if (_earlyFeePercent <= _baseFeePercent) revert InvalidFeeParameters();
if (_cooldown <= _minCooldown) revert CooldownTooShort();
_setTieredExitFeePercent(_baseFeePercent, _earlyFeePercent, _cooldown, _minCooldown);
}
/// @inheritdoc IDynamicExitQueueFee
function setFixedExitFeePercent(
uint256 _feePercent,
uint48 _minCooldown
) external auth(QUEUE_ADMIN_ROLE) {
if (_feePercent > MAX_FEE_PERCENT) revert FeePercentTooHigh(MAX_FEE_PERCENT);
_setFixedExitFeePercent(_feePercent, _minCooldown);
}
function _setDynamicExitFeePercent(
uint256 _minFeePercent,
uint256 _maxFeePercent,
uint48 _cooldown,
uint48 _minCooldown
) internal {
feePercent = _maxFeePercent;
minFeePercent = _minFeePercent;
cooldown = _cooldown;
minCooldown = _minCooldown;
_slope = _computeSlope(_minFeePercent, _maxFeePercent, _cooldown, _minCooldown);
emit ExitFeePercentAdjusted(
_maxFeePercent,
_minFeePercent,
_minCooldown,
ExitFeeType.Dynamic
);
}
function _setTieredExitFeePercent(
uint256 _baseFeePercent,
uint256 _earlyFeePercent,
uint48 _cooldown,
uint48 _minCooldown
) internal {
feePercent = _earlyFeePercent;
minFeePercent = _baseFeePercent;
cooldown = _cooldown;
minCooldown = _minCooldown;
_slope = 0; // No decay in tiered system
emit ExitFeePercentAdjusted(
_earlyFeePercent,
_baseFeePercent,
_minCooldown,
ExitFeeType.Tiered
);
}
function _setFixedExitFeePercent(uint256 _feePercent, uint48 _cooldown) internal {
feePercent = _feePercent;
minFeePercent = _feePercent;
cooldown = _cooldown;
minCooldown = _cooldown;
_slope = 0; // No decay in fixed system
emit ExitFeePercentAdjusted(_feePercent, _feePercent, minCooldown, ExitFeeType.Fixed);
}
/*//////////////////////////////////////////////////////////////
SLOPE
//////////////////////////////////////////////////////////////*/
function _computeSlope(
uint256 _minFeePercent,
uint256 _maxFeePercent,
uint48 _cooldown,
uint48 _minCooldown
) internal pure returns (uint256) {
// Calculate slope in 1e18 scale for maximum precision
uint256 scaledMaxFee = (_maxFeePercent * INTERNAL_PRECISION) / MAX_FEE_PERCENT;
uint256 scaledMinFee = (_minFeePercent * INTERNAL_PRECISION) / MAX_FEE_PERCENT;
uint256 scaledFeeRange = scaledMaxFee - scaledMinFee;
uint256 timeRange = _cooldown - _minCooldown;
return scaledFeeRange / timeRange;
}
/*//////////////////////////////////////////////////////////////
WITHDRAWER
//////////////////////////////////////////////////////////////*/
/// @notice withdraw staked tokens sent as part of fee collection to the caller
/// @dev The caller must be authorized to withdraw by the DAO
function withdraw(uint256 _amount) external auth(WITHDRAW_ROLE) {
IERC20 underlying = IERC20(IVotingEscrow(escrow).token());
underlying.safeTransfer(msg.sender, _amount);
}
/*//////////////////////////////////////////////////////////////
Exit Logic
//////////////////////////////////////////////////////////////*/
/// @notice queue an exit for a given tokenId, granting the ticket to the passed holder
/// @param _tokenId the tokenId to queue an exit for
/// @param _ticketHolder the address that will be granted the ticket
/// @dev we don't check that the ticket holder is the caller
/// this is because the escrow contract is the only one that can queue an exit
/// and we leave that logic to the escrow contract
function queueExit(uint256 _tokenId, address _ticketHolder) external onlyEscrow {
if (_ticketHolder == address(0)) revert ZeroAddress();
if (_queue[_tokenId].holder != address(0)) revert AlreadyQueued();
// get time to min lock and revert if it hasn't been reached
uint48 minLockTime = timeToMinLock(_tokenId);
if (minLockTime > block.timestamp) {
revert MinLockNotReached(_tokenId, minLock, minLockTime);
}
uint48 queuedAt = uint48(block.timestamp);
_queue[_tokenId] = TicketV2({
holder: _ticketHolder,
queuedAt: queuedAt,
feePercent: uint16(feePercent),
minFeePercent: uint16(minFeePercent),
cooldown: cooldown,
minCooldown: minCooldown,
slope: _slope
});
emit ExitQueuedV2(_tokenId, _ticketHolder, queuedAt);
}
/// @notice Exits the queue for that tokenID.
/// @dev The holder is not checked. This is left up to the escrow contract to manage.
function exit(uint256 _tokenId) external onlyEscrow returns (uint256 fee) {
if (!canExit(_tokenId)) revert CannotExit();
// calculate fee before resetting ticket
fee = calculateFee(_tokenId);
// reset the ticket for that tokenId
delete _queue[_tokenId];
emit Exit(_tokenId, fee);
}
/// @notice Cancels the exit.
/// @dev The token must have a holder.
function cancelExit(uint256 _tokenId) external onlyEscrow {
TicketV2 memory ticket = _queue[_tokenId];
// This should never occur as escrow already checks this
// but for safety, still advisable to have this check.
if (ticket.holder == address(0)) {
revert CannotCancelExit();
}
delete _queue[_tokenId];
emit ExitCancelled(_tokenId, ticket.holder);
}
/// @notice Calculate the absolute fee amount for exiting a specific token
/// @param _tokenId The token ID to calculate fee for
/// @return Fee amount in underlying token units
function calculateFee(uint256 _tokenId) public view returns (uint256) {
TicketV2 memory ticket = _queue[_tokenId];
if (ticket.holder == address(0)) return 0;
uint256 underlyingBalance = IVotingEscrow(escrow).locked(_tokenId).amount;
if (underlyingBalance == 0) revert NoLockBalance();
uint256 timeElapsed = block.timestamp - ticket.queuedAt;
uint256 scaledFeePercent = _getScaledTimeBasedFee(timeElapsed, ticket);
return (underlyingBalance * scaledFeePercent) / INTERNAL_PRECISION;
}
/// @notice Internal function to get time-based fee in 1e18 scale
/// @param timeElapsed Time elapsed since ticket was queued
/// @param ticket The ticket to calculate fee for - ensures changes to global params don't affect existing tickets
/// @return Fee percent in 1e18 scale (0 = 0%, 1e18 = 100%)
function _getScaledTimeBasedFee(
uint256 timeElapsed,
TicketV2 memory ticket
) internal view returns (uint256) {
uint256 scaledMaxFee = (ticket.feePercent * INTERNAL_PRECISION) / MAX_FEE_PERCENT;
uint256 scaledMinFee = (ticket.minFeePercent * INTERNAL_PRECISION) / MAX_FEE_PERCENT;
// Fixed fee system (no decay, no tiers)
if (ticket.minFeePercent == ticket.feePercent) return scaledMaxFee;
// Tiered system (no slope) or fixed system
if (ticket.slope == 0) {
return timeElapsed <= ticket.cooldown ? scaledMaxFee : scaledMinFee;
}
// Dynamic system (linear decay using stored slope)
if (timeElapsed <= ticket.minCooldown) return scaledMaxFee;
if (timeElapsed >= ticket.cooldown) return scaledMinFee;
// Calculate fee reduction using high-precision slope
uint256 timeInDecay = timeElapsed - ticket.minCooldown;
uint256 feeReduction = ticket.slope * timeInDecay;
// Ensure we don't go below minimum fee
if (feeReduction >= (scaledMaxFee - scaledMinFee)) {
return scaledMinFee;
}
return scaledMaxFee - feeReduction;
}
/// @notice Calculate the exit fee percent for a given time elapsed
/// @param timeElapsed Time elapsed since ticket was queued
/// @return Fee percent in basis points
function getTimeBasedFee(uint256 timeElapsed) public view returns (uint256) {
uint256 scaledFee = _getScaledTimeBasedFee(timeElapsed, _globalTicket());
return (scaledFee * MAX_FEE_PERCENT) / INTERNAL_PRECISION;
}
/// @dev global parameters as a ticket for fee calculation
function _globalTicket() internal view returns (TicketV2 memory) {
return
TicketV2({
holder: address(0),
queuedAt: 0,
feePercent: uint16(feePercent),
minFeePercent: uint16(minFeePercent),
cooldown: cooldown,
minCooldown: minCooldown,
slope: _slope
});
}
/*//////////////////////////////////////////////////////////////
View Functions
//////////////////////////////////////////////////////////////*/
/// @notice Check if a token has completed its full cooldown period (minimum fee applies)
/// @param _tokenId The token ID to check
/// @return True if full cooldown elapsed, false otherwise
function isCool(uint256 _tokenId) public view returns (bool) {
TicketV2 memory ticket = _queue[_tokenId];
if (ticket.holder == address(0)) return false;
return block.timestamp - ticket.queuedAt >= ticket.cooldown;
}
/// @return true if the tokenId corresponds to a valid ticket and the minimum cooldown period has passed
function canExit(uint256 _tokenId) public view returns (bool) {
TicketV2 memory ticket = _queue[_tokenId];
if (ticket.holder == address(0)) return false;
return block.timestamp - ticket.queuedAt >= ticket.minCooldown;
}
/// @return holder of a ticket for a given tokenId
function ticketHolder(uint256 _tokenId) external view returns (address) {
return _queue[_tokenId].holder;
}
function queue(uint256 _tokenId) external view override returns (TicketV2 memory) {
return _queue[_tokenId];
}
function timeToMinLock(uint256 _tokenId) public view returns (uint48) {
uint48 lockStart = IVotingEscrow(escrow).locked(_tokenId).start;
return lockStart + minLock;
}
/*///////////////////////////////////////////////////////////////
UUPS Upgrade
//////////////////////////////////////////////////////////////*/
/// @notice Returns the address of the implementation contract in the [proxy storage slot](https://eips.ethereum.org/EIPS/eip-1967) slot the [UUPS proxy](https://eips.ethereum.org/EIPS/eip-1822) is pointing to.
/// @return The address of the implementation contract.
function implementation() public view returns (address) {
return _getImplementation();
}
/// @notice Internal method authorizing the upgrade of the contract via the [upgradeability mechanism for UUPS proxies](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable) (see [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822)).
function _authorizeUpgrade(address) internal virtual override auth(QUEUE_ADMIN_ROLE) {}
/// @dev Reserved storage space to allow for layout changes in the future.
uint256[42] private __gap; // Reduced to account for new state variables
}
"
},
"lib/ve-governance/lib/osx-commons/contracts/src/dao/IDAO.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.8;
/// @title IDAO
/// @author Aragon X - 2022-2024
/// @notice The interface required for DAOs within the Aragon App DAO framework.
/// @custom:security-contact sirt@aragon.org
interface IDAO {
/// @notice Checks if an address has permission on a contract via a permission identifier and considers if `ANY_ADDRESS` was used in the granting process.
/// @param _where The address of the contract.
/// @param _who The address of a EOA or contract to give the permissions.
/// @param _permissionId The permission identifier.
/// @param _data The optional data passed to the `PermissionCondition` registered.
/// @return Returns true if the address has permission, false if not.
function hasPermission(
address _where,
address _who,
bytes32 _permissionId,
bytes memory _data
) external view returns (bool);
/// @notice Updates the DAO metadata (e.g., an IPFS hash).
/// @param _metadata The IPFS hash of the new metadata object.
function setMetadata(bytes calldata _metadata) external;
/// @notice Emitted when the DAO metadata is updated.
/// @param metadata The IPFS hash of the new metadata object.
event MetadataSet(bytes metadata);
/// @notice Emitted when a standard callback is registered.
/// @param interfaceId The ID of the interface.
/// @param callbackSelector The selector of the callback function.
/// @param magicNumber The magic number to be registered for the callback function selector.
event StandardCallbackRegistered(
bytes4 interfaceId,
bytes4 callbackSelector,
bytes4 magicNumber
);
/// @notice Deposits (native) tokens to the DAO contract with a reference string.
/// @param _token The address of the token or address(0) in case of the native token.
/// @param _amount The amount of tokens to deposit.
/// @param _reference The reference describing the deposit reason.
function deposit(address _token, uint256 _amount, string calldata _reference) external payable;
/// @notice Emitted when a token deposit has been made to the DAO.
/// @param sender The address of the sender.
/// @param token The address of the deposited token.
/// @param amount The amount of tokens deposited.
/// @param _reference The reference describing the deposit reason.
event Deposited(
address indexed sender,
address indexed token,
uint256 amount,
string _reference
);
/// @notice Emitted when a native token deposit has been made to the DAO.
/// @dev This event is intended to be emitted in the `receive` function and is therefore bound by the gas limitations for `send`/`transfer` calls introduced by [ERC-2929](https://eips.ethereum.org/EIPS/eip-2929).
/// @param sender The address of the sender.
/// @param amount The amount of native tokens deposited.
event NativeTokenDeposited(address sender, uint256 amount);
/// @notice Setter for the trusted forwarder verifying the meta transaction.
/// @param _trustedForwarder The trusted forwarder address.
function setTrustedForwarder(address _trustedForwarder) external;
/// @notice Getter for the trusted forwarder verifying the meta transaction.
/// @return The trusted forwarder address.
function getTrustedForwarder() external view returns (address);
/// @notice Emitted when a new TrustedForwarder is set on the DAO.
/// @param forwarder the new forwarder address.
event TrustedForwarderSet(address forwarder);
/// @notice Checks whether a signature is valid for a provided hash according to [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271).
/// @param _hash The hash of the data to be signed.
/// @param _signature The signature byte array associated with `_hash`.
/// @return Returns the `bytes4` magic value `0x1626ba7e` if the signature is valid and `0xffffffff` if not.
function isValidSignature(bytes32 _hash, bytes memory _signature) external returns (bytes4);
/// @notice Registers an ERC standard having a callback by registering its [ERC-165](https://eips.ethereum.org/EIPS/eip-165) interface ID and callback function signature.
/// @param _interfaceId The ID of the interface.
/// @param _callbackSelector The selector of the callback function.
/// @param _magicNumber The magic number to be registered for the function signature.
function registerStandardCallback(
bytes4 _interfaceId,
bytes4 _callbackSelector,
bytes4 _magicNumber
) external;
/// @notice Removed function being left here to not corrupt the IDAO interface ID. Any call will revert.
/// @dev Introduced in v1.0.0. Removed in v1.4.0.
function setSignatureValidator(address) external;
}
"
},
"lib/ve-governance/src/queue/IDynamicExitQueue.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {
IExitQueueMinLock,
IExitMinLockCooldownErrorsAndEvents,
IExitQueueCoreErrorsAndEvents,
IExitQueueCancelErrorsAndEvents
} from "./IExitQueue.sol";
interface ITicketV2 {
struct TicketV2 {
address holder;
uint48 queuedAt;
uint48 minCooldown;
uint48 cooldown;
uint16 feePercent;
uint16 minFeePercent;
uint256 slope;
}
event ExitQueuedV2(uint256 indexed tokenId, address indexed holder, uint48 queuedAt);
}
/*///////////////////////////////////////////////////////////////
Fee Collection
//////////////////////////////////////////////////////////////*/
interface IExitFeeWithdrawErrorsAndEvents {
event Withdraw(address indexed to, uint256 amount);
}
interface IExitFeeWithdraw is IExitFeeWithdrawErrorsAndEvents {
/// @notice withdraw accumulated fees
function withdraw(uint256 _amount) external;
}
/*///////////////////////////////////////////////////////////////
Early Exit Queue
//////////////////////////////////////////////////////////////*/
interface IDynamicExitQueueEventsAndErrors {
enum ExitFeeType {
Fixed,
Tiered,
Dynamic
}
// Events
event ExitFeePercentAdjusted(
uint256 maxFeePercent,
uint256 minFeePercent,
uint48 minCooldown,
ExitFeeType feeType
);
// Errors
error EarlyExitDisabled();
error MinCooldownNotMet();
error InvalidFeeParameters();
error FeePercentTooHigh(uint256 maxAllowed);
error CooldownTooShort();
error LegacyFunctionDeprecated();
}
interface IDynamicExitQueueFee is IDynamicExitQueueEventsAndErrors {
/// @notice Calculate the absolute fee amount for exiting a specific token
/// @param tokenId The token ID to calculate fee for
/// @return Fee amount in underlying token units
function calculateFee(uint256 tokenId) external view returns (uint256);
/// @notice Check if a token has completed its full cooldown period (minimum fee applies)
/// @param tokenId The token ID to check
/// @return True if full cooldown elapsed, false otherwise
function isCool(uint256 tokenId) external view returns (bool);
/// @notice Configure linear fee decay system where fees decrease continuously over time
/// @param _minFeePercent Fee percent after full cooldown (basis points, 0-10000)
/// @param _maxFeePercent Fee percent immediately after minCooldown (basis points, 0-10000)
/// @param _cooldown Total cooldown period in seconds
/// @param _minCooldown Minimum wait before any exit allowed in seconds
function setDynamicExitFeePercent(
uint256 _minFeePercent,
uint256 _maxFeePercent,
uint48 _cooldown,
uint48 _minCooldown
) external;
/// @notice Configure two-tier fee system with early exit penalty and normal exit rate
/// @param _baseFeePercent Fee percent for normal exits after cooldown (basis points, 0-10000)
/// @param _earlyFeePercent Fee percent for early exits after minCooldown (basis points, 0-10000)
/// @param _cooldown Total cooldown period in seconds
/// @param _minCooldown Minimum wait before any exit allowed in seconds
function setTieredExitFeePercent(
uint256 _baseFeePercent,
uint256 _earlyFeePercent,
uint48 _cooldown,
uint48 _minCooldown
) external;
/// @notice Configure single fee rate system with optional early exit control
/// @param _feePercent Fee percent for all exits (basis points, 0-10000)
/// @param _minCooldown Total cooldown period in seconds - can be zero for instant exits w. fee
function setFixedExitFeePercent(uint256 _feePercent, uint48 _minCooldown) external;
/// @notice Minimum fee percent charged after full cooldown
/// @return Fee percent in basis points (0-10000)
function minFeePercent() external view returns (uint256);
/// @notice Minimum wait time before any exit is possible
/// @return Time in seconds
function minCooldown() external view returns (uint48);
}
/*///////////////////////////////////////////////////////////////
Exit Queue
//////////////////////////////////////////////////////////////*/
interface IDynamicExitQueueErrorsAndEvents is
IExitQueueCoreErrorsAndEvents,
IExitMinLockCooldownErrorsAndEvents,
IDynamicExitQueueEventsAndErrors,
IExitQueueCancelErrorsAndEvents
{}
interface IDynamicExitQueue is
IDynamicExitQueueErrorsAndEvents,
ITicketV2,
IExitQueueMinLock,
IDynamicExitQueueFee
{
/// @notice tokenId => TicketV2
function queue(uint256 _tokenId) external view returns (TicketV2 memory);
/// @notice queue an exit for a given tokenId, granting the ticket to the passed holder
/// @param _tokenId the tokenId to queue an exit for
/// @param _ticketHolder the address that will be granted the ticket
function queueExit(uint256 _tokenId, address _ticketHolder) external;
/// @notice exit the queue for a given tokenId. Requires the cooldown period to have passed
/// @return exitAmount the amount of tokens that can be withdrawn
function exit(uint256 _tokenId) external returns (uint256 exitAmount);
/// @notice return true if the tokenId corresponds to a valid ticket and the cooldown period has passed
function canExit(uint256 _tokenId) external view returns (bool);
/// @notice return the ticket holder for a given tokenId
function ticketHolder(uint256 _tokenId) external view returns (address);
}
"
},
"lib/ve-governance/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount) external returns (bool);
}
"
},
"lib/ve-governance/src/escrow/IVotingEscrowIncreasing.sol": {
"content": "/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*///////////////////////////////////////////////////////////////
CORE FUNCTIONALITY
//////////////////////////////////////////////////////////////*/
interface ILockedBalanceIncreasing {
struct LockedBalance {
uint208 amount;
uint48 start; // mirrors oz ERC20 timestamp clocks
}
}
interface IVotingEscrowCoreErrors {
error NoLockFound();
error NotOwner();
error NoOwner();
error NotSameOwner();
error NonExistentToken();
error NotApprovedOrOwner();
error ZeroAddress();
error ZeroAmount();
error ZeroBalance();
error SameAddress();
error LockNFTAlreadySet();
error MustBe18Decimals();
error TransferBalanceIncorrect();
error AmountTooSmall();
error OnlyLockNFT();
error OnlyIVotesAdapter();
error AddressAlreadySet();
}
interface IVotingEscrowCoreEvents {
event MinDepositSet(uint256 minDeposit);
event Deposit(
address indexed depositor,
uint256 indexed tokenId,
uint256 indexed startTs,
uint256 value,
uint256 newTotalLocked
);
event Withdraw(
address indexed depositor,
uint256 indexed tokenId,
uint256 value,
uint256 ts,
uint256 newTotalLocked
);
}
interface IVotingEscrowCore is
ILockedBalanceIncreasing,
IVotingEscrowCoreErrors,
IVotingEscrowCoreEvents
{
/// @notice Address of the underying ERC20 token.
function token() external view returns (address);
/// @notice Address of the lock receipt NFT.
function lockNFT() external view returns (address);
/// @notice Total underlying tokens deposited in the contract
function totalLocked() external view returns (uint256);
/// @notice Get the raw locked balance for `_tokenId`
function locked(uint256 _tokenId) external view returns (LockedBalance memory);
/// @notice Deposit `_value` tokens for `msg.sender`
/// @param _value Amount to deposit
/// @return TokenId of created veNFT
function createLock(uint256 _value) external returns (uint256);
/// @notice Deposit `_value` tokens for `_to`
/// @param _value Amount to deposit
/// @param _to Address to deposit
/// @return TokenId of created veNFT
function createLockFor(uint256 _value, address _to) external returns (uint256);
/// @notice Withdraw all tokens for `_tokenId`
function withdraw(uint256 _tokenId) external;
/// @notice helper utility for NFT checks
function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool);
}
/*///////////////////////////////////////////////////////////////
WITHDRAWAL QUEUE
//////////////////////////////////////////////////////////////*/
interface IWithdrawalQueueErrors {
error NotTicketHolder();
error CannotExit();
error CannotWithdrawInSameBlock();
}
interface IWithdrawalQueueEvents {}
interface IWithdrawalQueue is IWithdrawalQueueErrors, IWithdrawalQueueEvents {
/// @notice Enters a tokenId into the withdrawal queue by transferring to this contract and creating a ticket.
/// @param _tokenId The tokenId to begin withdrawal for. Will be transferred to this contract before burning.
/// @dev The user must not have active votes in the voter contract.
function beginWithdrawal(uint256 _tokenId) external;
/// @notice Address of the contract that manages exit queue logic for withdrawals
function queue() external view returns (address);
}
/*///////////////////////////////////////////////////////////////
SWEEPER
//////////////////////////////////////////////////////////////*/
interface ISweeperEvents {
event Sweep(address indexed to, uint256 amount);
event SweepNFT(address indexed to, uint256 tokenId);
}
interface ISweeperErrors {
error NothingToSweep();
}
interface ISweeper is ISweeperEvents, ISweeperErrors {
/// @notice sweeps excess tokens from the contract to a designated address
function sweep() external;
function sweepNFT(uint256 _tokenId, address _to) external;
}
/*///////////////////////////////////////////////////////////////
DYNAMIC VOTER
//////////////////////////////////////////////////////////////*/
interface IDynamicVoterErrors {
error NotVoter();
error OwnershipChange();
error AlreadyVoted();
}
interface IDynamicVoter is IDynamicVoterErrors {
/// @notice Address of the voting contract.
/// @dev We need to ensure votes are not left in this contract before allowing positing changes
function voter() external view returns (address);
/// @notice Address of the voting Escrow Curve contract that will calculate the voting power
function curve() external view returns (address);
/// @notice Get the voting power for _tokenId at the current timestamp
/// @dev Returns 0 if called in the same block as a transfer.
/// @param _tokenId .
/// @return Voting power
function votingPower(uint256 _tokenId) external view returns (uint256);
/// @notice Get the voting power for _tokenId at a given timestamp
/// @param _tokenId .
/// @param _t Timestamp to query voting power
/// @return Voting power
function votingPowerAt(uint256 _tokenId, uint256 _t) external view returns (uint256);
/// @notice Get the voting power for _account at the current timestamp
/// Aggregtes all voting power for all tokens owned by the account
/// @dev This cannot be used historically without token snapshots
function votingPowerForAccount(address _account) external view returns (uint256);
/// @notice Calculate total voting power at current timestamp
/// @return Total voting power at current timestamp
function totalVotingPower() external view returns (uint256);
/// @notice Calculate total voting power at a given timestamp
/// @param _t Timestamp to query total voting power
/// @return Total voting power at given timestamp
function totalVotingPowerAt(uint256 _t) external view returns (uint256);
/// @notice See if a queried _tokenId has actively voted
/// @return True if voted, else false
function isVoting(uint256 _tokenId) external view returns (bool);
/// @notice Set the global state voter
function setVoter(address _voter) external;
}
/*///////////////////////////////////////////////////////////////
INCREASED ESCROW
//////////////////////////////////////////////////////////////*/
interface IVotingEscrowIncreasing is IVotingEscrowCore, IDynamicVoter, IWithdrawalQueue, ISweeper {}
/// @dev useful for testing
interface IVotingEscrowEventsStorageErrorsEvents is
IVotingEscrowCoreErrors,
IVotingEscrowCoreEvents,
IWithdrawalQueueErrors,
IWithdrawalQueueEvents,
ILockedBalanceIncreasing,
ISweeperEvents,
ISweeperErrors
{}
"
},
"lib/ve-governance/src/clock/IClock.sol": {
"content": "/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IClockUser {
function clock() external view returns (address);
}
interface IClock {
function epochDuration() external pure returns (uint256);
function checkpointInterval() external pure returns (uint256);
function voteDuration() external pure returns (uint256);
function voteWindowBuffer() external pure returns (uint256);
function currentEpoch() external view returns (uint256);
function resolveEpoch(uint256 timestamp) external pure returns (uint256);
function elapsedInEpoch() external view returns (uint256);
function resolveElapsedInEpoch(uint256 timestamp) external pure returns (uint256);
function epochStartsIn() external view returns (uint256);
function resolveEpochStartsIn(uint256 timestamp) external pure returns (uint256);
function epochStartTs() external view returns (uint256);
function resolveEpochStartTs(uint256 timestamp) external pure returns (uint256);
function votingActive() external view returns (bool);
function resolveVotingActive(uint256 timestamp) external pure returns (bool);
function epochVoteStartsIn() external view returns (uint256);
function resolveEpochVoteStartsIn(uint256 timestamp) external pure returns (uint256);
function epochVoteStartTs() external view returns (uint256);
function resolveEpochVoteStartTs(uint256 timestamp) external pure returns (uint256);
function epochVoteEndsIn() external view returns (uint256);
function resolveEpochVoteEndsIn(uint256 timestamp) external pure returns (uint256);
function epochVoteEndTs() external view returns (uint256);
function resolveEpochVoteEndTs(uint256 timestamp) external pure returns (uint256);
function epochNextCheckpointIn() external view returns (uint256);
function resolveEpochNextCheckpointIn(uint256 timestamp) external pure returns (uint256);
function epochNextCheckpointTs() external view returns (uint256);
function resolveEpochNextCheckpointTs(uint256 timestamp) external pure returns (uint256);
}
"
},
"lib/ve-governance/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using AddressUpgradeable for address;
/**
* @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(IERC20Upgradeable token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, 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(IERC20Upgradeable token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 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(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
* 0 before setting it to a non-zero value.
*/
function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20PermitUpgradeable token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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(IERC20Upgradeable 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, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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(IERC20Upgradeable 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))) && AddressUpgradeable.isContract(address(token));
}
}
"
},
"lib/ve-governance/lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.0;
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
address private immutable __self = address(this);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
_;
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeTo(address newImplementation) public virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data, true);
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeTo} and {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal override onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
"
},
"lib/ve-governance/lib/osx-commons/contracts/src/permission/auth/DaoAuthorizableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.8;
import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import {IDAO} from "../../dao/IDAO.sol";
import {_auth} from "./auth.sol";
/// @title DaoAuthorizableUpgradeable
/// @author Aragon X - 2022-2023
/// @notice An abstract contract providing a meta-transaction compatible modifier for upgradeable or cloneable contracts to authorize function calls through an associated DAO.
/// @dev Make sure to call `__DaoAuthorizableUpgradeable_init` during initialization of the inheriting contract.
/// @custom:security-contact sirt@aragon.org
abstract contract DaoAuthorizableUpgradeable is ContextUpgradeable {
/// @notice The associated DAO managing the permissions of inheriting contracts.
IDAO private dao_;
/// @notice Initializes the contract by setting the associated DAO.
/// @param _dao The associated DAO address.
// solhint-disable-next-line func-name-mixedcase
function __DaoAuthorizableUpgradeable_init(IDAO _dao) internal onlyInitializing {
dao_ = _dao;
}
/// @notice Returns the DAO contract.
/// @return The DAO contract.
function dao() public view returns (IDAO) {
return dao_;
}
/// @notice A modifier to make functions on inheriting contracts authorized. Permissions to call the function are checked through the associated DAO's permission manager.
/// @param _permissionId The permission identifier required to call the method this modifier is applied to.
modifier auth(bytes32 _permissionId) {
_auth(dao_, address(this), _msgSender(), _permissionId, _msgData());
_;
}
/// @notice This empty reserved space is put in place to allow future versions to add new variables without shifting down storage in the inheritance chain (see [OpenZeppelin's guide about storage gaps](https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps)).
uint256[49] private __gap;
}
"
},
"lib/ve-governance/src/queue/IExitQueue.sol": {
"content": "/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IExitQueueCoreErrorsAndEvents {
error OnlyEscrow();
error AlreadyQueued();
error ZeroAddress();
error CannotExit();
error NoLockBalance();
event ExitQueued(uint256 indexed tokenId, address indexed holder, uint256 exitDate);
event Exit(uint256 indexed tokenId, uint256 fee);
}
interface ITicket {
struct Ticket {
address holder;
uint256 exitDate;
}
}
/*///////////////////////////////////////////////////////////////
Fee Collection
//////////////////////////////////////////////////////////////*/
interface IExitQueueFeeErrorsAndEvents {
error FeeTooHigh(uint256 maxFee);
event Withdraw(address indexed to, uint256 amount);
event FeePercentSet(uint256 feePercent);
}
interface IExitQueueFee is IExitQueueFeeErrorsAndEvents {
/// @notice optional fee charged for exiting the queue
function feePercent() external view returns (uint256);
/// @notice The exit queue manager can set the fee
function setFeePercent(uint256 _fee) external;
/// @notice withdraw accumulated fees
function withdraw(uint256 _amount) external;
}
/*///////////////////////////////////////////////////////////////
Cooldown
//////////////////////////////////////////////////////////////*/
interface IExitQueueCooldownErrorsAndEvents {
error CooldownTooHigh();
event CooldownSet(uint48 cooldown);
}
interface IExitQueueCooldown is IExitQueueCooldownErrorsAndEvents {
/// @notice time in seconds between exit and withdrawal
function cooldown() external view returns (uint48);
/// @notice The exit queue manager can set the cooldown period
/// @param _cooldown time in seconds between exit and withdrawal
function setCooldown(uint48 _cooldown) external;
}
/*///////////////////////////////////////////////////////////////
Min Lock
//////////////////////////////////////////////////////////////*/
interface IExitMinLockCooldownErrorsAndEvents {
event MinLockSet(uint48 minLock);
error MinLockOutOfBounds();
error MinLockNotReached(uint256 tokenId, uint48 minLock, uint48 earliestExitDate);
}
interface IExitQueueMinLock is IExitMinLockCooldownErrorsAndEvents {
/// @notice minimum time from the original lock date before one can enter the queue
function minLock() external view returns (uint48);
/// @notice The exit queue manager can set the minimum lock time
function setMinLock(uint48 _cooldown) external;
}
/*///////////////////////////////////////////////////////////////
Exit Queue
//////////////////////////////////////////////////////////////*/
interface IExitQueueCancelErrorsAndEvents {
error CannotCancelExit();
event ExitCancelled(uint256 indexed tokenId, address indexed holder);
}
interface IExitQueueCancel {
function cancelExit(uint256 _tokenId) external;
}
/*///////////////////////////////////////////////////////////////
Exit Queue
//////////////////////////////////////////////////////////////*/
interface IExitQueueErrorsAndEvents is
IExitQueueCoreErrorsAndEvents,
IExitQueueFeeErrorsAndEvents,
IExitQueueCooldownErrorsAndEvents,
IExitMinLockCooldownErrorsAndEvents,
IExitQueueCancelErrorsAndEvents
{}
interface IExitQueue is
IExitQueueErrorsAndEvents,
ITicket,
IExitQueueFee,
IExitQueueCooldown,
IExitQueueMinLock,
IExitQueueCancel
{
/// @notice tokenId => Ticket
function queue(uint256 _tokenId) external view returns (Ticket memory);
/// @notice queue an exit for a given tokenId, granting the ticket to the passed holder
/// @param _tokenId the tokenId to queue an exit for
/// @param _ticketHolder the address that will be granted the ticket
function queueExit(uint256 _tokenId, address _ticketHolder) external;
function cancelExit(uint256 _tokenId) external;
/// @notice exit the queue for a given tokenId. Requires the cooldown period to have passed
/// @return exitAmount the amount of tokens that can be withdrawn
function exit(uint256 _tokenId) external returns (uint256 exitAmount);
/// @return true if the tokenId corresponds to a valid ticket and the cooldown period has passed
function canExit(uint256 _tokenId) external view returns (bool);
/// @return the ticket holder for a given tokenId
function ticketHolder(uint256 _tokenId) external view returns (address);
}
"
},
"lib/ve-governance/lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20PermitUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @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.
*/
interface IERC20PermitUpgradeable {
/**
* @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].
*/
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);
}
"
},
"lib/ve-governance/lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @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 vi
Submitted on: 2025-11-07 20:51:50
Comments
Log in to comment.
No comments yet.