// SPDX-License-Identifier: MIT pragma solidity >= 0.8.11; import { ISuperfluidToken } from "./ISuperfluidToken.sol"; import { IERC20, IERC20Metadata } from "@openzeppelin-v5/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { IERC20Permit } from "@openzeppelin-v5/contracts/token/ERC20/extensions/IERC20Permit.sol"; import { IERC5267 } from "@openzeppelin-v5/contracts/interfaces/IERC5267.sol"; import { IERC777 } from "@openzeppelin-v5/contracts/interfaces/IERC777.sol"; import { IPoolAdminNFT } from "../agreements/gdav1/IPoolAdminNFT.sol"; /** * @title Super token (Superfluid Token + ERC20 + ERC777) interface * @author Superfluid */ interface ISuperToken is ISuperfluidToken, IERC20Metadata, IERC777, IERC20Permit, IERC5267 { /************************************************************************** * Errors *************************************************************************/ error SUPER_TOKEN_CALLER_IS_NOT_OPERATOR_FOR_HOLDER(); // 0xf7f02227 error SUPER_TOKEN_NOT_ERC777_TOKENS_RECIPIENT(); // 0xfe737d05 error SUPER_TOKEN_INFLATIONARY_DEFLATIONARY_NOT_SUPPORTED(); // 0xe3e13698 error SUPER_TOKEN_NO_UNDERLYING_TOKEN(); // 0xf79cf656 error SUPER_TOKEN_ONLY_SELF(); // 0x7ffa6648 error SUPER_TOKEN_ONLY_ADMIN(); // 0x0484acab error SUPER_TOKEN_ONLY_GOV_OWNER(); // 0xd9c7ed08 error SUPER_TOKEN_APPROVE_FROM_ZERO_ADDRESS(); // 0x81638627 error SUPER_TOKEN_APPROVE_TO_ZERO_ADDRESS(); // 0xdf070274 error SUPER_TOKEN_BURN_FROM_ZERO_ADDRESS(); // 0xba2ab184 error SUPER_TOKEN_MINT_TO_ZERO_ADDRESS(); // 0x0d243157 error SUPER_TOKEN_TRANSFER_FROM_ZERO_ADDRESS(); // 0xeecd6c9b error SUPER_TOKEN_TRANSFER_TO_ZERO_ADDRESS(); // 0xe219bd39 error SUPER_TOKEN_NFT_PROXY_ADDRESS_CHANGED(); // 0xef1b6ddf error SUPER_TOKEN_PERMIT_EXPIRED_SIGNATURE(uint256 deadline); // 0x6e72b90f error SUPER_TOKEN_PERMIT_INVALID_SIGNER(address signer, address owner); // 0xb6422105 /** * @dev Return the version of the SuperToken logic */ function VERSION() external view returns (string memory); /** * @dev Initialize the contract */ function initialize( IERC20 underlyingToken, uint8 underlyingDecimals, string calldata n, string calldata s ) external; /** * @dev Initialize the contract with an admin */ function initializeWithAdmin( IERC20 underlyingToken, uint8 underlyingDecimals, string calldata n, string calldata s, address admin ) external; /** * @notice Changes the admin for the SuperToken * @dev Only the current admin can call this function * if admin is address(0), it is implicitly the host address * @param newAdmin New admin address */ function changeAdmin(address newAdmin) external; event AdminChanged(address indexed oldAdmin, address indexed newAdmin); /** * @dev Returns the admin address for the SuperToken * The admin account has the exclusive privilege of * - updating the contract (change implementation) * - enabling/disabling a yield backend * - setting another admin * If no admin is set (zero address), this privileges are delegated to the host contract. */ function getAdmin() external view returns (address admin); /** * @dev Returns the address of the yield backend contract (see `IYieldBackend`). * The yield backend contract is responsible for managing the yield of the SuperToken. */ function getYieldBackend() external view returns (address yieldBackend); /** * @dev Yield backend enabled event * @param yieldBackend The address of the yield backend that was enabled * @param depositAmount The amount deposited to the yield backend */ event YieldBackendEnabled( address indexed yieldBackend, uint256 depositAmount ); /** * @dev Yield backend disabled event * @param yieldBackend The address of the yield backend that was disabled */ event YieldBackendDisabled( address indexed yieldBackend ); /************************************************************************** * Immutable variables *************************************************************************/ // solhint-disable-next-line func-name-mixedcase function POOL_ADMIN_NFT() external view returns (IPoolAdminNFT); /************************************************************************** * IERC20Metadata & ERC777 *************************************************************************/ /** * @dev Returns the name of the token. */ function name() external view override(IERC777, IERC20Metadata) returns (string memory); /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() external view override(IERC777, IERC20Metadata) returns (string memory); /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * @custom:note SuperToken always uses 18 decimals. * * 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() external view override(IERC20Metadata) returns (uint8); /************************************************************************** * ERC20 & ERC777 *************************************************************************/ /** * @dev See {IERC20-totalSupply}. */ function totalSupply() external view override(IERC777, IERC20) returns (uint256); /** * @dev Returns the amount of tokens owned by an account (`owner`). */ function balanceOf(address account) external view override(IERC777, IERC20) returns(uint256 balance); /************************************************************************** * ERC20 *************************************************************************/ /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * @return Returns Success a boolean value indicating whether the operation succeeded. * * @custom:emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external override(IERC20) 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. * * @notice This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external override(IERC20) view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * @return Returns Success a boolean value indicating whether the operation succeeded. * * @custom:note 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 * * @custom:emits an {Approval} event. */ function approve(address spender, uint256 amount) external override(IERC20) returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * @return Returns Success a boolean value indicating whether the operation succeeded. * * @custom:emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external override(IERC20) returns (bool); /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * @custom:emits an {Approval} event indicating the updated allowance. * * @custom:requirements * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) external returns (bool); /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * @custom:emits an {Approval} event indicating the updated allowance. * * @custom:requirements * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); /************************************************************************** * ERC777 *************************************************************************/ /** * @dev Returns the smallest part of the token that is not divisible. This * means all token operations (creation, movement and destruction) must have * amounts that are a multiple of this number. * * @custom:note For super token contracts, this value is always 1 */ function granularity() external view override(IERC777) returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * @dev If send or receive hooks are registered for the caller and `recipient`, * the corresponding functions will be called with `userData` and empty * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. * * @custom:emits a {Sent} event. * * @custom:requirements * - the caller must have at least `amount` tokens. * - `recipient` cannot be the zero address. * - if `recipient` is a contract, it must implement the {IERC777Recipient} * interface. */ function send(address recipient, uint256 amount, bytes calldata userData) external override(IERC777); /** * @dev Destroys `amount` tokens from the caller's account, reducing the * total supply and transfers the underlying token to the caller's account. * * If a send hook is registered for the caller, the corresponding function * will be called with `userData` and empty `operatorData`. See {IERC777Sender}. * * @custom:emits a {Burned} event. * * @custom:requirements * - the caller must have at least `amount` tokens. */ function burn(uint256 amount, bytes calldata userData) external override(IERC777); /** * @dev Returns true if an account is an operator of `tokenHolder`. * Operators can send and burn tokens on behalf of their owners. All * accounts are their own operator. * * See {operatorSend} and {operatorBurn}. */ function isOperatorFor(address operator, address tokenHolder) external override(IERC777) view returns (bool); /** * @dev Make an account an operator of the caller. * * See {isOperatorFor}. * * @custom:emits an {AuthorizedOperator} event. * * @custom:requirements * - `operator` cannot be calling address. */ function authorizeOperator(address operator) external override(IERC777); /** * @dev Revoke an account's operator status for the caller. * * See {isOperatorFor} and {defaultOperators}. * * @custom:emits a {RevokedOperator} event. * * @custom:requirements * - `operator` cannot be calling address. */ function revokeOperator(address operator) external override(IERC777); /** * @dev Returns the list of default operators. These accounts are operators * for all token holders, even if {authorizeOperator} was never called on * them. * * This list is immutable, but individual holders may revoke these via * {revokeOperator}, in which case {isOperatorFor} will return false. */ function defaultOperators() external override(IERC777) view returns (address[] memory); /** * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must * be an operator of `sender`. * * If send or receive hooks are registered for `sender` and `recipient`, * the corresponding functions will be called with `userData` and * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. * * @custom:emits a {Sent} event. * * @custom:requirements * - `sender` cannot be the zero address. * - `sender` must have at least `amount` tokens. * - the caller must be an operator for `sender`. * - `recipient` cannot be the zero address. * - if `recipient` is a contract, it must implement the {IERC777Recipient} * interface. */ function operatorSend( address sender, address recipient, uint256 amount, bytes calldata userData, bytes calldata operatorData ) external override(IERC777); /** * @dev Destroys `amount` tokens from `account`, reducing the total supply. * The caller must be an operator of `account`. * * If a send hook is registered for `account`, the corresponding function * will be called with `userData` and `operatorData`. See {IERC777Sender}. * * @custom:emits a {Burned} event. * * @custom:requirements * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. * - the caller must be an operator for `account`. */ function operatorBurn( address account, uint256 amount, bytes calldata userData, bytes calldata operatorData ) external override(IERC777); /************************************************************************** * SuperToken custom token functions *************************************************************************/ /** * @dev Mint new tokens for the account * If `userData` is not empty, the `tokensReceived` hook is invoked according to ERC777 semantics. * * @custom:modifiers * - onlySelf */ function selfMint( address account, uint256 amount, bytes memory userData ) external; /** * @dev Burn existing tokens for the account * If `userData` is not empty, the `tokensToSend` hook is invoked according to ERC777 semantics. * * @custom:modifiers * - onlySelf */ function selfBurn( address account, uint256 amount, bytes memory userData ) external; /** * @dev Transfer `amount` tokens from the `sender` to `recipient`. * If `spender` isn't the same as `sender`, checks if `spender` has allowance to * spend tokens of `sender`. * * @custom:modifiers * - onlySelf */ function selfTransferFrom( address sender, address spender, address recipient, uint256 amount ) external; /** * @dev Give `spender`, `amount` allowance to spend the tokens of * `account`. * * @custom:modifiers * - onlySelf */ function selfApproveFor( address account, address spender, uint256 amount ) external; /************************************************************************** * SuperToken extra functions *************************************************************************/ /** * @dev Transfer all available balance from `msg.sender` to `recipient` */ function transferAll(address recipient) external; /************************************************************************** * ERC20 wrapping *************************************************************************/ /** * @dev Return the underlying token contract * @return tokenAddr Underlying token address */ function getUnderlyingToken() external view returns(address tokenAddr); /** * @dev Return the underlying token decimals * @return underlyingDecimals Underlying token decimals */ function getUnderlyingDecimals() external view returns (uint8 underlyingDecimals); /** * @dev Return the underlying token conversion rate * @param amount Number of tokens to be upgraded (in 18 decimals) * @return underlyingAmount The underlying token amount after scaling * @return adjustedAmount The super token amount after scaling */ function toUnderlyingAmount(uint256 amount) external view returns (uint256 underlyingAmount, uint256 adjustedAmount); /** * @dev Upgrade ERC20 to SuperToken. * @param amount Number of tokens to be upgraded (in 18 decimals) * * @custom:note It will use `transferFrom` to get tokens. Before calling this * function you should `approve` this contract */ function upgrade(uint256 amount) external; /** * @dev Upgrade ERC20 to SuperToken and transfer immediately * @param to The account to receive upgraded tokens * @param amount Number of tokens to be upgraded (in 18 decimals) * @param userData User data for the TokensRecipient callback * * @custom:note It will use `transferFrom` to get tokens. Before calling this * function you should `approve` this contract * * @custom:warning * - there is potential of reentrancy IF the "to" account is a registered ERC777 recipient. * @custom:requirements * - if `userData` is NOT empty AND `to` is a contract, it MUST be a registered ERC777 recipient * otherwise it reverts. */ function upgradeTo(address to, uint256 amount, bytes calldata userData) external; /** * @dev Token upgrade event * @param account Account where tokens are upgraded to * @param amount Amount of tokens upgraded (in 18 decimals) */ event TokenUpgraded( address indexed account, uint256 amount ); /** * @dev Downgrade SuperToken to ERC20. * @dev It will call transfer to send tokens * @param amount Number of tokens to be downgraded */ function downgrade(uint256 amount) external; /** * @dev Downgrade SuperToken to ERC20 and transfer immediately * @param to The account to receive downgraded tokens * @param amount Number of tokens to be downgraded (in 18 decimals) */ function downgradeTo(address to, uint256 amount) external; /** * @dev Token downgrade event * @param account Account whose tokens are downgraded * @param amount Amount of tokens downgraded */ event TokenDowngraded( address indexed account, uint256 amount ); /************************************************************************** * Batch Operations *************************************************************************/ /** * @dev Perform ERC20 approve by host contract. * @param account The account owner to be approved. * @param spender The spender of account owner's funds. * @param amount Number of tokens to be approved. * * @custom:modifiers * - onlyHost */ function operationApprove( address account, address spender, uint256 amount ) external; function operationIncreaseAllowance( address account, address spender, uint256 addedValue ) external; function operationDecreaseAllowance( address account, address spender, uint256 subtractedValue ) external; /** * @dev Perform ERC20 transferFrom by host contract. * @param account The account to spend sender's funds. * @param spender The account where the funds is sent from. * @param recipient The recipient of the funds. * @param amount Number of tokens to be transferred. * * @custom:modifiers * - onlyHost */ function operationTransferFrom( address account, address spender, address recipient, uint256 amount ) external; /** * @dev Perform ERC777 send by host contract. * @param spender The account where the funds is sent from. * @param recipient The recipient of the funds. * @param amount Number of tokens to be transferred. * @param userData Arbitrary user inputted data * * @custom:modifiers * - onlyHost */ function operationSend( address spender, address recipient, uint256 amount, bytes memory userData ) external; /** * @dev Upgrade ERC20 to SuperToken by host contract. * @param account The account to be changed. * @param amount Number of tokens to be upgraded (in 18 decimals) * * @custom:modifiers * - onlyHost */ function operationUpgrade(address account, uint256 amount) external; /** * @dev Downgrade ERC20 to SuperToken by host contract. * @param account The account to be changed. * @param amount Number of tokens to be downgraded (in 18 decimals) * * @custom:modifiers * - onlyHost */ function operationDowngrade(address account, uint256 amount) external; /** * @dev Upgrade ERC20 to SuperToken by host contract and transfer immediately. * @param account The account to be changed. * @param to The account to receive upgraded tokens * @param amount Number of tokens to be upgraded (in 18 decimals) * * @custom:modifiers * - onlyHost */ function operationUpgradeTo(address account, address to, uint256 amount) external; /** * @dev Downgrade ERC20 to SuperToken by host contract and transfer immediately. * @param account The account to be changed. * @param to The account to receive downgraded tokens * @param amount Number of tokens to be downgraded (in 18 decimals) * * @custom:modifiers * - onlyHost */ function operationDowngradeTo(address account, address to, uint256 amount) external; /** * @dev Pool Admin NFT proxy created event * @param poolAdminNFT pool admin nft address */ event PoolAdminNFTCreated( IPoolAdminNFT indexed poolAdminNFT ); /************************************************************************** * Function modifiers for access control and parameter validations * * While they cannot be explicitly stated in function definitions, they are * listed in function definition comments instead for clarity. * * NOTE: solidity-coverage not supporting it *************************************************************************/ /// @dev The msg.sender must be the contract itself //modifier onlySelf() virtual }