编译器
0.8.13+commit.abaa5c0e
文件 1 的 21:AccessControl.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 21:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 3 的 21:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 21:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 21:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
文件 6 的 21:IAccessControl.sol
pragma solidity ^0.8.0;
interface IAccessControl {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
文件 7 的 21:IBlue.sol
pragma solidity 0.8.13;
interface IBlue {
function totalSupply() external view returns (uint);
function balanceOf(address) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address, uint) external returns (bool);
function transferFrom(address,address,uint) external returns (bool);
function mint(address, uint) external returns (bool);
function minter() external returns (address);
function setMinter(address) external;
}
文件 8 的 21:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 9 的 21:IERC20.sol
pragma solidity ^0.8.0;
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 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
文件 10 的 21:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 11 的 21:IOptionFeeDistributor.sol
interface IOptionFeeDistributor {
function distribute(address token, uint256 amount) external;
}
文件 12 的 21:IRewardsDistributor.sol
pragma solidity 0.8.13;
interface IRewardsDistributor {
function checkpoint_token() external;
function voting_escrow() external view returns(address);
function checkpoint_total_supply() external;
function claim(uint _tokenId) external returns(uint);
function claimable(uint _tokenId) external view returns (uint);
}
文件 13 的 21:IUniswapV3Twap.sol
interface IUniswapV3Twap {
function token0() external view returns (address);
function token1() external view returns (address);
function pool() external view returns (address);
function estimateAmountOut(
address tokenIn,
uint128 amountIn,
uint32 secondsAgo
) external view returns (uint amountOut);
}
文件 14 的 21:IVoter.sol
pragma solidity 0.8.13;
interface IVoter {
function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 _tokenId) external;
function claimFees(address[] memory _fees, address[][] memory _tokens, uint256 _tokenId) external;
function reset(uint256 _tokenId) external;
function vote(uint256 _tokenId, address[] calldata _poolVote, uint256[] calldata _weights) external;
function poke(uint256 _tokenId) external;
function _epochTimestamp() external view returns(uint256);
function _ve() external view returns (address);
function gauges(address _pair) external view returns (address);
function isGauge(address _gauge) external view returns (bool);
function poolForGauge(address _gauge) external view returns (address);
function factory() external view returns (address);
function minter() external view returns(address);
function isWhitelisted(address token) external view returns (bool);
function notifyRewardAmount(uint amount) external;
function distributeAll() external;
function distributeFees(address[] memory _gauges) external;
function internal_bribes(address _gauge) external view returns (address);
function external_bribes(address _gauge) external view returns (address);
function usedWeights(uint id) external view returns(uint);
function lastVoted(uint id) external view returns(uint);
function poolVote(uint id, uint _index) external view returns(address _pair);
function votes(uint id, address _pool) external view returns(uint votes);
function poolVoteLength(uint tokenId) external view returns(uint);
}
文件 15 的 21:IVotingEscrow.sol
pragma solidity 0.8.13;
interface IVotingEscrow {
struct Point {
int128 bias;
int128 slope;
uint256 ts;
uint256 blk;
}
struct LockedBalance {
int128 amount;
uint end;
}
function create_lock(uint _value, uint _lock_duration) external returns (uint);
function create_lock_for(uint _value, uint _lock_duration, address _to) external returns (uint);
function merge(uint _from, uint _to) external;
function increase_amount(uint _tokenId, uint _value) external;
function increase_unlock_time(uint _tokenId, uint _lock_duration) external;
function split(uint[] memory amounts, uint _tokenId) external;
function withdraw(uint _tokenId) external;
function setApprovalForAll(address _operator, bool _approved) external;
function locked(uint id) external view returns(LockedBalance memory);
function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint);
function token() external view returns (address);
function team() external returns (address);
function epoch() external view returns (uint);
function point_history(uint loc) external view returns (Point memory);
function user_point_history(uint tokenId, uint loc) external view returns (Point memory);
function user_point_epoch(uint tokenId) external view returns (uint);
function optionToken() external view returns (address);
function ownerOf(uint) external view returns (address);
function isApprovedOrOwner(address, uint) external view returns (bool);
function transferFrom(address, address, uint) external;
function safeTransferFrom(
address _from,
address _to,
uint _tokenId
) external;
function voted(uint) external view returns (bool);
function attachments(uint) external view returns (uint);
function voting(uint tokenId) external;
function abstain(uint tokenId) external;
function attach(uint tokenId) external;
function detach(uint tokenId) external;
function checkpoint() external;
function deposit_for(uint tokenId, uint value) external;
function balanceOfNFT(uint _id) external view returns (uint);
function balanceOf(address _owner) external view returns (uint);
function totalSupply() external view returns (uint);
function supply() external view returns (uint);
function balanceOfNFTAt(uint _tokenId, uint _t) external view returns (uint);
function balanceOfAtNFT(uint _tokenId, uint _t) external view returns (uint);
function decimals() external view returns(uint8);
}
文件 16 的 21:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1);
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
文件 17 的 21:OptionTokenV2.sol
pragma solidity 0.8.13;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IBlue} from "./interfaces/IBlue.sol";
import {IVotingEscrow} from "./interfaces/IVotingEscrow.sol";
import {IUniswapV3Twap} from "./interfaces/IUniswapV3Twap.sol";
import {IOptionFeeDistributor} from "./interfaces/IOptionFeeDistributor.sol";
import {IVoter} from "./interfaces/IVoter.sol";
import {IRewardsDistributor} from "./interfaces/IRewardsDistributor.sol";
contract OptionTokenV2 is ERC20, AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
uint256 public constant MAX_DISCOUNT = 100;
uint256 public constant MIN_DISCOUNT = 0;
uint256 public constant MAX_TWAP_SECONDS = 86400;
uint256 public constant FULL_LOCK = 2 * 365 * 86400;
uint256 public constant feeDenominator = 10000;
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN");
bytes32 public constant VOTER_ROLE = keccak256("VOTER");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER");
error OptionToken_PastDeadline();
error OptionToken_NoAdminRole();
error OptionToken_NoVoterRole();
error OptionToken_NoPauserRole();
error OptionToken_SlippageTooHigh();
error OptionToken_InvalidDiscount();
error OptionToken_Paused();
error OptionToken_InvalidTwapSeconds();
error OptionToken_IncorrectPairToken();
error InvalidArrayLength();
event Exercise(
address indexed sender,
address indexed recipient,
uint256 amount,
uint256 paymentAmount
);
event ExerciseVe(
address indexed sender,
address indexed recipient,
uint256 amount,
uint256 paymentAmount,
uint256 nftId
);
event SetTwapOracleAndPaymentToken(
IUniswapV3Twap indexed _twapOracle,
address indexed _paymentToken
);
event SetFeeDistributor(IOptionFeeDistributor indexed newFeeDistributor);
event SetDiscount(uint256 discount);
event SetVeDiscount(uint256 veDiscount);
event PauseStateChanged(bool isPaused);
event SetTwapSeconds(uint32 twapSeconds);
ERC20 public paymentToken;
ERC20 public immutable underlyingToken;
address public votingEscrow;
address public feeReceiver;
uint256 public fee;
IUniswapV3Twap public twapOracle;
IOptionFeeDistributor public feeDistributor;
IVoter public voter;
IRewardsDistributor public rewardsDistributor;
uint256 public discount;
uint256 public veDiscount;
uint256 public veNftId;
uint32 public twapSeconds = 60 * 30 * 4;
bool public isPaused;
mapping(uint256 => address[]) public _savedPoolVote;
mapping(uint256 => uint256[]) public _savedWeights;
modifier onlyAdmin() {
if (!hasRole(ADMIN_ROLE, msg.sender)) revert OptionToken_NoAdminRole();
_;
}
modifier onlyVoter() {
if (
!hasRole(ADMIN_ROLE, msg.sender) &&
!hasRole(VOTER_ROLE, msg.sender)
) revert OptionToken_NoVoterRole();
_;
}
modifier onlyPauser() {
if (!hasRole(PAUSER_ROLE, msg.sender))
revert OptionToken_NoPauserRole();
_;
}
constructor(
string memory _name,
string memory _symbol,
address _admin,
ERC20 _paymentToken,
ERC20 _underlyingToken,
IUniswapV3Twap _twapOracle,
IOptionFeeDistributor _feeDistributor,
uint256 _discount,
uint256 _veDiscount,
address _votingEscrow
) ERC20(_name, _symbol) {
_grantRole(ADMIN_ROLE, _admin);
_grantRole(PAUSER_ROLE, _admin);
_setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
_setRoleAdmin(VOTER_ROLE, ADMIN_ROLE);
_setRoleAdmin(PAUSER_ROLE, ADMIN_ROLE);
paymentToken = _paymentToken;
underlyingToken = _underlyingToken;
twapOracle = _twapOracle;
feeDistributor = _feeDistributor;
discount = _discount;
veDiscount = _veDiscount;
votingEscrow = _votingEscrow;
if(address(paymentToken) != address(0)){
paymentToken.approve(address(_feeDistributor), type(uint256).max);
}
if(_votingEscrow != address(0)){
underlyingToken.approve(_votingEscrow, type(uint256).max);
}
emit SetTwapOracleAndPaymentToken(_twapOracle, address(_paymentToken));
emit SetFeeDistributor(_feeDistributor);
emit SetDiscount(_discount);
emit SetVeDiscount(_veDiscount);
}
function exercise(
uint256 _amount,
uint256 _maxPaymentAmount,
address _recipient
) external nonReentrant returns (uint256) {
return _exercise(_amount, _maxPaymentAmount, _recipient);
}
function exercise(
uint256 _amount,
uint256 _maxPaymentAmount,
address _recipient,
uint256 _deadline
) external nonReentrant returns (uint256) {
if (block.timestamp > _deadline) revert OptionToken_PastDeadline();
return _exercise(_amount, _maxPaymentAmount, _recipient);
}
function exerciseVe(
uint256 _amount,
uint256 _maxPaymentAmount,
address _recipient,
uint256 _deadline
) external nonReentrant returns (uint256, uint256) {
if (block.timestamp > _deadline) revert OptionToken_PastDeadline();
return _exerciseVe(_amount, _maxPaymentAmount, _recipient);
}
function getDiscountedPrice(uint256 _amount) public view returns (uint256) {
return (getTimeWeightedAveragePrice(_amount) * discount) / 100;
}
function getVeDiscountedPrice(
uint256 _amount
) public view returns (uint256) {
return (getTimeWeightedAveragePrice(_amount) * veDiscount) / 100;
}
function getTimeWeightedAveragePrice(
uint256 _amount
) public view returns (uint256) {
return
twapOracle.estimateAmountOut(
address(underlyingToken),
uint128(_amount),
twapSeconds
);
}
function addGaugeFactory(address _gaugeFactory) public onlyAdmin {
_grantRole(ADMIN_ROLE, _gaugeFactory);
}
function setTwapOracleAndPaymentToken(
IUniswapV3Twap _twapOracle,
address _paymentToken
) external onlyAdmin {
if (
!((_twapOracle.token0() == _paymentToken &&
_twapOracle.token1() == address(underlyingToken)) ||
(_twapOracle.token0() == address(underlyingToken) &&
_twapOracle.token1() == _paymentToken))
) revert OptionToken_IncorrectPairToken();
twapOracle = _twapOracle;
paymentToken = ERC20(_paymentToken);
paymentToken.approve(address(feeDistributor), type(uint256).max);
emit SetTwapOracleAndPaymentToken(_twapOracle, _paymentToken);
}
function setFeeDistributor(
IOptionFeeDistributor _feeDistributor
) external onlyAdmin {
feeDistributor = _feeDistributor;
paymentToken.approve(address(_feeDistributor), type(uint256).max);
emit SetFeeDistributor(_feeDistributor);
}
function setVoterAndDistributor(
IVoter _voter,
IRewardsDistributor _rewardsDistributor
) external onlyAdmin {
voter = _voter;
rewardsDistributor = _rewardsDistributor;
}
function setFeeConfig(address _feeReceiver, uint256 _fee) external onlyAdmin {
feeReceiver = _feeReceiver;
fee = _fee;
}
function updateApproval() external onlyAdmin {
underlyingToken.approve(votingEscrow, type(uint256).max);
}
function setDiscount(uint256 _discount) external onlyAdmin {
if (_discount > MAX_DISCOUNT || _discount == MIN_DISCOUNT)
revert OptionToken_InvalidDiscount();
discount = _discount;
emit SetDiscount(_discount);
}
function setVeDiscount(uint256 _veDiscount) external onlyAdmin {
if (_veDiscount > MAX_DISCOUNT || _veDiscount == MIN_DISCOUNT)
revert OptionToken_InvalidDiscount();
veDiscount = _veDiscount;
emit SetVeDiscount(_veDiscount);
}
function setTwapSeconds(uint32 _twapSeconds) external onlyAdmin {
if (_twapSeconds > MAX_TWAP_SECONDS || _twapSeconds == 0)
revert OptionToken_InvalidTwapSeconds();
twapSeconds = _twapSeconds;
emit SetTwapSeconds(_twapSeconds);
}
function mint(address _to, uint256 _amount) external nonReentrant {
if (isPaused) revert OptionToken_Paused();
uint256 totalBlue = getVeBalance();
uint256 totalShares = totalSupply();
uint256 _fee;
if(feeReceiver != address(0) && !voter.isGauge(msg.sender)){
_fee = _amount * fee / feeDenominator;
underlyingToken.transferFrom(msg.sender, feeReceiver, _fee);
_amount = _amount - _fee;
}
if(totalBlue == 0 || totalShares == 0){
_mint(_to, _amount);
}else{
uint256 what = _amount * totalShares / totalBlue;
_mint(_to, what);
}
underlyingToken.transferFrom(msg.sender, address(this), _amount);
if(veNftId == 0){
veNftId = IVotingEscrow(votingEscrow).create_lock(underlyingToken.balanceOf(address(this)), FULL_LOCK);
}else{
IVotingEscrow(votingEscrow).increase_amount(veNftId, _amount);
}
_vote();
}
function donate(uint256 _amount) external nonReentrant {
require(veNftId != 0, "no venftId");
underlyingToken.transferFrom(msg.sender, address(this), _amount);
IVotingEscrow(votingEscrow).increase_amount(veNftId, _amount);
_vote();
}
function getVeBalance() public view returns(uint256) {
if(veNftId == 0){
return 0;
}
return uint(int256(IVotingEscrow(votingEscrow).locked(veNftId).amount));
}
function burn(uint256 _amount) external onlyAdmin nonReentrant {
uint256 totalShares = totalSupply();
uint256 what = _amount * getVeBalance() / totalShares;
voter.reset(veNftId);
IVotingEscrow(votingEscrow).withdraw(veNftId);
_burn(msg.sender, _amount);
underlyingToken.transfer(msg.sender, what);
veNftId = IVotingEscrow(votingEscrow).create_lock(underlyingToken.balanceOf(address(this)), FULL_LOCK);
_vote();
}
function claimRebase() external onlyAdmin nonReentrant {
rewardsDistributor.claim(veNftId);
}
function vote(address[] calldata _poolVote, uint256[] calldata _weights) external onlyVoter {
_savedPoolVote[voter._epochTimestamp()] = _poolVote;
_savedWeights[voter._epochTimestamp()] = _weights;
_vote();
}
function clearStorage(uint256 epochTimestamp) external onlyVoter {
delete _savedPoolVote[epochTimestamp];
delete _savedWeights[epochTimestamp];
}
function _vote() internal {
if(_savedPoolVote[voter._epochTimestamp()].length > 0 && _savedPoolVote[voter._epochTimestamp()].length == _savedWeights[voter._epochTimestamp()].length){
voter.vote(veNftId, _savedPoolVote[voter._epochTimestamp()], _savedWeights[voter._epochTimestamp()]);
}
}
function sendRewards(address[][] calldata tokens_, address _to) internal {
for (uint256 i = 0; i < tokens_.length; ) {
for (uint256 j = 0; j < tokens_[i].length; ) {
IERC20 token = IERC20(tokens_[i][j]);
token.safeTransfer(_to, token.balanceOf(address(this)));
unchecked {
j++;
}
}
unchecked {
i++;
}
}
}
function claimBribes(address[] calldata bribes_, address[][] calldata bribeTokens_, address _to) external onlyAdmin {
if (bribes_.length != bribeTokens_.length) {
revert InvalidArrayLength();
}
voter.claimBribes(bribes_, bribeTokens_, veNftId);
sendRewards(bribeTokens_, _to);
}
function unPause() external onlyAdmin {
if (!isPaused) return;
isPaused = false;
emit PauseStateChanged(false);
}
function pause() external onlyPauser {
if (isPaused) return;
isPaused = true;
emit PauseStateChanged(true);
}
function _exercise(
uint256 _amount,
uint256 _maxPaymentAmount,
address _recipient
) internal returns (uint256 paymentAmount) {
if (isPaused) revert OptionToken_Paused();
uint256 totalShares = totalSupply();
uint256 what = _amount * getVeBalance() / totalShares;
voter.reset(veNftId);
IVotingEscrow(votingEscrow).withdraw(veNftId);
_burn(msg.sender, _amount);
if(discount > 0){
paymentAmount = getDiscountedPrice(what);
if (paymentAmount > _maxPaymentAmount)
revert OptionToken_SlippageTooHigh();
paymentToken.transferFrom(msg.sender, address(this), paymentAmount);
feeDistributor.distribute(address(paymentToken), paymentAmount);
}
underlyingToken.transfer(_recipient, what);
veNftId = IVotingEscrow(votingEscrow).create_lock(underlyingToken.balanceOf(address(this)), FULL_LOCK);
_vote();
emit Exercise(msg.sender, _recipient, what, paymentAmount);
}
function _exerciseVe(
uint256 _amount,
uint256 _maxPaymentAmount,
address _recipient
) internal returns (uint256 paymentAmount, uint256 nftId) {
if (isPaused) revert OptionToken_Paused();
uint256 totalShares = totalSupply();
uint256 what = _amount * getVeBalance() / totalShares;
voter.reset(veNftId);
IVotingEscrow(votingEscrow).withdraw(veNftId);
_burn(msg.sender, _amount);
if(veDiscount > 0){
paymentAmount = getVeDiscountedPrice(what);
if (paymentAmount > _maxPaymentAmount)
revert OptionToken_SlippageTooHigh();
paymentToken.transferFrom(msg.sender, address(this), paymentAmount);
feeDistributor.distribute(address(paymentToken), paymentAmount);
}
nftId = IVotingEscrow(votingEscrow).create_lock_for(
what,
FULL_LOCK,
_recipient
);
veNftId = IVotingEscrow(votingEscrow).create_lock(underlyingToken.balanceOf(address(this)), FULL_LOCK);
_vote();
emit ExerciseVe(msg.sender, _recipient, what, paymentAmount, nftId);
}
}
文件 18 的 21:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
}
文件 19 的 21:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
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));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit 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");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 20 的 21:Strings.sol
pragma solidity ^0.8.0;
import "./math/Math.sol";
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
文件 21 的 21:draft-IERC20Permit.sol
pragma solidity ^0.8.0;
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);
}
{
"compilationTarget": {
"contracts/OptionTokenV2.sol": "OptionTokenV2"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 100
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"contract ERC20","name":"_paymentToken","type":"address"},{"internalType":"contract ERC20","name":"_underlyingToken","type":"address"},{"internalType":"contract IUniswapV3Twap","name":"_twapOracle","type":"address"},{"internalType":"contract IOptionFeeDistributor","name":"_feeDistributor","type":"address"},{"internalType":"uint256","name":"_discount","type":"uint256"},{"internalType":"uint256","name":"_veDiscount","type":"uint256"},{"internalType":"address","name":"_votingEscrow","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidArrayLength","type":"error"},{"inputs":[],"name":"OptionToken_IncorrectPairToken","type":"error"},{"inputs":[],"name":"OptionToken_InvalidDiscount","type":"error"},{"inputs":[],"name":"OptionToken_InvalidTwapSeconds","type":"error"},{"inputs":[],"name":"OptionToken_NoAdminRole","type":"error"},{"inputs":[],"name":"OptionToken_NoPauserRole","type":"error"},{"inputs":[],"name":"OptionToken_NoVoterRole","type":"error"},{"inputs":[],"name":"OptionToken_PastDeadline","type":"error"},{"inputs":[],"name":"OptionToken_Paused","type":"error"},{"inputs":[],"name":"OptionToken_SlippageTooHigh","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"Exercise","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paymentAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"ExerciseVe","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"discount","type":"uint256"}],"name":"SetDiscount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IOptionFeeDistributor","name":"newFeeDistributor","type":"address"}],"name":"SetFeeDistributor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IUniswapV3Twap","name":"_twapOracle","type":"address"},{"indexed":true,"internalType":"address","name":"_paymentToken","type":"address"}],"name":"SetTwapOracleAndPaymentToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"twapSeconds","type":"uint32"}],"name":"SetTwapSeconds","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veDiscount","type":"uint256"}],"name":"SetVeDiscount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FULL_LOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_DISCOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TWAP_SECONDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_DISCOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"_savedPoolVote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"_savedWeights","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_gaugeFactory","type":"address"}],"name":"addGaugeFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"bribes_","type":"address[]"},{"internalType":"address[][]","name":"bribeTokens_","type":"address[][]"},{"internalType":"address","name":"_to","type":"address"}],"name":"claimBribes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimRebase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epochTimestamp","type":"uint256"}],"name":"clearStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"discount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"donate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_maxPaymentAmount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"exercise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_maxPaymentAmount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"exercise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_maxPaymentAmount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"exerciseVe","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeDenominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeDistributor","outputs":[{"internalType":"contract IOptionFeeDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getDiscountedPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getTimeWeightedAveragePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVeBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getVeDiscountedPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paymentToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsDistributor","outputs":[{"internalType":"contract IRewardsDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_discount","type":"uint256"}],"name":"setDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeReceiver","type":"address"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFeeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOptionFeeDistributor","name":"_feeDistributor","type":"address"}],"name":"setFeeDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IUniswapV3Twap","name":"_twapOracle","type":"address"},{"internalType":"address","name":"_paymentToken","type":"address"}],"name":"setTwapOracleAndPaymentToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_twapSeconds","type":"uint32"}],"name":"setTwapSeconds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_veDiscount","type":"uint256"}],"name":"setVeDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IVoter","name":"_voter","type":"address"},{"internalType":"contract IRewardsDistributor","name":"_rewardsDistributor","type":"address"}],"name":"setVoterAndDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"twapOracle","outputs":[{"internalType":"contract IUniswapV3Twap","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"twapSeconds","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlyingToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"veDiscount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"veNftId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_poolVote","type":"address[]"},{"internalType":"uint256[]","name":"_weights","type":"uint256[]"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"contract IVoter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingEscrow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]