编译器
0.8.18+commit.87f61d96
文件 1 的 18: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 的 18:GPv2Order.sol
pragma solidity ^0.8.18;
import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
library GPv2Order {
struct Data {
IERC20 sellToken;
IERC20 buyToken;
address receiver;
uint256 sellAmount;
uint256 buyAmount;
uint32 validTo;
bytes32 appData;
uint256 feeAmount;
bytes32 kind;
bool partiallyFillable;
bytes32 sellTokenBalance;
bytes32 buyTokenBalance;
}
bytes32 internal constant TYPE_HASH =
hex"d5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e489";
bytes32 internal constant KIND_SELL =
hex"f3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775";
bytes32 internal constant KIND_BUY =
hex"6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc";
bytes32 internal constant BALANCE_ERC20 =
hex"5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9";
bytes32 internal constant BALANCE_EXTERNAL =
hex"abee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632";
bytes32 internal constant BALANCE_INTERNAL =
hex"4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce";
address internal constant RECEIVER_SAME_AS_OWNER = address(0);
uint256 internal constant UID_LENGTH = 56;
function actualReceiver(Data memory order, address owner)
internal
pure
returns (address receiver)
{
if (order.receiver == RECEIVER_SAME_AS_OWNER) {
receiver = owner;
} else {
receiver = order.receiver;
}
}
function hash(Data memory order, bytes32 domainSeparator)
internal
pure
returns (bytes32 orderDigest)
{
bytes32 structHash;
assembly {
let dataStart := sub(order, 32)
let temp := mload(dataStart)
mstore(dataStart, TYPE_HASH)
structHash := keccak256(dataStart, 416)
mstore(dataStart, temp)
}
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, "\x19\x01")
mstore(add(freeMemoryPointer, 2), domainSeparator)
mstore(add(freeMemoryPointer, 34), structHash)
orderDigest := keccak256(freeMemoryPointer, 66)
}
}
function packOrderUidParams(
bytes memory orderUid,
bytes32 orderDigest,
address owner,
uint32 validTo
) internal pure {
require(orderUid.length == UID_LENGTH, "GPv2: uid buffer overflow");
assembly {
mstore(add(orderUid, 56), validTo)
mstore(add(orderUid, 52), owner)
mstore(add(orderUid, 32), orderDigest)
}
}
function extractOrderUidParams(bytes calldata orderUid)
internal
pure
returns (
bytes32 orderDigest,
address owner,
uint32 validTo
)
{
require(orderUid.length == UID_LENGTH, "GPv2: invalid uid");
assembly {
orderDigest := calldataload(orderUid.offset)
owner := shr(96, calldataload(add(orderUid.offset, 32)))
validTo := shr(224, calldataload(add(orderUid.offset, 52)))
}
}
}
文件 3 的 18:ICowSettlement.sol
pragma solidity >=0.5.0;
import "./IERC20Detailed.sol";
interface ICowSettlement {
struct GPv2TradeData {
uint256 sellTokenIndex;
uint256 buyTokenIndex;
address receiver;
uint256 sellAmount;
uint256 buyAmount;
uint32 validTo;
bytes32 appData;
uint256 feeAmount;
uint256 flags;
uint256 executedAmount;
bytes signature;
}
struct GPv2InteractionData {
address target;
uint256 value;
bytes callData;
}
function setPreSignature(bytes calldata orderUid, bool signed) external;
function invalidateOrder(bytes calldata orderUid) external;
function filledAmount(bytes calldata orderUid) external view returns (uint256);
function vaultRelayer() external view returns (address);
function domainSeparator() external view returns (bytes32);
function settle(
IERC20Detailed[] calldata tokens,
uint256[] calldata clearingPrices,
GPv2TradeData[] calldata trades,
GPv2InteractionData[][3] calldata interactions
) external;
function preSignature(bytes memory) external returns (uint);
}
文件 4 的 18: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);
}
文件 5 的 18:IERC20Detailed.sol
pragma solidity >=0.5.0;
interface IERC20Detailed {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
}
文件 6 的 18:IEnzymeFundValueCalculatorRouter.sol
pragma solidity >=0.5.0;
interface IEnzymeFundValueCalculatorRouter {
function calcGrossShareValue(address _vaultProxy)
external
returns (address denominationAsset_, uint256 grossShareValue_);
function calcNetShareValue(address _vaultProxy)
external
returns (address denominationAsset_, uint256 netShareValue_);
}
文件 7 的 18:IEnzymePolicyManager.sol
pragma solidity >=0.5.0;
interface IPolicyManager {
function updatePolicySettingsForFund(
address _comptrollerProxy,
address _policy,
bytes calldata _settingsData
) external;
}
文件 8 的 18:IEnzymeV4Comptroller.sol
pragma solidity >=0.5.0;
interface IEnzymeV4Comptroller {
function getDenominationAsset() external view returns (address denominationAsset_);
function redeemSharesForSpecificAssets(
address _recipient,
uint256 _sharesQuantity,
address[] calldata _payoutAssets,
uint256[] calldata _payoutAssetPercentages
) external returns (uint256[] memory payoutAmounts_);
function vaultCallOnContract(
address _contract,
bytes4 _selector,
bytes calldata _encodedArgs
) external;
function buyShares(uint _investmentAmount, uint _minSharesQuantity) external;
}
文件 9 的 18:IEnzymeV4Vault.sol
pragma solidity >=0.5.0;
interface IEnzymeV4Vault {
function getAccessor() external view returns (address);
function getOwner() external view returns (address);
function mintShares(address, uint256) external;
}
文件 10 的 18:INXMMaster.sol
pragma solidity >=0.5.0;
interface INXMMaster {
function tokenAddress() external view returns (address);
function owner() external view returns (address);
function emergencyAdmin() external view returns (address);
function masterInitialized() external view returns (bool);
function isInternal(address _add) external view returns (bool);
function isPause() external view returns (bool check);
function isMember(address _add) external view returns (bool);
function checkIsAuthToGoverned(address _add) external view returns (bool);
function getLatestAddress(bytes2 _contractName) external view returns (address payable contractAddress);
function contractAddresses(bytes2 code) external view returns (address payable);
function upgradeMultipleContracts(
bytes2[] calldata _contractCodes,
address payable[] calldata newAddresses
) external;
function removeContracts(bytes2[] calldata contractCodesToRemove) external;
function addNewInternalContracts(
bytes2[] calldata _contractCodes,
address payable[] calldata newAddresses,
uint[] calldata _types
) external;
function updateOwnerParameters(bytes8 code, address payable val) external;
}
文件 11 的 18:IPool.sol
pragma solidity >=0.5.0;
import "./IPriceFeedOracle.sol";
struct SwapDetails {
uint104 minAmount;
uint104 maxAmount;
uint32 lastSwapTime;
uint16 maxSlippageRatio;
}
struct Asset {
address assetAddress;
bool isCoverAsset;
bool isAbandoned;
}
interface IPool {
error RevertedWithoutReason(uint index);
error AssetNotFound();
error UnknownParameter();
error OrderInProgress();
function swapOperator() external view returns (address);
function getAsset(uint assetId) external view returns (Asset memory);
function getAssets() external view returns (Asset[] memory);
function transferAssetToSwapOperator(address asset, uint amount) external;
function setSwapDetailsLastSwapTime(address asset, uint32 lastSwapTime) external;
function getAssetSwapDetails(address assetAddress) external view returns (SwapDetails memory);
function sendPayout(uint assetIndex, address payable payoutAddress, uint amount, uint ethDepositAmount) external;
function sendEth(address payoutAddress, uint amount) external;
function upgradeCapitalPool(address payable newPoolAddress) external;
function priceFeedOracle() external view returns (IPriceFeedOracle);
function getPoolValueInEth() external view returns (uint);
function calculateMCRRatio(uint totalAssetValue, uint mcrEth) external pure returns (uint);
function getInternalTokenPriceInAsset(uint assetId) external view returns (uint tokenPrice);
function getInternalTokenPriceInAssetAndUpdateTwap(uint assetId) external returns (uint tokenPrice);
function getTokenPrice() external view returns (uint tokenPrice);
function getMCRRatio() external view returns (uint);
function setSwapAssetAmount(address assetAddress, uint value) external;
}
文件 12 的 18:IPriceFeedOracle.sol
pragma solidity >=0.5.0;
interface Aggregator {
function decimals() external view returns (uint8);
function latestAnswer() external view returns (int);
}
interface IPriceFeedOracle {
struct OracleAsset {
Aggregator aggregator;
uint8 decimals;
}
function ETH() external view returns (address);
function assets(address) external view returns (Aggregator, uint8);
function getAssetToEthRate(address asset) external view returns (uint);
function getAssetForEth(address asset, uint ethIn) external view returns (uint);
function getEthForAsset(address asset, uint amount) external view returns (uint);
}
文件 13 的 18:ISafeTracker.sol
pragma solidity >=0.5.0;
import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
interface ISafeTracker is IERC20 {
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function safe() external view returns (address);
event CoverReInvestmentUSDCUpdated(uint investedUSDC);
error OnlySafe();
error InvestmentSurpassesLimit();
}
文件 14 的 18:ISwapOperator.sol
pragma solidity >=0.5.0;
import "../external/cow/GPv2Order.sol";
import "../external/enzyme/IEnzymeFundValueCalculatorRouter.sol";
import "./ICowSettlement.sol";
import "./INXMMaster.sol";
import "./IPool.sol";
import "./IWeth.sol";
interface ISwapOperator {
enum SwapOperationType {
EthToAsset,
AssetToEth,
AssetToAsset
}
function getDigest(GPv2Order.Data calldata order) external view returns (bytes32);
function getUID(GPv2Order.Data calldata order) external view returns (bytes memory);
function orderInProgress() external view returns (bool);
function currentOrderUID() external view returns (bytes memory);
function cowSettlement() external view returns (ICowSettlement);
function cowVaultRelayer() external view returns (address);
function master() external view returns (INXMMaster);
function swapController() external view returns (address);
function weth() external view returns (IWeth);
function domainSeparator() external view returns (bytes32);
function enzymeV4VaultProxyAddress() external view returns (address);
function enzymeFundValueCalculatorRouter() external view returns (IEnzymeFundValueCalculatorRouter);
function minPoolEth() external view returns (uint);
function placeOrder(GPv2Order.Data calldata order, bytes calldata orderUID) external;
function closeOrder(GPv2Order.Data calldata order) external;
function swapEnzymeVaultShareForETH(uint amountIn, uint amountOutMin) external;
function swapETHForEnzymeVaultShare(uint amountIn, uint amountOutMin) external;
function recoverAsset(address assetAddress, address receiver) external;
function requestAsset(address asset, uint amount) external;
function transferRequestedAsset(address requestedAsset, uint requestedAmount) external;
event OrderPlaced(GPv2Order.Data order);
event OrderClosed(GPv2Order.Data order, uint filledAmount);
event Swapped(address indexed fromAsset, address indexed toAsset, uint amountIn, uint amountOut);
event TransferredToSafe(address asset, uint amount);
error OrderInProgress(bytes currentOrderUID);
error NoOrderInPlace();
error OrderUidMismatch(bytes providedOrderUID, bytes expectedOrderUID);
error UnsupportedTokenBalance(string kind);
error InvalidReceiver(address validReceiver);
error TokenDisabled(address token);
error AmountOutTooLow(uint amountOut, uint minAmount);
error InvalidTokenAddress(string token);
error InvalidDenominationAsset(address invalidAsset, address validAsset);
error BelowMinValidTo(uint minValidTo);
error AboveMaxValidTo(uint maxValidTo);
error InvalidBalance(uint tokenBalance, uint limit);
error InvalidPostSwapBalance(uint postSwapBalance, uint limit);
error OnlyController();
error TransferFailed(address to, uint value, address token);
error InsufficientTimeBetweenSwaps(uint minValidSwapTime);
error AboveMaxFee(uint feeInEth, uint maxFee);
}
文件 15 的 18:IWeth.sol
pragma solidity >=0.5.0;
interface IWeth {
function deposit() external payable;
function withdraw(uint256 wad) external;
function approve(address spender, uint256 value) external;
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}
文件 16 的 18: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");
}
}
}
文件 17 的 18:SwapOperator.sol
pragma solidity ^0.8.18;
import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol";
import "../../interfaces/IPool.sol";
import "../../interfaces/ISwapOperator.sol";
import "../../interfaces/IPriceFeedOracle.sol";
import "../../interfaces/IWeth.sol";
import "../../interfaces/IERC20Detailed.sol";
import "../../interfaces/ISwapOperator.sol";
import "../../interfaces/ISafeTracker.sol";
import "../../external/enzyme/IEnzymeFundValueCalculatorRouter.sol";
import "../../external/enzyme/IEnzymeV4Vault.sol";
import "../../external/enzyme/IEnzymeV4Comptroller.sol";
import "../../external/enzyme/IEnzymePolicyManager.sol";
contract SwapOperator is ISwapOperator {
using SafeERC20 for IERC20;
struct Request {
address asset;
uint amount;
}
bytes public currentOrderUID;
ICowSettlement public immutable cowSettlement;
address public immutable cowVaultRelayer;
INXMMaster public immutable master;
address public immutable swapController;
IWeth public immutable weth;
bytes32 public immutable domainSeparator;
address public immutable enzymeV4VaultProxyAddress;
IEnzymeFundValueCalculatorRouter public immutable enzymeFundValueCalculatorRouter;
uint public immutable minPoolEth;
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint public constant MAX_SLIPPAGE_DENOMINATOR = 10000;
uint public constant MIN_VALID_TO_PERIOD = 600;
uint public constant MAX_VALID_TO_PERIOD = 31 days;
uint public constant MIN_TIME_BETWEEN_ORDERS = 900;
uint public constant MAX_FEE = 0.3 ether;
address public safe;
Request public transferRequest;
mapping(address => bool) public allowedSafeTransferAssets;
modifier onlyController() {
if (msg.sender != swapController) {
revert OnlyController();
}
_;
}
modifier onlySafe() {
require(msg.sender == safe, "SwapOp: only Safe can execute");
_;
}
constructor(
address _cowSettlement,
address _swapController,
address _master,
address _weth,
address _enzymeV4VaultProxyAddress,
address _safe,
address _dai,
address _usdc,
IEnzymeFundValueCalculatorRouter _enzymeFundValueCalculatorRouter,
uint _minPoolEth
) {
cowSettlement = ICowSettlement(_cowSettlement);
cowVaultRelayer = cowSettlement.vaultRelayer();
master = INXMMaster(_master);
swapController = _swapController;
weth = IWeth(_weth);
domainSeparator = cowSettlement.domainSeparator();
enzymeV4VaultProxyAddress = _enzymeV4VaultProxyAddress;
enzymeFundValueCalculatorRouter = _enzymeFundValueCalculatorRouter;
minPoolEth = _minPoolEth;
safe = _safe;
allowedSafeTransferAssets[_dai] = true;
allowedSafeTransferAssets[_usdc] = true;
allowedSafeTransferAssets[ETH] = true;
}
receive() external payable {}
function getDigest(GPv2Order.Data calldata order) public view returns (bytes32) {
bytes32 hash = GPv2Order.hash(order, domainSeparator);
return hash;
}
function getUID(GPv2Order.Data calldata order) public view returns (bytes memory) {
bytes memory uid = new bytes(56);
bytes32 digest = getDigest(order);
GPv2Order.packOrderUidParams(uid, digest, order.receiver, order.validTo);
return uid;
}
function getOracleAmount(address fromAsset, address toAsset, uint fromAmount) internal view returns (uint) {
IPriceFeedOracle priceFeedOracle = _pool().priceFeedOracle();
if (fromAsset == address(weth)) {
return priceFeedOracle.getAssetForEth(toAsset, fromAmount);
}
if (toAsset == address(weth)) {
return priceFeedOracle.getEthForAsset(fromAsset, fromAmount);
}
uint fromAmountInEth = priceFeedOracle.getEthForAsset(fromAsset, fromAmount);
return priceFeedOracle.getAssetForEth(toAsset, fromAmountInEth);
}
function validateAmountOut(uint amountOut, uint amountOutMin) internal pure {
if (amountOut < amountOutMin) {
revert AmountOutTooLow(amountOut, amountOutMin);
}
}
function validateOrderAmount(
GPv2Order.Data calldata order,
SwapDetails memory sellSwapDetails,
SwapDetails memory buySwapDetails
) internal view {
uint oracleBuyAmount = getOracleAmount(address(order.sellToken), address(order.buyToken), order.sellAmount);
uint16 higherMaxSlippageRatio = sellSwapDetails.maxSlippageRatio > buySwapDetails.maxSlippageRatio
? sellSwapDetails.maxSlippageRatio
: buySwapDetails.maxSlippageRatio;
uint maxSlippageAmount = (oracleBuyAmount * higherMaxSlippageRatio) / MAX_SLIPPAGE_DENOMINATOR;
uint minBuyAmountOnMaxSlippage = oracleBuyAmount - maxSlippageAmount;
validateAmountOut(order.buyAmount, minBuyAmountOnMaxSlippage);
}
function validateTokenIsEnabled(address token, SwapDetails memory swapDetails) internal pure {
if (swapDetails.minAmount == 0 && swapDetails.maxAmount == 0) {
revert TokenDisabled(token);
}
}
function validateTokenIsEnabledSkipWeth(address token, SwapDetails memory swapDetails) internal view {
if (token != address(weth)) {
validateTokenIsEnabled(token, swapDetails);
}
}
function validateSellTokenBalance(
IPool pool,
GPv2Order.Data calldata order,
SwapDetails memory sellSwapDetails,
SwapOperationType swapOperationType,
uint totalOutAmount
) internal view {
uint sellTokenBalance = order.sellToken.balanceOf(address(pool));
if (swapOperationType == SwapOperationType.EthToAsset) {
uint ethPostSwap = address(pool).balance - totalOutAmount;
if (ethPostSwap < minPoolEth) {
revert InvalidPostSwapBalance(ethPostSwap, minPoolEth);
}
return;
}
if (sellTokenBalance <= sellSwapDetails.maxAmount) {
revert InvalidBalance(sellTokenBalance, sellSwapDetails.maxAmount);
}
uint postSellTokenSwapBalance = sellTokenBalance - totalOutAmount;
if (postSellTokenSwapBalance < sellSwapDetails.minAmount) {
revert InvalidPostSwapBalance(postSellTokenSwapBalance, sellSwapDetails.minAmount);
}
}
function validateBuyTokenBalance(
IPool pool,
GPv2Order.Data calldata order,
SwapDetails memory buySwapDetails
) internal view {
uint buyTokenBalance = order.buyToken.balanceOf(address(pool));
if (address(order.buyToken) == address(weth)) {
return;
}
if (buyTokenBalance >= buySwapDetails.minAmount) {
revert InvalidBalance(buyTokenBalance, buySwapDetails.minAmount);
}
uint postBuyTokenSwapBalance = buyTokenBalance + order.buyAmount;
if (postBuyTokenSwapBalance > buySwapDetails.maxAmount) {
revert InvalidPostSwapBalance(postBuyTokenSwapBalance, buySwapDetails.maxAmount);
}
}
function getSwapOperationType(GPv2Order.Data memory order) internal view returns (SwapOperationType) {
if (address(order.sellToken) == address(weth)) {
return SwapOperationType.EthToAsset;
}
if (address(order.buyToken) == address(weth)) {
return SwapOperationType.AssetToEth;
}
return SwapOperationType.AssetToAsset;
}
function performPreSwapValidations(
IPool pool,
IPriceFeedOracle priceFeedOracle,
GPv2Order.Data calldata order,
SwapOperationType swapOperationType,
uint totalOutAmount
) internal view {
SwapDetails memory sellSwapDetails = pool.getAssetSwapDetails(address(order.sellToken));
SwapDetails memory buySwapDetails = pool.getAssetSwapDetails(address(order.buyToken));
validateTokenIsEnabledSkipWeth(address(order.sellToken), sellSwapDetails);
validateTokenIsEnabledSkipWeth(address(order.buyToken), buySwapDetails);
validateSellTokenBalance(pool, order, sellSwapDetails, swapOperationType, totalOutAmount);
validateBuyTokenBalance(pool, order, buySwapDetails);
validateSwapFrequency(sellSwapDetails);
validateSwapFrequency(buySwapDetails);
validateMaxFee(priceFeedOracle, address(order.sellToken), order.feeAmount);
validateOrderAmount(order, sellSwapDetails, buySwapDetails);
}
function executeAssetTransfer(
IPool pool,
GPv2Order.Data calldata order,
SwapOperationType swapOperationType,
uint totalOutAmount
) internal {
address sellTokenAddress = address(order.sellToken);
address buyTokenAddress = address(order.buyToken);
if (swapOperationType == SwapOperationType.EthToAsset) {
pool.setSwapDetailsLastSwapTime(buyTokenAddress, uint32(block.timestamp));
pool.setSwapAssetAmount(ETH, totalOutAmount);
pool.transferAssetToSwapOperator(ETH, totalOutAmount);
weth.deposit{value: totalOutAmount}();
} else if (swapOperationType == SwapOperationType.AssetToEth) {
pool.setSwapDetailsLastSwapTime(sellTokenAddress, uint32(block.timestamp));
pool.setSwapAssetAmount(sellTokenAddress, totalOutAmount);
pool.transferAssetToSwapOperator(sellTokenAddress, totalOutAmount);
} else {
pool.setSwapDetailsLastSwapTime(sellTokenAddress, uint32(block.timestamp));
pool.setSwapDetailsLastSwapTime(buyTokenAddress, uint32(block.timestamp));
pool.setSwapAssetAmount(sellTokenAddress, totalOutAmount);
pool.transferAssetToSwapOperator(sellTokenAddress, totalOutAmount);
}
}
function placeOrder(GPv2Order.Data calldata order, bytes calldata orderUID) public onlyController {
if (orderInProgress()) {
revert OrderInProgress(currentOrderUID);
}
validateUID(order, orderUID);
validateBasicCowParams(order);
IPool pool = _pool();
IPriceFeedOracle priceFeedOracle = pool.priceFeedOracle();
uint totalOutAmount = order.sellAmount + order.feeAmount;
SwapOperationType swapOperationType = getSwapOperationType(order);
performPreSwapValidations(pool, priceFeedOracle, order, swapOperationType, totalOutAmount);
executeAssetTransfer(pool, order, swapOperationType, totalOutAmount);
order.sellToken.safeApprove(cowVaultRelayer, totalOutAmount);
currentOrderUID = orderUID;
cowSettlement.setPreSignature(orderUID, true);
emit OrderPlaced(order);
}
function closeOrder(GPv2Order.Data calldata order) external {
if (!orderInProgress()) {
revert NoOrderInPlace();
}
if (block.timestamp <= order.validTo && msg.sender != swapController) {
revert OnlyController();
}
validateUID(order, currentOrderUID);
uint filledAmount = cowSettlement.filledAmount(currentOrderUID);
cowSettlement.setPreSignature(currentOrderUID, false);
cowSettlement.invalidateOrder(currentOrderUID);
order.sellToken.safeApprove(cowVaultRelayer, 0);
delete currentOrderUID;
IPool pool = _pool();
returnAssetToPool(pool, order.buyToken);
returnAssetToPool(pool, order.sellToken);
address sellToken = address(order.sellToken) == address(weth) ? ETH : address(order.sellToken);
pool.setSwapAssetAmount(sellToken, 0);
emit OrderClosed(order, filledAmount);
}
function returnAssetToPool(IPool pool, IERC20 asset) internal {
uint balance = asset.balanceOf(address(this));
if (balance == 0) {
return;
}
if (address(asset) == address(weth)) {
weth.withdraw(balance);
(bool sent, ) = payable(address(pool)).call{value: balance}("");
if (!sent) {
revert TransferFailed(address(pool), balance, ETH);
}
} else {
asset.safeTransfer(address(pool), balance);
}
}
function validateBasicCowParams(GPv2Order.Data calldata order) internal view {
uint minValidTo = block.timestamp + MIN_VALID_TO_PERIOD;
uint maxValidTo = block.timestamp + MAX_VALID_TO_PERIOD;
if (order.validTo < minValidTo) {
revert BelowMinValidTo(minValidTo);
}
if (order.validTo > maxValidTo) {
revert AboveMaxValidTo(maxValidTo);
}
if (order.receiver != address(this)) {
revert InvalidReceiver(address(this));
}
if (address(order.sellToken) == ETH) {
revert InvalidTokenAddress('sellToken');
}
if (address(order.buyToken) == ETH) {
revert InvalidTokenAddress('buyToken');
}
if (order.sellTokenBalance != GPv2Order.BALANCE_ERC20) {
revert UnsupportedTokenBalance('sell');
}
if (order.buyTokenBalance != GPv2Order.BALANCE_ERC20) {
revert UnsupportedTokenBalance('buy');
}
}
function validateUID(GPv2Order.Data calldata order, bytes memory providedOrderUID) internal view {
bytes memory calculatedOrderUID = getUID(order);
if (keccak256(calculatedOrderUID) != keccak256(providedOrderUID)) {
revert OrderUidMismatch(providedOrderUID, calculatedOrderUID);
}
}
function _pool() internal view returns (IPool) {
return IPool(master.getLatestAddress("P1"));
}
function safeTracker() internal view returns (ISafeTracker) {
return ISafeTracker(master.getLatestAddress("ST"));
}
function validateSwapFrequency(SwapDetails memory swapDetails) internal view {
uint minValidSwapTime = swapDetails.lastSwapTime + MIN_TIME_BETWEEN_ORDERS;
if (block.timestamp < minValidSwapTime) {
revert InsufficientTimeBetweenSwaps(minValidSwapTime);
}
}
function validateMaxFee(
IPriceFeedOracle priceFeedOracle,
address sellToken,
uint feeAmount
) internal view {
uint feeInEther = sellToken == address(weth)
? feeAmount
: priceFeedOracle.getEthForAsset(sellToken, feeAmount);
if (feeInEther > MAX_FEE) {
revert AboveMaxFee(feeInEther, MAX_FEE);
}
}
function swapETHForEnzymeVaultShare(uint amountIn, uint amountOutMin) external onlyController {
if (orderInProgress()) {
revert OrderInProgress(currentOrderUID);
}
IPool pool = _pool();
IEnzymeV4Comptroller comptrollerProxy = IEnzymeV4Comptroller(IEnzymeV4Vault(enzymeV4VaultProxyAddress).getAccessor());
IERC20Detailed toToken = IERC20Detailed(enzymeV4VaultProxyAddress);
SwapDetails memory swapDetails = pool.getAssetSwapDetails(address(toToken));
validateTokenIsEnabled(address(toToken), swapDetails);
validateSwapFrequency(swapDetails);
{
(, uint netShareValue) = enzymeFundValueCalculatorRouter.calcNetShareValue(enzymeV4VaultProxyAddress);
uint avgAmountOut = amountIn * 1e18 / netShareValue;
uint maxSlippageAmount = avgAmountOut * swapDetails.maxSlippageRatio / MAX_SLIPPAGE_DENOMINATOR;
uint minOutOnMaxSlippage = avgAmountOut - maxSlippageAmount;
validateAmountOut(amountOutMin, minOutOnMaxSlippage);
}
uint balanceBefore = toToken.balanceOf(address(pool));
pool.transferAssetToSwapOperator(ETH, amountIn);
address denominationAsset = comptrollerProxy.getDenominationAsset();
if (denominationAsset != address(weth)) {
revert InvalidDenominationAsset(denominationAsset, address(weth));
}
weth.deposit{ value: amountIn }();
weth.approve(address(comptrollerProxy), amountIn);
comptrollerProxy.buyShares(amountIn, amountOutMin);
pool.setSwapDetailsLastSwapTime(address(toToken), uint32(block.timestamp));
uint amountOut = toToken.balanceOf(address(this));
validateAmountOut(amountOut, amountOutMin);
if (balanceBefore >= swapDetails.minAmount) {
revert InvalidBalance(balanceBefore, swapDetails.minAmount);
}
if (balanceBefore + amountOutMin > swapDetails.maxAmount) {
revert InvalidPostSwapBalance(balanceBefore + amountOutMin, swapDetails.maxAmount);
}
uint ethBalanceAfter = address(pool).balance;
if (ethBalanceAfter < minPoolEth) {
revert InvalidPostSwapBalance(ethBalanceAfter, minPoolEth);
}
transferAssetTo(enzymeV4VaultProxyAddress, address(pool), amountOut);
emit Swapped(ETH, enzymeV4VaultProxyAddress, amountIn, amountOut);
}
function swapEnzymeVaultShareForETH(
uint amountIn,
uint amountOutMin
) external onlyController {
if (orderInProgress()) {
revert OrderInProgress(currentOrderUID);
}
IPool pool = _pool();
IERC20Detailed fromToken = IERC20Detailed(enzymeV4VaultProxyAddress);
uint balanceBefore = fromToken.balanceOf(address(pool));
{
SwapDetails memory swapDetails = pool.getAssetSwapDetails(address(fromToken));
validateTokenIsEnabled(address(fromToken), swapDetails);
validateSwapFrequency(swapDetails);
uint netShareValue;
{
address denominationAsset;
(denominationAsset, netShareValue) =
enzymeFundValueCalculatorRouter.calcNetShareValue(enzymeV4VaultProxyAddress);
if (denominationAsset != address(weth)) {
revert InvalidDenominationAsset(denominationAsset, address(weth));
}
}
uint avgAmountOut = amountIn * netShareValue / (10 ** fromToken.decimals());
uint maxSlippageAmount = avgAmountOut * swapDetails.maxSlippageRatio / MAX_SLIPPAGE_DENOMINATOR;
uint minOutOnMaxSlippage = avgAmountOut - maxSlippageAmount;
validateAmountOut(amountOutMin, minOutOnMaxSlippage);
if (balanceBefore <= swapDetails.maxAmount) {
revert InvalidBalance(balanceBefore, swapDetails.maxAmount);
}
if (balanceBefore - amountIn < swapDetails.minAmount) {
revert InvalidPostSwapBalance(balanceBefore - amountIn, swapDetails.minAmount);
}
}
pool.transferAssetToSwapOperator(address(fromToken), amountIn);
IEnzymeV4Comptroller comptrollerProxy = IEnzymeV4Comptroller(IEnzymeV4Vault(enzymeV4VaultProxyAddress).getAccessor());
fromToken.approve(address(comptrollerProxy), amountIn);
address[] memory payoutAssets = new address[](1);
uint[] memory payoutAssetsPercentages = new uint[](1);
payoutAssets[0] = address(weth);
payoutAssetsPercentages[0] = 10000;
comptrollerProxy.redeemSharesForSpecificAssets(address(this), amountIn, payoutAssets, payoutAssetsPercentages);
uint amountOut = weth.balanceOf(address(this));
weth.withdraw(amountOut);
pool.setSwapDetailsLastSwapTime(address(fromToken), uint32(block.timestamp));
validateAmountOut(amountOut, amountOutMin);
transferAssetTo(ETH, address(pool), amountOut);
emit Swapped(enzymeV4VaultProxyAddress, ETH, amountIn, amountOut);
}
function transferAssetTo (address asset, address to, uint amount) internal {
if (asset == ETH) {
(bool ok, ) = to.call{ value: amount }("");
if (!ok) {
revert TransferFailed(to, amount, ETH);
}
return;
}
IERC20 token = IERC20(asset);
token.safeTransfer(to, amount);
}
function requestAsset(address asset, uint amount) external onlySafe {
require(allowedSafeTransferAssets[asset] == true, "SwapOp: asset not allowed");
transferRequest = Request(asset, amount);
}
function transferRequestedAsset(address requestedAsset, uint requestedAmount) external onlyController {
require(transferRequest.amount > 0, "SwapOp: request amount must be greater than 0");
(address asset, uint amount) = (transferRequest.asset, transferRequest.amount);
delete transferRequest;
require(requestedAsset == asset, "SwapOp: request assets need to match");
require(requestedAmount == amount, "SwapOp: request amounts need to match");
_pool().transferAssetToSwapOperator(asset, amount);
transferAssetTo(asset, safe, amount);
emit TransferredToSafe(asset, amount);
}
function recoverAsset(address assetAddress, address receiver) public onlyController {
if (orderInProgress()) {
revert OrderInProgress(currentOrderUID);
}
IPool pool = _pool();
if (assetAddress == ETH) {
uint ethBalance = address(this).balance;
if (ethBalance == 0) {
revert InvalidBalance(ethBalance, 0);
}
(bool sent, ) = payable(address(pool)).call{value: ethBalance}("");
if (!sent) {
revert TransferFailed(address(pool), ethBalance, ETH);
}
return;
}
IERC20 asset = IERC20(assetAddress);
uint balance = asset.balanceOf(address(this));
if (balance == 0) {
revert InvalidBalance(balance, 0);
}
SwapDetails memory swapDetails = pool.getAssetSwapDetails(assetAddress);
if (swapDetails.minAmount == 0 && swapDetails.maxAmount == 0) {
asset.transfer(receiver, balance);
return;
}
asset.transfer(address(pool), balance);
}
function orderInProgress() public view returns (bool) {
return currentOrderUID.length > 0;
}
}
文件 18 的 18: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/modules/capital/SwapOperator.sol": "SwapOperator"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_cowSettlement","type":"address"},{"internalType":"address","name":"_swapController","type":"address"},{"internalType":"address","name":"_master","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_enzymeV4VaultProxyAddress","type":"address"},{"internalType":"address","name":"_safe","type":"address"},{"internalType":"address","name":"_dai","type":"address"},{"internalType":"address","name":"_usdc","type":"address"},{"internalType":"contract IEnzymeFundValueCalculatorRouter","name":"_enzymeFundValueCalculatorRouter","type":"address"},{"internalType":"uint256","name":"_minPoolEth","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"feeInEth","type":"uint256"},{"internalType":"uint256","name":"maxFee","type":"uint256"}],"name":"AboveMaxFee","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxValidTo","type":"uint256"}],"name":"AboveMaxValidTo","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"AmountOutTooLow","type":"error"},{"inputs":[{"internalType":"uint256","name":"minValidTo","type":"uint256"}],"name":"BelowMinValidTo","type":"error"},{"inputs":[{"internalType":"uint256","name":"minValidSwapTime","type":"uint256"}],"name":"InsufficientTimeBetweenSwaps","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenBalance","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"InvalidBalance","type":"error"},{"inputs":[{"internalType":"address","name":"invalidAsset","type":"address"},{"internalType":"address","name":"validAsset","type":"address"}],"name":"InvalidDenominationAsset","type":"error"},{"inputs":[{"internalType":"uint256","name":"postSwapBalance","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"InvalidPostSwapBalance","type":"error"},{"inputs":[{"internalType":"address","name":"validReceiver","type":"address"}],"name":"InvalidReceiver","type":"error"},{"inputs":[{"internalType":"string","name":"token","type":"string"}],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"NoOrderInPlace","type":"error"},{"inputs":[],"name":"OnlyController","type":"error"},{"inputs":[{"internalType":"bytes","name":"currentOrderUID","type":"bytes"}],"name":"OrderInProgress","type":"error"},{"inputs":[{"internalType":"bytes","name":"providedOrderUID","type":"bytes"},{"internalType":"bytes","name":"expectedOrderUID","type":"bytes"}],"name":"OrderUidMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenDisabled","type":"error"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"TransferFailed","type":"error"},{"inputs":[{"internalType":"string","name":"kind","type":"string"}],"name":"UnsupportedTokenBalance","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"contract IERC20","name":"sellToken","type":"address"},{"internalType":"contract IERC20","name":"buyToken","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"bytes32","name":"kind","type":"bytes32"},{"internalType":"bool","name":"partiallyFillable","type":"bool"},{"internalType":"bytes32","name":"sellTokenBalance","type":"bytes32"},{"internalType":"bytes32","name":"buyTokenBalance","type":"bytes32"}],"indexed":false,"internalType":"struct GPv2Order.Data","name":"order","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"filledAmount","type":"uint256"}],"name":"OrderClosed","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"contract IERC20","name":"sellToken","type":"address"},{"internalType":"contract IERC20","name":"buyToken","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"bytes32","name":"kind","type":"bytes32"},{"internalType":"bool","name":"partiallyFillable","type":"bool"},{"internalType":"bytes32","name":"sellTokenBalance","type":"bytes32"},{"internalType":"bytes32","name":"buyTokenBalance","type":"bytes32"}],"indexed":false,"internalType":"struct GPv2Order.Data","name":"order","type":"tuple"}],"name":"OrderPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromAsset","type":"address"},{"indexed":true,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Swapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferredToSafe","type":"event"},{"inputs":[],"name":"ETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SLIPPAGE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_VALID_TO_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_TIME_BETWEEN_ORDERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_VALID_TO_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedSafeTransferAssets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"sellToken","type":"address"},{"internalType":"contract IERC20","name":"buyToken","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"bytes32","name":"kind","type":"bytes32"},{"internalType":"bool","name":"partiallyFillable","type":"bool"},{"internalType":"bytes32","name":"sellTokenBalance","type":"bytes32"},{"internalType":"bytes32","name":"buyTokenBalance","type":"bytes32"}],"internalType":"struct GPv2Order.Data","name":"order","type":"tuple"}],"name":"closeOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cowSettlement","outputs":[{"internalType":"contract ICowSettlement","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cowVaultRelayer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentOrderUID","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enzymeFundValueCalculatorRouter","outputs":[{"internalType":"contract IEnzymeFundValueCalculatorRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enzymeV4VaultProxyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"sellToken","type":"address"},{"internalType":"contract IERC20","name":"buyToken","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"bytes32","name":"kind","type":"bytes32"},{"internalType":"bool","name":"partiallyFillable","type":"bool"},{"internalType":"bytes32","name":"sellTokenBalance","type":"bytes32"},{"internalType":"bytes32","name":"buyTokenBalance","type":"bytes32"}],"internalType":"struct GPv2Order.Data","name":"order","type":"tuple"}],"name":"getDigest","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"sellToken","type":"address"},{"internalType":"contract IERC20","name":"buyToken","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"bytes32","name":"kind","type":"bytes32"},{"internalType":"bool","name":"partiallyFillable","type":"bool"},{"internalType":"bytes32","name":"sellTokenBalance","type":"bytes32"},{"internalType":"bytes32","name":"buyTokenBalance","type":"bytes32"}],"internalType":"struct GPv2Order.Data","name":"order","type":"tuple"}],"name":"getUID","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"master","outputs":[{"internalType":"contract INXMMaster","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minPoolEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orderInProgress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"sellToken","type":"address"},{"internalType":"contract IERC20","name":"buyToken","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint32","name":"validTo","type":"uint32"},{"internalType":"bytes32","name":"appData","type":"bytes32"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"bytes32","name":"kind","type":"bytes32"},{"internalType":"bool","name":"partiallyFillable","type":"bool"},{"internalType":"bytes32","name":"sellTokenBalance","type":"bytes32"},{"internalType":"bytes32","name":"buyTokenBalance","type":"bytes32"}],"internalType":"struct GPv2Order.Data","name":"order","type":"tuple"},{"internalType":"bytes","name":"orderUID","type":"bytes"}],"name":"placeOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetAddress","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"name":"recoverAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"requestAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"safe","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapController","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"swapETHForEnzymeVaultShare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"swapEnzymeVaultShareForETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferRequest","outputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"requestedAsset","type":"address"},{"internalType":"uint256","name":"requestedAmount","type":"uint256"}],"name":"transferRequestedAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWeth","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]