编译器
0.8.16+commit.07a7930e
文件 1 的 14:AccessControl.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 14: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);
}
}
}
}
文件 3 的 14:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 14:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 14:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, _allowances[owner][spender] + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = _allowances[owner][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
文件 6 的 14:IAccessControl.sol
pragma solidity ^0.8.0;
interface IAccessControl {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
文件 7 的 14:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 8 的 14:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
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);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 9 的 14:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 10 的 14:IStrategyAPS.sol
pragma solidity ^0.8.0;
interface IStrategyAPS {
function deposit(uint256 amount) external returns (uint256);
function withdraw(
address withdrawer,
uint256 userRatioOfCrvLps,
uint256 tokenAmount
) external returns (bool);
function withdrawAll() external;
function totalHoldings() external view returns (uint256);
function claimManagementFees() external returns (uint256);
function autoCompound() external;
function calcWithdrawOneCoin(uint256 userRatioOfCrvLps)
external
view
returns (uint256 tokenAmount);
function calcSharesAmount(uint256 tokenAmount, bool isDeposit)
external
view
returns (uint256 sharesAmount);
}
文件 11 的 14:Pausable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Pausable is Context {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor() {
_paused = false;
}
function paused() public view virtual returns (bool) {
return _paused;
}
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 12 的 14:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.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 _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");
}
}
}
文件 13 的 14:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
文件 14 的 14:ZunamiAPS.sol
pragma solidity ^0.8.0;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/security/Pausable.sol';
import '@openzeppelin/contracts/access/AccessControl.sol';
import './interfaces/IStrategyAPS.sol';
contract ZunamiAPS is ERC20, Pausable, AccessControl {
using SafeERC20 for IERC20Metadata;
bytes32 public constant OPERATOR_ROLE = keccak256('OPERATOR_ROLE');
uint8 public constant POOL_ASSETS = 1;
struct PendingWithdrawal {
uint256 lpShares;
uint256 tokenAmount;
}
struct PoolInfo {
IStrategyAPS strategy;
uint256 startTime;
uint256 lpShares;
}
uint256 public constant LP_RATIO_MULTIPLIER = 1e18;
uint256 public constant FEE_DENOMINATOR = 1000;
uint256 public constant MAX_FEE = 300;
uint256 public constant MIN_LOCK_TIME = 1 days;
uint256 public constant FUNDS_DENOMINATOR = 10_000;
PoolInfo[] internal _poolInfo;
uint256 public defaultDepositPid;
uint256 public defaultWithdrawPid;
address public token;
mapping(address => uint256) internal _pendingDeposits;
mapping(address => PendingWithdrawal) internal _pendingWithdrawals;
uint256 public totalDeposited = 0;
uint256 public managementFee = 100;
bool public launched = false;
event ManagementFeeSet(uint256 oldManagementFee, uint256 newManagementFee);
event CreatedPendingDeposit(address indexed depositor, uint256 amount);
event CreatedPendingWithdrawal(
address indexed withdrawer,
uint256 lpShares,
uint256 tokenAmount
);
event Deposited(address indexed depositor, uint256 amount, uint256 lpShares, uint256 pid);
event Withdrawn(
address indexed withdrawer,
uint256 tokenAmount,
uint256 lpShares
);
event AddedPool(uint256 pid, address strategyAddr, uint256 startTime);
event FailedDeposit(address indexed depositor, uint256 amounts, uint256 lpShares);
event FailedWithdrawal(
address indexed withdrawer,
uint256 amounts,
uint256 lpShares
);
event SetDefaultDepositPid(uint256 pid);
event SetDefaultWithdrawPid(uint256 pid);
event ClaimedAllManagementFee(uint256 feeValue);
event AutoCompoundAll();
modifier startedPool() {
require(_poolInfo.length != 0, 'Zunami: pool not existed!');
require(
block.timestamp >= _poolInfo[defaultDepositPid].startTime,
'Zunami: default deposit pool not started yet!'
);
require(
block.timestamp >= _poolInfo[defaultWithdrawPid].startTime,
'Zunami: default withdraw pool not started yet!'
);
_;
}
constructor(address _token) ERC20('ZunamiAPSLP', 'ZAPSLP') {
token = _token;
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(OPERATOR_ROLE, msg.sender);
}
function poolInfo(uint256 pid) external view returns (PoolInfo memory) {
return _poolInfo[pid];
}
function pendingDeposits(address user) external view returns (uint256) {
return _pendingDeposits[user];
}
function pendingWithdrawals(address user) external view returns (PendingWithdrawal memory) {
return _pendingWithdrawals[user];
}
function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_pause();
}
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
}
function setManagementFee(uint256 newManagementFee) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(newManagementFee <= MAX_FEE, 'Zunami: wrong fee');
emit ManagementFeeSet(managementFee, newManagementFee);
managementFee = newManagementFee;
}
function calcManagementFee(uint256 amount) external view returns (uint256) {
return (amount * managementFee) / FEE_DENOMINATOR;
}
function claimAllManagementFee() external {
uint256 feeTotalValue;
for (uint256 i = 0; i < _poolInfo.length; i++) {
feeTotalValue += _poolInfo[i].strategy.claimManagementFees();
}
emit ClaimedAllManagementFee(feeTotalValue);
}
function autoCompoundAll() external {
for (uint256 i = 0; i < _poolInfo.length; i++) {
PoolInfo memory poolInfo_ = _poolInfo[i];
if (poolInfo_.lpShares > 0) {
poolInfo_.strategy.autoCompound();
}
}
emit AutoCompoundAll();
}
function totalHoldings() public view returns (uint256) {
uint256 length = _poolInfo.length;
uint256 totalHold = 0;
for (uint256 pid = 0; pid < length; pid++) {
PoolInfo memory poolInfo_ = _poolInfo[pid];
if (poolInfo_.lpShares > 0) {
totalHold += poolInfo_.strategy.totalHoldings();
}
}
return totalHold;
}
function lpPrice() external view returns (uint256) {
return (totalHoldings() * 1e18) / totalSupply();
}
function poolCount() external view returns (uint256) {
return _poolInfo.length;
}
function delegateDeposit(uint256 amount) external whenNotPaused {
if (amount > 0) {
IERC20Metadata(token).safeTransferFrom(_msgSender(), address(this), amount);
_pendingDeposits[_msgSender()] += amount;
}
emit CreatedPendingDeposit(_msgSender(), amount);
}
function delegateWithdrawal(uint256 lpShares, uint256 tokenAmount)
external
whenNotPaused
{
require(lpShares > 0, 'Zunami: lpAmount must be higher 0');
PendingWithdrawal memory withdrawal;
address userAddr = _msgSender();
withdrawal.lpShares = lpShares;
withdrawal.tokenAmount = tokenAmount;
_pendingWithdrawals[userAddr] = withdrawal;
emit CreatedPendingWithdrawal(userAddr, lpShares, tokenAmount);
}
function completeDeposits(address[] memory userList)
external
onlyRole(OPERATOR_ROLE)
startedPool
{
IStrategyAPS strategy = _poolInfo[defaultDepositPid].strategy;
uint256 currentTotalHoldings = totalHoldings();
uint256 newHoldings = 0;
uint256 totalAmount;
uint256[] memory userCompleteHoldings = new uint256[](userList.length);
for (uint256 i = 0; i < userList.length; i++) {
newHoldings = 0;
uint256 userTokenDeposit = _pendingDeposits[userList[i]];
totalAmount += userTokenDeposit;
newHoldings += userTokenDeposit;
userCompleteHoldings[i] = newHoldings;
}
newHoldings = 0;
if (totalAmount > 0) {
newHoldings += totalAmount;
IERC20Metadata(token).safeTransfer(address(strategy), totalAmount);
}
uint256 totalDepositedNow = strategy.deposit(totalAmount);
require(totalDepositedNow > 0, 'Zunami: too low deposit!');
uint256 lpShares = 0;
uint256 addedHoldings = 0;
uint256 userDeposited = 0;
for (uint256 z = 0; z < userList.length; z++) {
userDeposited = (totalDepositedNow * userCompleteHoldings[z]) / newHoldings;
address userAddr = userList[z];
if (totalSupply() == 0) {
lpShares = userDeposited;
} else {
lpShares = (totalSupply() * userDeposited) / (currentTotalHoldings + addedHoldings);
}
addedHoldings += userDeposited;
_mint(userAddr, lpShares);
_poolInfo[defaultDepositPid].lpShares += lpShares;
emit Deposited(userAddr, _pendingDeposits[userAddr], lpShares, defaultDepositPid);
delete _pendingDeposits[userAddr];
}
totalDeposited += addedHoldings;
}
function completeWithdrawals(address[] memory userList)
external
onlyRole(OPERATOR_ROLE)
startedPool
{
require(userList.length > 0, 'Zunami: there are no pending withdrawals requests');
IStrategyAPS withdrawStrategy = _poolInfo[defaultWithdrawPid].strategy;
address user;
PendingWithdrawal memory withdrawal;
for (uint256 i = 0; i < userList.length; i++) {
user = userList[i];
withdrawal = _pendingWithdrawals[user];
if (balanceOf(user) < withdrawal.lpShares) {
emit FailedWithdrawal(user, withdrawal.tokenAmount, withdrawal.lpShares);
delete _pendingWithdrawals[user];
continue;
}
if (
!(
withdrawStrategy.withdraw(
user,
calcLpRatioSafe(
withdrawal.lpShares,
_poolInfo[defaultWithdrawPid].lpShares
),
withdrawal.tokenAmount
)
)
) {
emit FailedWithdrawal(user, withdrawal.tokenAmount, withdrawal.lpShares);
delete _pendingWithdrawals[user];
continue;
}
uint256 userDeposit = (totalDeposited * withdrawal.lpShares) / totalSupply();
_burn(user, withdrawal.lpShares);
_poolInfo[defaultWithdrawPid].lpShares -= withdrawal.lpShares;
totalDeposited -= userDeposit;
emit Withdrawn(
user,
withdrawal.tokenAmount,
withdrawal.lpShares
);
delete _pendingWithdrawals[user];
}
}
function calcLpRatioSafe(uint256 outLpShares, uint256 strategyLpShares)
internal
pure
returns (uint256 lpShareRatio)
{
lpShareRatio = (outLpShares * LP_RATIO_MULTIPLIER) / strategyLpShares;
require(
lpShareRatio > 0 && lpShareRatio <= LP_RATIO_MULTIPLIER,
'Zunami: Wrong out lp ratio'
);
}
function completeWithdrawalsOptimized(address[] memory userList)
external
onlyRole(OPERATOR_ROLE)
startedPool
{
require(userList.length > 0, 'Zunami: there are no pending withdrawals requests');
IStrategyAPS strategy = _poolInfo[defaultWithdrawPid].strategy;
uint256 lpSharesTotal;
uint256 minAmountTotal;
uint256 i;
address user;
PendingWithdrawal memory withdrawal;
for (i = 0; i < userList.length; i++) {
user = userList[i];
withdrawal = _pendingWithdrawals[user];
if (balanceOf(user) < withdrawal.lpShares) {
emit FailedWithdrawal(user, withdrawal.tokenAmount, withdrawal.lpShares);
delete _pendingWithdrawals[user];
continue;
}
lpSharesTotal += withdrawal.lpShares;
minAmountTotal += withdrawal.tokenAmount;
}
require(
lpSharesTotal <= _poolInfo[defaultWithdrawPid].lpShares,
'Zunami: Insufficient pool LP shares'
);
uint256 prevBalance = IERC20Metadata(token).balanceOf(address(this));
if (
!strategy.withdraw(
address(this),
calcLpRatioSafe(lpSharesTotal, _poolInfo[defaultWithdrawPid].lpShares),
minAmountTotal
)
) {
for (i = 0; i < userList.length; i++) {
user = userList[i];
withdrawal = _pendingWithdrawals[user];
emit FailedWithdrawal(user, withdrawal.tokenAmount, withdrawal.lpShares);
delete _pendingWithdrawals[user];
}
return;
}
uint256 diffBalance = IERC20Metadata(token).balanceOf(address(this)) - prevBalance;
for (i = 0; i < userList.length; i++) {
user = userList[i];
withdrawal = _pendingWithdrawals[user];
uint256 userDeposit = (totalDeposited * withdrawal.lpShares) / totalSupply();
_burn(user, withdrawal.lpShares);
_poolInfo[defaultWithdrawPid].lpShares -= withdrawal.lpShares;
totalDeposited -= userDeposit;
uint256 transferAmount = (diffBalance * withdrawal.lpShares) / lpSharesTotal;
if (transferAmount > 0) {
IERC20Metadata(token).safeTransfer(user, transferAmount);
}
emit Withdrawn(
user,
withdrawal.tokenAmount,
withdrawal.lpShares
);
delete _pendingWithdrawals[user];
}
}
function deposit(uint256 amount)
external
whenNotPaused
startedPool
returns (uint256)
{
IStrategyAPS strategy = _poolInfo[defaultDepositPid].strategy;
uint256 holdings = totalHoldings();
if (amount > 0) {
IERC20Metadata(token).safeTransferFrom(
_msgSender(),
address(strategy),
amount
);
}
uint256 newDeposited = strategy.deposit(amount);
require(newDeposited > 0, 'Zunami: too low deposit!');
uint256 lpShares = 0;
if (totalSupply() == 0) {
lpShares = newDeposited;
} else {
lpShares = (totalSupply() * newDeposited) / holdings;
}
_mint(_msgSender(), lpShares);
_poolInfo[defaultDepositPid].lpShares += lpShares;
totalDeposited += newDeposited;
emit Deposited(_msgSender(), amount, lpShares, defaultDepositPid);
return lpShares;
}
function withdraw(
uint256 lpShares,
uint256 tokenAmount
) external whenNotPaused startedPool {
IStrategyAPS strategy = _poolInfo[defaultWithdrawPid].strategy;
address userAddr = _msgSender();
require(balanceOf(userAddr) >= lpShares, 'Zunami: not enough LP balance');
require(
strategy.withdraw(
userAddr,
calcLpRatioSafe(lpShares, _poolInfo[defaultWithdrawPid].lpShares),
tokenAmount
),
'Zunami: incorrect withdraw params'
);
uint256 userDeposit = (totalDeposited * lpShares) / totalSupply();
_burn(userAddr, lpShares);
_poolInfo[defaultWithdrawPid].lpShares -= lpShares;
totalDeposited -= userDeposit;
emit Withdrawn(userAddr, tokenAmount, lpShares);
}
function calcWithdrawOneCoin(uint256 lpShares)
external
view
returns (uint256 tokenAmount)
{
require(lpShares <= balanceOf(_msgSender()), 'Zunami: not enough LP balance');
uint256 lpShareRatio = calcLpRatioSafe(lpShares, _poolInfo[defaultWithdrawPid].lpShares);
return _poolInfo[defaultWithdrawPid].strategy.calcWithdrawOneCoin(lpShareRatio);
}
function calcSharesAmount(uint256 tokenAmount, bool isDeposit)
external
view
returns (uint256 lpShares)
{
return _poolInfo[defaultWithdrawPid].strategy.calcSharesAmount(tokenAmount, isDeposit);
}
function addPool(address _strategyAddr) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_strategyAddr != address(0), 'Zunami: zero strategy addr');
for(uint256 i = 0; i < _poolInfo.length; i++) {
require(_strategyAddr != address(_poolInfo[i].strategy), 'Zunami: dublicate strategy addr');
}
uint256 startTime = block.timestamp + (launched ? MIN_LOCK_TIME : 0);
_poolInfo.push(
PoolInfo({ strategy: IStrategyAPS(_strategyAddr), startTime: startTime, lpShares: 0 })
);
emit AddedPool(_poolInfo.length - 1, _strategyAddr, startTime);
}
function setDefaultDepositPid(uint256 _newPoolId) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_newPoolId < _poolInfo.length, 'Zunami: incorrect default deposit pool id');
defaultDepositPid = _newPoolId;
emit SetDefaultDepositPid(_newPoolId);
}
function setDefaultWithdrawPid(uint256 _newPoolId) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_newPoolId < _poolInfo.length, 'Zunami: incorrect default withdraw pool id');
defaultWithdrawPid = _newPoolId;
emit SetDefaultWithdrawPid(_newPoolId);
}
function launch() external onlyRole(DEFAULT_ADMIN_ROLE) {
launched = true;
}
function moveFundsBatch(
uint256[] memory _strategies,
uint256[] memory withdrawalsPercents,
uint256 _receiverStrategyId
) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(
_strategies.length == withdrawalsPercents.length,
'Zunami: incorrect arguments for the moveFundsBatch'
);
require(_receiverStrategyId < _poolInfo.length, 'Zunami: incorrect a receiver strategy ID');
uint256 tokenBalance = IERC20Metadata(token).balanceOf(address(this));
uint256 pid;
uint256 zunamiLp;
for (uint256 i = 0; i < _strategies.length; i++) {
pid = _strategies[i];
zunamiLp += _moveFunds(pid, withdrawalsPercents[i]);
}
uint256 tokensRemainder = IERC20Metadata(token).balanceOf(address(this)) - tokenBalance;
if (tokensRemainder > 0) {
IERC20Metadata(token).safeTransfer(
address(_poolInfo[_receiverStrategyId].strategy),
tokensRemainder
);
}
_poolInfo[_receiverStrategyId].lpShares += zunamiLp;
require(
_poolInfo[_receiverStrategyId].strategy.deposit(tokensRemainder) > 0,
'Zunami: Too low amount!'
);
}
function _moveFunds(uint256 pid, uint256 withdrawAmount) private returns (uint256) {
uint256 currentLpAmount;
if (withdrawAmount == FUNDS_DENOMINATOR) {
_poolInfo[pid].strategy.withdrawAll();
currentLpAmount = _poolInfo[pid].lpShares;
_poolInfo[pid].lpShares = 0;
} else {
currentLpAmount = (_poolInfo[pid].lpShares * withdrawAmount) / FUNDS_DENOMINATOR;
_poolInfo[pid].strategy.withdraw(
address(this),
calcLpRatioSafe(currentLpAmount, _poolInfo[pid].lpShares),
0
);
_poolInfo[pid].lpShares = _poolInfo[pid].lpShares - currentLpAmount;
}
return currentLpAmount;
}
function removePendingDeposit() external {
uint256 pendingDeposit_ = _pendingDeposits[_msgSender()];
if (pendingDeposit_ > 0) {
IERC20Metadata(token).safeTransfer(
_msgSender(),
pendingDeposit_
);
}
delete _pendingDeposits[_msgSender()];
}
function removePendingWithdrawal() external {
delete _pendingWithdrawals[_msgSender()];
}
function withdrawStuckToken(IERC20Metadata _token) external onlyRole(DEFAULT_ADMIN_ROLE) {
uint256 tokenBalance = _token.balanceOf(address(this));
if (tokenBalance > 0) {
_token.safeTransfer(_msgSender(), tokenBalance);
}
}
function updateOperator(address _newOperator) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(OPERATOR_ROLE, _newOperator);
}
}
{
"compilationTarget": {
"contracts/ZunamiAPS.sol": "ZunamiAPS"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"address","name":"strategyAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"}],"name":"AddedPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[],"name":"AutoCompoundAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feeValue","type":"uint256"}],"name":"ClaimedAllManagementFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreatedPendingDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"CreatedPendingWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pid","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amounts","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpShares","type":"uint256"}],"name":"FailedDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amounts","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpShares","type":"uint256"}],"name":"FailedWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldManagementFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newManagementFee","type":"uint256"}],"name":"ManagementFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","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":"pid","type":"uint256"}],"name":"SetDefaultDepositPid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pid","type":"uint256"}],"name":"SetDefaultWithdrawPid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpShares","type":"uint256"}],"name":"Withdrawn","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":"FUNDS_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LP_RATIO_MULTIPLIER","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":"MIN_LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_ASSETS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_strategyAddr","type":"address"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"autoCompoundAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calcManagementFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"bool","name":"isDeposit","type":"bool"}],"name":"calcSharesAmount","outputs":[{"internalType":"uint256","name":"lpShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpShares","type":"uint256"}],"name":"calcWithdrawOneCoin","outputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAllManagementFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"userList","type":"address[]"}],"name":"completeDeposits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"userList","type":"address[]"}],"name":"completeWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"userList","type":"address[]"}],"name":"completeWithdrawalsOptimized","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultDepositPid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultWithdrawPid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"delegateDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpShares","type":"uint256"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"delegateWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"launch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"launched","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managementFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_strategies","type":"uint256[]"},{"internalType":"uint256[]","name":"withdrawalsPercents","type":"uint256[]"},{"internalType":"uint256","name":"_receiverStrategyId","type":"uint256"}],"name":"moveFundsBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"pendingDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"pendingWithdrawals","outputs":[{"components":[{"internalType":"uint256","name":"lpShares","type":"uint256"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"internalType":"struct ZunamiAPS.PendingWithdrawal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"}],"name":"poolInfo","outputs":[{"components":[{"internalType":"contract IStrategyAPS","name":"strategy","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"lpShares","type":"uint256"}],"internalType":"struct ZunamiAPS.PoolInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"removePendingDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removePendingWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPoolId","type":"uint256"}],"name":"setDefaultDepositPid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newPoolId","type":"uint256"}],"name":"setDefaultWithdrawPid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newManagementFee","type":"uint256"}],"name":"setManagementFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDeposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalHoldings","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOperator","type":"address"}],"name":"updateOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpShares","type":"uint256"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"_token","type":"address"}],"name":"withdrawStuckToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]