编译器
0.8.23+commit.f704f362
文件 1 的 9:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 9:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 3 的 9:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 4 的 9:IOracle.sol
pragma solidity ^0.8.23;
interface IOracle {
error ZeroAddress();
error UnsupportedToken();
function getUSDPrice(address token) external view returns (uint256);
function isTokenSupported(address token) external view returns (bool);
}
文件 5 的 9:IPool.sol
pragma solidity ^0.8.23;
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { IStrategy } from './IStrategy.sol';
interface IPool is IERC20 {
error WrongDeposit(uint256 sid, uint256[5] amounts);
error AbsentStrategy(uint256 sid);
error NotStartedStrategy(uint256 sid);
error DisabledStrategy(uint256 sid);
error WrongAmount();
error WrongWithdrawParams(uint256 sid);
error WrongRatio();
error ZeroAddress();
error DuplicatedStrategy();
error IncorrectArguments();
error WrongWithdrawPercent();
error WrongReceiver();
error IncorrectSid();
error WrongTokens();
error WrongDecimalMultipliers();
struct StrategyInfo {
IStrategy strategy;
uint256 startTime;
uint256 minted;
bool enabled;
}
event Deposited(
address indexed depositor,
uint256 deposited,
uint256[5] amounts,
uint256 indexed sid
);
event Withdrawn(address indexed withdrawer, uint256 withdrawn, uint256 indexed sid);
event FailedWithdrawal(address indexed withdrawer, uint256[5] amounts, uint256 withdrawn);
event AddedStrategy(uint256 indexed sid, address indexed strategyAddr, uint256 startTime);
event ClaimedRewards(address indexed receiver, IERC20[] rewardTokens);
event ClaimedExtraGains(address indexed receiver, uint256 amount);
event EnabledStrategy(address indexed pool);
event DisableStrategy(address indexed pool);
event UpdatedToken(
uint256 indexed tid,
address indexed token,
uint256 tokenDecimalMultiplier,
address tokenOld
);
function tokens() external view returns (IERC20[5] memory);
function token(uint256 tid) external view returns (IERC20);
function tokenDecimalsMultipliers() external view returns (uint256[5] memory);
function strategyInfo(uint256 sid) external view returns (StrategyInfo memory);
function claimRewards(address receiver, IERC20[] memory rewardTokens) external;
function totalHoldings() external view returns (uint256);
function strategyCount() external view returns (uint256);
function deposit(
uint256 sid,
uint256[5] memory amounts,
address receiver
) external returns (uint256);
function depositStrategy(uint256 sid, uint256[5] memory amounts) external returns (uint256);
function withdraw(
uint256 sid,
uint256 stableAmount,
uint256[5] memory minTokenAmounts,
address receiver
) external;
function mintAndClaimExtraGains(address receiver) external;
}
文件 6 的 9:IPoolController.sol
pragma solidity ^0.8.23;
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './IPool.sol';
interface IPoolController is IERC20 {
function pool() external view returns (IPool);
function deposit(uint256[5] memory amounts, address receiver) external returns (uint256);
function withdraw(
uint256 stableAmount,
uint256[5] memory minTokenAmounts,
address receiver
) external;
function defaultDepositSid() external view returns (uint256);
function defaultWithdrawSid() external view returns (uint256);
}
文件 7 的 9:IStrategy.sol
pragma solidity ^0.8.23;
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface IStrategy {
function deposit(uint256[5] memory amounts) external returns (uint256);
function withdraw(
address receiver,
uint256 userDepositRatio,
uint256[5] memory minTokenAmounts
) external returns (bool);
function withdrawAll(uint256[5] memory minTokenAmounts) external;
function totalHoldings() external view returns (uint256);
function claimRewards(address receiver, IERC20[] memory rewardTokens) external;
function calcTokenAmount(
uint256[5] memory tokenAmounts,
bool isDeposit
) external view returns (uint256 sharesAmount);
}
文件 8 的 9:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 9 的 9:ZunamiStableZap.sol
pragma solidity ^0.8.23;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '../interfaces/IPoolController.sol';
import "../lib/Oracle/interfaces/IOracle.sol";
contract ZunamiStableZap {
using SafeERC20 for IERC20;
error ZeroAddress();
error SameAddress();
error DailyMintLimitOverflow();
error DailyRedeemLimitOverflow();
error BrokenMinimumAmount();
error FeeWronglyHigh();
uint8 public constant POOL_ASSETS = 5;
uint256 public constant FEE_DENOMINATOR = 1000000;
uint256 public constant MAX_FEE = 50000;
IPoolController immutable public zunStableController;
IOracle immutable public oracle;
address immutable public basedToken;
uint256 immutable public dailyMintDuration;
uint256 immutable public dailyMintLimit;
uint256 immutable public dailyRedeemDuration;
uint256 immutable public dailyRedeemLimit;
uint256 public dailyMintTotal;
uint256 public dailyMintCountingTimestamp;
uint256 public dailyRedeemTotal;
uint256 public dailyRedeemCountingTimestamp;
uint256 immutable public withdrawFee;
address immutable public feeDistributor;
constructor(
address _zunStableController,
address _oracle,
uint256 _dailyMintDuration,
uint256 _dailyMintLimit,
uint256 _dailyRedeemDuration,
uint256 _dailyRedeemLimit,
address _basedToken,
uint256 _withdrawFee,
address _feeDistributor
) {
if (_zunStableController == address(0))
revert ZeroAddress();
zunStableController = IPoolController(_zunStableController);
if (_oracle == address(0))
revert ZeroAddress();
oracle = IOracle(_oracle);
dailyMintDuration = _dailyMintDuration;
dailyMintLimit = _dailyMintLimit;
dailyMintCountingTimestamp = _dailyMintDuration > 0 ? block.timestamp : 0;
dailyRedeemDuration = _dailyRedeemDuration;
dailyRedeemLimit = _dailyRedeemLimit;
dailyRedeemCountingTimestamp = _dailyRedeemDuration > 0 ? block.timestamp : 0;
basedToken = _basedToken;
if (_withdrawFee > MAX_FEE) revert FeeWronglyHigh();
withdrawFee = _withdrawFee;
if (_feeDistributor == address(0)) revert ZeroAddress();
feeDistributor = _feeDistributor;
}
function mint(
uint256[POOL_ASSETS] memory amounts,
address receiver,
uint256 minAmountStable
) external returns (uint256) {
if (receiver == address(0)) {
receiver = msg.sender;
}
uint256 depositingAmount;
IPool pool = zunStableController.pool();
IERC20[POOL_ASSETS] memory tokens = pool.tokens();
uint256[POOL_ASSETS] memory tokenDecimalsMultipliers = pool.tokenDecimalsMultipliers();
for (uint256 i = 0; i < POOL_ASSETS; ++i) {
IERC20 token = tokens[i];
uint256 amount = amounts[i];
if (address(token) != address(0) && amount > 0) {
token.safeTransferFrom(msg.sender, address(this), amount);
token.safeIncreaseAllowance(address(zunStableController), amount);
depositingAmount += amount * tokenDecimalsMultipliers[i];
}
}
_verifyMintLimits(depositingAmount);
uint256 zunStableAmount = zunStableController.deposit(amounts, receiver);
if (zunStableAmount < minAmountStable) revert BrokenMinimumAmount();
return zunStableAmount;
}
function estimateMint(
uint256[POOL_ASSETS] memory amounts
) external view returns (uint256) {
uint256 depositSid = zunStableController.defaultDepositSid();
IPool pool = zunStableController.pool();
IStrategy strategy = pool.strategyInfo(depositSid).strategy;
return strategy.calcTokenAmount(amounts, true);
}
function _verifyMintLimits(
uint256 value
) internal {
uint256 dailyDuration = dailyMintDuration;
if (dailyDuration > 0) {
if (block.timestamp > dailyMintCountingTimestamp + dailyDuration) {
dailyMintTotal = value;
dailyMintCountingTimestamp = block.timestamp;
} else {
dailyMintTotal += value;
}
if(dailyMintTotal > dailyMintLimit) revert DailyMintLimitOverflow();
}
}
function redeem(
uint256 zunStableAmount,
address receiver,
uint256 minAmountBased
) external {
_verifyRedeemLimits(zunStableAmount);
IERC20 zunStable = IERC20(zunStableController.pool());
zunStable.safeTransferFrom(msg.sender, address(this), zunStableAmount);
uint256 nominalFee = _calcFee(zunStableAmount);
if (nominalFee > 0) {
zunStable.safeTransfer(feeDistributor, nominalFee);
zunStableAmount -= nominalFee;
}
zunStable.safeIncreaseAllowance(address(zunStableController), zunStableAmount);
zunStableController.withdraw(zunStableAmount, [uint256(0),0,0,0,0], address(this));
uint256 usdAmount;
IPool pool = zunStableController.pool();
IERC20[POOL_ASSETS] memory tokens = pool.tokens();
uint256[POOL_ASSETS] memory tokenDecimalsMultipliers = pool.tokenDecimalsMultipliers();
for (uint256 i = 0; i < POOL_ASSETS; ++i) {
IERC20 token = tokens[i];
if (address(token) != address(0)) {
uint256 tokenBalance = token.balanceOf(address(this));
if (tokenBalance > 0) {
token.safeTransfer(receiver, tokenBalance);
usdAmount += oracle.getUSDPrice(address(token)) * tokenBalance * tokenDecimalsMultipliers[i] / 1e18;
}
}
}
uint256 basedAmount = usdAmount;
if (basedToken != address(0)) {
basedAmount = usdAmount * 1e18 / oracle.getUSDPrice(basedToken);
}
if ( basedAmount < minAmountBased) revert BrokenMinimumAmount();
}
function _verifyRedeemLimits(
uint256 value
) internal {
uint256 dailyDuration = dailyRedeemDuration;
if (dailyDuration > 0) {
if (block.timestamp > dailyRedeemCountingTimestamp + dailyDuration) {
dailyRedeemTotal = value;
dailyRedeemCountingTimestamp = block.timestamp;
} else {
dailyRedeemTotal += value;
}
if(dailyRedeemTotal > dailyRedeemLimit) revert DailyRedeemLimitOverflow();
}
}
function _calcFee(uint256 value) internal view returns (uint256 nominalFee) {
uint256 withdrawFee_ = withdrawFee;
if (withdrawFee_ > 0 ) {
nominalFee = (value * withdrawFee_) / FEE_DENOMINATOR;
}
}
}
{
"compilationTarget": {
"contracts/utils/ZunamiStableZap.sol": "ZunamiStableZap"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_zunStableController","type":"address"},{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"uint256","name":"_dailyMintDuration","type":"uint256"},{"internalType":"uint256","name":"_dailyMintLimit","type":"uint256"},{"internalType":"uint256","name":"_dailyRedeemDuration","type":"uint256"},{"internalType":"uint256","name":"_dailyRedeemLimit","type":"uint256"},{"internalType":"address","name":"_basedToken","type":"address"},{"internalType":"uint256","name":"_withdrawFee","type":"uint256"},{"internalType":"address","name":"_feeDistributor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"BrokenMinimumAmount","type":"error"},{"inputs":[],"name":"DailyMintLimitOverflow","type":"error"},{"inputs":[],"name":"DailyRedeemLimitOverflow","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"FeeWronglyHigh","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SameAddress","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_ASSETS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"basedToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyMintCountingTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyMintDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyMintLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyMintTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyRedeemCountingTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyRedeemDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyRedeemLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dailyRedeemTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[5]","name":"amounts","type":"uint256[5]"}],"name":"estimateMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[5]","name":"amounts","type":"uint256[5]"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"minAmountStable","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"zunStableAmount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"minAmountBased","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"zunStableController","outputs":[{"internalType":"contract IPoolController","name":"","type":"address"}],"stateMutability":"view","type":"function"}]