컴파일러
0.8.17+commit.8df45f5f
파일 1 / 21 : 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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
파일 2 / 21 : AddressArray.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
library AddressArray {
error IndexOutOfBounds();
error PopFromEmptyArray();
error OutputArrayTooSmall();
struct Data {
mapping(uint256 => uint256) _raw;
}
function length(Data storage self) internal view returns (uint256) {
return self._raw[0] >> 160;
}
function at(Data storage self, uint256 i) internal view returns (address) {
return address(uint160(self._raw[i]));
}
function get(Data storage self) internal view returns (address[] memory arr) {
uint256 lengthAndFirst = self._raw[0];
arr = new address[](lengthAndFirst >> 160);
_get(self, arr, lengthAndFirst);
}
function get(Data storage self, address[] memory output) internal view returns (address[] memory) {
return _get(self, output, self._raw[0]);
}
function _get(
Data storage self,
address[] memory output,
uint256 lengthAndFirst
) private view returns (address[] memory) {
uint256 len = lengthAndFirst >> 160;
if (len > output.length) revert OutputArrayTooSmall();
if (len > 0) {
output[0] = address(uint160(lengthAndFirst));
unchecked {
for (uint256 i = 1; i < len; i++) {
output[i] = address(uint160(self._raw[i]));
}
}
}
return output;
}
function push(Data storage self, address account) internal returns (uint256) {
unchecked {
uint256 lengthAndFirst = self._raw[0];
uint256 len = lengthAndFirst >> 160;
if (len == 0) {
self._raw[0] = (1 << 160) + uint160(account);
} else {
self._raw[0] = lengthAndFirst + (1 << 160);
self._raw[len] = uint160(account);
}
return len + 1;
}
}
function pop(Data storage self) internal {
unchecked {
uint256 lengthAndFirst = self._raw[0];
uint256 len = lengthAndFirst >> 160;
if (len == 0) revert PopFromEmptyArray();
self._raw[len - 1] = 0;
if (len > 1) {
self._raw[0] = lengthAndFirst - (1 << 160);
}
}
}
function set(
Data storage self,
uint256 index,
address account
) internal {
uint256 len = length(self);
if (index >= len) revert IndexOutOfBounds();
if (index == 0) {
self._raw[0] = (len << 160) | uint160(account);
} else {
self._raw[index] = uint160(account);
}
}
}
파일 3 / 21 : AddressSet.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
import "./AddressArray.sol";
library AddressSet {
using AddressArray for AddressArray.Data;
struct Data {
AddressArray.Data items;
mapping(address => uint256) lookup;
}
function length(Data storage s) internal view returns (uint256) {
return s.items.length();
}
function at(Data storage s, uint256 index) internal view returns (address) {
return s.items.at(index);
}
function contains(Data storage s, address item) internal view returns (bool) {
return s.lookup[item] != 0;
}
function add(Data storage s, address item) internal returns (bool) {
if (s.lookup[item] > 0) {
return false;
}
s.lookup[item] = s.items.push(item);
return true;
}
function remove(Data storage s, address item) internal returns (bool) {
uint256 index = s.lookup[item];
if (index == 0) {
return false;
}
if (index < s.items.length()) {
unchecked {
address lastItem = s.items.at(s.items.length() - 1);
s.items.set(index - 1, lastItem);
s.lookup[lastItem] = index;
}
}
s.items.pop();
delete s.lookup[item];
return true;
}
}
파일 4 / 21 : 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;
}
}
파일 5 / 21 : ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
_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 / 21 : ERC20Pods.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@1inch/solidity-utils/contracts/libraries/AddressSet.sol";
import "./interfaces/IERC20Pods.sol";
import "./interfaces/IPod.sol";
import "./libs/ReentrancyGuard.sol";
abstract contract ERC20Pods is ERC20, IERC20Pods, ReentrancyGuardExt {
using AddressSet for AddressSet.Data;
using AddressArray for AddressArray.Data;
using ReentrancyGuardLib for ReentrancyGuardLib.Data;
error PodAlreadyAdded();
error PodNotFound();
error InvalidPodAddress();
error PodsLimitReachedForAccount();
error InsufficientGas();
error ZeroPodsLimit();
uint256 public immutable podsLimit;
uint256 public immutable podCallGasLimit;
ReentrancyGuardLib.Data private _guard;
mapping(address => AddressSet.Data) private _pods;
constructor(uint256 podsLimit_, uint256 podCallGasLimit_) {
if (podsLimit_ == 0) revert ZeroPodsLimit();
podsLimit = podsLimit_;
podCallGasLimit = podCallGasLimit_;
_guard.init();
}
function hasPod(address account, address pod) public view virtual returns(bool) {
return _pods[account].contains(pod);
}
function podsCount(address account) public view virtual returns(uint256) {
return _pods[account].length();
}
function podAt(address account, uint256 index) public view virtual returns(address) {
return _pods[account].at(index);
}
function pods(address account) public view virtual returns(address[] memory) {
return _pods[account].items.get();
}
function balanceOf(address account) public nonReentrantView(_guard) view override(IERC20, ERC20) virtual returns(uint256) {
return super.balanceOf(account);
}
function podBalanceOf(address pod, address account) public nonReentrantView(_guard) view virtual returns(uint256) {
if (hasPod(account, pod)) {
return super.balanceOf(account);
}
return 0;
}
function addPod(address pod) public virtual {
_addPod(msg.sender, pod);
}
function removePod(address pod) public virtual {
_removePod(msg.sender, pod);
}
function removeAllPods() public virtual {
_removeAllPods(msg.sender);
}
function _addPod(address account, address pod) internal virtual {
if (pod == address(0)) revert InvalidPodAddress();
if (!_pods[account].add(pod)) revert PodAlreadyAdded();
if (_pods[account].length() > podsLimit) revert PodsLimitReachedForAccount();
emit PodAdded(account, pod);
uint256 balance = balanceOf(account);
if (balance > 0) {
_updateBalances(pod, address(0), account, balance);
}
}
function _removePod(address account, address pod) internal virtual {
if (!_pods[account].remove(pod)) revert PodNotFound();
emit PodRemoved(account, pod);
uint256 balance = balanceOf(account);
if (balance > 0) {
_updateBalances(pod, account, address(0), balance);
}
}
function _removeAllPods(address account) internal virtual {
address[] memory items = _pods[account].items.get();
uint256 balance = balanceOf(account);
unchecked {
for (uint256 i = items.length; i > 0; i--) {
_pods[account].remove(items[i - 1]);
emit PodRemoved(account, items[i - 1]);
if (balance > 0) {
_updateBalances(items[i - 1], account, address(0), balance);
}
}
}
}
function _updateBalances(address pod, address from, address to, uint256 amount) private {
bytes4 selector = IPod.updateBalances.selector;
bytes4 exception = InsufficientGas.selector;
uint256 gasLimit = podCallGasLimit;
assembly {
let ptr := mload(0x40)
mstore(ptr, selector)
mstore(add(ptr, 0x04), from)
mstore(add(ptr, 0x24), to)
mstore(add(ptr, 0x44), amount)
if lt(div(mul(gas(), 63), 64), gasLimit) {
mstore(0, exception)
revert(0, 4)
}
pop(call(gasLimit, pod, 0, ptr, 0x64, 0, 0))
}
}
function _afterTokenTransfer(address from, address to, uint256 amount) internal nonReentrant(_guard) override virtual {
super._afterTokenTransfer(from, to, amount);
unchecked {
if (amount > 0 && from != to) {
address[] memory a = _pods[from].items.get();
address[] memory b = _pods[to].items.get();
uint256 aLength = a.length;
uint256 bLength = b.length;
for (uint256 i = 0; i < aLength; i++) {
address pod = a[i];
uint256 j;
for (j = 0; j < bLength; j++) {
if (pod == b[j]) {
_updateBalances(pod, from, to, amount);
b[j] = address(0);
break;
}
}
if (j == bLength) {
_updateBalances(pod, from, address(0), amount);
}
}
for (uint256 j = 0; j < bLength; j++) {
address pod = b[j];
if (pod != address(0)) {
_updateBalances(pod, address(0), to, amount);
}
}
}
}
}
}
파일 7 / 21 : IDaiLikePermit.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
interface IDaiLikePermit {
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
파일 8 / 21 : IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
파일 9 / 21 : 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 / 21 : IERC20Pods.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IERC20Pods is IERC20 {
event PodAdded(address account, address pod);
event PodRemoved(address account, address pod);
function hasPod(address account, address pod) external view returns(bool);
function podsCount(address account) external view returns(uint256);
function podAt(address account, uint256 index) external view returns(address);
function pods(address account) external view returns(address[] memory);
function podBalanceOf(address pod, address account) external view returns(uint256);
function addPod(address pod) external;
function removePod(address pod) external;
function removeAllPods() external;
}
파일 11 / 21 : IPod.sol
pragma solidity ^0.8.0;
interface IPod {
function updateBalances(address from, address to, uint256 amount) external;
}
파일 12 / 21 : IVotable.sol
pragma solidity 0.8.17;
pragma abicoder v1;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IVotable is IERC20 {
function votingPowerOf(address account) external view returns (uint256);
}
파일 13 / 21 : Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1);
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
파일 14 / 21 : Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
파일 15 / 21 : Pod.sol
pragma solidity ^0.8.0;
import "./interfaces/IPod.sol";
import "./interfaces/IERC20Pods.sol";
abstract contract Pod is IPod {
error AccessDenied();
IERC20Pods public immutable token;
modifier onlyToken {
if (msg.sender != address(token)) revert AccessDenied();
_;
}
constructor(IERC20Pods token_) {
token = token_;
}
function updateBalances(address from, address to, uint256 amount) external onlyToken {
_updateBalances(from, to, amount);
}
function _updateBalances(address from, address to, uint256 amount) internal virtual;
}
파일 16 / 21 : ReentrancyGuard.sol
pragma solidity ^0.8.0;
library ReentrancyGuardLib {
error ReentrantCall();
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
struct Data {
uint256 _status;
}
function init(Data storage self) internal {
self._status = _NOT_ENTERED;
}
function enter(Data storage self) internal {
if (self._status == _ENTERED) revert ReentrantCall();
self._status = _ENTERED;
}
function exit(Data storage self) internal {
self._status = _NOT_ENTERED;
}
function check(Data storage self) internal view returns (bool) {
return self._status == _ENTERED;
}
}
contract ReentrancyGuardExt {
using ReentrancyGuardLib for ReentrancyGuardLib.Data;
modifier nonReentrant(ReentrancyGuardLib.Data storage self) {
self.enter();
_;
self.exit();
}
modifier nonReentrantView(ReentrancyGuardLib.Data storage self) {
if (self.check()) revert ReentrancyGuardLib.ReentrantCall();
_;
}
}
파일 17 / 21 : RevertReasonForwarder.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
library RevertReasonForwarder {
function reRevert() internal pure {
assembly {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
파일 18 / 21 : SafeERC20.sol
pragma solidity ^0.8.0;
pragma abicoder v1;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "../interfaces/IDaiLikePermit.sol";
import "../libraries/RevertReasonForwarder.sol";
library SafeERC20 {
error SafeTransferFailed();
error SafeTransferFromFailed();
error ForceApproveFailed();
error SafeIncreaseAllowanceFailed();
error SafeDecreaseAllowanceFailed();
error SafePermitBadLength();
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
bytes4 selector = token.transferFrom.selector;
bool success;
assembly {
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
success := call(gas(), token, 0, data, 100, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
if (!success) revert SafeTransferFromFailed();
}
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
if (!_makeCall(token, token.transfer.selector, to, value)) {
revert SafeTransferFailed();
}
}
function forceApprove(
IERC20 token,
address spender,
uint256 value
) internal {
if (!_makeCall(token, token.approve.selector, spender, value)) {
if (
!_makeCall(token, token.approve.selector, spender, 0) ||
!_makeCall(token, token.approve.selector, spender, value)
) {
revert ForceApproveFailed();
}
}
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
forceApprove(token, spender, allowance + value);
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > allowance) revert SafeDecreaseAllowanceFailed();
forceApprove(token, spender, allowance - value);
}
function safePermit(IERC20 token, bytes calldata permit) internal {
if (!tryPermit(token, permit)) RevertReasonForwarder.reRevert();
}
function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool) {
if (permit.length == 32 * 7) {
return _makeCalldataCall(token, IERC20Permit.permit.selector, permit);
}
if (permit.length == 32 * 8) {
return _makeCalldataCall(token, IDaiLikePermit.permit.selector, permit);
}
revert SafePermitBadLength();
}
function _makeCall(
IERC20 token,
bytes4 selector,
address to,
uint256 amount
) private returns (bool success) {
assembly {
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), to)
mstore(add(data, 0x24), amount)
success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
}
function _makeCalldataCall(
IERC20 token,
bytes4 selector,
bytes calldata args
) private returns (bool success) {
assembly {
let len := add(4, args.length)
let data := mload(0x40)
mstore(data, selector)
calldatacopy(add(data, 0x04), args.offset, args.length)
success := call(gas(), token, 0, data, len, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
}
}
파일 19 / 21 : St1inch.sol
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@1inch/erc20-pods/contracts/ERC20Pods.sol";
import "@1inch/erc20-pods/contracts/Pod.sol";
import "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import "./helpers/VotingPowerCalculator.sol";
import "./interfaces/IVotable.sol";
contract St1inch is ERC20Pods, Ownable, VotingPowerCalculator, IVotable {
using SafeERC20 for IERC20;
event EmergencyExitSet(bool status);
event MaxLossRatioSet(uint256 ratio);
event MinLockPeriodRatioSet(uint256 ratio);
event FeeReceiverSet(address receiver);
event DefaultFarmSet(address defaultFarm);
error ApproveDisabled();
error TransferDisabled();
error LockTimeMoreMaxLock();
error LockTimeLessMinLock();
error UnlockTimeHasNotCome();
error StakeUnlocked();
error MinLockPeriodRatioNotReached();
error MinReturnIsNotMet();
error MaxLossIsNotMet();
error MaxLossOverflow();
error LossIsTooBig();
error RescueAmountIsTooLarge();
error ExpBaseTooBig();
error ExpBaseTooSmall();
error DefaultFarmTokenMismatch();
error DepositsDisabled();
error ZeroAddress();
uint256 public constant MIN_LOCK_PERIOD = 30 days;
uint256 public constant MAX_LOCK_PERIOD = 2 * 365 days;
uint256 private constant _VOTING_POWER_DIVIDER = 20;
uint256 private constant _PODS_LIMIT = 5;
uint256 private constant _POD_CALL_GAS_LIMIT = 500_000;
uint256 private constant _ONE = 1e9;
IERC20 public immutable oneInch;
struct Depositor {
uint40 lockTime;
uint40 unlockTime;
uint176 amount;
}
mapping(address => Depositor) public depositors;
uint256 public totalDeposits;
bool public emergencyExit;
uint256 public maxLossRatio;
uint256 public minLockPeriodRatio;
address public feeReceiver;
address public defaultFarm;
constructor(IERC20 oneInch_, uint256 expBase_)
ERC20Pods(_PODS_LIMIT, _POD_CALL_GAS_LIMIT)
ERC20("Staking 1INCH v2", "st1INCH")
VotingPowerCalculator(expBase_, block.timestamp)
{
if (_votingPowerAt(1e18, block.timestamp + MAX_LOCK_PERIOD) * _VOTING_POWER_DIVIDER < 1e18) revert ExpBaseTooBig();
if (_votingPowerAt(1e18, block.timestamp + MAX_LOCK_PERIOD + 1) * _VOTING_POWER_DIVIDER > 1e18) revert ExpBaseTooSmall();
oneInch = oneInch_;
}
function setFeeReceiver(address feeReceiver_) external onlyOwner {
if (feeReceiver_ == address(0)) revert ZeroAddress();
feeReceiver = feeReceiver_;
emit FeeReceiverSet(feeReceiver_);
}
function setDefaultFarm(address defaultFarm_) external onlyOwner {
if (defaultFarm_ != address(0) && Pod(defaultFarm_).token() != this) revert DefaultFarmTokenMismatch();
defaultFarm = defaultFarm_;
emit DefaultFarmSet(defaultFarm_);
}
function setMaxLossRatio(uint256 maxLossRatio_) external onlyOwner {
if (maxLossRatio_ > _ONE) revert MaxLossOverflow();
maxLossRatio = maxLossRatio_;
emit MaxLossRatioSet(maxLossRatio_);
}
function setMinLockPeriodRatio(uint256 minLockPeriodRatio_) external onlyOwner {
if (minLockPeriodRatio_ > _ONE) revert MaxLossOverflow();
minLockPeriodRatio = minLockPeriodRatio_;
emit MinLockPeriodRatioSet(minLockPeriodRatio_);
}
function setEmergencyExit(bool emergencyExit_) external onlyOwner {
emergencyExit = emergencyExit_;
emit EmergencyExitSet(emergencyExit_);
}
function votingPowerOf(address account) external view returns (uint256) {
return _votingPowerAt(balanceOf(account), block.timestamp);
}
function votingPowerOfAt(address account, uint256 timestamp) external view returns (uint256) {
return _votingPowerAt(balanceOf(account), timestamp);
}
function votingPower(uint256 balance) external view returns (uint256) {
return _votingPowerAt(balance, block.timestamp);
}
function votingPowerAt(uint256 balance, uint256 timestamp) external view returns (uint256) {
return _votingPowerAt(balance, timestamp);
}
function deposit(uint256 amount, uint256 duration) external {
_deposit(msg.sender, amount, duration);
}
function depositWithPermit(uint256 amount, uint256 duration, bytes calldata permit) external {
oneInch.safePermit(permit);
_deposit(msg.sender, amount, duration);
}
function depositFor(address account, uint256 amount) external {
_deposit(account, amount, 0);
}
function depositForWithPermit(address account, uint256 amount, bytes calldata permit) external {
oneInch.safePermit(permit);
_deposit(account, amount, 0);
}
function _deposit(address account, uint256 amount, uint256 duration) private {
if (emergencyExit) revert DepositsDisabled();
Depositor memory depositor = depositors[account];
uint256 lockedTill = Math.max(depositor.unlockTime, block.timestamp) + duration;
uint256 lockLeft = lockedTill - block.timestamp;
if (lockLeft < MIN_LOCK_PERIOD) revert LockTimeLessMinLock();
if (lockLeft > MAX_LOCK_PERIOD) revert LockTimeMoreMaxLock();
uint256 balanceDiff = _balanceAt(depositor.amount + amount, lockedTill) / _VOTING_POWER_DIVIDER - balanceOf(account);
depositor.lockTime = uint40(duration == 0 ? depositor.lockTime : block.timestamp);
depositor.unlockTime = uint40(lockedTill);
depositor.amount += uint176(amount);
depositors[account] = depositor;
totalDeposits += amount;
_mint(account, balanceDiff);
if (amount > 0) {
oneInch.safeTransferFrom(msg.sender, address(this), amount);
}
if (defaultFarm != address(0) && !hasPod(account, defaultFarm)) {
_addPod(account, defaultFarm);
}
}
function earlyWithdraw(uint256 minReturn, uint256 maxLoss) external {
earlyWithdrawTo(msg.sender, minReturn, maxLoss);
}
function earlyWithdrawTo(address to, uint256 minReturn, uint256 maxLoss) public {
Depositor memory depositor = depositors[msg.sender];
if (emergencyExit || block.timestamp >= depositor.unlockTime) revert StakeUnlocked();
uint256 allowedExitTime = depositor.lockTime + (depositor.unlockTime - depositor.lockTime) * minLockPeriodRatio / _ONE;
if (block.timestamp < allowedExitTime) revert MinLockPeriodRatioNotReached();
uint256 amount = depositor.amount;
if (amount > 0) {
uint256 balance = balanceOf(msg.sender);
(uint256 loss, uint256 ret) = _earlyWithdrawLoss(amount, balance);
if (ret < minReturn) revert MinReturnIsNotMet();
if (loss > maxLoss) revert MaxLossIsNotMet();
if (loss > amount * maxLossRatio / _ONE) revert LossIsTooBig();
_withdraw(depositor, balance);
oneInch.safeTransfer(to, ret);
oneInch.safeTransfer(feeReceiver, loss);
}
}
function earlyWithdrawLoss(address account) external view returns (uint256 loss, uint256 ret, bool canWithdraw) {
uint256 amount = depositors[account].amount;
(loss, ret) = _earlyWithdrawLoss(amount, balanceOf(account));
canWithdraw = loss <= amount * maxLossRatio / _ONE;
}
function _earlyWithdrawLoss(uint256 depAmount, uint256 stBalance) private view returns (uint256 loss, uint256 ret) {
ret = (depAmount - _votingPowerAt(stBalance, block.timestamp)) * 100 / 95;
loss = depAmount - ret;
}
function withdraw() external {
withdrawTo(msg.sender);
}
function withdrawTo(address to) public {
Depositor memory depositor = depositors[msg.sender];
if (!emergencyExit && block.timestamp < depositor.unlockTime) revert UnlockTimeHasNotCome();
uint256 amount = depositor.amount;
if (amount > 0) {
_withdraw(depositor, balanceOf(msg.sender));
oneInch.safeTransfer(to, amount);
}
}
function _withdraw(Depositor memory depositor, uint256 balance) private {
totalDeposits -= depositor.amount;
depositor.amount = 0;
depositor.unlockTime = uint40(Math.min(depositor.unlockTime, block.timestamp));
depositors[msg.sender] = depositor;
_burn(msg.sender, balance);
}
function rescueFunds(IERC20 token, uint256 amount) external onlyOwner {
if (address(token) == address(0)) {
Address.sendValue(payable(msg.sender), amount);
} else {
if (token == oneInch) {
if (amount > oneInch.balanceOf(address(this)) - totalDeposits) revert RescueAmountIsTooLarge();
}
token.safeTransfer(msg.sender, amount);
}
}
function approve(address, uint256) public pure override(IERC20, ERC20) returns (bool) {
revert ApproveDisabled();
}
function transfer(address, uint256) public pure override(IERC20, ERC20) returns (bool) {
revert TransferDisabled();
}
function transferFrom(address, address, uint256) public pure override(IERC20, ERC20) returns (bool) {
revert TransferDisabled();
}
function increaseAllowance(address, uint256) public pure override returns (bool) {
revert ApproveDisabled();
}
function decreaseAllowance(address, uint256) public pure override returns (bool) {
revert ApproveDisabled();
}
}
파일 20 / 21 : VotingPowerCalculator.sol
pragma solidity 0.8.17;
contract VotingPowerCalculator {
error OriginInTheFuture();
uint256 private constant _ONE = 1e18;
uint256 public immutable origin;
uint256 public immutable expBase;
uint256 private immutable _expTable0;
uint256 private immutable _expTable1;
uint256 private immutable _expTable2;
uint256 private immutable _expTable3;
uint256 private immutable _expTable4;
uint256 private immutable _expTable5;
uint256 private immutable _expTable6;
uint256 private immutable _expTable7;
uint256 private immutable _expTable8;
uint256 private immutable _expTable9;
uint256 private immutable _expTable10;
uint256 private immutable _expTable11;
uint256 private immutable _expTable12;
uint256 private immutable _expTable13;
uint256 private immutable _expTable14;
uint256 private immutable _expTable15;
uint256 private immutable _expTable16;
uint256 private immutable _expTable17;
uint256 private immutable _expTable18;
uint256 private immutable _expTable19;
uint256 private immutable _expTable20;
uint256 private immutable _expTable21;
uint256 private immutable _expTable22;
uint256 private immutable _expTable23;
uint256 private immutable _expTable24;
uint256 private immutable _expTable25;
uint256 private immutable _expTable26;
uint256 private immutable _expTable27;
uint256 private immutable _expTable28;
uint256 private immutable _expTable29;
constructor(uint256 expBase_, uint256 origin_) {
if (origin_ > block.timestamp) revert OriginInTheFuture();
origin = origin_;
expBase = expBase_;
_expTable0 = expBase_;
_expTable1 = (_expTable0 * _expTable0) / _ONE;
_expTable2 = (_expTable1 * _expTable1) / _ONE;
_expTable3 = (_expTable2 * _expTable2) / _ONE;
_expTable4 = (_expTable3 * _expTable3) / _ONE;
_expTable5 = (_expTable4 * _expTable4) / _ONE;
_expTable6 = (_expTable5 * _expTable5) / _ONE;
_expTable7 = (_expTable6 * _expTable6) / _ONE;
_expTable8 = (_expTable7 * _expTable7) / _ONE;
_expTable9 = (_expTable8 * _expTable8) / _ONE;
_expTable10 = (_expTable9 * _expTable9) / _ONE;
_expTable11 = (_expTable10 * _expTable10) / _ONE;
_expTable12 = (_expTable11 * _expTable11) / _ONE;
_expTable13 = (_expTable12 * _expTable12) / _ONE;
_expTable14 = (_expTable13 * _expTable13) / _ONE;
_expTable15 = (_expTable14 * _expTable14) / _ONE;
_expTable16 = (_expTable15 * _expTable15) / _ONE;
_expTable17 = (_expTable16 * _expTable16) / _ONE;
_expTable18 = (_expTable17 * _expTable17) / _ONE;
_expTable19 = (_expTable18 * _expTable18) / _ONE;
_expTable20 = (_expTable19 * _expTable19) / _ONE;
_expTable21 = (_expTable20 * _expTable20) / _ONE;
_expTable22 = (_expTable21 * _expTable21) / _ONE;
_expTable23 = (_expTable22 * _expTable22) / _ONE;
_expTable24 = (_expTable23 * _expTable23) / _ONE;
_expTable25 = (_expTable24 * _expTable24) / _ONE;
_expTable26 = (_expTable25 * _expTable25) / _ONE;
_expTable27 = (_expTable26 * _expTable26) / _ONE;
_expTable28 = (_expTable27 * _expTable27) / _ONE;
_expTable29 = (_expTable28 * _expTable28) / _ONE;
}
function _votingPowerAt(uint256 balance, uint256 timestamp) internal view returns (uint256 votingPower) {
timestamp = timestamp < origin ? origin : timestamp;
unchecked {
uint256 t = timestamp - origin;
votingPower = balance;
if (t & 0x01 != 0) {
votingPower = (votingPower * _expTable0) / _ONE;
}
if (t & 0x02 != 0) {
votingPower = (votingPower * _expTable1) / _ONE;
}
if (t & 0x04 != 0) {
votingPower = (votingPower * _expTable2) / _ONE;
}
if (t & 0x08 != 0) {
votingPower = (votingPower * _expTable3) / _ONE;
}
if (t & 0x10 != 0) {
votingPower = (votingPower * _expTable4) / _ONE;
}
if (t & 0x20 != 0) {
votingPower = (votingPower * _expTable5) / _ONE;
}
if (t & 0x40 != 0) {
votingPower = (votingPower * _expTable6) / _ONE;
}
if (t & 0x80 != 0) {
votingPower = (votingPower * _expTable7) / _ONE;
}
if (t & 0x100 != 0) {
votingPower = (votingPower * _expTable8) / _ONE;
}
if (t & 0x200 != 0) {
votingPower = (votingPower * _expTable9) / _ONE;
}
if (t & 0x400 != 0) {
votingPower = (votingPower * _expTable10) / _ONE;
}
if (t & 0x800 != 0) {
votingPower = (votingPower * _expTable11) / _ONE;
}
if (t & 0x1000 != 0) {
votingPower = (votingPower * _expTable12) / _ONE;
}
if (t & 0x2000 != 0) {
votingPower = (votingPower * _expTable13) / _ONE;
}
if (t & 0x4000 != 0) {
votingPower = (votingPower * _expTable14) / _ONE;
}
if (t & 0x8000 != 0) {
votingPower = (votingPower * _expTable15) / _ONE;
}
if (t & 0x10000 != 0) {
votingPower = (votingPower * _expTable16) / _ONE;
}
if (t & 0x20000 != 0) {
votingPower = (votingPower * _expTable17) / _ONE;
}
if (t & 0x40000 != 0) {
votingPower = (votingPower * _expTable18) / _ONE;
}
if (t & 0x80000 != 0) {
votingPower = (votingPower * _expTable19) / _ONE;
}
if (t & 0x100000 != 0) {
votingPower = (votingPower * _expTable20) / _ONE;
}
if (t & 0x200000 != 0) {
votingPower = (votingPower * _expTable21) / _ONE;
}
if (t & 0x400000 != 0) {
votingPower = (votingPower * _expTable22) / _ONE;
}
if (t & 0x800000 != 0) {
votingPower = (votingPower * _expTable23) / _ONE;
}
if (t & 0x1000000 != 0) {
votingPower = (votingPower * _expTable24) / _ONE;
}
if (t & 0x2000000 != 0) {
votingPower = (votingPower * _expTable25) / _ONE;
}
if (t & 0x4000000 != 0) {
votingPower = (votingPower * _expTable26) / _ONE;
}
if (t & 0x8000000 != 0) {
votingPower = (votingPower * _expTable27) / _ONE;
}
if (t & 0x10000000 != 0) {
votingPower = (votingPower * _expTable28) / _ONE;
}
if (t & 0x20000000 != 0) {
votingPower = (votingPower * _expTable29) / _ONE;
}
}
return votingPower;
}
function _balanceAt(uint256 votingPower, uint256 timestamp) internal view returns (uint256 balance) {
timestamp = timestamp < origin ? origin : timestamp;
unchecked {
uint256 t = timestamp - origin;
balance = votingPower;
if (t & 0x01 != 0) {
balance = (balance * _ONE) / _expTable0;
}
if (t & 0x02 != 0) {
balance = (balance * _ONE) / _expTable1;
}
if (t & 0x04 != 0) {
balance = (balance * _ONE) / _expTable2;
}
if (t & 0x08 != 0) {
balance = (balance * _ONE) / _expTable3;
}
if (t & 0x10 != 0) {
balance = (balance * _ONE) / _expTable4;
}
if (t & 0x20 != 0) {
balance = (balance * _ONE) / _expTable5;
}
if (t & 0x40 != 0) {
balance = (balance * _ONE) / _expTable6;
}
if (t & 0x80 != 0) {
balance = (balance * _ONE) / _expTable7;
}
if (t & 0x100 != 0) {
balance = (balance * _ONE) / _expTable8;
}
if (t & 0x200 != 0) {
balance = (balance * _ONE) / _expTable9;
}
if (t & 0x400 != 0) {
balance = (balance * _ONE) / _expTable10;
}
if (t & 0x800 != 0) {
balance = (balance * _ONE) / _expTable11;
}
if (t & 0x1000 != 0) {
balance = (balance * _ONE) / _expTable12;
}
if (t & 0x2000 != 0) {
balance = (balance * _ONE) / _expTable13;
}
if (t & 0x4000 != 0) {
balance = (balance * _ONE) / _expTable14;
}
if (t & 0x8000 != 0) {
balance = (balance * _ONE) / _expTable15;
}
if (t & 0x10000 != 0) {
balance = (balance * _ONE) / _expTable16;
}
if (t & 0x20000 != 0) {
balance = (balance * _ONE) / _expTable17;
}
if (t & 0x40000 != 0) {
balance = (balance * _ONE) / _expTable18;
}
if (t & 0x80000 != 0) {
balance = (balance * _ONE) / _expTable19;
}
if (t & 0x100000 != 0) {
balance = (balance * _ONE) / _expTable20;
}
if (t & 0x200000 != 0) {
balance = (balance * _ONE) / _expTable21;
}
if (t & 0x400000 != 0) {
balance = (balance * _ONE) / _expTable22;
}
if (t & 0x800000 != 0) {
balance = (balance * _ONE) / _expTable23;
}
if (t & 0x1000000 != 0) {
balance = (balance * _ONE) / _expTable24;
}
if (t & 0x2000000 != 0) {
balance = (balance * _ONE) / _expTable25;
}
if (t & 0x4000000 != 0) {
balance = (balance * _ONE) / _expTable26;
}
if (t & 0x8000000 != 0) {
balance = (balance * _ONE) / _expTable27;
}
if (t & 0x10000000 != 0) {
balance = (balance * _ONE) / _expTable28;
}
if (t & 0x20000000 != 0) {
balance = (balance * _ONE) / _expTable29;
}
}
return balance;
}
}
파일 21 / 21 : draft-IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
{
"compilationTarget": {
"contracts/St1inch.sol": "St1inch"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"oneInch_","type":"address"},{"internalType":"uint256","name":"expBase_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApproveDisabled","type":"error"},{"inputs":[],"name":"DefaultFarmTokenMismatch","type":"error"},{"inputs":[],"name":"DepositsDisabled","type":"error"},{"inputs":[],"name":"ExpBaseTooBig","type":"error"},{"inputs":[],"name":"ExpBaseTooSmall","type":"error"},{"inputs":[],"name":"IndexOutOfBounds","type":"error"},{"inputs":[],"name":"InsufficientGas","type":"error"},{"inputs":[],"name":"InvalidPodAddress","type":"error"},{"inputs":[],"name":"LockTimeLessMinLock","type":"error"},{"inputs":[],"name":"LockTimeMoreMaxLock","type":"error"},{"inputs":[],"name":"LossIsTooBig","type":"error"},{"inputs":[],"name":"MaxLossIsNotMet","type":"error"},{"inputs":[],"name":"MaxLossOverflow","type":"error"},{"inputs":[],"name":"MinLockPeriodRatioNotReached","type":"error"},{"inputs":[],"name":"MinReturnIsNotMet","type":"error"},{"inputs":[],"name":"OriginInTheFuture","type":"error"},{"inputs":[],"name":"OutputArrayTooSmall","type":"error"},{"inputs":[],"name":"PodAlreadyAdded","type":"error"},{"inputs":[],"name":"PodNotFound","type":"error"},{"inputs":[],"name":"PodsLimitReachedForAccount","type":"error"},{"inputs":[],"name":"PopFromEmptyArray","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RescueAmountIsTooLarge","type":"error"},{"inputs":[],"name":"SafePermitBadLength","type":"error"},{"inputs":[],"name":"SafeTransferFailed","type":"error"},{"inputs":[],"name":"SafeTransferFromFailed","type":"error"},{"inputs":[],"name":"StakeUnlocked","type":"error"},{"inputs":[],"name":"TransferDisabled","type":"error"},{"inputs":[],"name":"UnlockTimeHasNotCome","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroPodsLimit","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"defaultFarm","type":"address"}],"name":"DefaultFarmSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"EmergencyExitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"FeeReceiverSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ratio","type":"uint256"}],"name":"MaxLossRatioSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ratio","type":"uint256"}],"name":"MinLockPeriodRatioSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"pod","type":"address"}],"name":"PodAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"pod","type":"address"}],"name":"PodRemoved","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"},{"inputs":[],"name":"MAX_LOCK_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LOCK_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pod","type":"address"}],"name":"addPod","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":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"defaultFarm","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"depositForWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"depositWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositors","outputs":[{"internalType":"uint40","name":"lockTime","type":"uint40"},{"internalType":"uint40","name":"unlockTime","type":"uint40"},{"internalType":"uint176","name":"amount","type":"uint176"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256","name":"maxLoss","type":"uint256"}],"name":"earlyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"earlyWithdrawLoss","outputs":[{"internalType":"uint256","name":"loss","type":"uint256"},{"internalType":"uint256","name":"ret","type":"uint256"},{"internalType":"bool","name":"canWithdraw","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256","name":"maxLoss","type":"uint256"}],"name":"earlyWithdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyExit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expBase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"pod","type":"address"}],"name":"hasPod","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"maxLossRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minLockPeriodRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oneInch","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"origin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"podAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pod","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"podBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"podCallGasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"pods","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"podsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"podsLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"removeAllPods","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pod","type":"address"}],"name":"removePod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"defaultFarm_","type":"address"}],"name":"setDefaultFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"emergencyExit_","type":"bool"}],"name":"setEmergencyExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeReceiver_","type":"address"}],"name":"setFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxLossRatio_","type":"uint256"}],"name":"setMaxLossRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minLockPeriodRatio_","type":"uint256"}],"name":"setMinLockPeriodRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDeposits","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":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"name":"votingPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"votingPowerAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"votingPowerOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"votingPowerOfAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]