// SPDX-License-Identifier: AGPL-3.0-or-later // hevm: flattened sources of contracts/StakeLocker.sol
pragma solidity =0.6.11 >=0.6.0 <0.8.0 >=0.6.2 <0.8.0;
////// contracts/interfaces/IMapleGlobals.sol
/* pragma solidity 0.6.11; */
interface IMapleGlobals {
function pendingGovernor() external view returns (address);
function governor() external view returns (address);
function globalAdmin() external view returns (address);
function mpl() external view returns (address);
function mapleTreasury() external view returns (address);
function isValidBalancerPool(address) external view returns (bool);
function treasuryFee() external view returns (uint256);
function investorFee() external view returns (uint256);
function defaultGracePeriod() external view returns (uint256);
function fundingPeriod() external view returns (uint256);
function swapOutRequired() external view returns (uint256);
function isValidLiquidityAsset(address) external view returns (bool);
function isValidCollateralAsset(address) external view returns (bool);
function isValidPoolDelegate(address) external view returns (bool);
function validCalcs(address) external view returns (bool);
function isValidCalc(address, uint8) external view returns (bool);
function getLpCooldownParams() external view returns (uint256, uint256);
function isValidLoanFactory(address) external view returns (bool);
function isValidSubFactory(address, address, uint8) external view returns (bool);
function isValidPoolFactory(address) external view returns (bool);
function getLatestPrice(address) external view returns (uint256);
function defaultUniswapPath(address, address) external view returns (address);
function minLoanEquity() external view returns (uint256);
function maxSwapSlippage() external view returns (uint256);
function protocolPaused() external view returns (bool);
function stakerCooldownPeriod() external view returns (uint256);
function lpCooldownPeriod() external view returns (uint256);
function stakerUnstakeWindow() external view returns (uint256);
function lpWithdrawWindow() external view returns (uint256);
function oracleFor(address) external view returns (address);
function validSubFactories(address, address) external view returns (bool);
function setStakerCooldownPeriod(uint256) external;
function setLpCooldownPeriod(uint256) external;
function setStakerUnstakeWindow(uint256) external;
function setLpWithdrawWindow(uint256) external;
function setMaxSwapSlippage(uint256) external;
function setGlobalAdmin(address) external;
function setValidBalancerPool(address, bool) external;
function setProtocolPause(bool) external;
function setValidPoolFactory(address, bool) external;
function setValidLoanFactory(address, bool) external;
function setValidSubFactory(address, address, bool) external;
function setDefaultUniswapPath(address, address, address) external;
function setPoolDelegateAllowlist(address, bool) external;
function setCollateralAsset(address, bool) external;
function setLiquidityAsset(address, bool) external;
function setCalc(address, bool) external;
function setInvestorFee(uint256) external;
function setTreasuryFee(uint256) external;
function setMapleTreasury(address) external;
function setDefaultGracePeriod(uint256) external;
function setMinLoanEquity(uint256) external;
function setFundingPeriod(uint256) external;
function setSwapOutRequired(uint256) external;
function setPriceOracle(address, address) external;
function setPendingGovernor(address) external;
function acceptGovernor() external;
}
////// contracts/token/interfaces/IBaseFDT.sol
/* pragma solidity 0.6.11; */
interface IBaseFDT {
/**
@dev Returns the total amount of funds a given address is able to withdraw currently.
@param owner Address of FDT holder.
@return A uint256 representing the available funds for a given account.
*/
function withdrawableFundsOf(address owner) external view returns (uint256);
/**
@dev Withdraws all available funds for a FDT holder.
*/
function withdrawFunds() external;
/**
@dev This event emits when new funds are distributed.
@param by The address of the sender that distributed funds.
@param fundsDistributed The amount of funds received for distribution.
*/
event FundsDistributed(address indexed by, uint256 fundsDistributed);
/**
@dev This event emits when distributed funds are withdrawn by a token holder.
@param by The address of the receiver of funds.
@param fundsWithdrawn The amount of funds that were withdrawn.
@param totalWithdrawn The total amount of funds that were withdrawn.
*/
event FundsWithdrawn(address indexed by, uint256 fundsWithdrawn, uint256 totalWithdrawn);
}
////// lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
/* pragma solidity >=0.6.0 <0.8.0; */
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
////// contracts/token/interfaces/IBasicFDT.sol
/* pragma solidity 0.6.11; */
/* import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; */
/* import "./IBaseFDT.sol"; */
interface IBasicFDT is IBaseFDT, IERC20 {
event PointsPerShareUpdated(uint256);
event PointsCorrectionUpdated(address indexed, int256);
function withdrawnFundsOf(address) external view returns (uint256);
function accumulativeFundsOf(address) external view returns (uint256);
function updateFundsReceived() external;
}
////// contracts/token/interfaces/IExtendedFDT.sol
/* pragma solidity 0.6.11; */
/* import "./IBasicFDT.sol"; */
interface IExtendedFDT is IBasicFDT {
event LossesPerShareUpdated(uint256);
event LossesCorrectionUpdated(address indexed, int256);
event LossesDistributed(address indexed, uint256);
event LossesRecognized(address indexed, uint256, uint256);
function lossesPerShare() external view returns (uint256);
function recognizableLossesOf(address) external view returns (uint256);
function recognizedLossesOf(address) external view returns (uint256);
function accumulativeLossesOf(address) external view returns (uint256);
function updateLossesReceived() external;
}
////// contracts/token/interfaces/IPoolFDT.sol
/* pragma solidity 0.6.11; */
/* import "./IExtendedFDT.sol"; */
interface IPoolFDT is IExtendedFDT {
function interestSum() external view returns (uint256);
function poolLosses() external view returns (uint256);
function interestBalance() external view returns (uint256);
function lossesBalance() external view returns (uint256);
}
////// contracts/interfaces/IPool.sol
/* pragma solidity 0.6.11; */
/* import "../token/interfaces/IPoolFDT.sol"; */
interface IPool is IPoolFDT {
function poolDelegate() external view returns (address);
function poolAdmins(address) external view returns (bool);
function deposit(uint256) external;
function increaseCustodyAllowance(address, uint256) external;
function transferByCustodian(address, address, uint256) external;
function poolState() external view returns (uint256);
function deactivate() external;
function finalize() external;
function claim(address, address) external returns (uint256[7] memory);
function setLockupPeriod(uint256) external;
function setStakingFee(uint256) external;
function setPoolAdmin(address, bool) external;
function fundLoan(address, address, uint256) external;
function withdraw(uint256) external;
function superFactory() external view returns (address);
function triggerDefault(address, address) external;
function isPoolFinalized() external view returns (bool);
function setOpenToPublic(bool) external;
function setAllowList(address, bool) external;
function allowedLiquidityProviders(address) external view returns (bool);
function openToPublic() external view returns (bool);
function intendToWithdraw() external;
function DL_FACTORY() external view returns (uint8);
function liquidityAsset() external view returns (address);
function liquidityLocker() external view returns (address);
function stakeAsset() external view returns (address);
function stakeLocker() external view returns (address);
function stakingFee() external view returns (uint256);
function delegateFee() external view returns (uint256);
function principalOut() external view returns (uint256);
function liquidityCap() external view returns (uint256);
function lockupPeriod() external view returns (uint256);
function depositDate(address) external view returns (uint256);
function debtLockers(address, address) external view returns (address);
function withdrawCooldown(address) external view returns (uint256);
function setLiquidityCap(uint256) external;
function cancelWithdraw() external;
function reclaimERC20(address) external;
function BPTVal(address, address, address, address) external view returns (uint256);
function isDepositAllowed(uint256) external view returns (bool);
function getInitialStakeRequirements() external view returns (uint256, uint256, bool, uint256, uint256);
}
////// contracts/interfaces/IPoolFactory.sol
/* pragma solidity 0.6.11; */
interface IPoolFactory {
function LL_FACTORY() external view returns (uint8);
function SL_FACTORY() external view returns (uint8);
function poolsCreated() external view returns (uint256);
function globals() external view returns (address);
function pools(uint256) external view returns (address);
function isPool(address) external view returns (bool);
function poolFactoryAdmins(address) external view returns (bool);
function setGlobals(address) external;
function createPool(address, address, address, address, uint256, uint256, uint256) external returns (address);
function setPoolFactoryAdmin(address, bool) external;
function pause() external;
function unpause() external;
}
////// contracts/math/SafeMathInt.sol
/* pragma solidity 0.6.11; */
library SafeMathInt {
function toUint256Safe(int256 a) internal pure returns (uint256) {
require(a >= 0, "SMI:NEG");
return uint256(a);
}
}
////// contracts/math/SafeMathUint.sol
/* pragma solidity 0.6.11; */
library SafeMathUint {
function toInt256Safe(uint256 a) internal pure returns (int256 b) {
b = int256(a);
require(b >= 0, "SMU:OOB");
}
}
////// lib/openzeppelin-contracts/contracts/math/SafeMath.sol
/* pragma solidity >=0.6.0 <0.8.0; */
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
////// lib/openzeppelin-contracts/contracts/math/SignedSafeMath.sol
/* pragma solidity >=0.6.0 <0.8.0; */
/**
* @title SignedSafeMath
* @dev Signed math operations with safety checks that revert on error.
*/
library SignedSafeMath {
int256 constant private _INT256_MIN = -2**255;
/**
* @dev Returns the multiplication of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two signed integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
require(b != 0, "SignedSafeMath: division by zero");
require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
return c;
}
/**
* @dev Returns the subtraction of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
return c;
}
/**
* @dev Returns the addition of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
return c;
}
}
////// lib/openzeppelin-contracts/contracts/GSN/Context.sol
/* pragma solidity >=0.6.0 <0.8.0; */
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
////// lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol
/* pragma solidity >=0.6.0 <0.8.0; */
/* import "../../GSN/Context.sol"; */
/* import "./IERC20.sol"; */
/* import "../../math/SafeMath.sol"; */
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name_, string memory symbol_) public {
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
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);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
////// contracts/token/BasicFDT.sol
/* pragma solidity 0.6.11; */
/* import "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; */
/* import "lib/openzeppelin-contracts/contracts/math/SafeMath.sol"; */
/* import "lib/openzeppelin-contracts/contracts/math/SignedSafeMath.sol"; */
/* import "./interfaces/IBaseFDT.sol"; */
/* import "../math/SafeMathUint.sol"; */
/* import "../math/SafeMathInt.sol"; */
/// @title BasicFDT implements base level FDT functionality for accounting for revenues.
abstract contract BasicFDT is IBaseFDT, ERC20 {
using SafeMath for uint256;
using SafeMathUint for uint256;
using SignedSafeMath for int256;
using SafeMathInt for int256;
uint256 internal constant pointsMultiplier = 2 ** 128;
uint256 internal pointsPerShare;
mapping(address => int256) internal pointsCorrection;
mapping(address => uint256) internal withdrawnFunds;
event PointsPerShareUpdated(uint256 pointsPerShare);
event PointsCorrectionUpdated(address indexed account, int256 pointsCorrection);
constructor(string memory name, string memory symbol) ERC20(name, symbol) public { }
/**
@dev Distributes funds to token holders.
@dev It reverts if the total supply of tokens is 0.
@dev It emits a `FundsDistributed` event if the amount of received funds is greater than 0.
@dev It emits a `PointsPerShareUpdated` event if the amount of received funds is greater than 0.
About undistributed funds:
In each distribution, there is a small amount of funds which do not get distributed,
which is `(value pointsMultiplier) % totalSupply()`.
With a well-chosen `pointsMultiplier`, the amount funds that are not getting distributed
in a distribution can be less than 1 (base unit).
We can actually keep track of the undistributed funds in a distribution
and try to distribute it in the next distribution.
*/
function _distributeFunds(uint256 value) internal {
require(totalSupply() > 0, "FDT:ZERO_SUPPLY");
if (value == 0) return;
pointsPerShare = pointsPerShare.add(value.mul(pointsMultiplier) / totalSupply());
emit FundsDistributed(msg.sender, value);
emit PointsPerShareUpdated(pointsPerShare);
}
/**
@dev Prepares the withdrawal of funds.
@dev It emits a `FundsWithdrawn` event if the amount of withdrawn funds is greater than 0.
@return withdrawableDividend The amount of dividend funds that can be withdrawn.
*/
function _prepareWithdraw() internal returns (uint256 withdrawableDividend) {
withdrawableDividend = withdrawableFundsOf(msg.sender);
uint256 _withdrawnFunds = withdrawnFunds[msg.sender].add(withdrawableDividend);
withdrawnFunds[msg.sender] = _withdrawnFunds;
emit FundsWithdrawn(msg.sender, withdrawableDividend, _withdrawnFunds);
}
/**
@dev Returns the amount of funds that an account can withdraw.
@param _owner The address of a token holder.
@return The amount funds that `_owner` can withdraw.
*/
function withdrawableFundsOf(address _owner) public view override returns (uint256) {
return accumulativeFundsOf(_owner).sub(withdrawnFunds[_owner]);
}
/**
@dev Returns the amount of funds that an account has withdrawn.
@param _owner The address of a token holder.
@return The amount of funds that `_owner` has withdrawn.
*/
function withdrawnFundsOf(address _owner) external view returns (uint256) {
return withdrawnFunds[_owner];
}
/**
@dev Returns the amount of funds that an account has earned in total.
@dev accumulativeFundsOf(_owner) = withdrawableFundsOf(_owner) + withdrawnFundsOf(_owner)
= (pointsPerShare * balanceOf(_owner) + pointsCorrection[_owner]) / pointsMultiplier
@param _owner The address of a token holder.
@return The amount of funds that `_owner` has earned in total.
*/
function accumulativeFundsOf(address _owner) public view returns (uint256) {
return
pointsPerShare
.mul(balanceOf(_owner))
.toInt256Safe()
.add(pointsCorrection[_owner])
.toUint256Safe() / pointsMultiplier;
}
/**
@dev Transfers tokens from one account to another. Updates pointsCorrection to keep funds unchanged.
@dev It emits two `PointsCorrectionUpdated` events, one for the sender and one for the receiver.
@param from The address to transfer from.
@param to The address to transfer to.
@param value The amount to be transferred.
*/
function _transfer(
address from,
address to,
uint256 value
) internal virtual override {
super._transfer(from, to, value);
int256 _magCorrection = pointsPerShare.mul(value).toInt256Safe();
int256 pointsCorrectionFrom = pointsCorrection[from].add(_magCorrection);
pointsCorrection[from] = pointsCorrectionFrom;
int256 pointsCorrectionTo = pointsCorrection[to].sub(_magCorrection);
pointsCorrection[to] = pointsCorrectionTo;
emit PointsCorrectionUpdated(from, pointsCorrectionFrom);
emit PointsCorrectionUpdated(to, pointsCorrectionTo);
}
/**
@dev Mints tokens to an account. Updates pointsCorrection to keep funds unchanged.
@param account The account that will receive the created tokens.
@param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal virtual override {
super._mint(account, value);
int256 _pointsCorrection = pointsCorrection[account].sub(
(pointsPerShare.mul(value)).toInt256Safe()
);
pointsCorrection[account] = _pointsCorrection;
emit PointsCorrectionUpdated(account, _pointsCorrection);
}
/**
@dev Burns an amount of the token of a given account. Updates pointsCorrection to keep funds unchanged.
@dev It emits a `PointsCorrectionUpdated` event.
@param account The account whose tokens will be burnt.
@param value The amount that will be burnt.
*/
function _burn(address account, uint256 value) internal virtual override {
super._burn(account, value);
int256 _pointsCorrection = pointsCorrection[account].add(
(pointsPerShare.mul(value)).toInt256Safe()
);
pointsCorrection[account] = _pointsCorrection;
emit PointsCorrectionUpdated(account, _pointsCorrection);
}
/**
@dev Withdraws all available funds for a token holder.
*/
function withdrawFunds() public virtual override {}
/**
@dev Updates the current `fundsToken` balance and returns the difference of the new and previous `fundsToken` balance.
@return A int256 representing the difference of the new and previous `fundsToken` balance.
*/
function _updateFundsTokenBalance() internal virtual returns (int256) {}
/**
@dev Registers a payment of funds in tokens. May be called directly after a deposit is made.
@dev Calls _updateFundsTokenBalance(), whereby the contract computes the delta of the new and previous
`fundsToken` balance and increments the total received funds (cumulative), by delta, by calling _distributeFunds().
*/
function updateFundsReceived() public virtual {
int256 newFunds = _updateFundsTokenBalance();
if (newFunds <= 0) return;
_distributeFunds(newFunds.toUint256Safe());
}
}
////// contracts/token/ExtendedFDT.sol
/* pragma solidity 0.6.11; */
/* import "./BasicFDT.sol"; */
/// @title ExtendedFDT implements FDT functionality for accounting for losses.
abstract contract ExtendedFDT is BasicFDT {
using SafeMath for uint256;
using SafeMathUint for uint256;
using SignedSafeMath for int256;
using SafeMathInt for int256;
uint256 internal lossesPerShare;
mapping(address => int256) internal lossesCorrection;
mapping(address => uint256) internal recognizedLosses;
event LossesPerShareUpdated(uint256 lossesPerShare);
event LossesCorrectionUpdated(address indexed account, int256 lossesCorrection);
/**
@dev This event emits when new losses are distributed.
@param by The address of the account that has distributed losses.
@param lossesDistributed The amount of losses received for distribution.
*/
event LossesDistributed(address indexed by, uint256 lossesDistributed);
/**
@dev This event emits when distributed losses are recognized by a token holder.
@param by The address of the receiver of losses.
@param lossesRecognized The amount of losses that were recognized.
@param totalLossesRecognized The total amount of losses that are recognized.
*/
event LossesRecognized(address indexed by, uint256 lossesRecognized, uint256 totalLossesRecognized);
constructor(string memory name, string memory symbol) BasicFDT(name, symbol) public { }
/**
@dev Distributes losses to token holders.
@dev It reverts if the total supply of tokens is 0.
@dev It emits a `LossesDistributed` event if the amount of received losses is greater than 0.
@dev It emits a `LossesPerShareUpdated` event if the amount of received losses is greater than 0.
About undistributed losses:
In each distribution, there is a small amount of losses which do not get distributed,
which is `(value * pointsMultiplier) % totalSupply()`.
With a well-chosen `pointsMultiplier`, the amount losses that are not getting distributed
in a distribution can be less than 1 (base unit).
We can actually keep track of the undistributed losses in a distribution
and try to distribute it in the next distribution.
*/
function _distributeLosses(uint256 value) internal {
require(totalSupply() > 0, "FDT:ZERO_SUPPLY");
if (value == 0) return;
uint256 _lossesPerShare = lossesPerShare.add(value.mul(pointsMultiplier) / totalSupply());
lossesPerShare = _lossesPerShare;
emit LossesDistributed(msg.sender, value);
emit LossesPerShareUpdated(_lossesPerShare);
}
/**
@dev Prepares losses for a withdrawal.
@dev It emits a `LossesWithdrawn` event if the amount of withdrawn losses is greater than 0.
@return recognizableDividend The amount of dividend losses that can be recognized.
*/
function _prepareLossesWithdraw() internal returns (uint256 recognizableDividend) {
recognizableDividend = recognizableLossesOf(msg.sender);
uint256 _recognizedLosses = recognizedLosses[msg.sender].add(recognizableDividend);
recognizedLosses[msg.sender] = _recognizedLosses;
emit LossesRecognized(msg.sender, recognizableDividend, _recognizedLosses);
}
/**
@dev Returns the amount of losses that an address can withdraw.
@param _owner The address of a token holder.
@return The amount of losses that `_owner` can withdraw.
*/
function recognizableLossesOf(address _owner) public view returns (uint256) {
return accumulativeLossesOf(_owner).sub(recognizedLosses[_owner]);
}
/**
@dev Returns the amount of losses that an address has recognized.
@param _owner The address of a token holder
@return The amount of losses that `_owner` has recognized
*/
function recognizedLossesOf(address _owner) external view returns (uint256) {
return recognizedLosses[_owner];
}
/**
@dev Returns the amount of losses that an address has earned in total.
@dev accumulativeLossesOf(_owner) = recognizableLossesOf(_owner) + recognizedLossesOf(_owner)
= (lossesPerShare * balanceOf(_owner) + lossesCorrection[_owner]) / pointsMultiplier
@param _owner The address of a token holder
@return The amount of losses that `_owner` has earned in total
*/
function accumulativeLossesOf(address _owner) public view returns (uint256) {
return
lossesPerShare
.mul(balanceOf(_owner))
.toInt256Safe()
.add(lossesCorrection[_owner])
.toUint256Safe() / pointsMultiplier;
}
/**
@dev Transfers tokens from one account to another. Updates pointsCorrection to keep funds unchanged.
@dev It emits two `LossesCorrectionUpdated` events, one for the sender and one for the receiver.
@param from The address to transfer from.
@param to The address to transfer to.
@param value The amount to be transferred.
*/
function _transfer(
address from,
address to,
uint256 value
) internal virtual override {
super._transfer(from, to, value);
int256 _lossesCorrection = lossesPerShare.mul(value).toInt256Safe();
int256 lossesCorrectionFrom = lossesCorrection[from].add(_lossesCorrection);
lossesCorrection[from] = lossesCorrectionFrom;
int256 lossesCorrectionTo = lossesCorrection[to].sub(_lossesCorrection);
lossesCorrection[to] = lossesCorrectionTo;
emit LossesCorrectionUpdated(from, lossesCorrectionFrom);
emit LossesCorrectionUpdated(to, lossesCorrectionTo);
}
/**
@dev Mints tokens to an account. Updates lossesCorrection to keep losses unchanged.
@dev It emits a `LossesCorrectionUpdated` event.
@param account The account that will receive the created tokens.
@param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal virtual override {
super._mint(account, value);
int256 _lossesCorrection = lossesCorrection[account].sub(
(lossesPerShare.mul(value)).toInt256Safe()
);
lossesCorrection[account] = _lossesCorrection;
emit LossesCorrectionUpdated(account, _lossesCorrection);
}
/**
@dev Burns an amount of the token of a given account. Updates lossesCorrection to keep losses unchanged.
@dev It emits a `LossesCorrectionUpdated` event.
@param account The account from which tokens will be burnt.
@param value The amount that will be burnt.
*/
function _burn(address account, uint256 value) internal virtual override {
super._burn(account, value);
int256 _lossesCorrection = lossesCorrection[account].add(
(lossesPerShare.mul(value)).toInt256Safe()
);
lossesCorrection[account] = _lossesCorrection;
emit LossesCorrectionUpdated(account, _lossesCorrection);
}
/**
@dev Registers a loss. May be called directly after a shortfall after BPT burning occurs.
@dev Calls _updateLossesTokenBalance(), whereby the contract computes the delta of the new and previous
losses balance and increments the total losses (cumulative), by delta, by calling _distributeLosses().
*/
function updateLossesReceived() public virtual {
int256 newLosses = _updateLossesBalance();
if (newLosses <= 0) return;
_distributeLosses(newLosses.toUint256Safe());
}
/**
@dev Recognizes all recognizable losses for an account using loss accounting.
*/
function _recognizeLosses() internal virtual returns (uint256 losses) { }
/**
@dev Updates the current losses balance and returns the difference of the new and previous losses balance.
@return A int256 representing the difference of the new and previous losses balance.
*/
function _updateLossesBalance() internal virtual returns (int256) { }
}
////// contracts/token/StakeLockerFDT.sol
/* pragma solidity 0.6.11; */
/* import "./ExtendedFDT.sol"; */
/// @title StakeLockerFDT inherits ExtendedFDT and accounts for gains/losses for Stakers.
abstract contract StakeLockerFDT is ExtendedFDT {
using SafeMath for uint256;
using SafeMathUint for uint256;
using SignedSafeMath for int256;
using SafeMathInt for int256;
IERC20 public immutable fundsToken;
uint256 public bptLosses; // Sum of all unrecognized losses.
uint256 public lossesBalance; // The amount of losses present and accounted for in this contract.
uint256 public fundsTokenBalance; // The amount of `fundsToken` (Liquidity Asset) currently present and accounted for in this contract.
constructor(string memory name, string memory symbol, address _fundsToken) ExtendedFDT(name, symbol) public {
fundsToken = IERC20(_fundsToken);
}
/**
@dev Updates loss accounting for `msg.sender`, recognizing losses.
@return losses Amount to be subtracted from a withdraw amount.
*/
function _recognizeLosses() internal override returns (uint256 losses) {
losses = _prepareLossesWithdraw();
bptLosses = bptLosses.sub(losses);
_updateLossesBalance();
}
/**
@dev Updates the current losses balance and returns the difference of the new and previous losses balance.
@return A int256 representing the difference of the new and previous losses balance.
*/
function _updateLossesBalance() internal override returns (int256) {
uint256 _prevLossesTokenBalance = lossesBalance;
lossesBalance = bptLosses;
return int256(lossesBalance).sub(int256(_prevLossesTokenBalance));
}
/**
@dev Updates the current interest balance and returns the difference of the new and previous interest balance.
@return A int256 representing the difference of the new and previous interest balance.
*/
function _updateFundsTokenBalance() internal virtual override returns (int256) {
uint256 _prevFundsTokenBalance = fundsTokenBalance;
fundsTokenBalance = fundsToken.balanceOf(address(this));
return int256(fundsTokenBalance).sub(int256(_prevFundsTokenBalance));
}
}
////// lib/openzeppelin-contracts/contracts/utils/Address.sol
/* pragma solidity >=0.6.2 <0.8.0; */
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
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");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
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");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
////// lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol
/* pragma solidity >=0.6.0 <0.8.0; */
/* import "./IERC20.sol"; */
/* import "../../math/SafeMath.sol"; */
/* import "../../utils/Address.sol"; */
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
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));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
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).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
////// lib/openzeppelin-contracts/contracts/utils/Pausable.sol
/* pragma solidity >=0.6.0 <0.8.0; */
/* import "../GSN/Context.sol"; */
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor () internal {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!_paused, "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(_paused, "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
////// contracts/StakeLocker.sol
/* pragma solidity 0.6.11; */
/* import "lib/openzeppelin-contracts/contracts/token/ERC20/SafeERC20.sol"; */
/* import "lib/openzeppelin-contracts/contracts/utils/Pausable.sol"; */
/* import "./interfaces/IMapleGlobals.sol"; */
/* import "./interfaces/IPool.sol"; */
/* import "./interfaces/IPoolFactory.sol"; */
/* import "./token/StakeLockerFDT.sol"; */
/// @title StakeLocker holds custody of stakeAsset tokens for a given Pool and earns revenue from interest.
contract StakeLocker is StakeLockerFDT, Pausable {
using SafeMathInt for int256;
using SignedSafeMath for int256;
using SafeERC20 for IERC20;
uint256 constant WAD = 10 ** 18; // Scaling factor for synthetic float division.
IERC20 public immutable stakeAsset; // The asset deposited by Stakers into this contract, for liquidation during defaults.
address public immutable liquidityAsset; // The Liquidity Asset for the Pool as well as the dividend token for StakeLockerFDT interest.
address public immutable pool; // The parent Pool.
uint256 public lockupPeriod; // Number of seconds for which unstaking is not allowed.
mapping(address => uint256) public stakeDate; // Map of account addresses to effective stake date.
mapping(address => uint256) public unstakeCooldown; // The timestamp of when a Staker called `cooldown()`.
mapping(address => bool) public allowed; // Map of addresses to allowed status.
mapping(address => mapping(address => uint256)) public custodyAllowance; // Amount of StakeLockerFDTs that are "locked" at a certain address.
mapping(address => uint256) public totalCustodyAllowance; // Total amount of StakeLockerFDTs that are "locked" for a given account, cannot be greater than balance.
bool public openToPublic; // Boolean opening StakeLocker to public for staking BPTs
event StakeLockerOpened();
event BalanceUpdated(address indexed staker, address indexed token, uint256 balance);
event AllowListUpdated(address indexed staker, bool status);
event StakeDateUpdated(address indexed staker, uint256 stakeDate);
event LockupPeriodUpdated(uint256 lockupPeriod);
event Cooldown(address indexed staker, uint256 cooldown);
event Stake(address indexed staker, uint256 amount);
event Unstake(address indexed staker, uint256 amount);
event CustodyTransfer(address indexed custodian, address indexed from, address indexed to, uint256 amount);
event CustodyAllowanceChanged(address indexed staker, address indexed custodian, uint256 oldAllowance, uint256 newAllowance);
event TotalCustodyAllowanceUpdated(address indexed staker, uint256 newTotalAllowance);
constructor(
address _stakeAsset,
address _liquidityAsset,
address _pool
) StakeLockerFDT("Maple StakeLocker", "MPLSTAKE", _liquidityAsset) public {
liquidityAsset = _liquidityAsset;
stakeAsset = IERC20(_stakeAsset);
pool = _pool;
lockupPeriod = 180 days;
}
/*****************/
/*** Modifiers ***/
/*****************/
/**
@dev Checks that an account can unstake given the following conditions:
1. The Account is not the Pool Delegate and the Pool is in Finalized state.
2. The Pool is in Initialized or Deactivated state.
*/
modifier canUnstake(address from) {
IPool _pool = IPool(pool);
// The Pool cannot be finalized, but if it is, account cannot be the Pool Delegate.
require(!_pool.isPoolFinalized() || from != _pool.poolDelegate(), "SL:STAKE_LOCKED");
_;
}
/**
@dev Checks that `msg.sender` is the Governor.
*/
modifier isGovernor() {
require(msg.sender == _globals().governor(), "SL:NOT_GOV");
_;
}
/**
@dev Checks that `msg.sender` is the Pool.
*/
modifier isPool() {
require(msg.sender == pool, "SL:NOT_P");
_;
}
/**********************/
/*** Pool Functions ***/
/**********************/
/**
@dev Updates Staker status on the allowlist. Only the Pool Delegate can call this function.
@dev It emits an `AllowListUpdated` event.
@param staker The address of the Staker to set status for.
@param status The status of the Staker on allowlist.
*/
function setAllowlist(address staker, bool status) public {
_whenProtocolNotPaused();
_isValidPoolDelegate();
allowed[staker] = status;
emit AllowListUpdated(staker, status);
}
/**
@dev Sets the StakeLocker as open to the public. Only the Pool Delegate can call this function.
@dev It emits a `StakeLockerOpened` event.
*/
function openStakeLockerToPublic() external {
_whenProtocolNotPaused();
_isValidPoolDelegate();
openToPublic = true;
emit StakeLockerOpened();
}
/**
@dev Sets the lockup period. Only the Pool Delegate can call this function.
@dev It emits a `LockupPeriodUpdated` event.
@param newLockupPeriod New lockup period used to restrict unstaking.
*/
function setLockupPeriod(uint256 newLockupPeriod) external {
_whenProtocolNotPaused();
_isValidPoolDelegate();
require(newLockupPeriod <= lockupPeriod, "SL:INVALID_VALUE");
lockupPeriod = newLockupPeriod;
emit LockupPeriodUpdated(newLockupPeriod);
}
/**
@dev Transfers an amount of Stake Asset to a destination account. Only the Pool can call this function.
@param dst Destination to transfer Stake Asset to.
@param amt Amount of Stake Asset to transfer.
*/
function pull(address dst, uint256 amt) isPool external {
stakeAsset.safeTransfer(dst, amt);
}
/**
@dev Updates loss accounting for StakeLockerFDTs after BPTs have been burned. Only the Pool can call this function.
@param bptsBurned Amount of BPTs that have been burned.
*/
function updateLosses(uint256 bptsBurned) isPool external {
bptLosses = bptLosses.add(bptsBurned);
updateLossesReceived();
}
/************************/
/*** Staker Functions ***/
/************************/
/**
@dev Handles a Staker's depositing of an amount of Stake Asset, minting them StakeLockerFDTs.
@dev It emits a `StakeDateUpdated` event.
@dev It emits a `Stake` event.
@dev It emits a `Cooldown` event.
@dev It emits a `BalanceUpdated` event.
@param amt Amount of Stake Asset (BPTs) to deposit.
*/
function stake(uint256 amt) whenNotPaused external {
_whenProtocolNotPaused();
_isAllowed(msg.sender);
unstakeCooldown[msg.sender] = uint256(0); // Reset account's unstake cooldown if Staker had previously intended to unstake.
_updateStakeDate(msg.sender, amt);
stakeAsset.safeTransferFrom(msg.sender, address(this), amt);
_mint(msg.sender, amt);
emit Stake(msg.sender, amt);
emit Cooldown(msg.sender, uint256(0));
emit BalanceUpdated(address(this), address(stakeAsset), stakeAsset.balanceOf(address(this)));
}
/**
@dev Updates information used to calculate unstake delay.
@dev It emits a `StakeDateUpdated` event.
@param account The Staker that deposited BPTs.
@param amt Amount of BPTs the Staker has deposited.
*/
function _updateStakeDate(address account, uint256 amt) internal {
uint256 prevDate = stakeDate[account];
uint256 balance = balanceOf(account);
// stakeDate + (now - stakeDate) * (amt / (balance + amt))
// NOTE: prevDate = 0 implies balance = 0, and equation reduces to now.
uint256 newDate = (balance + amt) > 0
? prevDate.add(block.timestamp.sub(prevDate).mul(amt).div(balance + amt))
: prevDate;
stakeDate[account] = newDate;
emit StakeDateUpdated(account, newDate);
}
/**
@dev Activates the cooldown period to unstake. It can't be called if the account is not staking.
@dev It emits a `Cooldown` event.
**/
function intendToUnstake() external {
require(balanceOf(msg.sender) != uint256(0), "SL:ZERO_BALANCE");
unstakeCooldown[msg.sender] = block.timestamp;
emit Cooldown(msg.sender, block.timestamp);
}
/**
@dev Cancels an initiated unstake by resetting the calling account's unstake cooldown.
@dev It emits a `Cooldown` event.
*/
function cancelUnstake() external {
require(unstakeCooldown[msg.sender] != uint256(0), "SL:NOT_UNSTAKING");
unstakeCooldown[msg.sender] = 0;
emit Cooldown(msg.sender, uint256(0));
}
/**
@dev Handles a Staker's withdrawing of an amount of Stake Asset, minus any losses. It also claims interest and burns StakeLockerFDTs for the calling account.
@dev It emits an `Unstake` event.
@dev It emits a `BalanceUpdated` event.
@param amt Amount of Stake Asset (BPTs) to withdraw.
*/
function unstake(uint256 amt) external canUnstake(msg.sender) {
_whenProtocolNotPaused();
require(balanceOf(msg.sender).sub(amt) >= totalCustodyAllowance[msg.sender], "SL:INSUF_UNSTAKEABLE_BAL"); // Account can only unstake tokens that aren't custodied
require(isUnstakeAllowed(msg.sender), "SL:OUTSIDE_COOLDOWN");
require(stakeDate[msg.sender].add(lockupPeriod) <= block.timestamp, "SL:FUNDS_LOCKED");
updateFundsReceived(); // Account for any funds transferred into contract since last call.
_burn(msg.sender, amt); // Burn the corresponding StakeLockerFDTs balance.
withdrawFunds(); // Transfer the full entitled Liquidity Asset interest.
stakeAsset.safeTransfer(msg.sender, amt.sub(_recognizeLosses())); // Unstake amount minus losses.
emit Unstake(msg.sender, amt);
emit BalanceUpdated(address(this), address(stakeAsset), stakeAsset.balanceOf(address(this)));
}
/**
@dev Withdraws all claimable interest earned from the StakeLocker for an account.
@dev It emits a `BalanceUpdated` event if there are withdrawable funds.
*/
function withdrawFunds() public override {
_whenProtocolNotPaused();
uint256 withdrawableFunds = _prepareWithdraw();
if (withdrawableFunds == uint256(0)) return;
fundsToken.safeTransfer(msg.sender, withdrawableFunds);
emit BalanceUpdated(address(this), address(fundsToken), fundsToken.balanceOf(address(this)));
_updateFundsTokenBalance();
}
/**
@dev Increases the custody allowance for a given Custodian corresponding to the account (`msg.sender`).
@dev It emits a `CustodyAllowanceChanged` event.
@dev It emits a `TotalCustodyAllowanceUpdated` event.
@param custodian Address which will act as Custodian of a given amount for an account.
@param amount Number of additional FDTs to be custodied by the Custodian.
*/
function increaseCustodyAllowance(address custodian, uint256 amount) external {
uint256 oldAllowance = custodyAllowance[msg.sender][custodian];
uint256 newAllowance = oldAllowance.add(amount);
uint256 newTotalAllowance = totalCustodyAllowance[msg.sender].add(amount);
require(custodian != address(0), "SL:INVALID_CUSTODIAN");
require(amount != uint256(0), "SL:INVALID_AMT");
require(newTotalAllowance <= balanceOf(msg.sender), "SL:INSUF_BALANCE");
custodyAllowance[msg.sender][custodian] = newAllowance;
totalCustodyAllowance[msg.sender] = newTotalAllowance;
emit CustodyAllowanceChanged(msg.sender, custodian, oldAllowance, newAllowance);
emit TotalCustodyAllowanceUpdated(msg.sender, newTotalAllowance);
}
/**
@dev Transfers custodied StakeLockerFDTs back to the account.
@dev `from` and `to` should always be equal in this implementation.
@dev This means that the Custodian can only decrease their own allowance and unlock funds for the original owner.
@dev It emits a `CustodyTransfer` event.
@dev It emits a `CustodyAllowanceChanged` event.
@dev It emits a `TotalCustodyAllowanceUpdated` event.
@param from Address which holds the StakeLockerFDTs.
@param to Address which will be the new owner of the amount of StakeLockerFDTs.
@param amount Amount of StakeLockerFDTs transferred.
*/
function transferByCustodian(address from, address to, uint256 amount) external {
uint256 oldAllowance = custodyAllowance[from][msg.sender];
uint256 newAllowance = oldAllowance.sub(amount);
require(to == from, "SL:INVALID_RECEIVER");
require(amount != uint256(0), "SL:INVALID_AMT");
custodyAllowance[from][msg.sender] = newAllowance;
uint256 newTotalAllowance = totalCustodyAllowance[from].sub(amount);
totalCustodyAllowance[from] = newTotalAllowance;
emit CustodyTransfer(msg.sender, from, to, amount);
emit CustodyAllowanceChanged(from, msg.sender, oldAllowance, newAllowance);
emit TotalCustodyAllowanceUpdated(msg.sender, newTotalAllowance);
}
/**
@dev Transfers StakeLockerFDTs.
@param from Address sending StakeLockerFDTs.
@param to Address receiving StakeLockerFDTs.
@param wad Amount of StakeLockerFDTs to transfer.
*/
function _transfer(address from, address to, uint256 wad) internal override canUnstake(from) {
_whenProtocolNotPaused();
require(stakeDate[from].add(lockupPeriod) <= block.timestamp, "SL:FUNDS_LOCKED"); // Restrict withdrawal during lockup period
require(balanceOf(from).sub(wad) >= totalCustodyAllowance[from], "SL:INSUF_TRANSFERABLE_BAL"); // Account can only transfer tokens that aren't custodied
require(isReceiveAllowed(unstakeCooldown[to]), "SL:RECIPIENT_NOT_ALLOWED"); // Recipient must not be currently unstaking
require(recognizableLossesOf(from) == uint256(0), "SL:RECOG_LOSSES"); // If a staker has unrecognized losses, they must recognize losses through unstake
_updateStakeDate(to, wad); // Update stake date of recipient
super._transfer(from, to, wad);
}
/***********************/
/*** Admin Functions ***/
/***********************/
/**
@dev Triggers paused state. Halts functionality for certain functions. Only the Pool Delegate or a Pool Admin can call this function.
*/
function pause() external {
_isValidPoolDelegateOrPoolAdmin();
super._pause();
}
/**
@dev Triggers unpaused state. Restores functionality for certain functions. Only the Pool Delegate or a Pool Admin can call this function.
*/
function unpause() external {
_isValidPoolDelegateOrPoolAdmin();
super._unpause();
}
/************************/
/*** Helper Functions ***/
/************************/
/**
@dev Returns if the unstake cooldown period has passed for `msg.sender` and if they are in the unstake window.
*/
function isUnstakeAllowed(address from) public view returns (bool) {
IMapleGlobals globals = _globals();
return (block.timestamp - (unstakeCooldown[from] + globals.stakerCooldownPeriod())) <= globals.stakerUnstakeWindow();
}
/**
@dev Returns if an account is allowed to receive a transfer.
This is only possible if they have zero cooldown or they are past their unstake window.
*/
function isReceiveAllowed(uint256 _unstakeCooldown) public view returns (bool) {
IMapleGlobals globals = _globals();
return block.timestamp > (_unstakeCooldown + globals.stakerCooldownPeriod() + globals.stakerUnstakeWindow());
}
/**
@dev Checks that `msg.sender` is the Pool Delegate or a Pool Admin.
*/
function _isValidPoolDelegateOrPoolAdmin() internal view {
require(msg.sender == IPool(pool).poolDelegate() || IPool(pool).poolAdmins(msg.sender), "SL:NOT_DELEGATE_OR_ADMIN");
}
/**
@dev Checks that `msg.sender` is the Pool Delegate.
*/
function _isValidPoolDelegate() internal view {
require(msg.sender == IPool(pool).poolDelegate(), "SL:NOT_DELEGATE");
}
/**
@dev Checks that `msg.sender` is allowed to stake.
*/
function _isAllowed(address account) internal view {
require(
openToPublic || allowed[account] || account == IPool(pool).poolDelegate(),
"SL:NOT_ALLOWED"
);
}
/**
@dev Returns the MapleGlobals instance.
*/
function _globals() internal view returns (IMapleGlobals) {
return IMapleGlobals(IPoolFactory(IPool(pool).superFactory()).globals());
}
/**
@dev Checks that the protocol is not in a paused state.
*/
function _whenProtocolNotPaused() internal view {
require(!_globals().protocolPaused(), "SL:PROTO_PAUSED");
}
}
{
"compilationTarget": {
"StakeLocker.sol": "StakeLocker"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_stakeAsset","type":"address"},{"internalType":"address","name":"_liquidityAsset","type":"address"},{"internalType":"address","name":"_pool","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AllowListUpdated","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":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"}],"name":"BalanceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"cooldown","type":"uint256"}],"name":"Cooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"custodian","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldAllowance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAllowance","type":"uint256"}],"name":"CustodyAllowanceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"custodian","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CustodyTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"fundsDistributed","type":"uint256"}],"name":"FundsDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"fundsWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalWithdrawn","type":"uint256"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lockupPeriod","type":"uint256"}],"name":"LockupPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"int256","name":"lossesCorrection","type":"int256"}],"name":"LossesCorrectionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"lossesDistributed","type":"uint256"}],"name":"LossesDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lossesPerShare","type":"uint256"}],"name":"LossesPerShareUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"lossesRecognized","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalLossesRecognized","type":"uint256"}],"name":"LossesRecognized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"int256","name":"pointsCorrection","type":"int256"}],"name":"PointsCorrectionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pointsPerShare","type":"uint256"}],"name":"PointsPerShareUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"stakeDate","type":"uint256"}],"name":"StakeDateUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"StakeLockerOpened","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"newTotalAllowance","type":"uint256"}],"name":"TotalCustodyAllowanceUpdated","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":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstake","type":"event"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"accumulativeFundsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"accumulativeLossesOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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"}],"name":"allowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bptLosses","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"custodyAllowance","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":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundsToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundsTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"internalType":"address","name":"custodian","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseCustodyAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"intendToUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_unstakeCooldown","type":"uint256"}],"name":"isReceiveAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"isUnstakeAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockupPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lossesBalance","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":"openStakeLockerToPublic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"openToPublic","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"pull","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"recognizableLossesOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"recognizedLossesOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setAllowlist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newLockupPeriod","type":"uint256"}],"name":"setLockupPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amt","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakeDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalCustodyAllowance","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":"recipient","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":"transferByCustodian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","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":"uint256","name":"amt","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unstakeCooldown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateFundsReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bptsBurned","type":"uint256"}],"name":"updateLosses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateLossesReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"withdrawableFundsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"withdrawnFundsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]