ERC-20
Source Code
Overview
Max Total Supply
50,093.261595902922051988 taoUSD
Holders
31
Transfers
-
0
Market
Onchain Market Cap
-
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Loading...
Loading
Loading...
Loading
Loading...
Loading
| # | Exchange | Pair | Price | 24H Volume | % Volume |
|---|
Contract Name:
MintableToken
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 1000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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) Ownable(msg.sender) {}
/// @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.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
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.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}.
*
* 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.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => 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 returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual 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 returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual 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 `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` 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 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
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 `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` 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.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` 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.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @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
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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 An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @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.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @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);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) 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 FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @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;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
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
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// 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 v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @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 is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 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 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[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._positions[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 '@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/utils/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 = 0.95 ether; // 95%
// 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 && _lowestHF < 1e18, '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) Ownable(msg.sender) {
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.safeIncreaseAllowance(
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;
interface IAuctionManager {
function newAuction() external;
}// 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/utils/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');
_;
}
constructor() Ownable(msg.sender) {}
/**
* @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/utils/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;
constructor() Ownable(msg.sender) {}
/**
* @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).safeIncreaseAllowance(stabilityPool, _amount);
IERC20(_collateral).safeIncreaseAllowance(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/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(msg.sender) {
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
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
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;
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
);
constructor() Ownable(msg.sender) {}
/**
* @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;
import '@openzeppelin/contracts/access/Ownable.sol';
// import openzeppelin reentrancy guard
import '@openzeppelin/contracts/utils/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) Ownable(msg.sender) {
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) Ownable(msg.sender) {
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;
/***
TTTTT BBBBB A N N K K
T B B A A NN N K K
T BBBBB A A N N N KK
T B B AAAAA N NN K K
T BBBBB A A N N K K
***/
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
contract TBANKStaking is ReentrancyGuard, Pausable, Ownable {
using SafeERC20 for IERC20;
/* ========== STATE VARIABLES ========== */
struct Reward {
address rewardsDistributor;
uint256 rewardsDuration;
uint256 periodFinish;
uint256 rewardRate;
uint256 lastUpdateTime;
uint256 rewardPerTokenStored;
}
IERC20 public stakingToken;
mapping(address => Reward) public rewardData;
address[] public rewardTokens;
// user -> reward token -> amount
mapping(address => mapping(address => uint256))
public userRewardPerTokenPaid;
mapping(address => mapping(address => uint256)) public rewards;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
address public strategist;
address public feeRecipient;
uint256 public constant MAX_FEE = 500; // 5%
uint256 public depositFee = 0; // 0%
uint256 public withdrawFee = 0; // 0%
/* ========== CONSTRUCTOR ========== */
modifier onlyOwnerOrStrategist() {
require(
msg.sender == owner() || msg.sender == strategist,
"permission denied"
);
_;
}
constructor(address _stakingToken) Ownable(msg.sender) {
stakingToken = IERC20(_stakingToken);
strategist = msg.sender;
feeRecipient = msg.sender;
}
function togglePause() external onlyOwnerOrStrategist {
if (paused()) _unpause();
else _pause();
}
function setStrategist(address _strategist) external onlyOwner {
require(_strategist != address(0), "strategist is 0");
emit StrategistUpdated(strategist, _strategist);
strategist = _strategist;
}
function setFeeRecipient(address _feeRecipient) external onlyOwner {
require(_feeRecipient != address(0), "feeRecipient is 0");
emit FeeRecipientUpdated(feeRecipient, _feeRecipient);
feeRecipient = _feeRecipient;
}
function setDepositFee(uint256 _depositFee) external onlyOwner {
require(_depositFee <= MAX_FEE, "fee too high");
emit DepositFeeUpdated(depositFee, _depositFee);
depositFee = _depositFee;
}
function setWithdrawFee(uint256 _withdrawFee) external onlyOwner {
require(_withdrawFee <= MAX_FEE, "fee too high");
emit WithdrawFeeUpdated(withdrawFee, _withdrawFee);
withdrawFee = _withdrawFee;
}
function addReward(
address _rewardsToken,
address _rewardsDistributor,
uint256 _rewardsDuration
) public onlyOwner {
require(rewardData[_rewardsToken].rewardsDuration == 0);
rewardTokens.push(_rewardsToken);
rewardData[_rewardsToken].rewardsDistributor = _rewardsDistributor;
rewardData[_rewardsToken].rewardsDuration = _rewardsDuration;
}
/* ========== VIEWS ========== */
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function lastTimeRewardApplicable(
address _rewardsToken
) public view returns (uint256) {
return (block.timestamp < rewardData[_rewardsToken].periodFinish
? block.timestamp
: rewardData[_rewardsToken].periodFinish);
}
function rewardPerToken(
address _rewardsToken
) public view returns (uint256) {
if (_totalSupply == 0) {
return rewardData[_rewardsToken].rewardPerTokenStored;
}
return
rewardData[_rewardsToken].rewardPerTokenStored +
(((lastTimeRewardApplicable(_rewardsToken) -
rewardData[_rewardsToken].lastUpdateTime) *
rewardData[_rewardsToken].rewardRate) * 1e18) /
_totalSupply;
}
function earned(
address account,
address _rewardsToken
) public view returns (uint256) {
return
((_balances[account] *
(rewardPerToken(_rewardsToken) -
userRewardPerTokenPaid[account][_rewardsToken])) / 1e18) +
rewards[account][_rewardsToken];
}
function getRewardForDuration(
address _rewardsToken
) external view returns (uint256) {
return
rewardData[_rewardsToken].rewardRate *
rewardData[_rewardsToken].rewardsDuration;
}
/* ========== MUTATIVE FUNCTIONS ========== */
function setRewardsDistributor(
address _rewardsToken,
address _rewardsDistributor
) external onlyOwnerOrStrategist {
rewardData[_rewardsToken].rewardsDistributor = _rewardsDistributor;
}
function stake(
uint256 amount
) external nonReentrant whenNotPaused updateReward(msg.sender) {
require(amount > 0, "Cannot stake 0");
if (depositFee > 0) {
uint256 _fee = (amount * depositFee) / 10000;
stakingToken.safeTransferFrom(msg.sender, feeRecipient, _fee);
amount = amount - _fee;
}
_totalSupply = _totalSupply + amount;
_balances[msg.sender] = _balances[msg.sender] + amount;
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}
function withdraw(
uint256 amount
) public nonReentrant updateReward(msg.sender) {
require(amount > 0, "Cannot withdraw 0");
_totalSupply = _totalSupply - amount;
_balances[msg.sender] = _balances[msg.sender] - amount;
if (withdrawFee > 0) {
uint256 fee = (amount * withdrawFee) / 10000;
stakingToken.safeTransfer(feeRecipient, fee);
amount = amount - fee;
}
stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
function getReward() public nonReentrant updateReward(msg.sender) {
for (uint i; i < rewardTokens.length; i++) {
address _rewardsToken = rewardTokens[i];
uint256 reward = rewards[msg.sender][_rewardsToken];
if (reward > 0) {
rewards[msg.sender][_rewardsToken] = 0;
IERC20(_rewardsToken).safeTransfer(msg.sender, reward);
emit RewardPaid(msg.sender, _rewardsToken, reward);
}
}
}
/* ========== RESTRICTED FUNCTIONS ========== */
function notifyRewardAmount(
address _rewardsToken,
uint256 reward
) external updateReward(address(0)) {
require(rewardData[_rewardsToken].rewardsDistributor == msg.sender);
// handle the transfer of reward tokens via `transferFrom` to reduce the number
// of transactions required and ensure correctness of the reward amount
IERC20(_rewardsToken).safeTransferFrom(
msg.sender,
address(this),
reward
);
if (block.timestamp >= rewardData[_rewardsToken].periodFinish) {
rewardData[_rewardsToken].rewardRate =
reward /
(rewardData[_rewardsToken].rewardsDuration);
} else {
uint256 remaining = rewardData[_rewardsToken].periodFinish -
(block.timestamp);
uint256 leftover = remaining *
(rewardData[_rewardsToken].rewardRate);
rewardData[_rewardsToken].rewardRate =
(reward + leftover) /
(rewardData[_rewardsToken].rewardsDuration);
}
rewardData[_rewardsToken].lastUpdateTime = block.timestamp;
rewardData[_rewardsToken].periodFinish =
block.timestamp +
(rewardData[_rewardsToken].rewardsDuration);
emit RewardAdded(reward);
}
// Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
function recoverERC20(
address tokenAddress,
uint256 tokenAmount
) external onlyOwner {
require(
tokenAddress != address(stakingToken),
"Cannot withdraw staking token"
);
require(
rewardData[tokenAddress].lastUpdateTime == 0,
"Cannot withdraw reward token"
);
IERC20(tokenAddress).safeTransfer(owner(), tokenAmount);
emit Recovered(tokenAddress, tokenAmount);
}
function setRewardsDuration(
address _rewardsToken,
uint256 _rewardsDuration
) external {
require(
block.timestamp > rewardData[_rewardsToken].periodFinish,
"Reward period still active"
);
require(rewardData[_rewardsToken].rewardsDistributor == msg.sender);
require(_rewardsDuration > 0, "Reward duration must be non-zero");
rewardData[_rewardsToken].rewardsDuration = _rewardsDuration;
emit RewardsDurationUpdated(
_rewardsToken,
rewardData[_rewardsToken].rewardsDuration
);
}
/* ========== MODIFIERS ========== */
modifier updateReward(address account) {
for (uint i; i < rewardTokens.length; i++) {
address token = rewardTokens[i];
rewardData[token].rewardPerTokenStored = rewardPerToken(token);
rewardData[token].lastUpdateTime = lastTimeRewardApplicable(token);
if (account != address(0)) {
rewards[account][token] = earned(account, token);
userRewardPerTokenPaid[account][token] = rewardData[token]
.rewardPerTokenStored;
}
}
_;
}
/* ========== EVENTS ========== */
event RewardAdded(uint256 reward);
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event RewardPaid(
address indexed user,
address indexed rewardsToken,
uint256 reward
);
event RewardsDurationUpdated(address token, uint256 newDuration);
event Recovered(address token, uint256 amount);
event Invested(address indexed strategist, uint256 amount);
event StrategistUpdated(address oldStrategist, address newStrategist);
event FeeRecipientUpdated(address oldFeeRecipient, address newFeeRecipient);
event DepositFeeUpdated(uint256 oldDepositFee, uint256 newDepositFee);
event WithdrawFeeUpdated(uint256 oldWithdrawFee, uint256 newWithdrawFee);
}// 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;
constructor() Ownable(msg.sender) {}
/**
* @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;
/**
* @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;
}
}// 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
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).safeIncreaseAllowance(
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) / 1e18;
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;
constructor() Ownable(msg.sender) {}
/**
* @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/utils/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) Ownable(msg.sender) {
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 onlyVaultOwner(_vault) {
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) Ownable(msg.sender) {
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).safeIncreaseAllowance(
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));
}
}{
"optimizer": {
"enabled": true,
"runs": 1000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":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":"value","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":"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":"value","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":"value","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"}]Contract Creation Code
60806040523480156200001157600080fd5b5060405162000d7f38038062000d7f8339810160408190526200003491620001b1565b3382826003620000458382620002aa565b506004620000548282620002aa565b5050506001600160a01b0381166200008657604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b62000091816200009a565b50505062000376565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200011457600080fd5b81516001600160401b0380821115620001315762000131620000ec565b604051601f8301601f19908116603f011681019082821181831017156200015c576200015c620000ec565b816040528381526020925086838588010111156200017957600080fd5b600091505b838210156200019d57858201830151818301840152908201906200017e565b600093810190920192909252949350505050565b60008060408385031215620001c557600080fd5b82516001600160401b0380821115620001dd57600080fd5b620001eb8683870162000102565b935060208501519150808211156200020257600080fd5b50620002118582860162000102565b9150509250929050565b600181811c908216806200023057607f821691505b6020821081036200025157634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620002a557600081815260208120601f850160051c81016020861015620002805750805b601f850160051c820191505b81811015620002a1578281556001016200028c565b5050505b505050565b81516001600160401b03811115620002c657620002c6620000ec565b620002de81620002d784546200021b565b8462000257565b602080601f831160018114620003165760008415620002fd5750858301515b600019600386901b1c1916600185901b178555620002a1565b600085815260208120601f198616915b82811015620003475788860151825594840194600190910190840162000326565b5085821015620003665787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6109f980620003866000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b41146101d8578063a9059cbb146101e0578063dd62ed3e146101f3578063f2fde38b1461022c57600080fd5b806370a082311461018c578063715018a6146101b55780638da5cb5b146101bd57600080fd5b806323b872dd116100c857806323b872dd14610142578063313ce5671461015557806340c10f191461016457806342966c681461017957600080fd5b806306fdde03146100ef578063095ea7b31461010d57806318160ddd14610130575b600080fd5b6100f761023f565b604051610104919061082a565b60405180910390f35b61012061011b366004610894565b6102d1565b6040519015158152602001610104565b6002545b604051908152602001610104565b6101206101503660046108be565b6102eb565b60405160128152602001610104565b610177610172366004610894565b61030f565b005b6101776101873660046108fa565b610325565b61013461019a366004610913565b6001600160a01b031660009081526020819052604090205490565b610177610332565b6005546040516001600160a01b039091168152602001610104565b6100f7610346565b6101206101ee366004610894565b610355565b610134610201366004610935565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b61017761023a366004610913565b610363565b60606003805461024e90610968565b80601f016020809104026020016040519081016040528092919081815260200182805461027a90610968565b80156102c75780601f1061029c576101008083540402835291602001916102c7565b820191906000526020600020905b8154815290600101906020018083116102aa57829003601f168201915b5050505050905090565b6000336102df8185856103bc565b60019150505b92915050565b6000336102f98582856103ce565b610304858585610465565b506001949350505050565b6103176104c4565b610321828261050a565b5050565b61032f3382610540565b50565b61033a6104c4565b6103446000610576565b565b60606004805461024e90610968565b6000336102df818585610465565b61036b6104c4565b6001600160a01b0381166103b3576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b61032f81610576565b6103c983838360016105e0565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461045f5781811015610450576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101829052604481018390526064016103aa565b61045f848484840360006105e0565b50505050565b6001600160a01b03831661048f57604051634b637e8f60e11b8152600060048201526024016103aa565b6001600160a01b0382166104b95760405163ec442f0560e01b8152600060048201526024016103aa565b6103c98383836106e7565b6005546001600160a01b03163314610344576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103aa565b6001600160a01b0382166105345760405163ec442f0560e01b8152600060048201526024016103aa565b610321600083836106e7565b6001600160a01b03821661056a57604051634b637e8f60e11b8152600060048201526024016103aa565b610321826000836106e7565b600580546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b038416610623576040517fe602df05000000000000000000000000000000000000000000000000000000008152600060048201526024016103aa565b6001600160a01b038316610666576040517f94280d62000000000000000000000000000000000000000000000000000000008152600060048201526024016103aa565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561045f57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516106d991815260200190565b60405180910390a350505050565b6001600160a01b03831661071257806002600082825461070791906109a2565b9091555061079d9050565b6001600160a01b0383166000908152602081905260409020548181101561077e576040517fe450d38c0000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260248101829052604481018390526064016103aa565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b0382166107b9576002805482900390556107d8565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161081d91815260200190565b60405180910390a3505050565b600060208083528351808285015260005b818110156108575785810183015185820160400152820161083b565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461088f57600080fd5b919050565b600080604083850312156108a757600080fd5b6108b083610878565b946020939093013593505050565b6000806000606084860312156108d357600080fd5b6108dc84610878565b92506108ea60208501610878565b9150604084013590509250925092565b60006020828403121561090c57600080fd5b5035919050565b60006020828403121561092557600080fd5b61092e82610878565b9392505050565b6000806040838503121561094857600080fd5b61095183610878565b915061095f60208401610878565b90509250929050565b600181811c9082168061097c57607f821691505b60208210810361099c57634e487b7160e01b600052602260045260246000fd5b50919050565b808201808211156102e557634e487b7160e01b600052601160045260246000fdfea2646970667358221220bddfa2236e01e4bce92d3f5d5465e53df91668cfaf830494e4f0d8dd39376d1064736f6c6343000815003300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000674616f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000674616f5553440000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b41146101d8578063a9059cbb146101e0578063dd62ed3e146101f3578063f2fde38b1461022c57600080fd5b806370a082311461018c578063715018a6146101b55780638da5cb5b146101bd57600080fd5b806323b872dd116100c857806323b872dd14610142578063313ce5671461015557806340c10f191461016457806342966c681461017957600080fd5b806306fdde03146100ef578063095ea7b31461010d57806318160ddd14610130575b600080fd5b6100f761023f565b604051610104919061082a565b60405180910390f35b61012061011b366004610894565b6102d1565b6040519015158152602001610104565b6002545b604051908152602001610104565b6101206101503660046108be565b6102eb565b60405160128152602001610104565b610177610172366004610894565b61030f565b005b6101776101873660046108fa565b610325565b61013461019a366004610913565b6001600160a01b031660009081526020819052604090205490565b610177610332565b6005546040516001600160a01b039091168152602001610104565b6100f7610346565b6101206101ee366004610894565b610355565b610134610201366004610935565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b61017761023a366004610913565b610363565b60606003805461024e90610968565b80601f016020809104026020016040519081016040528092919081815260200182805461027a90610968565b80156102c75780601f1061029c576101008083540402835291602001916102c7565b820191906000526020600020905b8154815290600101906020018083116102aa57829003601f168201915b5050505050905090565b6000336102df8185856103bc565b60019150505b92915050565b6000336102f98582856103ce565b610304858585610465565b506001949350505050565b6103176104c4565b610321828261050a565b5050565b61032f3382610540565b50565b61033a6104c4565b6103446000610576565b565b60606004805461024e90610968565b6000336102df818585610465565b61036b6104c4565b6001600160a01b0381166103b3576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024015b60405180910390fd5b61032f81610576565b6103c983838360016105e0565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461045f5781811015610450576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260248101829052604481018390526064016103aa565b61045f848484840360006105e0565b50505050565b6001600160a01b03831661048f57604051634b637e8f60e11b8152600060048201526024016103aa565b6001600160a01b0382166104b95760405163ec442f0560e01b8152600060048201526024016103aa565b6103c98383836106e7565b6005546001600160a01b03163314610344576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103aa565b6001600160a01b0382166105345760405163ec442f0560e01b8152600060048201526024016103aa565b610321600083836106e7565b6001600160a01b03821661056a57604051634b637e8f60e11b8152600060048201526024016103aa565b610321826000836106e7565b600580546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b038416610623576040517fe602df05000000000000000000000000000000000000000000000000000000008152600060048201526024016103aa565b6001600160a01b038316610666576040517f94280d62000000000000000000000000000000000000000000000000000000008152600060048201526024016103aa565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561045f57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516106d991815260200190565b60405180910390a350505050565b6001600160a01b03831661071257806002600082825461070791906109a2565b9091555061079d9050565b6001600160a01b0383166000908152602081905260409020548181101561077e576040517fe450d38c0000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260248101829052604481018390526064016103aa565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b0382166107b9576002805482900390556107d8565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161081d91815260200190565b60405180910390a3505050565b600060208083528351808285015260005b818110156108575785810183015185820160400152820161083b565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461088f57600080fd5b919050565b600080604083850312156108a757600080fd5b6108b083610878565b946020939093013593505050565b6000806000606084860312156108d357600080fd5b6108dc84610878565b92506108ea60208501610878565b9150604084013590509250925092565b60006020828403121561090c57600080fd5b5035919050565b60006020828403121561092557600080fd5b61092e82610878565b9392505050565b6000806040838503121561094857600080fd5b61095183610878565b915061095f60208401610878565b90509250929050565b600181811c9082168061097c57607f821691505b60208210810361099c57634e487b7160e01b600052602260045260246000fd5b50919050565b808201808211156102e557634e487b7160e01b600052601160045260246000fdfea2646970667358221220bddfa2236e01e4bce92d3f5d5465e53df91668cfaf830494e4f0d8dd39376d1064736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000674616f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000674616f5553440000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : name (string): taoUSD
Arg [1] : symbol (string): taoUSD
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [3] : 74616f5553440000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [5] : 74616f5553440000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.
Add Token to MetaMask (Web3)