编译器
0.8.23+commit.f704f362
文件 1 的 19:AccessControl.sol
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
文件 2 的 19: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();
}
}
}
文件 3 的 19:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 19:ERC165.sol
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 19:IAccessControl.sol
pragma solidity ^0.8.20;
interface IAccessControl {
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
error AccessControlBadConfirmation();
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address callerConfirmation) external;
}
文件 6 的 19:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 7 的 19: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);
}
文件 8 的 19: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);
}
文件 9 的 19: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;
}
文件 10 的 19:IRewardManager.sol
pragma solidity ^0.8.23;
interface IRewardManager {
function handle(address reward, uint256 amount, address feeToken) external;
function valuate(
address reward,
uint256 amount,
address feeToken
) external view returns (uint256);
}
文件 11 的 19: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);
}
文件 12 的 19:Pausable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Pausable is Context {
bool private _paused;
event Paused(address account);
event Unpaused(address account);
error EnforcedPause();
error ExpectedPause();
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 13 的 19:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 14 的 19:RewardTokenManager.sol
pragma solidity ^0.8.23;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import './interfaces/IRewardManager.sol';
abstract contract RewardTokenManager {
using SafeERC20 for IERC20;
error WrongRewardTokens(IERC20[] rewardTokens);
error WrongRewardTokensLength(uint256 length);
error ZeroRewardManager();
error ZeroTokenAddress(uint256 index);
IERC20[] public rewardTokens;
event SetRewardTokens(IERC20[] rewardTokens);
function _setRewardTokens(IERC20[] memory rewardTokens_) internal virtual {
if (rewardTokens_.length == 0) revert WrongRewardTokens(rewardTokens_);
for (uint256 i = 0; i < rewardTokens_.length; i++) {
if (address(rewardTokens_[i]) == address(0)) revert ZeroTokenAddress(i);
}
rewardTokens = rewardTokens_;
emit SetRewardTokens(rewardTokens);
}
function _sellRewards(
IRewardManager rewardManager,
IERC20 feeToken
) internal returns (uint256) {
if (address(rewardManager) == address(0)) revert ZeroRewardManager();
uint256 rewardsLength_ = rewardTokens.length;
uint256[] memory rewardBalances = new uint256[](rewardsLength_);
bool allRewardsEmpty = true;
for (uint256 i = 0; i < rewardsLength_; i++) {
rewardBalances[i] = rewardTokens[i].balanceOf(address(this));
if (rewardBalances[i] > 0) {
allRewardsEmpty = false;
}
}
if (allRewardsEmpty) {
return 0;
}
uint256 feeTokenBalanceBefore = feeToken.balanceOf(address(this));
IERC20 rewardToken_;
for (uint256 i = 0; i < rewardsLength_; i++) {
if (rewardBalances[i] == 0) continue;
rewardToken_ = rewardTokens[i];
_sellToken(rewardManager, rewardToken_, rewardBalances[i], address(feeToken));
}
return feeToken.balanceOf(address(this)) - feeTokenBalanceBefore;
}
function _sellRewards(
IRewardManager rewardManager,
IERC20 feeToken,
uint256[] memory rewardAmounts
) internal returns (uint256) {
if (address(rewardManager) == address(0)) revert ZeroRewardManager();
uint256 rewardsLength_ = rewardTokens.length;
if (rewardsLength_ != rewardAmounts.length) revert WrongRewardTokensLength(rewardsLength_);
uint256 feeTokenBalanceBefore = feeToken.balanceOf(address(this));
IERC20 rewardToken_;
for (uint256 i = 0; i < rewardsLength_; i++) {
if (rewardAmounts[i] == 0) continue;
rewardToken_ = rewardTokens[i];
_sellToken(rewardManager, rewardToken_, rewardAmounts[i], address(feeToken));
}
return feeToken.balanceOf(address(this)) - feeTokenBalanceBefore;
}
function _sellToken(
IRewardManager rewardManager,
IERC20 sellingToken,
uint256 sellingTokenAmount,
address receivedToken
) internal {
sellingToken.safeTransfer(address(rewardManager), sellingTokenAmount);
rewardManager.handle(address(sellingToken), sellingTokenAmount, receivedToken);
}
}
文件 15 的 19: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;
}
}
文件 16 的 19:ZunamiPoolControllerBase.sol
pragma solidity ^0.8.23;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@openzeppelin/contracts/access/AccessControl.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import './interfaces/IPool.sol';
import './RewardTokenManager.sol';
abstract contract ZunamiPoolControllerBase is
Pausable,
AccessControl,
ReentrancyGuard,
RewardTokenManager
{
using SafeERC20 for IERC20;
error ZeroAddress();
error WrongSid();
uint8 public constant POOL_ASSETS = 5;
uint256 public defaultDepositSid;
uint256 public defaultWithdrawSid;
IPool public immutable pool;
event SetDefaultDepositSid(uint256 sid);
event SetDefaultWithdrawSid(uint256 sid);
constructor(address pool_) {
if (pool_ == address(0)) revert ZeroAddress();
pool = IPool(pool_);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function setRewardTokens(IERC20[] memory rewardTokens_) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setRewardTokens(rewardTokens_);
}
function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_pause();
}
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
}
function setDefaultDepositSid(uint256 _newPoolId) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (_newPoolId >= pool.strategyCount()) revert WrongSid();
defaultDepositSid = _newPoolId;
emit SetDefaultDepositSid(_newPoolId);
}
function setDefaultWithdrawSid(uint256 _newPoolId) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (_newPoolId >= pool.strategyCount()) revert WrongSid();
defaultWithdrawSid = _newPoolId;
emit SetDefaultWithdrawSid(_newPoolId);
}
function claimPoolRewards(address collector) internal {
pool.claimRewards(collector, rewardTokens);
}
function deposit(
uint256[POOL_ASSETS] memory amounts,
address receiver
) external whenNotPaused nonReentrant returns (uint256 shares) {
if (receiver == address(0)) {
receiver = _msgSender();
}
IERC20[5] memory tokens = pool.tokens();
for (uint256 i = 0; i < amounts.length; i++) {
IERC20 token = tokens[i];
if (address(token) != address(0) && amounts[i] > 0) {
IERC20(tokens[i]).safeTransferFrom(_msgSender(), address(pool), amounts[i]);
}
}
return depositPool(amounts, receiver);
}
function depositPool(
uint256[POOL_ASSETS] memory amounts,
address receiver
) internal virtual returns (uint256);
function depositDefaultPool(
uint256[POOL_ASSETS] memory amounts,
address receiver
) internal returns (uint256) {
return pool.deposit(defaultDepositSid, amounts, receiver);
}
function withdraw(
uint256 shares,
uint256[POOL_ASSETS] memory minTokenAmounts,
address receiver
) external whenNotPaused nonReentrant {
if (receiver == address(0)) {
receiver = _msgSender();
}
withdrawPool(_msgSender(), shares, minTokenAmounts, receiver);
}
function withdrawDefaultPool(
uint256 shares,
uint256[POOL_ASSETS] memory minTokenAmounts,
address receiver
) internal virtual {
pool.withdraw(defaultWithdrawSid, shares, minTokenAmounts, receiver);
}
function withdrawPool(
address user,
uint256 shares,
uint256[POOL_ASSETS] memory minTokenAmounts,
address receiver
) internal virtual;
}
文件 17 的 19:ZunamiPoolControllerZunETH.sol
pragma solidity ^0.8.23;
import '../../ZunamiPoolThroughRedemptionFeeController.sol';
contract ZunamiPoolControllerZunETH is ZunamiPoolThroughRedemptionFeeController {
constructor(address pool) ZunamiPoolThroughRedemptionFeeController(pool) {}
}
文件 18 的 19:ZunamiPoolThroughController.sol
pragma solidity ^0.8.23;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import './ZunamiPoolControllerBase.sol';
contract ZunamiPoolThroughController is ZunamiPoolControllerBase {
using SafeERC20 for IERC20;
error OnlyIssuer();
bytes32 public constant ISSUER_ROLE = keccak256('ISSUER_ROLE');
address public rewardCollector;
bool public onlyIssuerMode = false;
event RewardCollectorChanged(address oldRewardCollector, address newRewardCollector);
event SetOnlyIssuerMode(bool onlyIssuerMode);
modifier onlyIssuance() {
if (onlyIssuerMode && !hasRole(ISSUER_ROLE, msg.sender)) revert OnlyIssuer();
_;
}
constructor(address pool_) ZunamiPoolControllerBase(pool_) {
rewardCollector = msg.sender;
}
function changeRewardCollector(address _rewardCollector) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (_rewardCollector == address(0)) revert ZeroAddress();
emit RewardCollectorChanged(rewardCollector, _rewardCollector);
rewardCollector = _rewardCollector;
}
function setOnlyIssuerMode(bool _onlyIssuerMode) external onlyRole(DEFAULT_ADMIN_ROLE) {
onlyIssuerMode = _onlyIssuerMode;
emit SetOnlyIssuerMode(onlyIssuerMode);
}
function claimRewards() external whenNotPaused nonReentrant {
claimPoolRewards(rewardCollector);
}
function depositPool(
uint256[POOL_ASSETS] memory amounts,
address receiver
) internal virtual override onlyIssuance returns (uint256) {
return depositDefaultPool(amounts, receiver);
}
function withdrawPool(
address user,
uint256 shares,
uint256[POOL_ASSETS] memory minTokenAmounts,
address receiver
) internal virtual override onlyIssuance {
IERC20(address(pool)).safeTransferFrom(user, address(this), shares);
withdrawDefaultPool(shares, minTokenAmounts, receiver);
}
}
文件 19 的 19:ZunamiPoolThroughRedemptionFeeController.sol
pragma solidity ^0.8.23;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import './ZunamiPoolThroughController.sol';
contract ZunamiPoolThroughRedemptionFeeController is ZunamiPoolThroughController {
using SafeERC20 for IERC20;
error FeeWronglyHigh();
uint256 public constant FEE_DENOMINATOR = 1000000;
uint256 public constant MAX_FEE = 50000;
uint256 public withdrawFee;
address public feeDistributor;
event WithdrawFeeChanged(uint256 withdrawFee);
event FeeDistributorChanged(address feeDistributor);
constructor(address pool_) ZunamiPoolThroughController(pool_) {}
function changeWithdrawFee(uint256 withdrawFee_) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (withdrawFee_ > MAX_FEE) revert FeeWronglyHigh();
withdrawFee = withdrawFee_;
emit WithdrawFeeChanged(withdrawFee_);
}
function changeFeeDistributor(address feeDistributor_) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (feeDistributor_ == address(0)) revert ZeroAddress();
feeDistributor = feeDistributor_;
emit FeeDistributorChanged(feeDistributor_);
}
function withdrawPool(
address user,
uint256 shares,
uint256[POOL_ASSETS] memory minTokenAmounts,
address receiver
) internal virtual override {
IERC20(address(pool)).safeTransferFrom(user, address(this), shares);
uint256 nominalFee = _calcFee(msg.sender, shares);
if (nominalFee > 0 && feeDistributor != address(0)) {
IERC20(address(pool)).safeTransfer(feeDistributor, nominalFee);
shares -= nominalFee;
}
withdrawDefaultPool(shares, minTokenAmounts, receiver);
}
function _calcFee(address caller, uint256 value) internal view returns (uint256 nominalFee) {
uint256 withdrawFee_ = withdrawFee;
if (withdrawFee_ > 0 && !hasRole(ISSUER_ROLE, caller)) {
nominalFee = (value * withdrawFee_) / FEE_DENOMINATOR;
}
}
}
{
"compilationTarget": {
"contracts/configs/omnipool/ZunamiPoolControllerZunETH.sol": "ZunamiPoolControllerZunETH"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"FeeWronglyHigh","type":"error"},{"inputs":[],"name":"OnlyIssuer","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"contract IERC20[]","name":"rewardTokens","type":"address[]"}],"name":"WrongRewardTokens","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"WrongRewardTokensLength","type":"error"},{"inputs":[],"name":"WrongSid","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroRewardManager","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ZeroTokenAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeDistributor","type":"address"}],"name":"FeeDistributorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRewardCollector","type":"address"},{"indexed":false,"internalType":"address","name":"newRewardCollector","type":"address"}],"name":"RewardCollectorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sid","type":"uint256"}],"name":"SetDefaultDepositSid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sid","type":"uint256"}],"name":"SetDefaultWithdrawSid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"onlyIssuerMode","type":"bool"}],"name":"SetOnlyIssuerMode","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20[]","name":"rewardTokens","type":"address[]"}],"name":"SetRewardTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawFee","type":"uint256"}],"name":"WithdrawFeeChanged","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ISSUER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":[{"internalType":"address","name":"feeDistributor_","type":"address"}],"name":"changeFeeDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardCollector","type":"address"}],"name":"changeRewardCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"withdrawFee_","type":"uint256"}],"name":"changeWithdrawFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultDepositSid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultWithdrawSid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[5]","name":"amounts","type":"uint256[5]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onlyIssuerMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"internalType":"contract IPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPoolId","type":"uint256"}],"name":"setDefaultDepositSid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPoolId","type":"uint256"}],"name":"setDefaultWithdrawSid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_onlyIssuerMode","type":"bool"}],"name":"setOnlyIssuerMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"rewardTokens_","type":"address[]"}],"name":"setRewardTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256[5]","name":"minTokenAmounts","type":"uint256[5]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]