// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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");
(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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
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);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
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 {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorInterface {
function latestAnswer() external view returns (int256);
function latestTimestamp() external view returns (uint256);
function latestRound() external view returns (uint256);
function getAnswer(uint256 roundId) external view returns (int256);
function getTimestamp(uint256 roundId) external view returns (uint256);
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./AggregatorInterface.sol";
import "./AggregatorV3Interface.sol";
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import './interfaces/IVault.sol';
import './interfaces/IVaultFactory.sol';
import './interfaces/IStabilityPool.sol';
import './interfaces/ILiquidationRouter.sol';
import './interfaces/ITokenPriceFeed.sol';
import './interfaces/IMintableToken.sol';
import './interfaces/ILastResortLiquidation.sol';
import './utils/constants.sol';
/**
* @title AuctionManager.
* @dev Manages auctions for liquidating collateral in case of debt default.
*/
contract AuctionManager is Ownable, ReentrancyGuard, Constants {
using SafeERC20 for IERC20;
using SafeERC20 for IMintableToken;
// Auction duration and lowest health factor
uint256 public auctionDuration = 2 hours;
uint256 public lowestHF = 1.05 ether; // 105%
// Struct to hold auction data
struct auctionData {
uint256 originalDebt;
uint256 lowestDebtToAuction;
uint256 highestDebtToAuction;
uint256 collateralsLength;
address[] collateral;
uint256[] collateralAmount;
uint256 auctionStartTime;
uint256 auctionEndTime;
bool auctionEnded;
}
// Array to store auction data.
auctionData[] public auctions;
address public vaultFactory;
// Events.
event VaultFactoryUpdated(address indexed _vaultFactory);
event AuctionDurationUpdated(uint256 _auctionDuration);
event AuctionCreated(
uint256 indexed _auctionId,
uint256 _originalDebt,
uint256 _lowestDebtToAuction,
uint256 _highestDebtToAuction,
uint256 _collateralsLength,
address[] _collateral,
uint256[] _collateralAmount,
uint256 _auctionStartTime,
uint256 _auctionEndTime
);
event AuctionWon(
uint256 indexed _auctionId,
address indexed _winner,
uint256 _debtRepaid,
uint256 _collateralValueGained
);
event AuctionEnded(uint256 indexed _auctionId);
/**
* @notice Sets the duration of each auction.
* @dev Can only be called by the contract owner.
* @param _auctionDuration Duration of the auction.
*/
function setAuctionDuration(uint256 _auctionDuration) external onlyOwner {
require(_auctionDuration > 0, 'auction-duration-is-0');
auctionDuration = _auctionDuration;
emit AuctionDurationUpdated(_auctionDuration);
}
/**
* @notice Sets the lowest health factor allowed for bidding.
* @dev Can only be called by the contract owner.
* @param _lowestHF Lowest health factor allowed for bidding.
*/
function setLowestHealthFactor(uint256 _lowestHF) external onlyOwner {
require(_lowestHF > 0, 'lowest-hf-is-0');
lowestHF = _lowestHF;
}
/**
* @dev Sets the address of the vault factory. Can only be called by the contract owner.
* @param _vaultFactory Address of the vault factory.
*/
function setVaultFactory(address _vaultFactory) external onlyOwner {
require(_vaultFactory != address(0x0), 'vault-factory-is-0');
vaultFactory = _vaultFactory;
emit VaultFactoryUpdated(_vaultFactory);
}
/**
* @dev Returns the total number of auctions created.
* @return The total number of auctions.
*/
function auctionsLength() external view returns (uint256) {
return auctions.length;
}
/**
* @dev Get auction information by ID.
* @param _auctionId The ID of the auction.
* @return Auction data structure.
*/
function auctionInfo(
uint256 _auctionId
) external view returns (auctionData memory) {
return auctions[_auctionId];
}
/**
* @dev Contract constructor to initialize the vault factory address.
* @param _vaultFactory Address of the vault factory.
*/
constructor(address _vaultFactory) {
require(_vaultFactory != address(0x0), 'vault-factory-is-0');
vaultFactory = _vaultFactory;
emit VaultFactoryUpdated(_vaultFactory);
}
/**
* @notice Calculate total collateral value for a specific auction.
* @param _auctionId The ID of the auction.
* @return Total collateral value.
*/
function getTotalCollateralValue(
uint256 _auctionId
) public view returns (uint256) {
auctionData memory _auction = auctions[_auctionId];
ITokenPriceFeed _priceFeed = ITokenPriceFeed(
IVaultFactory(vaultFactory).priceFeed()
);
uint256 _totalCollateralValue = 0;
for (uint256 i = 0; i < _auction.collateralsLength; i++) {
uint256 _price = _priceFeed.tokenPrice(_auction.collateral[i]);
uint256 _normalizedCollateralAmount = _auction.collateralAmount[i] *
(10 ** (18 - _priceFeed.decimals(_auction.collateral[i])));
uint256 _collateralValue = (_normalizedCollateralAmount * _price) /
DECIMAL_PRECISION;
_totalCollateralValue += _collateralValue;
}
return _totalCollateralValue;
}
/**
* @dev Creates a new auction to liquidate underwater debt against collaterals.
* Accessible only by the liquidation router.
* @notice Allows the liquidation router to initiate a new auction for the collateralized debt.
*/
function newAuction() external {
ILiquidationRouter liquidationRouter = ILiquidationRouter(
IVaultFactory(vaultFactory).liquidationRouter()
);
require(msg.sender == address(liquidationRouter), 'not-allowed');
uint256 _debtToAuction = liquidationRouter.underWaterDebt();
require(_debtToAuction > 0, 'no-debt-to-auction');
address[] memory _collaterals = liquidationRouter.collaterals();
uint256[] memory _collateralAmounts = new uint256[](
_collaterals.length
);
uint256 _collateralsLength = _collaterals.length;
require(_collateralsLength > 0, 'no-collaterals');
uint256 _totalCollateralValue = 0;
ITokenPriceFeed _priceFeed = ITokenPriceFeed(
IVaultFactory(vaultFactory).priceFeed()
);
for (uint256 i = 0; i < _collateralsLength; i++) {
IERC20 collateralToken = IERC20(_collaterals[i]);
uint256 _collateralAmount = liquidationRouter.collateral(
_collaterals[i]
);
collateralToken.safeTransferFrom(
address(liquidationRouter),
address(this),
_collateralAmount
);
_collateralAmounts[i] = _collateralAmount;
uint256 _price = _priceFeed.tokenPrice(address(collateralToken));
uint256 _normalizedCollateralAmount = _collateralAmount *
(10 ** (18 - _priceFeed.decimals(address(collateralToken))));
uint256 _collateralValue = (_normalizedCollateralAmount * _price) /
DECIMAL_PRECISION;
_totalCollateralValue += _collateralValue;
}
uint256 _auctionStartTime = block.timestamp;
uint256 _auctionEndTime = _auctionStartTime + auctionDuration;
uint256 _lowestDebtToAuction = (_totalCollateralValue * lowestHF) /
DECIMAL_PRECISION;
uint256 _highestDebtToAuction = _debtToAuction;
if (_highestDebtToAuction < _lowestDebtToAuction) {
uint256 _debtToAuctionTmp = _lowestDebtToAuction;
_lowestDebtToAuction = _highestDebtToAuction;
_highestDebtToAuction = _debtToAuctionTmp;
}
auctions.push(
auctionData({
originalDebt: _debtToAuction,
lowestDebtToAuction: _lowestDebtToAuction,
highestDebtToAuction: _highestDebtToAuction,
collateralsLength: _collateralsLength,
collateral: _collaterals,
collateralAmount: _collateralAmounts,
auctionStartTime: _auctionStartTime,
auctionEndTime: _auctionEndTime,
auctionEnded: false
})
);
emit AuctionCreated(
auctions.length - 1,
_debtToAuction,
_lowestDebtToAuction,
_highestDebtToAuction,
_collateralsLength,
_collaterals,
_collateralAmounts,
_auctionStartTime,
_auctionEndTime
);
}
/**
* @dev Get auction bid information.
* @param _auctionId The ID of the auction.
* @return _totalCollateralValue Total collateral value.
* @return _debtToAuctionAtCurrentTime Debt to auction at the current time.
*/
function bidInfo(
uint256 _auctionId
)
external
view
returns (
uint256 _totalCollateralValue,
uint256 _debtToAuctionAtCurrentTime
)
{
auctionData memory _auction = auctions[_auctionId];
require(
!_auction.auctionEnded &&
block.timestamp <= _auction.auctionEndTime,
'auction-ended'
);
_totalCollateralValue = getTotalCollateralValue(_auctionId);
uint256 _highestDebtToAuction = _auction.highestDebtToAuction;
uint256 _lowestDebtToAuction = _auction.lowestDebtToAuction;
// decrease _debtToAuction linearly to _lowestDebtToAuction over the auction duration
_debtToAuctionAtCurrentTime =
_highestDebtToAuction -
((_highestDebtToAuction - _lowestDebtToAuction) *
(block.timestamp - _auction.auctionStartTime)) /
auctionDuration;
}
/**
* @dev Transfer collateral to the last resort liquidation contract.
* @param _auctionId The ID of the auction.
*/
function _transferToLastResortLiquidation(uint256 _auctionId) internal {
ILiquidationRouter _liquidationRouter = ILiquidationRouter(
IVaultFactory(vaultFactory).liquidationRouter()
);
ILastResortLiquidation _lastResortLiquidation = ILastResortLiquidation(
_liquidationRouter.lastResortLiquidation()
);
auctionData memory _auction = auctions[_auctionId];
uint256 _collateralsLength = _auction.collateralsLength;
address[] memory _collaterals = _auction.collateral;
uint256[] memory _collateralAmounts = _auction.collateralAmount;
uint256 _badDebt = _auction.originalDebt;
_lastResortLiquidation.addBadDebt(_badDebt);
for (uint256 i = 0; i < _collateralsLength; i++) {
IERC20 collateralToken = IERC20(_collaterals[i]);
collateralToken.safeApprove(address(_lastResortLiquidation), 0);
collateralToken.safeApprove(
address(_lastResortLiquidation),
_collateralAmounts[i]
);
_lastResortLiquidation.addCollateral(
address(collateralToken),
_collateralAmounts[i]
);
}
}
/**
* @dev Sends a bid from the caller to the auction for a specific auction ID.
* @param _auctionId The ID of the auction.
* @notice Allows a bidder to participate in the auction by placing a bid.
* If the auction period is over or has been manually ended, it transfers the bid to the last resort liquidation.
*/
function bid(uint256 _auctionId) external nonReentrant {
auctionData memory _auction = auctions[_auctionId];
require(!_auction.auctionEnded, 'auction-ended');
if (block.timestamp > _auction.auctionEndTime) {
// auction ended
auctions[_auctionId].auctionEnded = true;
_transferToLastResortLiquidation(_auctionId);
emit AuctionEnded(_auctionId);
return;
}
uint256 _totalCollateralValue = getTotalCollateralValue(_auctionId);
uint256 _highestDebtToAuction = _auction.highestDebtToAuction;
uint256 _lowestDebtToAuction = _auction.lowestDebtToAuction;
// decrease _debtToAuction linearly to _lowestDebtToAuction over the auction duration
uint256 _debtToAuctionAtCurrentTime = _highestDebtToAuction -
((_highestDebtToAuction - _lowestDebtToAuction) *
(block.timestamp - _auction.auctionStartTime)) /
auctionDuration;
IMintableToken _stable = IMintableToken(
IVaultFactory(vaultFactory).stable()
);
_stable.safeTransferFrom(
msg.sender,
address(this),
_debtToAuctionAtCurrentTime
);
_stable.burn(_debtToAuctionAtCurrentTime);
uint256 _collateralsLength = _auction.collateralsLength;
for (uint256 i = 0; i < _collateralsLength; i++) {
IERC20 collateralToken = IERC20(_auction.collateral[i]);
collateralToken.safeTransfer(
msg.sender,
_auction.collateralAmount[i]
);
}
auctions[_auctionId].auctionEnded = true;
emit AuctionWon(
_auctionId,
msg.sender,
_debtToAuctionAtCurrentTime,
_totalCollateralValue
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol';
import '../interfaces/IPriceFeed.sol';
import '../interfaces/ITokenPriceFeed.sol';
import '../utils/constants.sol';
/**
* @title ChainlinkPriceOracle
* @dev Retrieves and manages price data from Chainlink's Oracle for specified tokens.
*/
contract ChainlinkPriceOracle is IPriceFeed, Constants {
AggregatorV2V3Interface public immutable oracle;
address public immutable override token;
uint256 public immutable precision;
uint256 public updateThreshold = 24 hours;
/**
* @dev Initializes the Chainlink price feed with the specified oracle and token.
* @param _oracle The address of the Chainlink oracle contract.
* @param _token The address of the associated token.
*/
constructor(address _token, address _oracle) {
require(
_oracle != address(0x0),
'oracle-is-zero-address'
);
require(
_token != address(0x0),
'token-is-zero-address'
);
token = _token;
oracle = AggregatorV2V3Interface(_oracle);
uint8 decimals = oracle.decimals();
require(decimals > 0, 'decimals-is-zero');
precision = 10 ** decimals;
}
/**
* @dev Retrieves the current price from the Chainlink oracle, ensuring it is not outdated.
* @return The latest recorded price of the associated token.
*/
function price() public view virtual override returns (uint256) {
(, int256 _price, , uint256 _timestamp, ) = oracle.latestRoundData();
require(_price > 0, 'invalid-price');
require(
block.timestamp - _timestamp <= updateThreshold,
'price-outdated'
);
return (uint256(_price) * DECIMAL_PRECISION) / precision;
}
/**
* @dev Retrieves the current price point.
* @return The current price of the associated token.
*/
function pricePoint() public view override returns (uint256) {
return price();
}
/**
* @dev Emits a price update signal for the associated token.
*/
function emitPriceSignal() public override {
emit PriceUpdate(token, price(), price());
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.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.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead 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, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override 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 default value returned by this function, unless
* it's overridden.
*
* 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 virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, 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}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, 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}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
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;
}
/**
* @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) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + 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) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This 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:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
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;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, 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:
*
* - `account` 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 += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(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);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(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 Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
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);
}
}
}
/**
* @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 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 {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '../interfaces/IPriceFeed.sol';
contract FixedPriceOracle is IPriceFeed {
IPriceFeed public immutable priceFeed = IPriceFeed(address(0));
IPriceFeed public immutable conversionPriceFeed = IPriceFeed(address(0));
address public immutable override token;
uint256 public fixedPrice;
constructor(address _token, uint256 _price) {
fixedPrice = _price;
token = _token;
}
function price() public view override returns (uint256) {
return fixedPrice;
}
function pricePoint() public view override returns (uint256) {
return price();
}
function emitPriceSignal() public {
emit PriceUpdate(token, price(), price());
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IAuctionManager {
function newAuction() external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/extensions/IERC20Metadata.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IExternalPriceFeed {
function token() external view returns (address);
function price() external view returns (uint256);
function pricePoint() external view returns (uint256);
function setPrice(uint256 _price) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IFeeRecipient {
function baseRate() external view returns (uint256);
function getBorrowingFee(uint256 _amount) external view returns (uint256);
function calcDecayedBaseRate(
uint256 _currentBaseRate
) external view returns (uint256);
/**
@dev is called to make the FeeRecipient contract transfer the fees to itself. It will use transferFrom to get the
fees from the msg.sender
@param _amount the amount in Wei of fees to transfer
*/
function takeFees(uint256 _amount) external returns (bool);
function increaseBaseRate(uint256 _increase) external returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface ILastResortLiquidation {
function addCollateral(address _collateral, uint256 _amount) external;
function addBadDebt(uint256 _amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface ILiquidationRouter {
function addSeizedCollateral(address _collateral, uint256 _amount) external;
function addUnderWaterDebt(address _vault, uint256 _amount) external;
function removeUnderWaterDebt(uint256 _amount) external;
function underWaterDebt() external view returns (uint256);
function collaterals() external view returns (address[] memory);
function collateral(address _collateral) external view returns (uint256);
function tryLiquidate() external;
function stabilityPool() external view returns (address);
function auctionManager() external view returns (address);
function lastResortLiquidation() external view returns (address);
function distributeBadDebt(address _vault, uint256 _amount) external;
function transferOwnership(address newOwner) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './IOwnable.sol';
interface IMintableToken is IERC20, IOwnable {
function mint(address recipient, uint256 amount) external;
function burn(uint256 amount) external;
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function approve(
address spender,
uint256 amount
) external override returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './IOwnable.sol';
import './IMintableToken.sol';
interface IMintableTokenOwner is IOwnable {
function token() external view returns (IMintableToken);
function mint(address _recipient, uint256 _amount) external;
function transferTokenOwnership(address _newOwner) external;
function addMinter(address _newMinter) external;
function revokeMinter(address _minter) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IOwnable {
/**
* @dev Returns the address of the current owner.
*/
function owner() external view returns (address);
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IPriceFeed {
function token() external view returns (address);
function price() external view returns (uint256);
function pricePoint() external view returns (uint256);
function emitPriceSignal() external;
event PriceUpdate(address token, uint256 price, uint256 average);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IRouter {
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] memory path,
address to,
uint256 deadline
) external;
function getAmountOut(
uint256 amountIn,
address token0,
address token1
) external view returns (uint256 amountOut);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IStabilityPool {
function liquidate() external;
function totalDeposit() external view returns (uint256);
function deposit(uint256 _amount) external;
function withdraw(uint256 _amount) external;
function tbankToken() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './IOwnable.sol';
interface ITokenPriceFeed is IOwnable {
struct TokenInfo {
address priceFeed;
uint256 mcr; // Minimum Collateralization Ratio
uint256 mlr; // Minimum Liquidation Ratio
uint256 borrowRate;
uint256 decimals;
}
function tokenPriceFeed(address) external view returns (address);
function tokenPrice(address _token) external view returns (uint256);
function mcr(address _token) external view returns (uint256);
function decimals(address _token) external view returns (uint256);
function mlr(address _token) external view returns (uint256);
function borrowRate(address _token) external view returns (uint256);
function setTokenPriceFeed(
address _token,
address _priceFeed,
uint256 _mcr,
uint256 _mlr,
uint256 _borrowRate,
uint256 /* _decimals */
) external;
event NewTokenPriceFeed(
address _token,
address _priceFeed,
string _name,
string _symbol,
uint256 _mcr,
uint256 _mlr,
uint256 _borrowRate,
uint256 _decimals
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IVault {
function vaultOwner() external view returns (address);
function debt() external view returns (uint256);
function transferVaultOwnership(address _newOwner) external;
function setName(string memory _name) external;
function containsCollateral(
address _collateral
) external view returns (bool);
function collateralsLength() external view returns (uint256);
function collateralAt(uint256 _index) external view returns (address);
function collaterals() external view returns (address[] memory);
function collateral(address _collateral) external view returns (uint256);
function factory() external view returns (address);
function addCollateral(address _collateral, uint256 _amount) external;
function removeCollateral(
address _collateral,
uint256 _amount,
address _to
) external;
function addBadDebt(uint256 _amount) external;
function borrowable()
external
view
returns (uint256 _maxBorrowable, uint256 _borrowable);
function borrow(uint256 _amount) external;
function repay(uint256 _amount) external;
function calcRedeem(
address _collateral,
uint256 _collateralAmount
)
external
view
returns (uint256 _stableAmountNeeded, uint256 _redemptionFee);
function redeem(
address _collateral,
uint256 _collateralAmount
) external returns (uint256 _debtRepaid, uint256 _feeCollected);
function healthFactor(
bool _useMlr
) external view returns (uint256 _healthFactor);
function newHealthFactor(
uint256 _newDebt,
bool _useMlr
) external view returns (uint256 _newHealthFactor);
function borrowableWithDiff(
address _collateral,
uint256 _diffAmount,
bool _isAdd,
bool _useMlr
) external view returns (uint256 _maxBorrowable, uint256 _borrowable);
function liquidate() external returns (uint256 _forgivenDebt);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IVaultBorrowRate {
function getBorrowRate(
address _vaultAddress
) external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IVaultDeployer {
function deployVault(
address _factory,
address _vaultOwner,
string memory _name
) external returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IVaultExtraSettings {
function setMaxRedeemablePercentage(
uint256 _debtTreshold,
uint256 _maxRedeemablePercentage
) external;
function setRedemptionKickback(uint256 _redemptionKickback) external;
function getExtraSettings()
external
view
returns (
uint256 _debtTreshold,
uint256 _maxRedeemablePercentage,
uint256 _redemptionKickback
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IVaultFactory {
event NewVault(address indexed vault, string name, address indexed owner);
event PriceFeedUpdated(address indexed priceFeed);
function setPriceFeed(address _priceFeed) external;
function vaultCount() external view returns (uint256);
function lastVault() external view returns (address);
function firstVault() external view returns (address);
function nextVault(address _vault) external view returns (address);
function prevVault(address _vault) external view returns (address);
function liquidationRouter() external view returns (address);
function MAX_TOKENS_PER_VAULT() external view returns (uint256);
function priceFeed() external view returns (address);
function transferVaultOwnership(address _vault, address _newOwner) external;
function createVault(string memory _name) external returns (address);
function addCollateralNative(address _vault) external payable;
function removeCollateralNative(
address _vault,
uint256 _amount,
address _to
) external;
function addCollateral(
address _vault,
address _collateral,
uint256 _amount
) external;
function removeCollateral(
address _vault,
address _collateral,
uint256 _amount,
address _to
) external;
function borrow(address _vault, uint256 _amount, address _to) external;
function distributeBadDebt(address _vault, uint256 _amount) external;
function closeVault(address _vault) external;
function repay(address _vault, uint256 _amount) external;
function redeem(
address _vault,
address _collateral,
uint256 _collateralAmount,
address _to
) external;
function liquidate(address _vault) external;
function isLiquidatable(address _vault) external view returns (bool);
function isReedemable(
address _vault,
address _collateral
) external view returns (bool);
function containsVault(address _vault) external view returns (bool);
function stable() external view returns (address);
function isCollateralSupported(
address _collateral
) external view returns (bool);
function vaultsByOwnerLength(
address _owner
) external view returns (uint256);
function redemptionHealthFactorLimit() external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IVaultFactoryConfig {
event PriceFeedUpdated(address indexed priceFeed);
event MaxTokensPerVaultUpdated(
uint256 oldMaxTokensPerVault,
uint256 newMaxTokensPerVault
);
event RedemptionRateUpdated(
uint256 oldRedemptionRate,
uint256 newRedemptionRate
);
event BorrowRateUpdated(uint256 oldBorrowRate, uint256 newBorrowRate);
event RedemptionHealthFactorLimitUpdated(
uint256 oldRedemptionHealthFactorLimit,
uint256 newRedemptionHealthFactorLimit
);
function setMaxTokensPerVault(uint256 _maxTokensPerVault) external;
function setPriceFeed(address _priceFeed) external;
function setRedemptionRate(uint256 _redemptionRate) external;
function setBorrowRate(uint256 _borrowRate) external;
function setRedemptionHealthFactorLimit(
uint256 _redemptionHealthFactorLimit
) external;
function setBorrowFeeRecipient(address _borrowFeeRecipient) external;
function setRedemptionFeeRecipient(
address _redemptionFeeRecipient
) external;
function priceFeed() external view returns (address);
function MAX_TOKENS_PER_VAULT() external view returns (uint256);
function redemptionRate() external view returns (uint256);
function borrowRate() external view returns (uint256);
function redemptionHealthFactorLimit() external view returns (uint256);
function borrowFeeRecipient() external view returns (address);
function redemptionFeeRecipient() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IWETH {
function deposit() external payable;
function approve(address, uint256) external returns (bool);
function transfer(address _to, uint256 _value) external returns (bool);
function withdraw(uint256) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import './interfaces/IVault.sol';
import './interfaces/IVaultFactory.sol';
import './interfaces/IStabilityPool.sol';
import './interfaces/IAuctionManager.sol';
import './interfaces/IMintableToken.sol';
import './interfaces/ILiquidationRouter.sol';
/**
* @title LastResortLiquidation
* @dev Contract to manage collateral and bad debt distribution for liquidation.
*/
contract LastResortLiquidation is Ownable, ReentrancyGuard {
event VaultFactoryUpdated(address indexed _vaultFactory);
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
using SafeERC20 for IMintableToken;
EnumerableSet.AddressSet private collateralSet;
EnumerableSet.AddressSet private allowedSet;
address public vaultFactory;
mapping(address => uint256) public collateral;
uint256 public badDebt;
modifier onlyAllowed() {
require(allowedSet.contains(msg.sender), 'not-allowed');
_;
}
/**
* @dev Adds an address to the allowed set.
* @param _allowed The address to add to the allowed set.
*/
function addAllowed(address _allowed) external onlyOwner {
require(_allowed != address(0x0), 'allowed-is-0');
allowedSet.add(_allowed);
}
/**
* @dev Removes an address from the allowed set.
* @param _allowed The address to remove from the allowed set.
*/
function removeAllowed(address _allowed) external onlyOwner {
require(_allowed != address(0x0), 'allowed-is-0');
allowedSet.remove(_allowed);
}
/**
* @dev Gets the number of addresses in the allowed set.
* @return The number of addresses in the allowed set.
*/
function allowedLength() external view returns (uint256) {
return allowedSet.length();
}
/**
* @dev Gets the address at the specified index in the allowed set.
* @param _index The index of the address.
* @return The address at the specified index in the allowed set.
*/
function allowedAt(uint256 _index) external view returns (address) {
return allowedSet.at(_index);
}
/**
* @dev Gets the number of addresses in the collateral set.
* @return The number of addresses in the collateral set.
*/
function collateralLength() external view returns (uint256) {
return collateralSet.length();
}
/**
* @dev Gets the address at the specified index in the collateral set.
* @param _index The index of the address.
* @return The address at the specified index in the collateral set.
*/
function collateralAt(uint256 _index) external view returns (address) {
return collateralSet.at(_index);
}
/**
* @dev Sets the address of the vault factory.
* @param _vaultFactory Address of the vault factory.
*/
function setVaultFactory(address _vaultFactory) external onlyOwner {
require(_vaultFactory != address(0x0), 'vault-factory-is-0');
vaultFactory = _vaultFactory;
emit VaultFactoryUpdated(_vaultFactory);
}
/**
* @dev Adds collateral to the contract and updates the collateral balance.
* @param _collateral The address of the collateral token.
* @param _amount The amount of collateral to add.
*/
function addCollateral(
address _collateral,
uint256 _amount
) external onlyAllowed {
require(_collateral != address(0x0), 'collateral-is-0');
require(_amount > 0, 'amount-is-0');
collateralSet.add(_collateral);
IERC20(_collateral).safeTransferFrom(
msg.sender,
address(this),
_amount
);
collateral[_collateral] += _amount;
}
/**
* @dev Withdraws collateral from the contract.
* @param _collateral The address of the collateral token.
* @param _amount The amount of collateral to withdraw.
* @param _to The address to receive the withdrawn collateral.
*/
function withdrawCollateral(
address _collateral,
uint256 _amount,
address _to
) external onlyOwner {
require(_collateral != address(0x0), 'collateral-is-0');
require(_amount > 0, 'amount-is-0');
collateral[_collateral] -= _amount;
if (collateral[_collateral] == 0) collateralSet.remove(_collateral);
IERC20(_collateral).safeTransfer(_to, _amount);
}
/**
* @dev Adds bad debt to the contract.
* @param _amount The amount of bad debt to add.
*/
function addBadDebt(uint256 _amount) external onlyAllowed {
require(_amount > 0, 'amount-is-0');
badDebt += _amount;
}
/**
* @dev Repays bad debt by burning stable tokens.
* @param _amount The amount of stable tokens to burn.
*/
function repayBadDebt(uint256 _amount) external onlyOwner {
require(_amount > 0, 'amount-is-0');
require(_amount <= badDebt, 'amount-too-high');
IMintableToken _stable = IMintableToken(
IVaultFactory(vaultFactory).stable()
);
_stable.safeTransferFrom(msg.sender, address(this), _amount);
_stable.burn(_amount);
badDebt -= _amount;
}
/**
* @dev Distributes bad debt to a specific vault.
* @param _vault The address of the vault to receive the bad debt.
* @param _amount The amount of bad debt to distribute.
*/
function distributeBadDebt(
address _vault,
uint256 _amount
) external onlyOwner {
require(_vault != address(0x0), 'vault-is-0');
require(_amount > 0, 'amount-is-0');
require(_amount <= badDebt, 'amount-too-high');
badDebt -= _amount;
IVaultFactory _vaultFactory = IVaultFactory(vaultFactory);
ILiquidationRouter _liquidationRouter = ILiquidationRouter(
_vaultFactory.liquidationRouter()
);
_liquidationRouter.distributeBadDebt(_vault, _amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import './interfaces/IVault.sol';
import './interfaces/IVaultFactory.sol';
import './interfaces/IStabilityPool.sol';
import './interfaces/IAuctionManager.sol';
/**
* @title LiquidationRouter
* @dev Handles liquidation and redistribution of collaterals and debts in the system.
*/
contract LiquidationRouter is Ownable, ReentrancyGuard {
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
event SeizedCollateralAdded(
address indexed collateral,
address indexed _vaultFactory,
address indexed _vault,
uint256 amount
);
event UnderWaterDebtAdded(
address indexed _vaultFactory,
address indexed _vault,
uint256 debtAmount
);
event UnderWaterDebtRemoved(
address indexed _vaultFactory,
uint256 debtAmount
);
event VaultFactoryUpdated(address indexed _vaultFactory);
event StabilityPoolUpdated(address indexed _stabilityPool);
event AuctionManagerUpdated(address indexed _auctionManager);
event LastResortLiquidationUpdated(address indexed _lastResortLiquidation);
event BadDebtDistributed(address indexed _vault, uint256 amount);
uint256 public underWaterDebt;
address public vaultFactory;
address public stabilityPool;
address public auctionManager;
address public lastResortLiquidation;
EnumerableSet.AddressSet private collateralSet;
mapping(address => uint256) public collateral;
/**
* @dev Sets the last resort liquidation contract address.
* @param _lastResortLiquidation Address of the last resort liquidation contract.
*/
function setLastResortLiquidation(
address _lastResortLiquidation
) external onlyOwner {
require(
_lastResortLiquidation != address(0x0),
'last-resort-liquidation-is-0'
);
lastResortLiquidation = _lastResortLiquidation;
emit LastResortLiquidationUpdated(_lastResortLiquidation);
}
/**
* @dev Sets the stability pool contract address.
* @param _stabilityPool Address of the stability pool contract.
*/
function setStabilityPool(address _stabilityPool) external onlyOwner {
require(_stabilityPool != address(0x0), 'stability-pool-is-0');
stabilityPool = _stabilityPool;
emit StabilityPoolUpdated(_stabilityPool);
}
/**
* @dev Sets the auction manager contract address.
* @param _auctionManager Address of the auction manager contract.
*/
function setAuctionManager(address _auctionManager) external onlyOwner {
require(_auctionManager != address(0x0), 'auction-manager-is-0');
auctionManager = _auctionManager;
emit AuctionManagerUpdated(_auctionManager);
}
modifier onlyVault() {
require(
IVaultFactory(vaultFactory).containsVault(msg.sender),
'not-a-vault'
);
_;
}
modifier onlyAllowed() {
require(msg.sender == stabilityPool, 'not-allowed');
_;
}
modifier onlyLastResortLiquidation() {
require(
msg.sender == lastResortLiquidation,
'not-last-resort-liquidation'
);
_;
}
/**
* @dev Checks if a specific collateral token is registered.
* @param _collateral Address of the collateral token to check.
* @return bool indicating the presence of the collateral token.
*/
function containsCollateral(
address _collateral
) external view returns (bool) {
return collateralSet.contains(_collateral);
}
/**
* @dev Returns the count of registered collateral tokens.
* @return uint256 representing the count of collateral tokens.
*/
function collateralsLength() external view returns (uint256) {
return collateralSet.length();
}
/**
* @dev Gets the collateral token at a specific index in the list of registered collaterals.
* @param _index Index of the collateral token.
* @return address representing the collateral token address.
*/
function collateralAt(uint256 _index) external view returns (address) {
return collateralSet.at(_index);
}
/**
* @dev Gets all the registered collateral tokens.
* @return address[] memory representing the list of collateral token addresses.
*/
function collaterals() external view returns (address[] memory) {
address[] memory _collaterals = new address[](collateralSet.length());
for (uint256 i = 0; i < collateralSet.length(); i++) {
_collaterals[i] = collateralSet.at(i);
}
return _collaterals;
}
/**
* @dev Sets the vault factory contract address.
* @param _vaultFactory Address of the vault factory contract.
*/
function setVaultFactory(address _vaultFactory) external onlyOwner {
require(_vaultFactory != address(0x0), 'vault-factory-is-0');
require(
IVaultFactory(_vaultFactory).liquidationRouter() == address(this),
'wrong-liquidation-router'
);
vaultFactory = _vaultFactory;
emit VaultFactoryUpdated(_vaultFactory);
}
/**
* @dev Adds seized collateral to the contract.
* @param _collateral Address of the seized collateral.
* @param _amount Amount of seized collateral.
*/
function addSeizedCollateral(
address _collateral,
uint256 _amount
) external onlyVault {
IERC20(_collateral).safeTransferFrom(
msg.sender,
address(this),
_amount
);
IERC20(_collateral).safeApprove(stabilityPool, 0);
IERC20(_collateral).safeApprove(stabilityPool, _amount);
IERC20(_collateral).safeApprove(auctionManager, 0);
IERC20(_collateral).safeApprove(auctionManager, _amount);
collateralSet.add(_collateral);
collateral[_collateral] += _amount;
emit SeizedCollateralAdded(
_collateral,
vaultFactory,
msg.sender,
_amount
);
}
/**
* @dev Adds underwater debt for a vault and increases the total underwater debt for the system.
* @param _vault Address of the vault.
* @param _amount Amount of underwater debt.
*/
function addUnderWaterDebt(
address _vault,
uint256 _amount
) external onlyVault {
underWaterDebt += _amount;
emit UnderWaterDebtAdded(vaultFactory, _vault, _amount);
}
/**
* @dev Removes underwater debt from the system and decreases the total underwater debt.
* @param _amount Amount of underwater debt to be removed.
*/
function _removeUnderWaterDebt(uint256 _amount) internal {
underWaterDebt -= _amount;
emit UnderWaterDebtRemoved(vaultFactory, _amount);
}
/**
* @dev Withdraws liquidated collateral.
* @param _collateral Address of the liquidated collateral.
* @param _to Address to receive the liquidated collateral.
* @param _amount Amount of liquidated collateral to withdraw.
*/
function withdrawLiquidatedCollateral(
address _collateral,
address _to,
uint256 _amount
) external onlyOwner {
IERC20(_collateral).safeTransfer(_to, _amount);
collateral[_collateral] -= _amount;
if (collateral[_collateral] == 0) {
collateralSet.remove(_collateral);
}
}
/**
* @dev Removes all collaterals from the contract.
* This function sets the collateral amount for each collateral token to 0.
*/
function _removeAllCollaterals() internal {
uint256 _length = collateralSet.length();
for (uint256 i; i < _length; i++) {
address _collateral = collateralSet.at(i);
collateral[_collateral] = 0;
}
}
/**
* @dev Initiates liquidation or auction if necessary.
*/
function tryLiquidate() external nonReentrant {
require(underWaterDebt > 0, 'no-underwater-debt');
uint256 _stabilityPoolDeposit = IStabilityPool(stabilityPool)
.totalDeposit();
if (_stabilityPoolDeposit >= underWaterDebt) {
IStabilityPool(stabilityPool).liquidate();
} else {
IAuctionManager(auctionManager).newAuction();
}
_removeAllCollaterals();
_removeUnderWaterDebt(underWaterDebt);
}
/**
* @dev Distributes bad debt in the system.
* @param _vault Address of the vault with bad debt.
* @param _amount Amount of bad debt to distribute.
*/
function distributeBadDebt(
address _vault,
uint256 _amount
) external onlyLastResortLiquidation {
IVaultFactory(vaultFactory).distributeBadDebt(_vault, _amount);
emit BadDebtDistributed(_vault, _amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
/// @title implements minting/burning functionality for owner
contract MintableToken is ERC20, Ownable {
// solhint-disable-next-line func-visibility
constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
/// @dev mints tokens to the recipient, to be called from owner
/// @param recipient address to mint
/// @param amount amount to be minted
function mint(address recipient, uint256 amount) public onlyOwner {
_mint(recipient, amount);
}
/// @dev burns token of specified amount from msg.sender
/// @param amount to burn
function burn(uint256 amount) public {
_burn(msg.sender, amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
import './interfaces/IMintableToken.sol';
/// @title implements owner of the MintableToken contract
contract MintableTokenOwner is Ownable {
IMintableToken public immutable token;
mapping(address => bool) public minters;
event MinterAdded(address newMinter);
// solhint-disable-next-line func-visibility
constructor(address _token) Ownable() {
token = IMintableToken(_token);
}
/// @dev mints tokens to the recipient, to be called from owner
/// @param _recipient address to mint
/// @param _amount amount to be minted
function mint(address _recipient, uint256 _amount) public {
require(
minters[msg.sender],
'MintableTokenOwner:mint: the sender must be in the minters list'
);
token.mint(_recipient, _amount);
}
function transferTokenOwnership(address _newOwner) public onlyOwner {
token.transferOwnership(_newOwner);
}
/// @dev adds new minter
/// @param _newMinter address of new minter
function addMinter(address _newMinter) public onlyOwner {
minters[_newMinter] = true;
emit MinterAdded(_newMinter);
}
/// @dev removes minter from minter list
/// @param _minter address of the minter
function revokeMinter(address _minter) public onlyOwner {
minters[_minter] = false;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '@openzeppelin/contracts/utils/Context.sol';
/**
* @title OwnerProxy
* @dev Allows the main owner to add fine-grained permissions to other operators.
*/
contract OwnerProxy is Context, Ownable {
mapping(uint256 => bool) public permissions;
event PermissionAdded(
address indexed caller,
address targetAddress,
bytes4 targetSignature,
uint256 permissionHash
);
event PermissionRemoved(uint256 indexed permissionHash);
event Executed(
address indexed caller,
address indexed target,
string func,
bytes data
);
/**
* @dev Adds permission for a specific caller to execute a function on a target address.
* @param caller The address allowed to call the function.
* @param targetAddress The target address where the function will be called.
* @param targetSignature The function signature to be executed.
*/
function addPermission(
address caller,
address targetAddress,
bytes4 targetSignature
) public onlyOwner {
require(caller != address(0), 'invalid-caller-address');
require(targetAddress != address(0), 'invalid-target-address');
uint256 _hash = uint256(
keccak256(abi.encodePacked(caller, targetAddress, targetSignature))
);
permissions[_hash] = true;
emit PermissionAdded(caller, targetAddress, targetSignature, _hash);
}
/**
* @dev Removes a specific permission.
* @param permissionHash The hash of the permission to be removed.
*/
function removePermission(uint256 permissionHash) public onlyOwner {
delete permissions[permissionHash];
emit PermissionRemoved(permissionHash);
}
/**
* @dev Executes a function on a target address only if the caller has the required permission.
* @param target The contract address where the function will be called.
* @param func The name of the function to be executed.
* @param data The data to be passed to the function.
* @return _result The result of the function execution.
*/
function execute(
address target,
string memory func,
bytes memory data
) public returns (bytes memory _result) {
bytes4 _targetSignature = bytes4(keccak256(bytes(func)));
uint256 _hash = uint256(
keccak256(abi.encodePacked(_msgSender(), target, _targetSignature))
);
require(permissions[_hash], 'invalid-permission');
emit Executed(_msgSender(), target, func, data);
_result = Address.functionCall(
target,
bytes.concat(_targetSignature, data)
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH =
0x5fd83e37b194e20b4858ffd8707ab464489099cc00c7985c0a048fa38836bbaa;
/// @notice The identifying key of the pool
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
/// @notice Returns PoolKey: the ordered tokens with the matched fee levels
/// @param tokenA The first token of a pool, unsorted
/// @param tokenB The second token of a pool, unsorted
/// @param fee The fee level of the pool
/// @return Poolkey The pool details with ordered token0 and token1 assignments
function getPoolKey(
address tokenA,
address tokenB,
uint24 fee
) internal pure returns (PoolKey memory) {
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
}
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param factory The Uniswap V3 factory contract address
/// @param key The PoolKey
/// @return pool The contract address of the V3 pool
function computeAddress(
address factory,
PoolKey memory key
) internal pure returns (address pool) {
require(key.token0 < key.token1);
pool = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex'ff',
factory,
keccak256(
abi.encode(key.token0, key.token1, key.fee)
),
POOL_INIT_CODE_HASH
)
)
)
)
);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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 Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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'
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
// import openzeppelin reentrancy guard
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import './utils/constants.sol';
import './interfaces/IVaultFactory.sol';
import './interfaces/IMintableToken.sol';
import './interfaces/IVault.sol';
import './interfaces/ILiquidationRouter.sol';
/**
* @title StabilityPool
* @dev A smart contract responsible for liquidating vaults and rewarding depositors with collateral redeemed.
* @notice is used to liquidate vaults and reward depositors with collateral redeemed
*/
contract StabilityPool is Ownable, ReentrancyGuard, Constants {
using SafeERC20 for IERC20;
// A structure defining token addresses and their respective 'Stable' values
struct TokenToS {
address tokenAddress;
uint256 S_value;
}
// A structure defining token addresses and their corresponding uint256 values
struct TokenToUint256 {
address tokenAddress;
uint256 value;
}
// A structure that holds snapshots of token balances, 'P' and 'G', and epoch information
struct Snapshots {
TokenToS[] tokenToSArray;
uint256 P;
uint256 G;
uint128 scale;
uint128 epoch;
}
IVaultFactory public factory;
IMintableToken public immutable stableCoin;
IERC20 public immutable tbankToken;
// Track total deposits and error offsets
uint256 public totalDeposit;
mapping(address => uint256) public collateralToLastErrorOffset;
uint256 public lastStableCoinLossErrorOffset;
mapping(address => uint256) public deposits;
mapping(address => Snapshots) public depositSnapshots; // depositor address -> snapshots struct
// Variables related to TBANK rewards and error tracking
uint256 public tbankPerMinute;
uint256 public totalTBANKRewardsLeft;
uint256 public latestTBANKRewardTime;
// Error tracker for the error correction in the TBANK redistribution calculation
uint256 public lastTBANKError;
/* Product 'P': Running product by which to multiply an initial deposit, in order to find the current compounded deposit,
* after a series of liquidations have occurred, each of which cancel some StableCoin debt with the deposit.
*
* During its lifetime, a deposit's value evolves from d_t to d_t * P / P_t , where P_t
* is the snapshot of P taken at the instant the deposit was made. 18-digit decimal.
*/
uint256 public P;
uint256 public constant SCALE_FACTOR = 1e9;
uint256 public constant SECONDS_IN_ONE_MINUTE = 60;
// Each time the scale of P shifts by SCALE_FACTOR, the scale is incremented by 1
uint128 public currentScale;
// With each offset that fully empties the Pool, the epoch is incremented by 1
uint128 public currentEpoch;
/* Collateral Gain sum 'S': During its lifetime, each deposit d_t earns an Collateral gain of ( d_t * [S - S_t] )/P_t, where S_t
* is the depositor's snapshot of S taken at the time t when the deposit was made.
*
* The 'S' sums are stored in a nested mapping (epoch => scale => sum):
*
* - The inner mapping records the sum S at different scales
* - The outer mapping records the (scale => sum) mappings, for different epochs.
*/
mapping(uint128 => mapping(uint128 => TokenToS[]))
public epochToScaleToTokenToSum;
/*
* Similarly, the sum 'G' is used to calculate TBANK gains. During it's lifetime, each deposit d_t earns a TBANK gain of
* ( d_t * [G - G_t] )/P_t, where G_t is the depositor's snapshot of G taken at time t when the deposit was made.
*
* TBANK reward events occur are triggered by depositor operations (new deposit, topup, withdrawal), and liquidations.
* In each case, the TBANK reward is issued (i.e. G is updated), before other state changes are made.
*/
mapping(uint128 => mapping(uint128 => uint256)) public epochToScaleToG;
event Deposit(address _contributor, uint256 _amount);
event TotalDepositUpdated(uint256 _newValue);
event Withdraw(address _contributor, uint256 _amount);
// Events
// solhint-disable-next-line event-name-camelcase
event TBANKRewardRedeemed(address _contributor, uint256 _amount);
event TBANKRewardIssue(uint256 issuance, uint256 _totalTBANKRewardsLeft);
event TBANKPerMinuteUpdated(uint256 _newAmount);
event TotalTBANKRewardsUpdated(uint256 _newAmount);
// solhint-disable-next-line event-name-camelcase
event CollateralRewardRedeemed(
address _contributor,
address _tokenAddress,
uint256 _amount
);
event DepositSnapshotUpdated(
address indexed _depositor,
uint256 _P,
uint256 _G,
uint256 _newDepositValue
);
/* solhint-disable event-name-camelcase */
event P_Updated(uint256 _P);
event S_Updated(
address _tokenAddress,
uint256 _S,
uint128 _epoch,
uint128 _scale
);
event G_Updated(uint256 _G, uint128 _epoch, uint128 _scale);
/* solhint-disable event-name-camelcase */
event EpochUpdated(uint128 _currentEpoch);
event ScaleUpdated(uint128 _currentScale);
/**
* @notice Initializes the StabilityPool contract with the given Vault factory and TBANK token addresses.
* @dev The constructor sets up essential contract parameters upon deployment.
* @param _factory Address of the Vault Factory contract responsible for creating Vault instances.
* @param _tbankToken Address of the TBANK token to be used within the Vault system.
*/
constructor(address _factory, address _tbankToken) {
require(_factory != address(0x0), 'factory-is-0');
require(_tbankToken != address(0x0), 'tbank-is-0');
factory = IVaultFactory(_factory);
stableCoin = IMintableToken(address(IVaultFactory(_factory).stable()));
tbankToken = IERC20(_tbankToken);
P = DECIMAL_PRECISION;
}
/// @dev to deposit StableCoin into StabilityPool this must be protected against a reentrant attack from the arbitrage
/// @param _amount amount to deposit
function deposit(uint256 _amount) public nonReentrant {
// address depositor = msg.sender;
require(_amount > 0, 'amount-is-0');
stableCoin.transferFrom(msg.sender, address(this), _amount);
uint256 initialDeposit = deposits[msg.sender];
_redeemReward();
Snapshots memory snapshots = depositSnapshots[msg.sender];
uint256 compoundedDeposit = _getCompoundedDepositFromSnapshots(
initialDeposit,
snapshots
);
// uint256 newValue = compoundedDeposit + _amount;
uint256 newTotalDeposit = totalDeposit + _amount;
totalDeposit = newTotalDeposit;
_updateDepositAndSnapshots(msg.sender, compoundedDeposit + _amount);
emit Deposit(msg.sender, _amount);
emit TotalDepositUpdated(newTotalDeposit);
}
/// @dev to withdraw StableCoin that was not spent if this function is called in a reentrantway during arbitrage it
/// @dev would skew the token allocation and must be protected against
/// @param _amount amount to withdraw
function withdraw(uint256 _amount) public nonReentrant {
uint256 contributorDeposit = deposits[msg.sender];
require(_amount > 0, 'amount-is-0');
require(contributorDeposit > 0, 'deposit-is-0');
_redeemReward();
Snapshots memory snapshots = depositSnapshots[msg.sender];
uint256 compoundedDeposit = _getCompoundedDepositFromSnapshots(
contributorDeposit,
snapshots
);
uint256 calculatedAmount = compoundedDeposit > _amount
? _amount
: compoundedDeposit;
uint256 newValue = compoundedDeposit - calculatedAmount;
totalDeposit = totalDeposit - calculatedAmount;
_updateDepositAndSnapshots(msg.sender, newValue);
stableCoin.transfer(msg.sender, calculatedAmount);
emit Withdraw(msg.sender, calculatedAmount);
emit TotalDepositUpdated(totalDeposit);
}
/// @dev to withdraw collateral rewards earned after liquidations
/// @dev this function does not provide an opportunity for a reentrancy attack
function redeemReward() external {
Snapshots memory snapshots = depositSnapshots[msg.sender];
uint256 contributorDeposit = deposits[msg.sender];
uint256 compoundedDeposit = _getCompoundedDepositFromSnapshots(
contributorDeposit,
snapshots
);
_redeemReward();
_updateDepositAndSnapshots(msg.sender, compoundedDeposit);
}
function setVaultFactory(address _factory) external onlyOwner {
require(_factory != address(0x0), 'factory-is-0');
factory = IVaultFactory(_factory);
}
/// @dev liquidates vault, must be called from that vault
/// @dev this function does not provide an opportunity for a reentrancy attack even though it would make the arbitrage
/// @dev fail because of the lowering of the stablecoin balance
/// @notice must be called by the valid vault
function liquidate() external {
require(
msg.sender == factory.liquidationRouter(),
'not-liquidation-router'
);
IVaultFactory factory_cached = factory;
ILiquidationRouter _liquidationRouter = ILiquidationRouter(
factory_cached.liquidationRouter()
);
uint256 _underWaterDebt = _liquidationRouter.underWaterDebt();
address[] memory _collaterals = _liquidationRouter.collaterals();
uint256 _collateralCount = _collaterals.length;
uint256 totalStableCoin = totalDeposit; // cached to save an SLOAD
for (uint256 i; i < _collateralCount; i++) {
IERC20 _collateralToken = IERC20(_collaterals[i]);
uint256 _collateralAmount = _liquidationRouter.collateral(
address(_collateralToken)
);
_collateralToken.safeTransferFrom(
address(_liquidationRouter),
address(this),
_collateralAmount
);
(
uint256 collateralGainPerUnitStaked,
uint256 stableCoinLossPerUnitStaked
) = _computeRewardsPerUnitStaked(
address(_collateralToken),
_collateralAmount,
_underWaterDebt,
totalStableCoin
);
_updateRewardSumAndProduct(
address(_collateralToken),
collateralGainPerUnitStaked,
stableCoinLossPerUnitStaked
);
}
_triggerTBANKdistribution();
stableCoin.burn(_underWaterDebt);
uint256 newTotalDeposit = totalStableCoin - _underWaterDebt;
totalDeposit = newTotalDeposit;
emit TotalDepositUpdated(newTotalDeposit);
//factory_cached.emitLiquidationEvent(address(collateralToken), msg.sender, address(this), vaultCollateral);
}
/**
* @dev Gets the current withdrawable deposit of a specified staker.
* @param staker The address of the staker
* @return uint256 The withdrawable deposit amount
*/ function getWithdrawableDeposit(
address staker
) public view returns (uint256) {
uint256 initialDeposit = deposits[staker];
Snapshots memory snapshots = depositSnapshots[staker];
return _getCompoundedDepositFromSnapshots(initialDeposit, snapshots);
}
/**
* @dev Retrieves the collateral reward of a specified `_depositor` for a specific `_token`.
* @param _token The address of the collateral token
* @param _depositor The address of the depositor
* @return uint256 The collateral reward amount
*/
function getCollateralReward(
address _token,
address _depositor
) external view returns (uint256) {
Snapshots memory _snapshots = depositSnapshots[_depositor];
uint256 _initialDeposit = deposits[_depositor];
uint128 epochSnapshot = _snapshots.epoch;
uint128 scaleSnapshot = _snapshots.scale;
TokenToS[] memory tokensToSum_cached = epochToScaleToTokenToSum[
epochSnapshot
][scaleSnapshot];
uint256 tokenArrayLength = tokensToSum_cached.length;
TokenToS memory cachedS;
for (uint128 i = 0; i < tokenArrayLength; i++) {
TokenToS memory S = tokensToSum_cached[i];
if (S.tokenAddress == _token) {
cachedS = S;
break;
}
}
if (cachedS.tokenAddress == address(0)) return 0;
uint256 relatedSValue_snapshot;
for (uint128 i = 0; i < _snapshots.tokenToSArray.length; i++) {
TokenToS memory S_snapsot = _snapshots.tokenToSArray[i];
if (S_snapsot.tokenAddress == _token) {
relatedSValue_snapshot = S_snapsot.S_value;
break;
}
}
TokenToS[] memory nextTokensToSum_cached = epochToScaleToTokenToSum[
epochSnapshot
][scaleSnapshot + 1];
uint256 nextScaleS;
for (uint128 i = 0; i < nextTokensToSum_cached.length; i++) {
TokenToS memory nextScaleTokenToS = nextTokensToSum_cached[i];
if (nextScaleTokenToS.tokenAddress == _token) {
nextScaleS = nextScaleTokenToS.S_value;
break;
}
}
uint256 P_Snapshot = _snapshots.P;
uint256 collateralGain = _getCollateralGainFromSnapshots(
_initialDeposit,
cachedS.S_value,
nextScaleS,
relatedSValue_snapshot,
P_Snapshot
);
return collateralGain;
}
/**
* @dev Retrieves the TBANK reward of a specified `_depositor`.
* @param _depositor The address of the user
* @return uint256 The TBANK reward amount
*/
function getDepositorTBANKGain(
address _depositor
) external view returns (uint256) {
uint256 totalTBANKRewardsLeft_cached = totalTBANKRewardsLeft;
uint256 totalStableCoin = totalDeposit;
if (
totalTBANKRewardsLeft_cached == 0 ||
tbankPerMinute == 0 ||
totalStableCoin == 0
) {
return 0;
}
uint256 _tbankIssuance = tbankPerMinute *
((block.timestamp - latestTBANKRewardTime) / SECONDS_IN_ONE_MINUTE);
if (totalTBANKRewardsLeft_cached < _tbankIssuance) {
_tbankIssuance = totalTBANKRewardsLeft_cached;
}
uint256 tbankGain = (_tbankIssuance * DECIMAL_PRECISION + lastTBANKError) /
totalStableCoin;
uint256 marginalTBANKGain = tbankGain * P;
return _getDepositorTBANKGain(_depositor, marginalTBANKGain);
}
/**
* @dev Sets the amount of TBANK tokens per minute for rewards.
* @param _tbankPerMinute The TBANK tokens per minute to be set
*/
function setTBANKPerMinute(uint256 _tbankPerMinute) external onlyOwner {
_triggerTBANKdistribution();
tbankPerMinute = _tbankPerMinute;
emit TBANKPerMinuteUpdated(tbankPerMinute);
}
/**
* @dev Sets the total amount of TBANK tokens to be rewarded.
* It pays per minute until it reaches the specified rewarded amount.
*/
function setTBANKAmountForRewards() external onlyOwner {
_triggerTBANKdistribution();
totalTBANKRewardsLeft = tbankToken.balanceOf(address(this));
emit TotalTBANKRewardsUpdated(totalTBANKRewardsLeft);
}
/**
* @dev Redeems rewards, calling internal functions for collateral and TBANK rewards.
* Private function for internal use.
*/
function _redeemReward() private {
_redeemCollateralReward();
_triggerTBANKdistribution();
_redeemTBANKReward();
}
/**
* @notice Allows a depositor to redeem collateral rewards.
*/
function _redeemCollateralReward() internal {
address depositor = msg.sender;
TokenToUint256[]
memory depositorCollateralGains = _getDepositorCollateralGains(
depositor
);
_sendCollateralRewardsToDepositor(depositorCollateralGains);
}
/**
* @notice Allows a depositor to redeem TBANK rewards.
*/
function _redeemTBANKReward() internal {
address depositor = msg.sender;
uint256 depositorTBANKGain = _getDepositorTBANKGain(depositor, 0);
_sendTBANKRewardsToDepositor(depositorTBANKGain);
emit TBANKRewardRedeemed(depositor, depositorTBANKGain);
}
/**
* @dev Updates user deposit snapshot data for a new deposit value.
* @param _depositor The address of the depositor.
* @param _newValue The new deposit value.
*/
function _updateDepositAndSnapshots(
address _depositor,
uint256 _newValue
) private {
deposits[_depositor] = _newValue;
if (_newValue == 0) {
delete depositSnapshots[_depositor];
emit DepositSnapshotUpdated(_depositor, 0, 0, 0);
return;
}
uint128 cachedEpoch = currentEpoch;
uint128 cachedScale = currentScale;
TokenToS[] storage cachedTokenToSArray = epochToScaleToTokenToSum[
cachedEpoch
][cachedScale]; // TODO: maybe remove and read twice?
uint256 cachedP = P;
uint256 cachedG = epochToScaleToG[cachedEpoch][cachedScale];
depositSnapshots[_depositor].tokenToSArray = cachedTokenToSArray; // TODO
depositSnapshots[_depositor].P = cachedP;
depositSnapshots[_depositor].G = cachedG;
depositSnapshots[_depositor].scale = cachedScale;
depositSnapshots[_depositor].epoch = cachedEpoch;
emit DepositSnapshotUpdated(_depositor, cachedP, cachedG, _newValue);
}
/**
* @notice Updates the reward sums and product based on collateral and stablecoin changes.
* @dev This function updates the reward sums and product based on changes in collateral and stablecoin values.
* @param _collateralTokenAddress Address of the collateral token.
* @param _collateralGainPerUnitStaked Collateral gains per unit staked.
* @param _stableCoinLossPerUnitStaked Stablecoin losses per unit staked.
*/
function _updateRewardSumAndProduct(
address _collateralTokenAddress,
uint256 _collateralGainPerUnitStaked,
uint256 _stableCoinLossPerUnitStaked
) internal {
assert(_stableCoinLossPerUnitStaked <= DECIMAL_PRECISION);
uint128 currentScaleCached = currentScale;
uint128 currentEpochCached = currentEpoch;
uint256 currentS;
uint256 currentSIndex;
bool _found;
TokenToS[] memory currentTokenToSArray = epochToScaleToTokenToSum[
currentEpochCached
][currentScaleCached];
for (uint128 i = 0; i < currentTokenToSArray.length; i++) {
if (
currentTokenToSArray[i].tokenAddress == _collateralTokenAddress
) {
currentS = currentTokenToSArray[i].S_value;
currentSIndex = i;
_found = true;
}
}
/*
* Calculate the new S first, before we update P.
* The Collateral gain for any given depositor from a liquidation depends on the value of their deposit
* (and the value of totalDeposits) prior to the Stability being depleted by the debt in the liquidation.
*
* Since S corresponds to Collateral gain, and P to deposit loss, we update S first.
*/
uint256 marginalCollateralGain = _collateralGainPerUnitStaked * P;
uint256 newS = currentS + marginalCollateralGain;
if (currentTokenToSArray.length == 0 || !_found) {
TokenToS memory tokenToS;
tokenToS.S_value = newS;
tokenToS.tokenAddress = _collateralTokenAddress;
epochToScaleToTokenToSum[currentEpochCached][currentScaleCached]
.push() = tokenToS;
} else {
epochToScaleToTokenToSum[currentEpochCached][currentScaleCached][
currentSIndex
].S_value = newS;
}
emit S_Updated(
_collateralTokenAddress,
newS,
currentEpochCached,
currentScaleCached
);
_updateP(_stableCoinLossPerUnitStaked, true);
}
function _updateP(
uint256 _stableCoinChangePerUnitStaked,
bool loss
) internal {
/*
* The newProductFactor is the factor by which to change all deposits, due to the depletion of Stability Pool StableCoin in the liquidation.
* We make the product factor 0 if there was a pool-emptying. Otherwise, it is (1 - StableCoinLossPerUnitStaked)
*/
uint256 newProductFactor;
if (loss) {
newProductFactor = uint256(
DECIMAL_PRECISION - _stableCoinChangePerUnitStaked
);
} else {
newProductFactor = uint256(
DECIMAL_PRECISION + _stableCoinChangePerUnitStaked
);
}
uint256 currentP = P;
uint256 newP;
// If the Stability Pool was emptied, increment the epoch, and reset the scale and product P
if (newProductFactor == 0) {
currentEpoch += 1;
emit EpochUpdated(currentEpoch);
currentScale = 0;
emit ScaleUpdated(0);
newP = DECIMAL_PRECISION;
// If multiplying P by a non-zero product factor would reduce P below the scale boundary, increment the scale
} else if (
(currentP * newProductFactor) / DECIMAL_PRECISION < SCALE_FACTOR
) {
newP =
(currentP * newProductFactor * SCALE_FACTOR) /
DECIMAL_PRECISION;
currentScale += 1;
emit ScaleUpdated(currentScale);
} else {
newP = (currentP * newProductFactor) / DECIMAL_PRECISION;
}
assert(newP > 0);
P = newP;
emit P_Updated(newP);
}
/**
* @dev Updates G when a new TBANK amount is issued.
* @param _tbankIssuance The new TBANK issuance amount
*/
function _updateG(uint256 _tbankIssuance) internal {
uint256 totalStableCoin = totalDeposit; // cached to save an SLOAD
/*
* When total deposits is 0, G is not updated. In this case, the TBANK issued can not be obtained by later
* depositors - it is missed out on, and remains in the balanceof the Stability Pool.
*
*/
if (totalStableCoin == 0 || _tbankIssuance == 0) {
return;
}
uint256 tbankPerUnitStaked;
tbankPerUnitStaked = _computeTBANKPerUnitStaked(
_tbankIssuance,
totalStableCoin
);
uint256 marginalTBANKGain = tbankPerUnitStaked * P;
uint128 currentEpoch_cached = currentEpoch;
uint128 currentScale_cached = currentScale;
uint256 newEpochToScaleToG = epochToScaleToG[currentEpoch_cached][
currentScale_cached
] + marginalTBANKGain;
epochToScaleToG[currentEpoch_cached][
currentScale_cached
] = newEpochToScaleToG;
emit G_Updated(
newEpochToScaleToG,
currentEpoch_cached,
currentScale_cached
);
}
/**
* @dev Retrieves the collateral gains of a specified `_depositor`.
* @param _depositor The address of the depositor
* @return TokenToUint256[] An array containing collateral gain information
*/
function _getDepositorCollateralGains(
address _depositor
) internal view returns (TokenToUint256[] memory) {
uint256 initialDeposit = deposits[_depositor];
if (initialDeposit == 0) {
TokenToUint256[] memory x;
return x;
}
Snapshots memory snapshots = depositSnapshots[_depositor];
TokenToUint256[]
memory gainPerCollateralArray = _getCollateralGainsArrayFromSnapshots(
initialDeposit,
snapshots
);
return gainPerCollateralArray;
}
// todo!
function _getCollateralGainsArrayFromSnapshots(
uint256 _initialDeposit,
Snapshots memory _snapshots
) internal view returns (TokenToUint256[] memory) {
/*
* Grab the sum 'S' from the epoch at which the stake was made. The Collateral gain may span up to one scale change.
* If it does, the second portion of the Collateral gain is scaled by 1e9.
* If the gain spans no scale change, the second portion will be 0.
*/
uint128 epochSnapshot = _snapshots.epoch;
uint128 scaleSnapshot = _snapshots.scale;
TokenToS[] memory tokensToSum_cached = epochToScaleToTokenToSum[
epochSnapshot
][scaleSnapshot];
uint256 tokenArrayLength = tokensToSum_cached.length;
TokenToUint256[] memory CollateralGainsArray = new TokenToUint256[](
tokenArrayLength
);
for (uint128 i = 0; i < tokenArrayLength; i++) {
TokenToS memory S = tokensToSum_cached[i];
uint256 relatedS_snapshot;
for (uint128 j = 0; j < _snapshots.tokenToSArray.length; j++) {
TokenToS memory S_snapsot = _snapshots.tokenToSArray[j];
if (S_snapsot.tokenAddress == S.tokenAddress) {
relatedS_snapshot = S_snapsot.S_value;
break;
}
}
TokenToS[] memory nextTokensToSum_cached = epochToScaleToTokenToSum[
epochSnapshot
][scaleSnapshot + 1];
uint256 nextScaleS;
for (uint128 j = 0; j < nextTokensToSum_cached.length; j++) {
TokenToS memory nextScaleTokenToS = nextTokensToSum_cached[j];
if (nextScaleTokenToS.tokenAddress == S.tokenAddress) {
nextScaleS = nextScaleTokenToS.S_value;
break;
}
}
uint256 P_Snapshot = _snapshots.P;
CollateralGainsArray[i].value = _getCollateralGainFromSnapshots(
_initialDeposit,
S.S_value,
nextScaleS,
relatedS_snapshot,
P_Snapshot
);
CollateralGainsArray[i].tokenAddress = S.tokenAddress;
}
return CollateralGainsArray;
}
function _getCollateralGainFromSnapshots(
uint256 initialDeposit,
uint256 S,
uint256 nextScaleS,
uint256 S_Snapshot,
uint256 P_Snapshot
) internal pure returns (uint256) {
uint256 firstPortion = S - S_Snapshot;
uint256 secondPortion = nextScaleS / SCALE_FACTOR;
uint256 collateralGain = (initialDeposit *
(firstPortion + secondPortion)) /
P_Snapshot /
DECIMAL_PRECISION;
return collateralGain;
}
function _getDepositorTBANKGain(
address _depositor,
uint256 _marginalTBANKGain
) internal view returns (uint256) {
uint256 initialDeposit = deposits[_depositor];
if (initialDeposit == 0) {
return 0;
}
Snapshots memory _snapshots = depositSnapshots[_depositor];
/*
* Grab the sum 'G' from the epoch at which the stake was made. The TBANK gain may span up to one scale change.
* If it does, the second portion of the TBANK gain is scaled by 1e9.
* If the gain spans no scale change, the second portion will be 0.
*/
uint256 firstEpochPortion = epochToScaleToG[_snapshots.epoch][
_snapshots.scale
];
uint256 secondEpochPortion = epochToScaleToG[_snapshots.epoch][
_snapshots.scale + 1
];
if (_snapshots.epoch == currentEpoch) {
if (_snapshots.scale == currentScale)
firstEpochPortion += _marginalTBANKGain;
if (_snapshots.scale + 1 == currentScale)
secondEpochPortion += _marginalTBANKGain;
}
uint256 gainPortions = firstEpochPortion -
_snapshots.G +
secondEpochPortion /
SCALE_FACTOR;
return
(initialDeposit * (gainPortions)) /
_snapshots.P /
DECIMAL_PRECISION;
}
/// @dev gets compounded deposit of the user
function _getCompoundedDepositFromSnapshots(
uint256 _initialStake,
Snapshots memory _snapshots
) internal view returns (uint256) {
uint256 snapshot_P = _snapshots.P;
// If stake was made before a pool-emptying event, then it has been fully cancelled with debt -- so, return 0
if (_snapshots.epoch < currentEpoch) {
return 0;
}
uint256 compoundedStake;
uint128 scaleDiff = currentScale - _snapshots.scale;
/* Compute the compounded stake. If a scale change in P was made during the stake's lifetime,
* account for it. If more than one scale change was made, then the stake has decreased by a factor of
* at least 1e-9 -- so return 0.
*/
uint256 calculatedSnapshotP = snapshot_P == 0
? DECIMAL_PRECISION
: snapshot_P;
if (scaleDiff == 0) {
compoundedStake = (_initialStake * P) / calculatedSnapshotP;
} else if (scaleDiff == 1) {
compoundedStake =
(_initialStake * P) /
calculatedSnapshotP /
SCALE_FACTOR;
} else {
// if scaleDiff >= 2
compoundedStake = 0;
}
/*
* If compounded deposit is less than a billionth of the initial deposit, return 0.
*
* NOTE: originally, this line was in place to stop rounding errors making the deposit too large. However, the error
* corrections should ensure the error in P "favors the Pool", i.e. any given compounded deposit should slightly less
* than it's theoretical value.
*
* Thus it's unclear whether this line is still really needed.
*/
if (compoundedStake < _initialStake / 1e9) {
return 0;
}
return compoundedStake;
}
/// @dev Compute the StableCoin and Collateral rewards. Uses a "feedback" error correction, to keep
/// the cumulative error in the P and S state variables low:s
function _computeRewardsPerUnitStaked(
address _collateralTokenAddress,
uint256 _collToAdd,
uint256 _debtToOffset,
uint256 _totalStableCoinDeposits
)
internal
returns (
uint256 collateralGainPerUnitStaked,
uint256 stableCoinLossPerUnitStaked
)
{
/*
* Compute the StableCoin and Collateral rewards. Uses a "feedback" error correction, to keep
* the cumulative error in the P and S state variables low:
*
* 1) Form numerators which compensate for the floor division errors that occurred the last time this
* function was called.
* 2) Calculate "per-unit-staked" ratios.
* 3) Multiply each ratio back by its denominator, to reveal the current floor division error.
* 4) Store these errors for use in the next correction when this function is called.
* 5) Note: static analysis tools complain about this "division before multiplication", however, it is intended.
*/
uint256 collateralNumerator = _collToAdd *
DECIMAL_PRECISION +
collateralToLastErrorOffset[_collateralTokenAddress];
assert(_debtToOffset <= _totalStableCoinDeposits);
if (_debtToOffset == _totalStableCoinDeposits) {
stableCoinLossPerUnitStaked = DECIMAL_PRECISION; // When the Pool depletes to 0, so does each deposit
lastStableCoinLossErrorOffset = 0;
} else {
uint256 stableCoinLossNumerator = _debtToOffset *
DECIMAL_PRECISION -
lastStableCoinLossErrorOffset;
/*
* Add 1 to make error in quotient positive. We want "slightly too much" StableCoin loss,
* which ensures the error in any given compoundedStableCoinDeposit favors the Stability Pool.
*/
stableCoinLossPerUnitStaked =
stableCoinLossNumerator /
_totalStableCoinDeposits +
1;
lastStableCoinLossErrorOffset =
stableCoinLossPerUnitStaked *
_totalStableCoinDeposits -
stableCoinLossNumerator;
}
collateralGainPerUnitStaked = (_totalStableCoinDeposits != 0)
? collateralNumerator / _totalStableCoinDeposits
: 0;
collateralToLastErrorOffset[_collateralTokenAddress] =
collateralNumerator -
collateralGainPerUnitStaked *
_totalStableCoinDeposits;
return (collateralGainPerUnitStaked, stableCoinLossPerUnitStaked);
}
/// @dev distributes TBANK per minutes that was not spent yet
function _triggerTBANKdistribution() internal {
uint256 issuance = _issueTBANKRewards();
_updateG(issuance);
}
function _issueTBANKRewards() internal returns (uint256) {
uint256 newTBANKRewardTime = block.timestamp;
uint256 totalTBANKRewardsLeft_cached = totalTBANKRewardsLeft;
if (
totalTBANKRewardsLeft_cached == 0 ||
tbankPerMinute == 0 ||
totalDeposit == 0
) {
latestTBANKRewardTime = newTBANKRewardTime;
return 0;
}
uint256 timePassedInMinutes = (newTBANKRewardTime - latestTBANKRewardTime) /
SECONDS_IN_ONE_MINUTE;
uint256 issuance = tbankPerMinute * timePassedInMinutes;
if (totalTBANKRewardsLeft_cached < issuance) {
issuance = totalTBANKRewardsLeft_cached; // event will capture that 0 tokens left
}
uint256 newTotalTBANKRewardsLeft = totalTBANKRewardsLeft_cached - issuance;
totalTBANKRewardsLeft = newTotalTBANKRewardsLeft;
latestTBANKRewardTime = newTBANKRewardTime;
emit TBANKRewardIssue(issuance, newTotalTBANKRewardsLeft);
return issuance;
}
function _computeTBANKPerUnitStaked(
uint256 _tbankIssuance,
uint256 _totalStableCoinDeposits
) internal returns (uint256) {
/*
* Calculate the TBANK-per-unit staked. Division uses a "feedback" error correction, to keep the
* cumulative error low in the running total G:
*
* 1) Form a numerator which compensates for the floor division error that occurred the last time this
* function was called.
* 2) Calculate "per-unit-staked" ratio.
* 3) Multiply the ratio back by its denominator, to reveal the current floor division error.
* 4) Store this error for use in the next correction when this function is called.
* 5) Note: static analysis tools complain about this "division before multiplication", however, it is intended.
*/
uint256 tbankNumerator = _tbankIssuance * DECIMAL_PRECISION + lastTBANKError;
uint256 tbankPerUnitStaked = tbankNumerator / _totalStableCoinDeposits;
lastTBANKError =
tbankNumerator -
(tbankPerUnitStaked * _totalStableCoinDeposits);
return tbankPerUnitStaked;
}
/// @dev transfers collateral rewards tokens precalculated to the depositor
function _sendCollateralRewardsToDepositor(
TokenToUint256[] memory _depositorCollateralGains
) internal {
for (uint256 i = 0; i < _depositorCollateralGains.length; i++) {
if (_depositorCollateralGains[i].value == 0) {
continue;
}
IERC20 collateralToken = IERC20(
_depositorCollateralGains[i].tokenAddress
);
collateralToken.safeTransfer(
msg.sender,
_depositorCollateralGains[i].value
);
emit CollateralRewardRedeemed(
msg.sender,
_depositorCollateralGains[i].tokenAddress,
_depositorCollateralGains[i].value
);
}
}
/// @dev transfers TBANK amount to the user
function _sendTBANKRewardsToDepositor(uint256 _tbankGain) internal {
tbankToken.transfer(msg.sender, _tbankGain);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import "./interfaces/IMintableTokenOwner.sol";
import "./interfaces/IMintableToken.sol";
import "hardhat/console.sol";
contract Stabilizer is Ownable {
using SafeERC20 for IERC20;
using SafeERC20 for IMintableToken;
IMintableTokenOwner public immutable mintableTokenOwner;
IMintableToken public immutable stableToken;
IERC20 public immutable collateralToken;
uint256 public immutable scalingFactor;
uint256 public feeBps;
address public feeRecipient;
event StabilizerMint(address indexed account, uint256 amount, uint256 fee);
event StabilizerBurn(address indexed account, uint256 amount, uint256 stableFee);
event FeeRecipientChanged(address indexed oldRecipient, address indexed newRecipient);
constructor(address _mintableTokenOwner, address _collateralToken, uint256 _feeBps) {
require(_mintableTokenOwner != address(0), "mintable-token-owner-is-zero");
require(_collateralToken != address(0), "collateral-token-is-zero");
mintableTokenOwner = IMintableTokenOwner(_mintableTokenOwner);
stableToken = IMintableToken(mintableTokenOwner.token());
collateralToken = IERC20(_collateralToken);
scalingFactor = 10 ** (ERC20(address(stableToken)).decimals() - IERC20Metadata(_collateralToken).decimals());
feeRecipient = msg.sender;
feeBps = _feeBps;
}
function setFeeRecipient(address _feeRecipient) external onlyOwner {
require(_feeRecipient != address(0), "fee-recipient-is-zero");
emit FeeRecipientChanged(feeRecipient, _feeRecipient);
feeRecipient = _feeRecipient;
}
function setFeeBps(uint256 _feeBps) external onlyOwner {
require(_feeBps <= 500, "fee-too-high");
feeBps = _feeBps;
}
function mint(uint256 _amount) external {
uint256 fee = (_amount * feeBps) / 10000;
require(_amount >= scalingFactor, "amount-too-small");
uint256 collateralAmount = _amount / scalingFactor;
collateralToken.safeTransferFrom(msg.sender, address(this), collateralAmount);
mintableTokenOwner.mint(msg.sender, _amount - fee);
mintableTokenOwner.mint(feeRecipient, fee);
emit StabilizerMint(msg.sender, _amount, fee);
}
function burn(uint256 _amount) external {
// mintableToken is 18 decimals, scale accordingly
uint256 collateralAmount = _amount / scalingFactor;
require(_amount >= scalingFactor, "amount-too-small");
uint256 fee = (collateralAmount * feeBps) / 10000;
stableToken.safeTransferFrom(msg.sender, address(this), _amount);
stableToken.burn(_amount);
collateralToken.safeTransfer(msg.sender, collateralAmount - fee);
collateralToken.safeTransfer(feeRecipient, fee);
emit StabilizerBurn(msg.sender, _amount, fee);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
/**
* @title TBANK
* @dev TBANK is an ERC20 token representing TBANK Governance Token.
*/
contract TBANK is ERC20 {
/**
* @dev Total supply of TBANK tokens.
*/
uint256 public constant TOTAL_SUPPLY = 15_000_000 ether;
/**
* @dev Constructor that mints the total supply of TBANK tokens to the deployer.
*/
constructor() ERC20('TaoBank', 'TBANK') {
_mint(msg.sender, TOTAL_SUPPLY);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import './interfaces/IVaultFactory.sol';
import './interfaces/IFeeRecipient.sol';
import './interfaces/IVault.sol';
import './utils/constants.sol';
/**
* @title TBANK Staking contract.
* @dev Rewards stakers in StableCoin that is used to pay fee.
*/
contract TBANKStaking is Ownable, Constants {
using SafeERC20 for IERC20;
// Mapping of stakers' addresses to their staked amount.
mapping(address => uint256) public stakes;
// Total amount of TBANK staked.
uint256 public totalTBANKStaked;
// Running sum of StableCoin fees per-TBANK-staked.
uint256 public F_StableCoin;
// Timestamp of the last fee operation.
uint256 public lastFeeOperationTime;
// User snapshots of F_TBANK and F_StableCoin, taken at the point at which their latest deposit was made.
mapping(address => uint256) public F_StableCoinSnapshots;
// User gains in StableCoin.
mapping(address => uint256) public stableCoinUserGains;
// Interfaces to interact with other contracts.
IVaultFactory public factory;
IERC20 public tbankToken;
IERC20 public stableCoin;
// --- Events ---
event FactoryAddressSet(address _factoryAddress);
event TbankTokenAddressSet(address _tbankTokenAddress);
event StableCoinAddressSet(address _stableCoinAddress);
event StakeChanged(address indexed _staker, uint256 _newStake);
event TotalTBANKStakedUpdated(uint256 _totalTBANKStaked);
event RewardRedeemed(
address _account,
uint256 _stableAmount
);
event StakerSnapshotsUpdated(
address _staker,
uint256 _F_StableCoin,
uint256 _stableGains
);
event FeeTaken(uint256 _amount, uint256 _F_StableCoin, bool _redemptionFee);
/**
* @notice Initializes the TBANKStaking contract.
* @param _vaultFactory Address of the VaultFactory contract.
* @param _tbankToken Address of the TBANK token contract.
*/
constructor(address _vaultFactory, address _tbankToken) {
factory = IVaultFactory(_vaultFactory);
tbankToken = IERC20(_tbankToken);
stableCoin = IERC20(factory.stable());
}
// --- Functions ---
/**
* @dev Sets the timestamp to calculate the next decayed rate from.
* @param _timestamp uint256 value representing time in seconds.
*/
function setInitialLastFee(uint256 _timestamp) public onlyOwner {
lastFeeOperationTime = _timestamp > 0 ? _timestamp : block.timestamp;
}
/**
* @dev Sets the VaultFactory contract if the address was updated.
* @param _factoryAddress Address of the updated VaultFactory contract.
*/
function setFactory(address _factoryAddress) external onlyOwner {
factory = IVaultFactory(_factoryAddress);
stableCoin = IERC20(address(factory.stable()));
emit FactoryAddressSet(address(factory));
emit StableCoinAddressSet(address(stableCoin));
}
/**
* @dev Allows users to stake TBANK tokens.
* @param _tbankAmount Amount of TBANK tokens to stake.
* @notice If caller has a pre-existing stake, records any accumulated StableCoin gains to them.
*/
function stake(uint256 _tbankAmount) external {
_requireNonZeroAmount(_tbankAmount);
uint256 currentStake = stakes[msg.sender];
// Transfer TBANK from caller to this contract.
require(
tbankToken.transferFrom(msg.sender, address(this), _tbankAmount),
'transfer-from-failed'
);
// Grab and record accumulated StableCoin gains from the current stake and update Snapshot.
uint256 currentTotalTBANKStaked = totalTBANKStaked;
if (currentTotalTBANKStaked == 0)
stableCoinUserGains[msg.sender] += F_StableCoin;
_updateUserSnapshot(msg.sender);
// Increase user’s stake and total TBANK staked.
uint256 newTotalTBANKStaked = currentTotalTBANKStaked + _tbankAmount;
totalTBANKStaked = newTotalTBANKStaked;
uint256 newUserStake = currentStake + _tbankAmount;
stakes[msg.sender] = newUserStake;
emit TotalTBANKStakedUpdated(newTotalTBANKStaked);
emit StakeChanged(msg.sender, newUserStake);
}
/**
* @dev Allows user to unstake TBANK.
* @param _tbankAmount Amount of TBANK to unstake.
* @notice Unstake the TBANK and send it back to the caller, and record accumulated StableCoin gains for the caller.
* If requested amount > stake, send their entire stake.
*/
function unstake(uint256 _tbankAmount) external {
_requireNonZeroAmount(_tbankAmount);
uint256 currentStake = stakes[msg.sender];
_requireUserHasStake(currentStake);
// Grab and record accumulated StableCoin gains from the current stake and update Snapshot.
_updateUserSnapshot(msg.sender);
uint256 TBANKToWithdraw = _tbankAmount > currentStake
? currentStake
: _tbankAmount;
uint256 newStake = currentStake - TBANKToWithdraw;
// Decrease user's stake and total TBANK staked.
stakes[msg.sender] = newStake;
totalTBANKStaked = totalTBANKStaked - TBANKToWithdraw;
emit TotalTBANKStakedUpdated(totalTBANKStaked);
// Transfer unstaked TBANK to user.
tbankToken.safeTransfer(msg.sender, TBANKToWithdraw);
emit StakeChanged(msg.sender, newStake);
}
/**
* @dev Increases the fees and updates F_StableCoin based on the received amount. Called by TBANK core contracts.
* @param _amount Amount of StableCoin received as fees.
* @return bool Returns true if the fee operation is successful.
*/
function takeFees(uint256 _amount) external returns (bool) {
_requireNonZeroAmount(_amount);
stableCoin.safeTransferFrom(msg.sender, address(this), _amount);
uint256 totalTBANKStaked_cached = totalTBANKStaked;
uint256 amountPerTBANKStaked = _amount;
if (totalTBANKStaked_cached > 0) {
amountPerTBANKStaked =
((_amount) * DECIMAL_PRECISION) /
totalTBANKStaked_cached;
}
uint256 newF_StableCoin = F_StableCoin + amountPerTBANKStaked;
F_StableCoin = newF_StableCoin;
lastFeeOperationTime = block.timestamp;
emit FeeTaken(_amount, newF_StableCoin, msg.sender == address(factory));
return true;
}
// --- Pending reward functions ---
function redeemReward(uint256 _amount) external {
_requireNonZeroAmount(_amount);
address account = msg.sender;
require(
(_getUnpaidStableCoinGain(msg.sender)) >= _amount,
'amount-must-fit-rewards-amount'
);
_updateUserSnapshot(account);
stableCoinUserGains[account] = stableCoinUserGains[account] - _amount;
stableCoin.safeTransfer(account, _amount);
emit RewardRedeemed(msg.sender, _amount);
}
/**
* @dev Retrieves the total amount of TBANK staked.
* @return uint256 Total amount of TBANK staked.
*/
function totalStake() external view returns (uint256) {
return totalTBANKStaked;
}
/**
* @dev Retrieves the unpaid rewards of the user.
* @param _user Address of the user to check.
* @return uint256 Unpaid rewards of the user in StableCoin.
*/
function getUnpaidStableCoinGain(
address _user
) external view returns (uint256) {
return _getUnpaidStableCoinGain(_user);
}
/**
* @dev Retrieves the total rewards accumulated in StableCoin.
* @return uint256 Total rewards accumulated in StableCoin.
*/
function getRewardsTotal() external view returns (uint256) {
return F_StableCoin;
}
// --- Internal helper functions ---
/**
* @dev Calculates the pending StableCoin gains for a user.
* @param _user Address of the user to calculate gains for.
* @return uint256 Pending StableCoin gains for the user.
*/
function _getPendingStableCoinGain(
address _user
) internal view returns (uint256) {
uint256 F_StableCoin_Snapshot = F_StableCoinSnapshots[_user];
uint256 stableCoinGain = (stakes[_user] *
(F_StableCoin - F_StableCoin_Snapshot)) / DECIMAL_PRECISION;
return stableCoinGain;
}
/**
* @dev Calculates the total unpaid StableCoin gains for a user.
* @param _user Address of the user to calculate gains for.
* @return uint256 Total unpaid StableCoin gains for the user.
*/
function _getUnpaidStableCoinGain(
address _user
) internal view returns (uint256) {
return stableCoinUserGains[_user] + _getPendingStableCoinGain(_user);
}
/**
* @dev Records the StableCoin gains for a user based on their stake.
* @param _user Address of the user to record gains for.
*/
function _recordStableCoinGain(address _user) internal {
uint256 userStake = stakes[_user];
if (userStake > 0) {
uint256 F_StableCoin_Snapshot = F_StableCoinSnapshots[_user];
uint256 stableCoinGain = (userStake *
(F_StableCoin - F_StableCoin_Snapshot)) / DECIMAL_PRECISION;
stableCoinUserGains[_user] += stableCoinGain;
}
}
/**
* @dev Updates user's snapshot of StableCoin gains and F_StableCoin.
* @param _user Address of the user to update snapshot for.
*/
function _updateUserSnapshot(address _user) internal {
_recordStableCoinGain(_user);
uint256 currentF_StableCoin = F_StableCoin;
F_StableCoinSnapshots[_user] = currentF_StableCoin;
emit StakerSnapshotsUpdated(
_user,
currentF_StableCoin,
stableCoinUserGains[_user]
);
}
// --- 'require' functions ---
/**
* @dev Requires the user to have a non-zero stake.
* @param currentStake Amount of current stake for the user.
*/
function _requireUserHasStake(uint256 currentStake) internal pure {
require(currentStake > 0, 'stakes-is-zero');
}
/**
* @dev Requires the amount to be non-zero.
* @param _amount Amount to check for non-zero.
*/
function _requireNonZeroAmount(uint256 _amount) internal pure {
require(_amount > 0, 'amount-is-zero');
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';
import './interfaces/IPriceFeed.sol';
import './utils/constants.sol';
import './interfaces/ITokenPriceFeed.sol';
/**
* @title TokenToPriceFeed
* @dev Manages mapping of token addresses to their respective price feed contracts.
*/
contract TokenToPriceFeed is Ownable, Constants, ITokenPriceFeed {
/// @dev Mapping of token address to its associated price feed contract.
mapping(address => TokenInfo) public tokens;
/**
* @dev Retrieves the contract owner's address.
*/
function owner() public view override(Ownable, IOwnable) returns (address) {
return Ownable.owner();
}
/**
* @dev Retrieves the token's current price from the respective price feed.
* @param _token Address of the token.
*/
function tokenPrice(address _token) public view override returns (uint256) {
return IPriceFeed(tokens[_token].priceFeed).price();
}
/**
* @dev Retrieves the price feed contract address for a given token.
* @param _token Address of the token.
*/
function tokenPriceFeed(
address _token
) public view override returns (address) {
return tokens[_token].priceFeed;
}
/**
* @dev Retrieves the minimal collateral ratio for a given token.
* @param _token Address of the token.
*/
function mcr(address _token) public view override returns (uint256) {
return tokens[_token].mcr;
}
/**
* @dev Retrieves the decimal places of a given token.
* @param _token Address of the token.
*/
function decimals(address _token) public view override returns (uint256) {
return tokens[_token].decimals;
}
/**
* @dev Retrieves the minimal liquidation ratio for a given token.
* @param _token Address of the token.
*/
function mlr(address _token) public view override returns (uint256) {
return tokens[_token].mlr;
}
/**
* @dev Retrieves the borrow rate for a given token.
* @param _token Address of the token.
*/
function borrowRate(address _token) public view override returns (uint256) {
return tokens[_token].borrowRate;
}
/**
* @dev Sets or updates the price feed contract for a specific token.
* @param _token Address of the token.
* @param _priceFeed Address of the PriceFeed contract for the token.
* @param _mcr Minimal Collateral Ratio of the token.
* @param _mlr Minimal Liquidation Ratio of the token.
* @param _borrowRate Borrow rate of the token.
*/
function setTokenPriceFeed(
address _token,
address _priceFeed,
uint256 _mcr,
uint256 _mlr,
uint256 _borrowRate,
uint256 /* _decimals */
) public override onlyOwner {
require(_mcr >= 100, 'MCR < 100');
require(_mlr >= 100 && _mlr <= _mcr, 'MLR < 100 or MLR > MCR');
require(_borrowRate < 10 ether, 'borrowRate >= 10%');
IERC20Metadata erc20 = IERC20Metadata(_token);
uint256 _decimals = erc20.decimals();
require(_decimals > 0 || _decimals <= 18, 'not-valid-decimals');
TokenInfo memory token = tokens[_token];
token.priceFeed = _priceFeed;
token.mcr = (DECIMAL_PRECISION * _mcr) / 100;
token.mlr = (DECIMAL_PRECISION * _mlr) / 100;
token.borrowRate = _borrowRate;
token.decimals = _decimals;
emit NewTokenPriceFeed(
_token,
_priceFeed,
erc20.name(),
erc20.symbol(),
token.mcr,
token.mlr,
token.borrowRate,
_decimals
);
tokens[_token] = token;
}
/**
* @dev Transfers ownership after revoking other roles from other addresses.
* @param _newOwner Address of the new owner.
*/
function transferOwnership(
address _newOwner
) public override(Ownable, IOwnable) {
Ownable.transferOwnership(_newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Context.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import './interfaces/IPriceFeed.sol';
import './interfaces/IVaultFactory.sol';
import './interfaces/IVaultFactoryConfig.sol';
import './interfaces/ILiquidationRouter.sol';
import './utils/constants.sol';
import './interfaces/ITokenPriceFeed.sol';
import './interfaces/IVaultExtraSettings.sol';
import './utils/linked-address-list.sol';
/**
* @title Vault
* @dev Manages creation, collateralization, borrowing, and liquidation of Vaults.
*/
contract Vault is Context, Constants {
string public constant VERSION = '1.2.0';
// Events emitted by the contract
event CollateralAdded(
address indexed collateral,
uint256 amount,
uint256 newTotalAmount
);
event CollateralRemoved(
address indexed collateral,
uint256 amount,
uint256 newTotalAmount
);
event CollateralRedeemed(
address indexed collateral,
uint256 amount,
uint256 newTotalAmount,
uint256 stableAmountUsed,
uint256 feePaid
);
event DebtAdded(uint256 amount, uint256 newTotalDebt);
event DebtRepaid(uint256 amount, uint256 newTotalDebt);
modifier onlyFactory() {
require(_msgSender() == factory, 'only-factory');
_;
}
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
address public immutable stable;
address public immutable factory;
address public vaultOwner;
string public name;
EnumerableSet.AddressSet private collateralSet;
EnumerableSet.AddressSet private operators;
IVaultExtraSettings public vaultExtraSettings;
mapping(address => uint256) public collateral;
uint256 public debt;
modifier onlyVaultOwner() {
require(_msgSender() == vaultOwner, 'only-vault-owner');
_;
}
/**
* @dev Constructor to initialize the Vault contract.
* @param _factory Address of the VaultFactory contract.
* @param _vaultOwner Address of the initial owner of the Vault.
* @param _name Name of the Vault.
*/
constructor(
address _factory,
address _vaultOwner,
string memory _name,
IVaultExtraSettings _vaultExtraSettings
) {
require(_vaultOwner != address(0x0), 'vault-owner-is-0');
require(bytes(_name).length > 0, 'name-is-empty');
require(_factory != address(0x0), 'factory-is-0');
require(
address(_vaultExtraSettings) != address(0x0),
'vault-extra-settings-is-0'
);
factory = _factory;
vaultOwner = _vaultOwner;
stable = IVaultFactory(factory).stable();
name = _name;
vaultExtraSettings = _vaultExtraSettings;
}
/**
* @dev Transfers ownership of the Vault to a new owner.
* @param _newOwner Address of the new owner.
*/
function transferVaultOwnership(address _newOwner) external onlyFactory {
vaultOwner = _newOwner;
}
/**
* @dev Sets a new name for the Vault.
* @param _name New name for the Vault.
*/
function setName(string memory _name) external onlyVaultOwner {
require(bytes(_name).length > 0, 'name-is-empty');
name = _name;
}
/**
* @dev Adds an operator to the Vault, allowing them certain permissions.
* @param _operator Address of the operator to be added.
*/
function addOperator(address _operator) external onlyVaultOwner {
require(_operator != address(0x0), 'operator-is-0');
operators.add(_operator);
}
/**
* @dev Removes an operator from the Vault, revoking their permissions.
* @param _operator Address of the operator to be removed.
*/
function removeOperator(address _operator) external onlyVaultOwner {
require(_operator != address(0x0), 'operator-is-0');
operators.remove(_operator);
}
/**
* @dev Checks if an address is an operator for this Vault.
* @param _operator Address to check.
* @return Boolean indicating whether the address is an operator.
*/
function isOperator(address _operator) external view returns (bool) {
return operators.contains(_operator);
}
/**
* @dev Returns the number of operators in the Vault.
* @return Length of the operators set.
*/
function operatorsLength() external view returns (uint256) {
return operators.length();
}
/**
* @dev Returns the operator at a given index in the operators set.
* @param _index Index of the operator.
* @return Address of the operator at the given index.
*/
function operatorAt(uint256 _index) external view returns (address) {
return operators.at(_index);
}
/**
* @dev Checks if a collateral token is added to the Vault.
* @param _collateral Address of the collateral token to check.
* @return Boolean indicating whether the collateral token is added.
*/
function containsCollateral(
address _collateral
) external view returns (bool) {
return collateralSet.contains(_collateral);
}
/**
* @dev Returns the number of collateral tokens added to the Vault.
* @return Length of the collateral set.
*/
function collateralsLength() external view returns (uint256) {
return collateralSet.length();
}
/**
* @dev Returns the collateral token address at a given index in the collateral set.
* @param _index Index of the collateral token.
* @return Address of the collateral token at the given index.
*/
function collateralAt(uint256 _index) external view returns (address) {
return collateralSet.at(_index);
}
/**
* @dev Returns an array containing all collateral token addresses in the Vault.
* @return Array of collateral token addresses.
*/
function collaterals() external view returns (address[] memory) {
address[] memory _collaterals = new address[](collateralSet.length());
for (uint256 i = 0; i < collateralSet.length(); i++) {
_collaterals[i] = collateralSet.at(i);
}
return _collaterals;
}
/**
* @dev Adds a new collateral token to the Vault and updates the collateral amount.
* @param _collateral Address of the collateral token to add.
* @param _amount Amount of the collateral token to add.
*/
function addCollateral(
address _collateral,
uint256 _amount
) external onlyFactory {
require(_collateral != address(0x0), 'collateral-is-0');
require(_amount > 0, 'amount-is-0');
collateralSet.add(_collateral);
uint256 _maxTokens = IVaultFactory(factory).MAX_TOKENS_PER_VAULT();
require(collateralSet.length() <= _maxTokens, 'max-tokens-reached');
collateral[_collateral] += _amount;
emit CollateralAdded(_collateral, _amount, collateral[_collateral]);
}
/**
* @dev Removes a collateral token from the Vault and transfers it back to the sender.
* @param _collateral Address of the collateral token to remove.
* @param _amount Amount of the collateral token to remove.
* @param _to Address to receive the removed collateral.
*/
function removeCollateral(
address _collateral,
uint256 _amount,
address _to
) external onlyFactory {
require(_collateral != address(0x0), 'collateral-is-0');
require(_amount > 0, 'amount-is-0');
collateral[_collateral] -= _amount;
if (collateral[_collateral] == 0) {
collateralSet.remove(_collateral);
}
uint256 _healthFactor = healthFactor(false);
require(_healthFactor >= DECIMAL_PRECISION, 'health-factor-below-1');
IERC20(_collateral).safeTransfer(_to, _amount);
emit CollateralRemoved(_collateral, _amount, collateral[_collateral]);
}
/**
* @dev Adds bad debt to the Vault.
* @param _amount Amount of bad debt to add.
*/
function addBadDebt(uint256 _amount) external onlyFactory {
require(_amount > 0, 'amount-is-0');
debt += _amount;
emit DebtAdded(_amount, debt);
}
/**
* @dev Calculates the maximum borrowable amount and the current borrowable amount.
* @return _maxBorrowable Maximum borrowable amount.
* @return _borrowable Current borrowable amount.
*/
function borrowable()
public
view
returns (uint256 _maxBorrowable, uint256 _borrowable)
{
(_maxBorrowable, _borrowable) = borrowableWithDiff(
address(0x0),
0,
false,
false
);
}
/**
* @dev Borrows a specified amount from the Vault.
* @param _amount Amount to borrow.
*/
function borrow(uint256 _amount) external onlyFactory {
require(_amount > 0, 'amount-is-0');
(uint256 _maxBorrowable, uint256 _borrowable) = borrowable();
require(_amount <= _borrowable, 'not-enough-borrowable');
debt += _amount;
require(debt <= _maxBorrowable, 'max-borrowable-reached');
emit DebtAdded(_amount, debt);
}
/**
* @dev Repays a specified amount to the Vault's debt.
* @param _amount Amount to repay.
*/
function repay(uint256 _amount) external onlyFactory {
require(_amount <= debt, 'amount-exceeds-debt');
debt -= _amount;
emit DebtRepaid(_amount, debt);
}
/**
* @dev Calculates the stable amount needed and the redemption fee for redeeming collateral.
* @param _collateral Address of the collateral token.
* @param _collateralAmount Amount of collateral to redeem.
* @return _stableAmountNeeded Stablecoin amount required to redeem collateral.
* @return _redemptionFee Fee charged for the redemption.
*/
function calcRedeem(
address _collateral,
uint256 _collateralAmount
)
public
view
returns (uint256 _stableAmountNeeded, uint256 _redemptionFee)
{
ITokenPriceFeed _priceFeed = ITokenPriceFeed(
IVaultFactory(factory).priceFeed()
);
uint256 _price = _priceFeed.tokenPrice(_collateral);
uint256 _normalizedCollateralAmount = _collateralAmount *
(10 ** (18 - _priceFeed.decimals(_collateral)));
_stableAmountNeeded =
(_normalizedCollateralAmount * _price) /
DECIMAL_PRECISION;
(, , uint256 _redemptionKickbackRate) = vaultExtraSettings
.getExtraSettings();
if (_redemptionKickbackRate > 0) {
uint256 _kickbackAmount = (_stableAmountNeeded *
_redemptionKickbackRate) / DECIMAL_PRECISION;
_stableAmountNeeded += _kickbackAmount;
}
uint256 _redemptionRate = IVaultFactoryConfig(factory).redemptionRate();
_redemptionFee =
(_stableAmountNeeded * _redemptionRate) /
DECIMAL_PRECISION;
}
/**
* @dev Redeems a specified amount of collateral, repays debt, and transfers collateral back to the redeemer.
* @param _collateral Address of the collateral token to redeem.
* @param _collateralAmount Amount of collateral to redeem.
* @return _debtRepaid Amount of debt repaid.
* @return _feeCollected Fee collected for the redemption.
*/
function redeem(
address _collateral,
uint256 _collateralAmount
)
external
onlyFactory
returns (uint256 _debtRepaid, uint256 _feeCollected)
{
require(_collateral != address(0x0), 'collateral-is-0');
require(_collateralAmount > 0, 'amount-is-0');
require(collateralSet.contains(_collateral), 'collateral-not-added');
require(
collateral[_collateral] >= _collateralAmount,
'not-enough-collateral'
);
uint256 _currentHealthFactor = healthFactor(true);
uint256 _redemptionHealthFactorLimit = IVaultFactoryConfig(factory)
.redemptionHealthFactorLimit();
require(
_currentHealthFactor < _redemptionHealthFactorLimit,
'health-factor-above-redemption-limit'
);
(uint256 _debtTreshold, uint256 _maxRedeemablePercentage, ) = vaultExtraSettings
.getExtraSettings();
collateral[_collateral] -= _collateralAmount;
(_debtRepaid, _feeCollected) = calcRedeem(
_collateral,
_collateralAmount
);
if (debt > _debtTreshold) {
uint256 _redeemableDebt = (debt * _maxRedeemablePercentage) /
DECIMAL_PRECISION;
require(_debtRepaid <= _redeemableDebt, 'redeemable-debt-exceeded');
}
debt -= _debtRepaid;
if (collateral[_collateral] == 0) {
collateralSet.remove(_collateral);
}
IERC20(_collateral).safeTransfer(_msgSender(), _collateralAmount);
emit CollateralRedeemed(
_collateral,
_collateralAmount,
collateral[_collateral],
_debtRepaid,
_feeCollected
);
emit DebtRepaid(_debtRepaid, debt);
}
/**
* @dev Computes the health factor of the Vault.
* @param _useMlr Flag to use Minimum Loan Ratio (MLR) in health factor computation.
* @return _healthFactor Current health factor.
*/
function healthFactor(
bool _useMlr
) public view returns (uint256 _healthFactor) {
if (debt == 0) {
return type(uint256).max;
}
(uint256 _maxBorrowable, ) = borrowableWithDiff(
address(0x0),
0,
false,
_useMlr
);
_healthFactor = (_maxBorrowable * DECIMAL_PRECISION) / debt;
}
/**
* @dev Computes a new health factor given a new debt value.
* @param _newDebt New debt amount to calculate the health factor.
* @param _useMlr Flag to use Minimum Loan Ratio (MLR) in health factor computation.
* @return _newHealthFactor Calculated new health factor based on the new debt value.
*/
function newHealthFactor(
uint256 _newDebt,
bool _useMlr
) public view returns (uint256 _newHealthFactor) {
if (_newDebt == 0) {
return type(uint256).max;
}
(uint256 _maxBorrowable, ) = borrowableWithDiff(
address(0x0),
0,
false,
_useMlr
);
_newHealthFactor = (_maxBorrowable * DECIMAL_PRECISION) / _newDebt;
}
/**
* @dev Computes the maximum borrowable amount and the current borrowable amount.
* @param _collateral Address of the collateral token (0x0 for total vault borrowable).
* @param _diffAmount Difference in collateral amount when adding/removing collateral.
* @param _isAdd Flag indicating whether the collateral is added or removed.
* @param _useMlr Flag to use Minimum Loan Ratio (MLR) in borrowable computation.
* @return _maxBorrowable Maximum borrowable amount.
* @return _borrowable Current borrowable amount based on the collateral.
*/
function borrowableWithDiff(
address _collateral,
uint256 _diffAmount,
bool _isAdd,
bool _useMlr
) public view returns (uint256 _maxBorrowable, uint256 _borrowable) {
uint256 _newCollateralAmount = collateral[_collateral];
uint256 _borrowableAmount = 0;
if (_collateral != address(0x0)) {
require(
IVaultFactory(factory).isCollateralSupported(_collateral),
'collateral-not-supported'
);
if (_isAdd) {
_newCollateralAmount += _diffAmount;
} else {
_newCollateralAmount -= _diffAmount;
}
}
ITokenPriceFeed _priceFeed = ITokenPriceFeed(
IVaultFactory(factory).priceFeed()
);
for (uint256 i = 0; i < collateralSet.length(); i++) {
address _c = collateralSet.at(i);
uint256 _collateralAmount = _c == _collateral
? _newCollateralAmount
: collateral[_c];
uint256 _price = _priceFeed.tokenPrice(_c);
uint256 _divisor = _useMlr
? _priceFeed.mlr(_c)
: _priceFeed.mcr(_c);
uint256 _normalizedCollateralAmount = _collateralAmount *
(10 ** (18 - _priceFeed.decimals(_c)));
uint256 _collateralBorrowable = (_normalizedCollateralAmount *
_price) / DECIMAL_PRECISION;
_borrowableAmount +=
(_collateralBorrowable * DECIMAL_PRECISION) /
_divisor;
}
return (
_borrowableAmount,
(_borrowableAmount > debt) ? _borrowableAmount - debt : 0
);
}
/**
* @dev Liquidates the vault by repaying all debts with seized collateral.
* @return _forgivenDebt Amount of debt forgiven during liquidation.
*/
function liquidate() external onlyFactory returns (uint256 _forgivenDebt) {
require(
healthFactor(true) < DECIMAL_PRECISION,
'liquidation-factor-above-1'
);
uint256 _debt = debt;
debt = 0;
ILiquidationRouter router = ILiquidationRouter(
IVaultFactory(factory).liquidationRouter()
);
for (uint256 i = 0; i < collateralSet.length(); i++) {
address _collateral = collateralSet.at(i);
uint256 _collateralAmount = collateral[_collateral];
uint256 _actualCollateralBalance = IERC20(_collateral).balanceOf(
address(this)
);
if (_actualCollateralBalance < _collateralAmount) {
_collateralAmount = _actualCollateralBalance;
}
collateral[_collateral] = 0;
IERC20(_collateral).safeApprove(
IVaultFactory(factory).liquidationRouter(),
0
);
IERC20(_collateral).safeApprove(
IVaultFactory(factory).liquidationRouter(),
_collateralAmount
);
router.addSeizedCollateral(_collateral, _collateralAmount);
}
router.addUnderWaterDebt(address(this), _debt);
router.tryLiquidate();
_forgivenDebt = _debt;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './interfaces/IVault.sol';
import './interfaces/ITokenPriceFeed.sol';
import './interfaces/IVaultFactory.sol';
/**
* @title VaultBorrowRate
* @notice Contract to calculate the borrow rate for a given Vault
*/
contract VaultBorrowRate {
/**
* @notice Calculates the borrow rate for a specified Vault
* @param _vaultAddress The address of the Vault for which to calculate the borrow rate
* @return uint256 The calculated borrow rate
*/
function getBorrowRate(
address _vaultAddress
) external view returns (uint256) {
IVault _vault = IVault(_vaultAddress);
IVaultFactory _vaultFactory = IVaultFactory(_vault.factory());
ITokenPriceFeed _priceFeed = ITokenPriceFeed(_vaultFactory.priceFeed());
uint256 _totalWeightedFee;
uint256 _totalCollateralValue;
uint256 _collateralsLength = _vault.collateralsLength();
for (uint256 i; i < _collateralsLength; i++) {
address _collateralAddress = _vault.collateralAt(i);
uint256 _collateralAmount = _vault.collateral(_collateralAddress);
uint256 _price = _priceFeed.tokenPrice(_collateralAddress);
uint256 _borrowRate = _priceFeed.borrowRate(_collateralAddress);
uint256 _normalizedCollateralAmount = _collateralAmount *
(10 ** (18 - _priceFeed.decimals(_collateralAddress)));
uint256 _collateralValue = (_normalizedCollateralAmount * _price) /
(10 ** _priceFeed.decimals(_collateralAddress));
uint256 _weightedFee = (_collateralValue * _borrowRate) / 1e18;
_totalCollateralValue += _collateralValue;
_totalWeightedFee += _weightedFee;
}
return ((_totalWeightedFee * 1e18) / _totalCollateralValue) / 100;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './Vault.sol';
import './interfaces/IVaultExtraSettings.sol';
/**
* @title VaultDeployer
* @notice A contract responsible for deploying new instances of the Vault contract.
*/
contract VaultDeployer {
IVaultExtraSettings public immutable vaultExtraSettings;
constructor(address _vaultExtraSettings) {
require(
_vaultExtraSettings != address(0x0),
'vault-extra-settings-is-zero'
);
vaultExtraSettings = IVaultExtraSettings(_vaultExtraSettings);
}
/**
* @notice Deploys a new Vault contract.
* @param _factory The address of the factory contract managing the vaults.
* @param _vaultOwner The address of the intended owner of the new vault.
* @param _name The name of the new vault.
* @return The address of the newly created Vault contract.
*/
function deployVault(
address _factory,
address _vaultOwner,
string memory _name
) external returns (address) {
// Deploy a new instance of the Vault contract
Vault vault = new Vault(
_factory,
_vaultOwner,
_name,
vaultExtraSettings
);
return address(vault);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './interfaces/IVaultExtraSettings.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
/**
* @title VaultExtraSettings
* @notice Contract to manage extra settings for a Vault
*/
contract VaultExtraSettings is IVaultExtraSettings, Ownable {
uint256 public debtTreshold;
uint256 public maxRedeemablePercentage;
uint256 public redemptionKickback;
/**
* @dev Sets the maximum redeemable percentage for a Vault.
* @param _debtTreshold The debt treshold for the Vault, in order to enable percentage redemption.
* @param _maxRedeemablePercentage The maximum redeemable percentage for the Vault.
*/
function setMaxRedeemablePercentage(
uint256 _debtTreshold,
uint256 _maxRedeemablePercentage
) external override onlyOwner {
debtTreshold = _debtTreshold;
maxRedeemablePercentage = _maxRedeemablePercentage;
}
/**
* @dev Sets the redemption kickback for a Vault.
* @param _redemptionKickback The redemption kickback for the Vault.
*/
function setRedemptionKickback(
uint256 _redemptionKickback
) external override onlyOwner {
redemptionKickback = _redemptionKickback;
}
/**
* @dev Retrieves the extra settings for a Vault.
* @return _debtTreshold debt treshold for enabling max redeemable percentage, _maxRedeemablePercentage maximum redeemable percentage, _redemptionKickback redemption fee kickback to the vault
*/
function getExtraSettings()
external
view
override
returns (
uint256 _debtTreshold,
uint256 _maxRedeemablePercentage,
uint256 _redemptionKickback
)
{
return (debtTreshold, maxRedeemablePercentage, redemptionKickback);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
/*
_____ _____ _____ _____ _____ _____ _____
|_ _| _ | | | __ | _ | | | | |
| | | | | | | __ -| | | | | -|
|_| |__|__|_____| |_____|__|__|_|___|__|__|
*/
import '@openzeppelin/contracts/interfaces/IERC20Metadata.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import './utils/linked-address-list.sol';
import './Vault.sol';
import './VaultFactoryConfig.sol';
import './VaultFactoryList.sol';
import './interfaces/IWETH.sol';
import './interfaces/ITokenPriceFeed.sol';
import './interfaces/IPriceFeed.sol';
import './interfaces/IMintableTokenOwner.sol';
import './interfaces/IMintableToken.sol';
import './interfaces/IVaultDeployer.sol';
import './interfaces/IVaultBorrowRate.sol';
/**
* @title VaultFactory
* @dev Manages the creation, configuration, and operations of Vaults with collateral and borrowing functionality.
*/
contract VaultFactory is ReentrancyGuard, VaultFactoryConfig, VaultFactoryList {
// Events emitted by the contract
event NewVault(address indexed vault, string name, address indexed owner);
event VaultOwnerChanged(
address indexed vault,
address indexed oldOwner,
address indexed newOwner
);
// Libraries used by the contract
using LinkedAddressList for LinkedAddressList.List;
using SafeERC20 for IERC20;
using SafeERC20 for IMintableToken;
// Immutable state variables
address public immutable stable;
address public immutable nativeWrapped;
IMintableTokenOwner public immutable mintableTokenOwner;
// State variables
mapping(address => uint256) public collateral;
uint256 public totalDebt;
/**
* @dev Constructor to initialize essential addresses and contracts for VaultFactory.
* @param _mintableTokenOwner Address of the Mintable Token Owner contract.
* @param _nativeWrapped Address of the native wrapped token.
* @param _priceFeed Address of the price feed contract.
* @param _vaultDeployer Address of the Vault Deployer contract.
* @param _liquidationRouter Address of the liquidation router contract.
* @param _borrowRate Address of the borrow rate contract.
*/
constructor(
address _mintableTokenOwner,
address _nativeWrapped,
address _priceFeed,
address _vaultDeployer,
address _liquidationRouter,
address _borrowRate
) VaultFactoryConfig(_vaultDeployer, _liquidationRouter) {
require(
_mintableTokenOwner != address(0x0),
'mintable-token-owner-is-0'
);
mintableTokenOwner = IMintableTokenOwner(_mintableTokenOwner);
stable = address(mintableTokenOwner.token());
require(stable != address(0x0), 'stable-is-0');
require(_nativeWrapped != address(0x0), 'nativew-is-0');
require(_priceFeed != address(0x0), 'pricefeed-is-0');
require(_borrowRate != address(0x0), 'borrow-rate-is-0');
borrowRate = _borrowRate;
nativeWrapped = _nativeWrapped;
priceFeed = _priceFeed;
}
/**
* @dev Fallback function to receive Ether and restricts its usage to a designated sender.
*/
receive() external payable {
require(msg.sender == nativeWrapped, 'only-native-wrapped');
}
/**
* @dev Modifier: Allows function execution only by the owner of a specific vault.
* @param _vault The address of the vault to check ownership.
*/
modifier onlyVaultOwner(address _vault) {
require(Vault(_vault).vaultOwner() == _msgSender(), 'only-vault-owner');
_;
}
/**
* @dev Modifier: Allows function execution by the owner or an operator of a specific vault.
* @param _vault The address of the vault to check ownership or operator status.
*/
modifier onlyVaultOwnerOrOperator(address _vault) {
require(
Vault(_vault).vaultOwner() == _msgSender() ||
Vault(_vault).isOperator(_msgSender()),
'only-vault-owner-or-operator'
);
_;
}
/**
* @dev Modifier: Allows function execution only by the liquidation router.
*/
modifier onlyLiquidationRouter() {
require(liquidationRouter == _msgSender(), 'only-liquidation-router');
_;
}
/**
* @dev Checks if a given collateral token is supported.
* @param _collateral The address of the collateral token.
* @return A boolean indicating whether the collateral token is supported.
*/
function isCollateralSupported(
address _collateral
) external view returns (bool) {
return _isCollateralSupported(_collateral);
}
/**
* @dev Transfers ownership of a vault to a new owner.
* @param _vault The address of the vault to transfer ownership.
* @param _newOwner The address of the new owner to receive the vault ownership.
*/
function transferVaultOwnership(
address _vault,
address _newOwner
) external onlyVaultOwner(_vault) {
address _msgSender = _msgSender();
require(_newOwner != address(0x0), 'new-owner-is-0');
require(containsVault(_vault), 'vault-not-found');
emit VaultOwnerChanged(_vault, _msgSender, _newOwner);
Vault(_vault).transferVaultOwnership(_newOwner);
_transferVault(_msgSender, _newOwner, _vault);
}
/**
* @dev Creates a new vault with a specified name.
* @param _name The name of the new vault.
* @return The address of the newly created vault.
*/
function createVault(string memory _name) public returns (address) {
address _msgSender = _msgSender();
address _vaultAddress = IVaultDeployer(vaultDeployer).deployVault(
address(this),
_msgSender,
_name
);
_addVault(_msgSender, _vaultAddress);
emit NewVault(_vaultAddress, _name, _msgSender);
return _vaultAddress;
}
/**
* @dev Checks if a specific collateral token is supported for the vault.
* @param _collateral The address of the collateral token to check.
* @return A boolean indicating whether the collateral token is supported.
*/
function _isCollateralSupported(
address _collateral
) internal view returns (bool) {
ITokenPriceFeed _priceFeed = ITokenPriceFeed(priceFeed);
return (_priceFeed.tokenPriceFeed(_collateral) != address(0x0));
}
/**
* @dev Adds native-wrapped collateral to a specific vault.
* @param _vault The address of the vault to add collateral.
*/
function addCollateralNative(address _vault) external payable {
require(containsVault(_vault), 'vault-not-found');
require(
_isCollateralSupported(nativeWrapped),
'collateral-not-supported'
);
uint256 _amount = msg.value;
collateral[nativeWrapped] += _amount;
require(
collateral[nativeWrapped] <= collateralCap[nativeWrapped],
'collateral-cap-reached'
);
IWETH(nativeWrapped).deposit{value: _amount}();
IERC20(nativeWrapped).safeTransfer(_vault, _amount);
Vault(_vault).addCollateral(nativeWrapped, _amount);
}
/**
* @dev Removes native-wrapped collateral from a specific vault.
* @param _vault The address of the vault to remove collateral.
* @param _amount The amount of collateral to be removed.
* @param _to The address where the removed collateral is transferred.
*/
function removeCollateralNative(
address _vault,
uint256 _amount,
address _to
) external onlyVaultOwner(_vault) {
require(containsVault(_vault), 'vault-not-found');
require(
_isCollateralSupported(nativeWrapped),
'collateral-not-supported'
);
Vault(_vault).removeCollateral(nativeWrapped, _amount, address(this));
collateral[nativeWrapped] -= _amount;
IWETH(nativeWrapped).withdraw(_amount);
(bool success, ) = payable(_to).call{value: _amount}("");
require(success, 'transfer-failed');
}
/**
* @dev Adds a specific collateral to a vault.
* @param _vault The address of the vault to add collateral.
* @param _collateral The address of the collateral token to add.
* @param _amount The amount of collateral to add.
*/
function addCollateral(
address _vault,
address _collateral,
uint256 _amount
) external {
require(containsVault(_vault), 'vault-not-found');
require(
_isCollateralSupported(_collateral),
'collateral-not-supported'
);
collateral[_collateral] += _amount;
require(
collateral[_collateral] <= collateralCap[_collateral],
'collateral-cap-reached'
);
IERC20(_collateral).safeTransferFrom(_msgSender(), _vault, _amount);
Vault(_vault).addCollateral(_collateral, _amount);
}
/**
* @dev Removes a specific collateral from a vault.
* @param _vault The address of the vault to remove collateral.
* @param _collateral The address of the collateral token to remove.
* @param _amount The amount of collateral to remove.
* @param _to The address where the removed collateral is transferred.
*/
function removeCollateral(
address _vault,
address _collateral,
uint256 _amount,
address _to
) external onlyVaultOwner(_vault) {
require(containsVault(_vault), 'vault-not-found');
require(
_isCollateralSupported(_collateral),
'collateral-not-supported'
);
collateral[_collateral] -= _amount;
Vault(_vault).removeCollateral(_collateral, _amount, _to);
}
/**
* @dev Borrows funds from a vault by its owner or an operator.
* @param _vault The address of the vault from which funds are borrowed.
* @param _amount The amount of funds to borrow.
* @param _to The address where borrowed funds are sent.
*/
function borrow(
address _vault,
uint256 _amount,
address _to
) external onlyVaultOwnerOrOperator(_vault) {
require(containsVault(_vault), 'vault-not-found');
require(_to != address(0x0), 'to-is-0');
totalDebt += _amount;
_updateDebtWindow(_amount);
Vault(_vault).borrow(_amount);
uint256 _borrowRate = IVaultBorrowRate(borrowRate).getBorrowRate(
_vault
);
uint256 _feeAmount = (_amount * _borrowRate) / DECIMAL_PRECISION;
mintableTokenOwner.mint(_to, _amount - _feeAmount);
mintableTokenOwner.mint(borrowFeeRecipient, _feeAmount);
}
/**
* @dev Distributes bad debt to a specific vault.
* @param _vault The address of the vault to distribute bad debt.
* @param _amount The amount of bad debt to be distributed.
*/
function distributeBadDebt(
address _vault,
uint256 _amount
) external nonReentrant onlyLiquidationRouter {
require(containsVault(_vault), 'vault-not-found');
totalDebt += _amount;
Vault(_vault).addBadDebt(_amount);
}
/**
* @dev Closes a vault if it meets specific conditions.
* @param _vault The address of the vault to close.
*/
function closeVault(address _vault) external onlyVaultOwner(_vault) {
require(containsVault(_vault), 'vault-not-found');
require(Vault(_vault).debt() == 0, 'debt-not-0');
require(Vault(_vault).collateralsLength() == 0, 'collateral-not-0');
_removeVault(_msgSender(), _vault);
}
/**
* @dev Repays borrowed funds for a specific vault.
* @param _vault The address of the vault for which funds are repaid.
* @param _amount The amount of funds to repay.
*/
function repay(address _vault, uint256 _amount) external {
require(containsVault(_vault), 'vault-not-found');
totalDebt -= _amount;
Vault(_vault).repay(_amount);
IMintableToken(stable).safeTransferFrom(
_msgSender(),
address(this),
_amount
);
IMintableToken(stable).burn(_amount);
}
/**
* @dev Redeems collateral from a vault after meeting specific conditions.
* @param _vault The address of the vault from which collateral is redeemed.
* @param _collateral The address of the collateral token to redeem.
* @param _collateralAmount The amount of collateral to redeem.
* @param _to The address where the redeemed collateral is transferred.
*/
function redeem(
address _vault,
address _collateral,
uint256 _collateralAmount,
address _to
) external nonReentrant {
require(publicRedemptions || isAddressRedemptionAllowed[_msgSender()], 'redemption-not-allowed');
require(containsVault(_vault), 'vault-not-found');
require(_to != address(0x0), 'to-is-0');
require(isReedemable(_vault, _collateral), 'not-redeemable');
(uint256 _debtRepaid, uint256 _feeCollected) = Vault(_vault).redeem(
_collateral,
_collateralAmount
);
totalDebt -= _debtRepaid;
collateral[_collateral] -= _collateralAmount;
IMintableToken(stable).safeTransferFrom(
_msgSender(),
address(this),
_debtRepaid + _feeCollected
);
IMintableToken(stable).burn(_debtRepaid);
IMintableToken(stable).transfer(redemptionFeeRecipient, _feeCollected);
IERC20(_collateral).safeTransfer(_to, _collateralAmount);
}
/**
* @dev Liquidates a specific vault if it is eligible for liquidation.
* @param _vault The address of the vault to be liquidated.
*/
function liquidate(address _vault) external nonReentrant {
require(containsVault(_vault), 'vault-not-found');
address _vaultOwner = Vault(_vault).vaultOwner();
uint256 _forgivenDebt = Vault(_vault).liquidate();
totalDebt -= _forgivenDebt;
_removeVault(_vaultOwner, _vault);
}
/**
* @dev Checks if a vault is eligible for liquidation.
* @param _vault The address of the vault to check for liquidation eligibility.
* @return A boolean indicating whether the vault is liquidatable.
*/
function isLiquidatable(address _vault) external view returns (bool) {
require(containsVault(_vault), 'vault-not-found');
return Vault(_vault).healthFactor(true) < DECIMAL_PRECISION;
}
/**
* @dev Checks if a specific collateral can be redeemed from a vault based on conditions.
* @param _vault The address of the vault to check for collateral redemption.
* @param _collateral The address of the collateral token to check for redemption.
* @notice Collateral with higher MCR can be redeemed first
* @return A boolean indicating whether the collateral is redeemable.
*/
function isReedemable(
address _vault,
address _collateral
) public view returns (bool) {
require(
_isCollateralSupported(_collateral),
'collateral-not-supported'
);
if (!Vault(_vault).containsCollateral(_collateral)) {
return false;
}
uint256 _healthFactor = Vault(_vault).healthFactor(false);
if (_healthFactor >= redemptionHealthFactorLimit) {
return false;
}
ITokenPriceFeed _priceFeed = ITokenPriceFeed(priceFeed);
uint256 _collateralMcr = _priceFeed.mcr(_collateral);
address[] memory _collaterals = Vault(_vault).collaterals();
uint256 _length = _collaterals.length;
for (uint256 i; i < _length; i++) {
if (_collaterals[i] != _collateral) {
uint256 _mcr = _priceFeed.mcr(_collaterals[i]);
if (_mcr > _collateralMcr) {
return false;
}
}
}
return true;
}
/**
* @dev Updates the debt window with the newly incurred debt.
* @param _newDebt The amount of new debt to update in the debt window.
*/
function _updateDebtWindow(uint256 _newDebt) internal {
require(totalDebt <= debtCeiling, 'debt-ceiling-reached');
if (block.timestamp > lastDebtWindow + debtWindowSize) {
debtWindowAmount = _newDebt;
lastDebtWindow = block.timestamp;
} else {
debtWindowAmount += _newDebt;
}
require(
debtWindowAmount <= maxDebtPerWindow,
'debt-window-amount-reached'
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './utils/constants.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
abstract contract VaultFactoryConfig is Constants, Ownable {
event PriceFeedUpdated(address indexed priceFeed);
event MaxTokensPerVaultUpdated(
uint256 oldMaxTokensPerVault,
uint256 newMaxTokensPerVault
);
event RedemptionRateUpdated(
uint256 oldRedemptionRate,
uint256 newRedemptionRate
);
event BorrowRateUpdated(address oldBorrowRate, address newBorrowRate);
event RedemptionHealthFactorLimitUpdated(
uint256 oldRedemptionHealthFactorLimit,
uint256 newRedemptionHealthFactorLimit
);
event DebtCeilingUpdated(uint256 oldDebtCeiling, uint256 newDebtCeiling);
event MaxDebtPerWindowUpdated(
uint256 oldMaxDebtPerWindow,
uint256 newMaxDebtPerWindow
);
event DebtWindowSizeUpdated(
uint256 oldDebtWindowSize,
uint256 newDebtWindowSize
);
event CollateralCapacityUpdated(
address indexed collateral,
uint256 oldCapacity,
uint256 newCapacity
);
event liquidationRouterUpdated(address indexed liquidationRouter);
event publicRedemptionsUpdated(bool publicRedemptions);
// Various configuration parameters
address public priceFeed;
address public borrowRate;
uint256 public MAX_TOKENS_PER_VAULT = 5;
uint256 public redemptionRate = PERCENT_05; // 0.5%
uint256 public redemptionHealthFactorLimit = 1.5 ether; // 1.5 HF
address public borrowFeeRecipient;
address public redemptionFeeRecipient;
mapping(address => uint256) public collateralCap;
uint256 public debtCeiling = type(uint256).max; // max stablecoin debt issued by the protocol
uint256 public maxDebtPerWindow = 200_000 ether; // 200K
uint256 public debtWindowSize = 1 hours;
uint256 public lastDebtWindow;
uint256 public debtWindowAmount;
address public vaultDeployer;
address public liquidationRouter;
bool public publicRedemptions;
mapping(address => bool) public isAddressRedemptionAllowed;
/**
* @dev Set the public redemptions flag
* @param _publicRedemptions The new public redemptions flag to be set.
*/
function setPublicRedemptions(bool _publicRedemptions) external onlyOwner {
publicRedemptions = _publicRedemptions;
emit publicRedemptionsUpdated(_publicRedemptions);
}
/**
* @dev Set the redemption allowed for a specific address
* @param _address Address of the account
* @param _allowed The new redemption allowed flag to be set.
*/
function setRedemptionAllowed(address _address, bool _allowed)
external
onlyOwner
{
isAddressRedemptionAllowed[_address] = _allowed;
}
/**
* @dev Set the address for the Vault Deployer
* @param _vaultDeployer Address of the Vault Deployer
*/
function setVaultDeployer(address _vaultDeployer) external onlyOwner {
require(_vaultDeployer != address(0x0), 'vault-deployer-is-0');
vaultDeployer = _vaultDeployer;
}
/**
* @dev Set the address for the Liquidation Router
* @param _liquidationRouter Address of the Liquidation Router
*/
function setLiquidationRouter(
address _liquidationRouter
) external onlyOwner {
require(_liquidationRouter != address(0x0), 'liquidation-router-is-0');
liquidationRouter = _liquidationRouter;
emit liquidationRouterUpdated(_liquidationRouter);
}
/**
* @dev Set the collateral capacity for a specific collateral token
* @param _collateral Address of the collateral token
* @param _cap The new capacity for the collateral token
*/
function setCollateralCapacity(
address _collateral,
uint256 _cap
) external onlyOwner {
require(_collateral != address(0x0), 'collateral-is-0');
emit CollateralCapacityUpdated(
_collateral,
collateralCap[_collateral],
_cap
);
collateralCap[_collateral] = _cap;
}
/**
* @dev Set the debt ceiling value.
* @param _debtCeiling The new debt ceiling value to be set.
*/
function setDebtCeiling(uint256 _debtCeiling) external onlyOwner {
emit DebtCeilingUpdated(debtCeiling, _debtCeiling);
debtCeiling = _debtCeiling;
}
/**
* @dev Set the maximum debt allowed per window.
* @param _maxDebtPerWindow The new maximum debt per window value to be set.
*/
function setMaxDebtPerWindow(uint256 _maxDebtPerWindow) external onlyOwner {
emit MaxDebtPerWindowUpdated(maxDebtPerWindow, _maxDebtPerWindow);
maxDebtPerWindow = _maxDebtPerWindow;
}
/**
* @dev Set the window size for debt.
* @param _debtWindowSize The new debt window size value to be set.
*/
function setDebtWindowSize(uint256 _debtWindowSize) external onlyOwner {
emit DebtWindowSizeUpdated(debtWindowSize, _debtWindowSize);
debtWindowSize = _debtWindowSize;
}
/**
* @dev Set the maximum tokens allowed per vault.
* @param _maxTokensPerVault The new maximum tokens per vault value to be set.
*/
function setMaxTokensPerVault(
uint256 _maxTokensPerVault
) external onlyOwner {
require(_maxTokensPerVault > 0, 'max-tokens-per-vault-is-0');
emit MaxTokensPerVaultUpdated(MAX_TOKENS_PER_VAULT, _maxTokensPerVault);
MAX_TOKENS_PER_VAULT = _maxTokensPerVault;
}
/**
* @dev Set the address for the price feed.
* @param _priceFeed Address of the new price feed contract.
*/
function setPriceFeed(address _priceFeed) external onlyOwner {
require(_priceFeed != address(0x0), 'pricefeed-is-0');
priceFeed = _priceFeed;
emit PriceFeedUpdated(_priceFeed);
}
/**
* @dev Set the redemption rate for the protocol.
* @param _redemptionRate The new redemption rate value to be set.
*/
function setRedemptionRate(uint256 _redemptionRate) external onlyOwner {
require(
_redemptionRate <= MAX_REDEMPTION_RATE,
'redemption-rate-too-high'
);
emit RedemptionRateUpdated(redemptionRate, _redemptionRate);
redemptionRate = _redemptionRate;
}
/**
* @dev Set the address for the borrow rate.
* @param _borrowRate Address of the new borrow rate contract.
*/
function setBorrowRate(address _borrowRate) external onlyOwner {
require(_borrowRate != address(0), 'borrow-rate-is-0');
emit BorrowRateUpdated(borrowRate, _borrowRate);
borrowRate = _borrowRate;
}
/**
* @dev Set the redemption health factor limit.
* @param _redemptionHealthFactorLimit The new redemption health factor limit to be set.
*/
function setRedemptionHealthFactorLimit(
uint256 _redemptionHealthFactorLimit
) external onlyOwner {
emit RedemptionHealthFactorLimitUpdated(
redemptionHealthFactorLimit,
_redemptionHealthFactorLimit
);
redemptionHealthFactorLimit = _redemptionHealthFactorLimit;
}
/**
* @dev Set the address for the borrow fee recipient.
* @param _borrowFeeRecipient Address of the new borrow fee recipient.
*/
function setBorrowFeeRecipient(
address _borrowFeeRecipient
) external onlyOwner {
require(
_borrowFeeRecipient != address(0x0),
'borrow-fee-recipient-is-0'
);
borrowFeeRecipient = _borrowFeeRecipient;
}
/**
* @dev Set the address for the redemption fee recipient.
* @param _redemptionFeeRecipient Address of the new redemption fee recipient.
*/
function setRedemptionFeeRecipient(
address _redemptionFeeRecipient
) external onlyOwner {
require(
_redemptionFeeRecipient != address(0x0),
'redemption-fee-recipient-is-0'
);
redemptionFeeRecipient = _redemptionFeeRecipient;
}
/**
* @dev Constructor to initialize the configuration settings upon deployment
* @param _vaultDeployer Address of the Vault Deployer
* @param _liquidationRouter Address of the Liquidation Router
*/
constructor(address _vaultDeployer, address _liquidationRouter) {
require(_vaultDeployer != address(0x0), 'vault-deployer-is-0');
require(_liquidationRouter != address(0x0), 'liquidation-factory-is-0');
vaultDeployer = _vaultDeployer;
borrowFeeRecipient = _msgSender();
redemptionFeeRecipient = _msgSender();
lastDebtWindow = block.timestamp;
liquidationRouter = _liquidationRouter;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './interfaces/IVaultFactory.sol';
import './interfaces/IVault.sol';
import './interfaces/ITokenPriceFeed.sol';
/**
* @title VaultFactoryHelper
* @notice Helper contract providing various functions to retrieve information about vaults in a vault factory
*/
contract VaultFactoryHelper {
uint256 public constant DECIMAL_PRECISION = 1e18;
/**
* @notice Retrieves all vault addresses within a vault factory
* @param _vaultFactory Address of the vault factory
* @return An array of vault addresses
*/
function getAllVaults(
address _vaultFactory
) public view returns (address[] memory) {
IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);
uint256 vaultCount = vaultFactory.vaultCount();
if (vaultCount == 0) {
return new address[](0);
} else {
address[] memory vaults = new address[](vaultCount);
vaults[0] = vaultFactory.firstVault();
for (uint256 i = 1; i < vaultCount; i++) {
vaults[i] = vaultFactory.nextVault(vaults[i - 1]);
}
return vaults;
}
}
/**
* @notice Retrieves the Total Value Locked (TVL) of a specific vault based on a single collateral type
* @param _vaultAddress Address of the vault
* @param _collateralAddress Address of the collateral asset
* @return The TVL of the vault for the given collateral
*/
function getVaultTvlByCollateral(
address _vaultAddress,
address _collateralAddress
) public view returns (uint256) {
IVault _vault = IVault(_vaultAddress);
uint256 _collateralAmount = _vault.collateral(_collateralAddress);
ITokenPriceFeed _priceFeed = ITokenPriceFeed(
IVaultFactory(_vault.factory()).priceFeed()
);
uint256 _price = _priceFeed.tokenPrice(_collateralAddress);
uint256 _normalizedCollateralAmount = _collateralAmount *
(10 ** (18 - _priceFeed.decimals(_collateralAddress)));
uint256 _tvl = (_normalizedCollateralAmount * _price) /
DECIMAL_PRECISION;
return _tvl;
}
/**
* @notice Retrieves the Total Value Locked (TVL) of a vault across all collateral types it holds
* @param _vault Address of the vault
* @return The total TVL of the vault across all collateral types
*/
function getVaultTvl(address _vault) public view returns (uint256) {
IVault vault = IVault(_vault);
uint256 tvl = 0;
for (uint256 i = 0; i < vault.collateralsLength(); i++) {
address _collateralAddress = vault.collateralAt(i);
tvl += getVaultTvlByCollateral(_vault, _collateralAddress);
}
return tvl;
}
/**
* @notice Retrieves an array of liquidatable vault addresses within a vault factory
* @param _vaultFactory Address of the vault factory
* @return An array of liquidatable vault addresses
*/
function getLiquidatableVaults(
address _vaultFactory
) public view returns (address[] memory) {
IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);
uint256 vaultCount = vaultFactory.vaultCount();
uint256 liquidatableVaultCount = 0;
if (vaultCount == 0) {
return new address[](0);
} else {
address[] memory _vaults = getAllVaults(_vaultFactory);
address[] memory _liquidatableVaults = new address[](vaultCount);
for (uint256 i = 0; i < vaultCount; i++) {
IVault _vault = IVault(_vaults[i]);
if (vaultFactory.isLiquidatable(address(_vault))) {
_liquidatableVaults[liquidatableVaultCount] = address(
_vault
);
liquidatableVaultCount++;
}
}
address[] memory liquidatableVaults = new address[](
liquidatableVaultCount
);
for (uint256 i = 0; i < liquidatableVaultCount; i++) {
liquidatableVaults[i] = _liquidatableVaults[i];
}
return liquidatableVaults;
}
}
/**
* @notice Retrieves an array of redeemable vault addresses and their corresponding redeemable collaterals
* @param _vaultFactory Address of the vault factory
* @param _useMlr Boolean indicating whether to use MLR for health factor calculation
* @return redeemableVaults An array of redeemable vault addresses
* @return redeemableCollaterals An array of corresponding redeemable collateral addresses
*/
function getRedeemableVaults(
address _vaultFactory,
bool _useMlr
)
public
view
returns (
address[] memory redeemableVaults,
address[] memory redeemableCollaterals
)
{
IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);
uint256 vaultCount = vaultFactory.vaultCount();
uint256 redeemableVaultCount = 0;
uint256 healthFactorLimit = vaultFactory.redemptionHealthFactorLimit();
if (vaultCount == 0) {
return (new address[](0), new address[](0));
} else {
address[] memory _vaults = getAllVaults(_vaultFactory);
address[] memory _redeemableVaults = new address[](vaultCount);
address[] memory _redeemableCollaterals = new address[](vaultCount);
for (uint256 i = 0; i < vaultCount; i++) {
IVault _vault = IVault(_vaults[i]);
if (_vault.healthFactor(_useMlr) < healthFactorLimit) {
_redeemableVaults[redeemableVaultCount] = address(_vault);
address[] memory _collaterals = getVaultCollaterals(
address(_vault)
);
for (uint256 j = 0; j < _collaterals.length; j++) {
if (
vaultFactory.isReedemable(
address(_vault),
_collaterals[j]
)
) {
_redeemableCollaterals[
redeemableVaultCount
] = _collaterals[j];
break;
}
}
redeemableVaultCount++;
}
}
redeemableVaults = new address[](redeemableVaultCount);
redeemableCollaterals = new address[](redeemableVaultCount);
for (uint256 i = 0; i < redeemableVaultCount; i++) {
redeemableVaults[i] = _redeemableVaults[i];
redeemableCollaterals[i] = _redeemableCollaterals[i];
}
}
}
/**
* @notice Retrieves an array of collateral asset addresses held by a specific vault
* @param _vault Address of the vault
* @return An array of collateral asset addresses
*/
function getVaultCollaterals(
address _vault
) public view returns (address[] memory) {
IVault vault = IVault(_vault);
uint256 collateralsLength = vault.collateralsLength();
if (collateralsLength == 0) {
return new address[](0);
} else {
address[] memory collaterals = new address[](collateralsLength);
for (uint256 i = 0; i < collateralsLength; i++) {
collaterals[i] = vault.collateralAt(i);
}
return collaterals;
}
}
/**
* @notice Calculates the Total Value Locked (TVL) across all vaults within a vault factory
* @param _vaultFactory Address of the vault factory
* @return The total TVL across all vaults in the factory
*/
function getProtocolTvl(
address _vaultFactory
) public view returns (uint256) {
IVaultFactory vaultFactory = IVaultFactory(_vaultFactory);
uint256 vaultCount = vaultFactory.vaultCount();
uint256 tvl = 0;
if (vaultCount == 0) {
return 0;
} else {
address[] memory _vaults = getAllVaults(_vaultFactory);
for (uint256 i = 0; i < vaultCount; i++) {
tvl += getVaultTvl(_vaults[i]);
}
return tvl;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './utils/constants.sol';
import './utils/linked-address-list.sol';
// import openzeppelin context
import '@openzeppelin/contracts/utils/Context.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
/**
* @title VaultFactoryList
* @dev Manages a list of vaults by their owners, allowing addition, removal, and transfer of vaults.
*/
abstract contract VaultFactoryList is Context {
using LinkedAddressList for LinkedAddressList.List;
using EnumerableSet for EnumerableSet.AddressSet;
LinkedAddressList.List _vaults;
mapping(address => EnumerableSet.AddressSet) private _vaultsByOwner;
function vaultsByOwnerLength(
address _owner
) external view returns (uint256) {
return _vaultsByOwner[_owner].length();
}
function vaultsByOwner(
address _owner,
uint256 _index
) external view returns (address) {
return _vaultsByOwner[_owner].at(_index);
}
function _addVault(address _owner, address _vault) internal {
require(
_vaults.add(_vault, address(0x0), false),
'vault-could-not-be-added'
);
_vaultsByOwner[_owner].add(_vault);
}
function _transferVault(
address _from,
address _to,
address _vault
) internal {
_vaultsByOwner[_from].remove(_vault);
_vaultsByOwner[_to].add(_vault);
}
function _removeVault(address _owner, address _vault) internal {
require(_vaults.remove(_vault), 'vault-could-not-be-removed');
_vaultsByOwner[_owner].remove(_vault);
}
/**
* @dev returns the number of vaults for specific token
*/
function vaultCount() public view returns (uint256) {
return _vaults._size;
}
/**
* @dev returns the last vault by maximum collaterization ratio
*/
function lastVault() public view returns (address) {
return _vaults._last;
}
/**
* @dev returns the first vault by minimal collaterization ratio
*/
function firstVault() public view returns (address) {
return _vaults._first;
}
/**
* @dev returns the next vault by collaterization ratio
*/
function nextVault(address _vault) public view returns (address) {
return _vaults._values[_vault].next;
}
/**
* @dev returns the previous vault by collaterization ratio
*/
function prevVault(address _vault) public view returns (address) {
return _vaults._values[_vault].prev;
}
/**
* @dev Checks if a vault exists for a specific token.
* @param _vault The address of the vault to check.
* @return A boolean indicating whether the vault exists.
*/
function containsVault(address _vault) public view returns (bool) {
return _vaults._values[_vault].next != address(0x0);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import './interfaces/IVaultFactory.sol';
import './interfaces/IVault.sol';
import './interfaces/ITokenPriceFeed.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
/**
* @title VaultFactoryZapper
* @dev A contract that facilitates the creation of Vaults and manages their operations.
*/
contract VaultFactoryZapper is Ownable {
using SafeERC20 for IERC20;
IVaultFactory public vaultFactory; // Interface for interacting with VaultFactory
string public prefix = 'MyVault'; // Prefix for the Vault name
receive() external payable {} // Fallback function to receive Matic
/**
* @dev Sets the VaultFactory contract address.
* @param _vaultFactory Address of the VaultFactory contract.
*/
function setVaultFactory(address _vaultFactory) public onlyOwner {
require(_vaultFactory != address(0), 'VaultFactory: zero address');
vaultFactory = IVaultFactory(_vaultFactory);
}
/**
* @dev Sets the prefix for Vault names.
* @param _prefix New prefix for Vault names.
*/
function setPrefix(string memory _prefix) public onlyOwner {
prefix = _prefix;
}
/**
* @dev Constructor to initialize the contract with the VaultFactory address.
* @param _vaultFactory Address of the VaultFactory contract.
*/
constructor(address _vaultFactory) {
setVaultFactory(_vaultFactory);
}
/**
* @dev Internal function to generate the name for the next Vault.
* @param _owner Address of the Vault owner.
* @return Name for the next Vault.
*/
function _getNextVaultName(
address _owner
) internal view returns (string memory) {
uint256 vaultCount = vaultFactory.vaultsByOwnerLength(_owner) + 1;
return string.concat(prefix, uint2str(vaultCount));
}
/**
* @dev Creates a new Vault.
* @param _collateralToken Address of the collateral token.
* @param _collateralAmount Amount of collateral tokens to be deposited.
* @param _borrowAmount Amount of tokens to be borrowed against the collateral.
* @return _vault Address of the newly created Vault.
*/
function createVault(
address _collateralToken,
uint256 _collateralAmount,
uint256 _borrowAmount
) external returns (address _vault) {
_vault = vaultFactory.createVault(_getNextVaultName(msg.sender));
if (_collateralAmount > 0) {
IERC20(_collateralToken).safeTransferFrom(
msg.sender,
address(this),
_collateralAmount
);
IERC20(_collateralToken).safeApprove(
address(vaultFactory),
_collateralAmount
);
vaultFactory.addCollateral(
_vault,
_collateralToken,
_collateralAmount
);
if (_borrowAmount > 0) {
vaultFactory.borrow(_vault, _borrowAmount, msg.sender);
}
}
vaultFactory.transferVaultOwnership(_vault, msg.sender);
}
/**
* @dev Creates a new Vault with native (Matic) collateral.
* @param _borrowAmount Amount of tokens to be borrowed against the collateral.
* @return _vault Address of the newly created Vault.
*/
function createVaultNative(
uint256 _borrowAmount
) external payable returns (address _vault) {
_vault = vaultFactory.createVault(_getNextVaultName(msg.sender));
if (msg.value > 0) {
vaultFactory.addCollateralNative{value: msg.value}(_vault);
if (_borrowAmount > 0) {
vaultFactory.borrow(_vault, _borrowAmount, msg.sender);
}
}
vaultFactory.transferVaultOwnership(_vault, msg.sender);
}
/**
* @dev Converts uint to a string.
* @param _i Unsigned integer to be converted.
* @return _uintAsString String representation of the input integer.
*/
function uint2str(
uint _i
) internal pure returns (string memory _uintAsString) {
if (_i == 0) {
return '0';
}
uint j = _i;
uint len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint k = len;
while (_i != 0) {
k = k - 1;
uint8 temp = (48 + uint8(_i - (_i / 10) * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
library console {
address constant CONSOLE_ADDRESS =
0x000000000000000000636F6e736F6c652e6c6f67;
function _sendLogPayloadImplementation(bytes memory payload) internal view {
address consoleAddress = CONSOLE_ADDRESS;
/// @solidity memory-safe-assembly
assembly {
pop(
staticcall(
gas(),
consoleAddress,
add(payload, 32),
mload(payload),
0,
0
)
)
}
}
function _castToPure(
function(bytes memory) internal view fnIn
) internal pure returns (function(bytes memory) pure fnOut) {
assembly {
fnOut := fnIn
}
}
function _sendLogPayload(bytes memory payload) internal pure {
_castToPure(_sendLogPayloadImplementation)(payload);
}
function log() internal pure {
_sendLogPayload(abi.encodeWithSignature("log()"));
}
function logInt(int256 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
}
function logUint(uint256 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
}
function logString(string memory p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function logBool(bool p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
}
function logAddress(address p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
}
function logBytes(bytes memory p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
}
function logBytes1(bytes1 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
}
function logBytes2(bytes2 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
}
function logBytes3(bytes3 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
}
function logBytes4(bytes4 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
}
function logBytes5(bytes5 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
}
function logBytes6(bytes6 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
}
function logBytes7(bytes7 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
}
function logBytes8(bytes8 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
}
function logBytes9(bytes9 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
}
function logBytes10(bytes10 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
}
function logBytes11(bytes11 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
}
function logBytes12(bytes12 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
}
function logBytes13(bytes13 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
}
function logBytes14(bytes14 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
}
function logBytes15(bytes15 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
}
function logBytes16(bytes16 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
}
function logBytes17(bytes17 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
}
function logBytes18(bytes18 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
}
function logBytes19(bytes19 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
}
function logBytes20(bytes20 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
}
function logBytes21(bytes21 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
}
function logBytes22(bytes22 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
}
function logBytes23(bytes23 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
}
function logBytes24(bytes24 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
}
function logBytes25(bytes25 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
}
function logBytes26(bytes26 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
}
function logBytes27(bytes27 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
}
function logBytes28(bytes28 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
}
function logBytes29(bytes29 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
}
function logBytes30(bytes30 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
}
function logBytes31(bytes31 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
}
function logBytes32(bytes32 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
}
function log(uint256 p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
}
function log(string memory p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function log(bool p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
}
function log(address p0) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
}
function log(uint256 p0, uint256 p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1));
}
function log(uint256 p0, string memory p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1));
}
function log(uint256 p0, bool p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1));
}
function log(uint256 p0, address p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1));
}
function log(string memory p0, uint256 p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
}
function log(string memory p0, string memory p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
}
function log(string memory p0, bool p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
}
function log(string memory p0, address p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
}
function log(bool p0, uint256 p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1));
}
function log(bool p0, string memory p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
}
function log(bool p0, bool p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
}
function log(bool p0, address p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
}
function log(address p0, uint256 p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1));
}
function log(address p0, string memory p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
}
function log(address p0, bool p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
}
function log(address p0, address p1) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
}
function log(uint256 p0, uint256 p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2));
}
function log(uint256 p0, uint256 p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2));
}
function log(uint256 p0, uint256 p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2));
}
function log(uint256 p0, uint256 p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2));
}
function log(uint256 p0, string memory p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2));
}
function log(uint256 p0, string memory p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2));
}
function log(uint256 p0, string memory p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2));
}
function log(uint256 p0, string memory p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2));
}
function log(uint256 p0, bool p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2));
}
function log(uint256 p0, bool p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2));
}
function log(uint256 p0, bool p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2));
}
function log(uint256 p0, bool p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2));
}
function log(uint256 p0, address p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2));
}
function log(uint256 p0, address p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2));
}
function log(uint256 p0, address p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2));
}
function log(uint256 p0, address p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2));
}
function log(string memory p0, uint256 p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2));
}
function log(string memory p0, uint256 p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2));
}
function log(string memory p0, uint256 p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2));
}
function log(string memory p0, uint256 p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2));
}
function log(string memory p0, string memory p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2));
}
function log(string memory p0, string memory p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
}
function log(string memory p0, string memory p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
}
function log(string memory p0, string memory p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
}
function log(string memory p0, bool p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2));
}
function log(string memory p0, bool p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
}
function log(string memory p0, bool p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
}
function log(string memory p0, bool p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
}
function log(string memory p0, address p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2));
}
function log(string memory p0, address p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
}
function log(string memory p0, address p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
}
function log(string memory p0, address p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
}
function log(bool p0, uint256 p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2));
}
function log(bool p0, uint256 p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2));
}
function log(bool p0, uint256 p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2));
}
function log(bool p0, uint256 p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2));
}
function log(bool p0, string memory p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2));
}
function log(bool p0, string memory p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
}
function log(bool p0, string memory p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
}
function log(bool p0, string memory p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
}
function log(bool p0, bool p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2));
}
function log(bool p0, bool p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
}
function log(bool p0, bool p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
}
function log(bool p0, bool p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
}
function log(bool p0, address p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2));
}
function log(bool p0, address p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
}
function log(bool p0, address p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
}
function log(bool p0, address p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
}
function log(address p0, uint256 p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2));
}
function log(address p0, uint256 p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2));
}
function log(address p0, uint256 p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2));
}
function log(address p0, uint256 p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2));
}
function log(address p0, string memory p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2));
}
function log(address p0, string memory p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
}
function log(address p0, string memory p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
}
function log(address p0, string memory p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
}
function log(address p0, bool p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2));
}
function log(address p0, bool p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
}
function log(address p0, bool p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
}
function log(address p0, bool p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
}
function log(address p0, address p1, uint256 p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2));
}
function log(address p0, address p1, string memory p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
}
function log(address p0, address p1, bool p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
}
function log(address p0, address p1, address p2) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
}
function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, string memory p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, bool p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3));
}
function log(uint256 p0, address p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint256 p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint256 p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3));
}
function log(address p0, uint256 p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint256 p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint256 p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint256 p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, uint256 p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, string memory p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, bool p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, address p3) internal pure {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
/**
* @title Constants
* @dev This contract defines various constants used within the system.
*/
contract Constants {
// Precision used for decimal calculations
uint256 public constant DECIMAL_PRECISION = 1e18;
// Reserve required for liquidation purposes
uint256 public constant LIQUIDATION_RESERVE = 1e18;
// Maximum value for uint256
uint256 public constant MAX_INT = 2 ** 256 - 1;
// Constants for percentage calculations
uint256 public constant PERCENT = (DECIMAL_PRECISION * 1) / 100; // Represents 1%
uint256 public constant PERCENT10 = PERCENT * 10; // Represents 10%
uint256 public constant PERCENT_05 = PERCENT / 2; // Represents 0.5%
// Maximum borrowing and redemption rates
uint256 public constant MAX_BORROWING_RATE = (DECIMAL_PRECISION * 5) / 100; // Represents 5%
uint256 public constant MAX_REDEMPTION_RATE =
(DECIMAL_PRECISION * 10) / 100; // Represents 10%
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
/**
* @title LinkedAddressList
* @dev Library implementing a linked list structure to store and operate sorted Troves.
*/
library LinkedAddressList {
struct EntryLink {
address prev;
address next;
}
struct List {
address _last;
address _first;
uint256 _size;
mapping(address => EntryLink) _values;
}
/**
* @dev Adds an element to the linked list.
* @param _list The storage pointer to the linked list.
* @param _element The element to be added.
* @param _reference The reference element to determine the position for addition.
* @param _before A boolean indicating whether to add the element before the reference.
* @return A boolean indicating the success of the addition.
*/
function add(
List storage _list,
address _element,
address _reference,
bool _before
) internal returns (bool) {
require(
_reference == address(0x0) ||
_list._values[_reference].next != address(0x0),
'79d3d _ref neither valid nor 0x'
);
// Element must not exist to be added
EntryLink storage element_values = _list._values[_element];
if (element_values.prev == address(0x0)) {
if (_list._last == address(0x0)) {
// If the list is empty, set the element as both first and last
element_values.prev = _element;
element_values.next = _element;
_list._first = _element;
_list._last = _element;
} else {
if (
_before &&
(_reference == address(0x0) || _reference == _list._first)
) {
// Adding the element as the first element
address first = _list._first;
_list._values[first].prev = _element;
element_values.prev = _element;
element_values.next = first;
_list._first = _element;
} else if (
!_before &&
(_reference == address(0x0) || _reference == _list._last)
) {
// Adding the element as the last element
address last = _list._last;
_list._values[last].next = _element;
element_values.prev = last;
element_values.next = _element;
_list._last = _element;
} else {
// Inserting the element between two elements
EntryLink memory ref = _list._values[_reference];
if (_before) {
element_values.prev = ref.prev;
element_values.next = _reference;
_list._values[_reference].prev = _element;
_list._values[ref.prev].next = _element;
} else {
element_values.prev = _reference;
element_values.next = ref.next;
_list._values[_reference].next = _element;
_list._values[ref.next].prev = _element;
}
}
}
_list._size = _list._size + 1;
return true;
}
return false;
}
/**
* @dev Removes an element from the linked list.
* @param _list The storage pointer to the linked list.
* @param _element The element to be removed.
* @return A boolean indicating the success of the removal.
*/
function remove(
List storage _list,
address _element
) internal returns (bool) {
EntryLink memory element_values = _list._values[_element];
if (element_values.next != address(0x0)) {
if (_element == _list._last && _element == _list._first) {
// Removing the last and only element in the list
delete _list._last;
delete _list._first;
} else if (_element == _list._first) {
// Removing the first element
address next = element_values.next;
_list._values[next].prev = next;
_list._first = next;
} else if (_element == _list._last) {
// Removing the last element
address new_list_last = element_values.prev;
_list._last = new_list_last;
_list._values[new_list_last].next = new_list_last;
} else {
// Removing an element in between two other elements
address next = element_values.next;
address prev = element_values.prev;
_list._values[next].prev = prev;
_list._values[prev].next = next;
}
// Delete the element itself
delete _list._values[_element];
_list._size = _list._size - 1;
return true;
}
return false;
}
}
{
"compilationTarget": {
"contracts/MintableToken.sol": "MintableToken"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","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":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"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":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]