编译器
0.8.17+commit.8df45f5f
文件 1 的 16: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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 16: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;
}
}
文件 3 的 16: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;
_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 {}
}
文件 4 的 16:GMXARBNeutralVault.sol
pragma solidity 0.8.17;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../../../interfaces/vaults/gmx/v2/arb/IGMXARBNeutralManager.sol";
import "../../../../interfaces/vaults/gmx/v2/arb/IGMXARBNeutralReader.sol";
import "../../../../interfaces/oracles/IChainlinkOracle.sol";
import "../../../../interfaces/tokens/IStakedGLP.sol";
import "../../../../enum/ManagerAction.sol";
contract GMXARBNeutralVault is ERC20, Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
IGMXARBNeutralManager public manager;
IGMXARBNeutralReader public reader;
VaultConfig public vaultConfig;
address public treasury;
uint256 public lastFeeCollected;
struct VaultConfig {
uint256 targetLeverage;
uint256 mgmtFeePerSecond;
uint256 perfFee;
uint256 maxCapacity;
}
uint256 public constant SAFE_MULTIPLIER = 1e18;
uint256 public constant DUST_AMOUNT = 1e17;
uint256 public constant USDC_DECIMALS = 6;
uint256 public constant WBTC_DECIMALS = 8;
address public constant WETH = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
address public constant WBTC = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
address public constant USDC = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8;
address public constant sGLP = 0x5402B5F40310bDED796c7D0F3FF6683f5C0cFfdf;
mapping(address => bool) public keepers;
modifier onlyKeeper() {
require(keepers[msg.sender], "Keeper not approved");
_;
}
event Deposit(address indexed user, address asset, uint256 assetAmt, uint256 sharesAmt);
event Withdraw(address indexed user, address asset, uint256 assetAmt, uint256 sharesAmt);
event UpdateVaultConfig(VaultConfig _vaultConfig);
constructor (
string memory _name,
string memory _symbol,
VaultConfig memory _vaultConfig,
address _treasury
) ERC20(_name, _symbol) {
vaultConfig = _vaultConfig;
treasury = _treasury;
lastFeeCollected = block.timestamp;
}
function svTokenValue() public view returns (uint256) {
uint256 equityValue = reader.equityValue();
if (equityValue == 0 || totalSupply() == 0) return SAFE_MULTIPLIER;
return equityValue * SAFE_MULTIPLIER / totalSupply();
}
function pendingMgmtFee() public view returns (uint256) {
uint256 secondsFromLastCollection = block.timestamp - lastFeeCollected;
return (totalSupply() * vaultConfig.mgmtFeePerSecond * secondsFromLastCollection) / SAFE_MULTIPLIER;
}
function valueToShares(uint256 _value, uint256 _currentEquity) public view returns (uint256) {
uint256 _sharesSupply = totalSupply() + pendingMgmtFee();
if (_sharesSupply == 0) return _value;
return _value * _sharesSupply / _currentEquity;
}
function deposit(address _token, uint256 _amt, uint256 _minSharesAmt) external nonReentrant whenNotPaused {
require(_amt > 0, "Amt must be > 0");
require(_token == USDC || _token == sGLP, "Invalid token");
mintMgmtFee();
uint256 glpPrice = reader.glpPrice(false);
uint256 depositValue;
uint256 equityBefore;
if (_token == USDC) {
depositValue = reader.tokenValue(USDC, _amt);
equityBefore = reader.assetValueWithPrice(glpPrice) - reader.debtValues();
IERC20(USDC).safeTransferFrom(msg.sender, address(manager), _amt);
} else {
depositValue = _amt * glpPrice / SAFE_MULTIPLIER;
equityBefore = reader.assetValueWithPrice(glpPrice) - reader.debtValues();
(bool success) = IStakedGLP(sGLP).transferFrom(msg.sender, address(manager), _amt);
require(success, "Transfer sGLP failed");
}
_deposit(_token, _amt, _minSharesAmt, glpPrice, depositValue, equityBefore);
}
function withdraw(address _token, uint256 _shareAmt, uint256 _minWithdrawAmt) external nonReentrant whenNotPaused {
require(_token == USDC || _token == sGLP, "Invalid token");
_burnAndWork(_token, _shareAmt);
uint256 withdrawAmt;
if (_token == USDC) {
withdrawAmt = IERC20(USDC).balanceOf(address(this));
IERC20(USDC).safeTransfer(msg.sender, withdrawAmt);
} else {
withdrawAmt = IStakedGLP(sGLP).balanceOf(address(this));
(bool success) = IStakedGLP(sGLP).transfer(msg.sender, withdrawAmt);
require(success, "Transfer sGLP failed");
}
require(withdrawAmt >= _minWithdrawAmt, "Assets received less than minimum");
emit Withdraw(
msg.sender,
USDC,
withdrawAmt,
_shareAmt
);
}
function emergencyWithdraw(uint256 _shareAmt) external nonReentrant whenPaused {
require(_shareAmt > 0, "Quantity must be > 0");
require(_shareAmt <= balanceOf(msg.sender), "Withdraw amt exceeds balance");
unchecked {
if (balanceOf(msg.sender) - _shareAmt < DUST_AMOUNT) {
_shareAmt = balanceOf(msg.sender);
}
}
uint256 shareRatio = _shareAmt * SAFE_MULTIPLIER / totalSupply();
_burn(msg.sender, _shareAmt);
uint256 withdrawAmt = shareRatio * IERC20(USDC).balanceOf(address(this)) / SAFE_MULTIPLIER;
IERC20(USDC).safeTransfer(msg.sender, withdrawAmt);
emit Withdraw(
msg.sender,
USDC,
withdrawAmt,
_shareAmt
);
}
function mintMgmtFee() public {
_mint(treasury, pendingMgmtFee());
lastFeeCollected = block.timestamp;
}
function _deposit(
address _token,
uint256 _amt,
uint256 _minSharesAmt,
uint256 _glpPrice,
uint256 _depositValue,
uint256 _equityBefore
) internal {
require(_depositValue >= DUST_AMOUNT, "Deposit value too low");
require(_equityBefore + _depositValue <= vaultConfig.maxCapacity, "Exceeded capacity");
require(_depositValue < reader.additionalCapacity(), "Insufficient lending liquidity");
uint256 totalValue = _depositValue
* vaultConfig.targetLeverage
/ SAFE_MULTIPLIER;
uint256 borrowWETHValue = totalValue
* reader.currentTokenWeight(WETH)
/ SAFE_MULTIPLIER;
uint256 borrowWBTCValue = totalValue
* reader.currentTokenWeight(WBTC)
/ SAFE_MULTIPLIER;
uint256 borrowUSDCValue = totalValue - _depositValue - borrowWETHValue - borrowWBTCValue;
IGMXARBNeutralManager.WorkData memory data = IGMXARBNeutralManager.WorkData(
{
token: _token,
lpAmt: 0,
borrowWETHAmt: borrowWETHValue * SAFE_MULTIPLIER / reader.tokenValue(WETH, 1e18),
borrowWBTCAmt: borrowWBTCValue * WBTC_DECIMALS / reader.tokenValue(WBTC, WBTC_DECIMALS),
borrowUSDCAmt: borrowUSDCValue * USDC_DECIMALS / reader.tokenValue(USDC, USDC_DECIMALS),
repayWETHAmt: 0,
repayWBTCAmt: 0,
repayUSDCAmt: 0
}
);
manager.work(
ManagerAction.Deposit,
data
);
uint256 _equityChange = (reader.assetValueWithPrice(_glpPrice) - reader.debtValues()) - _equityBefore;
uint256 sharesToUser = valueToShares(_equityChange, _equityBefore);
require(sharesToUser >= _minSharesAmt, "Shares received less than minimum");
_mint(msg.sender, sharesToUser);
emit Deposit(
msg.sender,
USDC,
_amt,
sharesToUser
);
}
function _burnAndWork(address _token, uint256 _shareAmt) internal {
require(_shareAmt > 0, "Quantity must be > 0");
require(_shareAmt <= balanceOf(msg.sender), "Withdraw amt exceeds balance");
mintMgmtFee();
unchecked {
if (balanceOf(msg.sender) - _shareAmt < DUST_AMOUNT) {
_shareAmt = balanceOf(msg.sender);
}
}
uint256 shareRatio = _shareAmt * SAFE_MULTIPLIER / totalSupply();
_burn(msg.sender, _shareAmt);
(uint256 debtWETH, uint256 debtWBTC, uint256 debtUSDC) = manager.debtAmts();
IGMXARBNeutralManager.WorkData memory data = IGMXARBNeutralManager.WorkData(
{
token: _token,
lpAmt: shareRatio * manager.lpAmt() / SAFE_MULTIPLIER,
borrowWETHAmt: 0,
borrowWBTCAmt: 0,
borrowUSDCAmt: 0,
repayWETHAmt: shareRatio * debtWETH / SAFE_MULTIPLIER,
repayWBTCAmt: shareRatio * debtWBTC / SAFE_MULTIPLIER,
repayUSDCAmt: shareRatio * debtUSDC / SAFE_MULTIPLIER
}
);
manager.work(
ManagerAction.Withdraw,
data
);
}
function updateVaultConfig(VaultConfig memory _vaultConfig) external onlyOwner {
vaultConfig = _vaultConfig;
emit UpdateVaultConfig(_vaultConfig);
}
function updateAddress(uint256 _type, address _addr) external onlyOwner {
if (_type == 1) {
manager = IGMXARBNeutralManager(_addr);
}
if (_type == 2) {
reader = IGMXARBNeutralReader(_addr);
}
if (_type == 3) {
treasury = _addr;
}
}
function updateKeeper(address _keeper, bool _approval) external onlyOwner {
keepers[_keeper] = _approval;
}
function emergencyShutDown() external onlyOwner whenNotPaused {
_pause();
(uint256 debtWETH, uint256 debtWBTC, uint256 debtUSDC) = manager.debtAmts();
IGMXARBNeutralManager.WorkData memory data = IGMXARBNeutralManager.WorkData(
{
token: USDC,
lpAmt: manager.lpAmt(),
borrowWETHAmt: 0,
borrowWBTCAmt: 0,
borrowUSDCAmt: 0,
repayWETHAmt: debtWETH,
repayWBTCAmt: debtWBTC,
repayUSDCAmt: debtUSDC
}
);
manager.work(
ManagerAction.Withdraw,
data
);
}
function togglePause() external onlyKeeper {
if (!paused()) {
_pause();
} else {
_unpause();
}
}
}
文件 5 的 16:IChainlinkOracle.sol
pragma solidity 0.8.17;
interface IChainlinkOracle {
function consult(address _token) external view returns (int256 price, uint8 decimals);
function consultIn18Decimals(address _token) external view returns (uint256 price);
function addTokenPriceFeed(address _token, address _feed) external;
function addTokenMaxDelay(address _token, uint256 _maxDelay) external;
function addTokenMaxDeviation(address _token, uint256 _maxDeviation) external;
function emergencyPause() external;
function emergencyResume() external;
}
文件 6 的 16: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);
}
文件 7 的 16: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);
}
文件 8 的 16:IGMXARBNeutralManager.sol
pragma solidity 0.8.17;
import "../../../../../enum/ManagerAction.sol";
interface IGMXARBNeutralManager {
struct WorkData {
address token;
uint256 lpAmt;
uint256 borrowWETHAmt;
uint256 borrowWBTCAmt;
uint256 borrowUSDCAmt;
uint256 repayWETHAmt;
uint256 repayWBTCAmt;
uint256 repayUSDCAmt;
}
function debtAmts() external view returns (uint256, uint256, uint256);
function debtAmt(address _token) external view returns (uint256);
function lpAmt() external view returns (uint256);
function work(
ManagerAction _action,
WorkData calldata _workData
) external;
function lendingPoolWETH() external view returns (address);
function lendingPoolWBTC() external view returns (address);
function lendingPoolUSDC() external view returns (address);
function stakePool() external view returns (address);
function compound() external;
function updateKeeper(address _keeper, bool _approval) external;
}
文件 9 的 16:IGMXARBNeutralReader.sol
pragma solidity 0.8.17;
interface IGMXARBNeutralReader {
function svTokenValue() external view returns (uint256);
function assetValue() external view returns (uint256);
function assetValueWithPrice(uint256 _glpPrice) external view returns (uint256);
function debtValues() external view returns (uint256);
function debtValue(address _token) external view returns (uint256);
function equityValue() external view returns (uint256);
function assetAmt() external view returns (address[] memory, uint256[] memory);
function debtAmt(address _token) external view returns (uint256);
function debtAmts() external view returns (uint256, uint256, uint256);
function lpAmt() external view returns (uint256);
function leverage() external view returns (uint256);
function debtRatio() external view returns (uint256);
function delta(address _token) external view returns (int256);
function tokenValue(address _token, uint256 _amt) external view returns (uint256);
function glpPrice(bool _bool) external view returns (uint256);
function currentTokenWeight(address _token) external view returns (uint256);
function currentTokenWeights() external view returns (address[] memory, uint256[] memory);
function additionalCapacity() external view returns (uint256);
function capacity() external view returns (uint256);
}
文件 10 的 16:IStakedGLP.sol
pragma solidity 0.8.17;
interface IStakedGLP {
function approve(address _spender, uint256 _amount) external returns (bool);
function transfer(address _recipient, uint256 _amount) external returns (bool);
function transferFrom(
address _sender,
address _recipient,
uint256 _amount
) external returns (bool);
function balanceOf(address _account) external view returns (uint256);
function totalSupply() external view returns (uint256);
}
文件 11 的 16:ManagerAction.sol
pragma solidity 0.8.17;
enum ManagerAction {
Deposit,
Withdraw,
AddLiquidity,
RemoveLiquidity
}
文件 12 的 16:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 13 的 16:Pausable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Pausable is Context {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 14 的 16: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() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 15 的 16: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");
}
}
}
文件 16 的 16: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/vaults/gmx/v2/arb/GMXARBNeutralVault.sol": "GMXARBNeutralVault"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"components":[{"internalType":"uint256","name":"targetLeverage","type":"uint256"},{"internalType":"uint256","name":"mgmtFeePerSecond","type":"uint256"},{"internalType":"uint256","name":"perfFee","type":"uint256"},{"internalType":"uint256","name":"maxCapacity","type":"uint256"}],"internalType":"struct GMXARBNeutralVault.VaultConfig","name":"_vaultConfig","type":"tuple"},{"internalType":"address","name":"_treasury","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"user","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesAmt","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"targetLeverage","type":"uint256"},{"internalType":"uint256","name":"mgmtFeePerSecond","type":"uint256"},{"internalType":"uint256","name":"perfFee","type":"uint256"},{"internalType":"uint256","name":"maxCapacity","type":"uint256"}],"indexed":false,"internalType":"struct GMXARBNeutralVault.VaultConfig","name":"_vaultConfig","type":"tuple"}],"name":"UpdateVaultConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesAmt","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DUST_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SAFE_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WBTC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WBTC_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[],"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":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amt","type":"uint256"},{"internalType":"uint256","name":"_minSharesAmt","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyShutDown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shareAmt","type":"uint256"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"","type":"address"}],"name":"keepers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastFeeCollected","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"contract IGMXARBNeutralManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintMgmtFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingMgmtFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reader","outputs":[{"internalType":"contract IGMXARBNeutralReader","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sGLP","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"svTokenValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"togglePause","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_type","type":"uint256"},{"internalType":"address","name":"_addr","type":"address"}],"name":"updateAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"bool","name":"_approval","type":"bool"}],"name":"updateKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"targetLeverage","type":"uint256"},{"internalType":"uint256","name":"mgmtFeePerSecond","type":"uint256"},{"internalType":"uint256","name":"perfFee","type":"uint256"},{"internalType":"uint256","name":"maxCapacity","type":"uint256"}],"internalType":"struct GMXARBNeutralVault.VaultConfig","name":"_vaultConfig","type":"tuple"}],"name":"updateVaultConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_currentEquity","type":"uint256"}],"name":"valueToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultConfig","outputs":[{"internalType":"uint256","name":"targetLeverage","type":"uint256"},{"internalType":"uint256","name":"mgmtFeePerSecond","type":"uint256"},{"internalType":"uint256","name":"perfFee","type":"uint256"},{"internalType":"uint256","name":"maxCapacity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_shareAmt","type":"uint256"},{"internalType":"uint256","name":"_minWithdrawAmt","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]