Contract Overview
Balance:
0 MATIC
My Name Tag:
Not Available
Txn Hash |
Method
|
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0xf851384871e03e86a0c38a8d13433103540c1266b4758e494adcdf1652abb198 | 0x60806040 | 33639556 | 73 days 7 hrs ago | 0xdead1426ed8b637ae26fe55f4fb71e65f90374a1 | IN | Create: GratefulSubscription | 0 MATIC | 0.002323848024 |
[ Download CSV Export ]
Contract Source Code Verified (Exact Match)
Contract Name:
GratefulSubscription
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20.sol"; import "../token/ERC20/extensions/IERC20Metadata.sol"; /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. * * _Available since v4.7._ */ interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw( uint256 assets, address receiver, address owner ) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem( uint256 shares, address receiver, address owner ) external returns (uint256 assets); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ 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]. */ 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 v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ 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 v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol) pragma solidity ^0.8.0; /** * @dev Provides a set of functions to operate with Base64 strings. * * _Available since v4.5._ */ library Base64 { /** * @dev Base64 Encoding/Decoding Table */ string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * @dev Converts a `bytes` to its Bytes64 `string` representation. */ function encode(bytes memory data) internal pure returns (string memory) { /** * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol */ if (data.length == 0) return ""; // Loads the table into memory string memory table = _TABLE; // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter // and split into 4 numbers of 6 bits. // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up // - `data.length + 2` -> Round up // - `/ 3` -> Number of 3-bytes chunks // - `4 *` -> 4 characters for each chunk string memory result = new string(4 * ((data.length + 2) / 3)); /// @solidity memory-safe-assembly assembly { // Prepare the lookup table (skip the first "length" byte) let tablePtr := add(table, 1) // Prepare result pointer, jump over length let resultPtr := add(result, 32) // Run over the input, 3 bytes at a time for { let dataPtr := data let endPtr := add(data, mload(data)) } lt(dataPtr, endPtr) { } { // Advance 3 bytes dataPtr := add(dataPtr, 3) let input := mload(dataPtr) // To write each character, shift the 3 bytes (18 bits) chunk // 4 times in blocks of 6 bits for each character (18, 12, 6, 0) // and apply logical AND with 0x3F which is the number of // the previous character in the ASCII table prior to the Base64 Table // The result is then added to the table to get the character to write, // and finally write it in the result pointer but with a left shift // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) resultPtr := add(resultPtr, 1) // Advance mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F)))) resultPtr := add(resultPtr, 1) // Advance } // When data `bytes` is not exactly 3 bytes long // it is padded with `=` characters at the end switch mod(mload(data), 3) case 1 { mstore8(sub(resultPtr, 1), 0x3d) mstore8(sub(resultPtr, 2), 0x3d) } case 2 { mstore8(sub(resultPtr, 1), 0x3d) } } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`. // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`. // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a // good first aproximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1; uint256 x = a; if (x >> 128 > 0) { x >>= 128; result <<= 64; } if (x >> 64 > 0) { x >>= 64; result <<= 32; } if (x >> 32 > 0) { x >>= 32; result <<= 16; } if (x >> 16 > 0) { x >>= 16; result <<= 8; } if (x >> 8 > 0) { x >>= 8; result <<= 4; } if (x >> 4 > 0) { x >>= 4; result <<= 2; } if (x >> 2 > 0) { result <<= 1; } // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { uint256 result = sqrt(a); if (rounding == Rounding.Up && result * result < a) { result += 1; } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol) pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248) { require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits"); return int248(value); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240) { require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits"); return int240(value); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232) { require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits"); return int232(value); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224) { require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits"); return int224(value); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216) { require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits"); return int216(value); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208) { require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits"); return int208(value); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200) { require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits"); return int200(value); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192) { require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits"); return int192(value); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184) { require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits"); return int184(value); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176) { require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits"); return int176(value); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168) { require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits"); return int168(value); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160) { require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits"); return int160(value); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152) { require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits"); return int152(value); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144) { require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits"); return int144(value); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136) { require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits"); return int136(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); return int128(value); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120) { require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits"); return int120(value); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112) { require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits"); return int112(value); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104) { require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits"); return int104(value); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96) { require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits"); return int96(value); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88) { require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits"); return int88(value); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80) { require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits"); return int80(value); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72) { require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits"); return int72(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); return int64(value); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56) { require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits"); return int56(value); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48) { require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits"); return int48(value); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40) { require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits"); return int40(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); return int32(value); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24) { require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits"); return int24(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title Library for access related errors. */ library AccessError { /** * @dev Thrown when an address tries to perform an unauthorized action. * @param addr The address that attempts the action. */ error Unauthorized(address addr); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title Library for address related errors. */ library AddressError { /** * @dev Thrown when a zero address was passed as a function parameter (0x0000000000000000000000000000000000000000). */ error ZeroAddress(); /** * @dev Thrown when an address representing a contract is expected, but no code is found at the address. * @param contr The address that was expected to be a contract. */ error NotAContract(address contr); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title Library for initialization related errors. */ library InitError { /** * @dev Thrown when attempting to initialize a contract that is already initialized. */ error AlreadyInitialized(); /** * @dev Thrown when attempting to interact with a contract that has not been initialized yet. */ error NotInitialized(); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title Library for errors related with expected function parameters. */ library ParameterError { /** * @dev Thrown when an invalid parameter is used in a function. * @param parameter The name of the parameter. * @param reason The reason why the received parameter is invalid. */ error InvalidParameter(string parameter, string reason); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "../errors/InitError.sol"; /** * @title Mixin for contracts that require initialization. */ abstract contract InitializableMixin { /** * @dev Reverts if contract is not initialized. */ modifier onlyIfInitialized() { if (!_isInitialized()) { revert InitError.NotInitialized(); } _; } /** * @dev Reverts if contract is already initialized. */ modifier onlyIfNotInitialized() { if (_isInitialized()) { revert InitError.AlreadyInitialized(); } _; } /** * @dev Override this function to determine if the contract is initialized. * @return True if initialized, false otherwise. */ function _isInitialized() internal view virtual returns (bool); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title ERC165 interface for determining if a contract supports a given interface. */ interface IERC165 { /** * @notice Determines if the contract in question supports the specified interface. * @param interfaceID XOR of all selectors in the contract. * @return True if the contract supports the specified interface. */ function supportsInterface(bytes4 interfaceID) external view returns (bool); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title ERC20 token implementation. */ interface IERC20 { /** * @notice Emitted when tokens have been transferred. * @param from The address that originally owned the tokens. * @param to The address that received the tokens. * @param amount The number of tokens that were transferred. */ event Transfer(address indexed from, address indexed to, uint amount); /** * @notice Emitted when a user has provided allowance to another user for transferring tokens on its behalf. * @param owner The address that is providing the allowance. * @param spender The address that received the allowance. * @param amount The number of tokens that were added to `spender`'s allowance. */ event Approval(address indexed owner, address indexed spender, uint amount); /** * @notice Thrown when the address interacting with the contract does not have sufficient allowance to transfer tokens from another contract. * @param required The necessary allowance. * @param existing The current allowance. */ error InsufficientAllowance(uint required, uint existing); /** * @notice Thrown when the address interacting with the contract does not have sufficient tokens. * @param required The necessary balance. * @param existing The current balance. */ error InsufficientBalance(uint required, uint existing); /** * @notice Retrieves the name of the token, e.g. "Synthetix Network Token". * @return A string with the name of the token. */ function name() external view returns (string memory); /** * @notice Retrieves the symbol of the token, e.g. "SNX". * @return A string with the symbol of the token. */ function symbol() external view returns (string memory); /** * @notice Retrieves the number of decimals used by the token. The default is 18. * @return The number of decimals. */ function decimals() external view returns (uint8); /** * @notice Returns the total number of tokens in circulation (minted - burnt). * @return The total number of tokens. */ function totalSupply() external view returns (uint); /** * @notice Returns the balance of a user. * @param owner The address whose balance is being retrieved. * @return The number of tokens owned by the user. */ function balanceOf(address owner) external view returns (uint); /** * @notice Returns how many tokens a user has allowed another user to transfer on its behalf. * @param owner The user who has given the allowance. * @param spender The user who was given the allowance. * @return The amount of tokens `spender` can transfer on `owner`'s behalf. */ function allowance(address owner, address spender) external view returns (uint); /** * @notice Transfer tokens from one address to another. * @param to The address that will receive the tokens. * @param amount The amount of tokens to be transferred. * @return A boolean which is true if the operation succeeded. */ function transfer(address to, uint amount) external returns (bool); /** * @notice Allows users to provide allowance to other users so that they can transfer tokens on their behalf. * @param spender The address that is receiving the allowance. * @param amount The amount of tokens that are being added to the allowance. * @return A boolean which is true if the operation succeeded. */ function approve(address spender, uint amount) external returns (bool); /** * @notice Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) external returns (bool); /** * @notice Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); /** * @notice Allows a user who has been given allowance to transfer tokens on another user's behalf. * @param from The address that owns the tokens that are being transferred. * @param to The address that will receive the tokens. * @param amount The number of tokens to transfer. * @return A boolean which is true if the operation succeeded. */ function transferFrom(address from, address to, uint amount) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title ERC721 non-fungible token (NFT) contract. */ interface IERC721 { /** * @notice Thrown when an address attempts to provide allowance to itself. * @param addr The address attempting to provide allowance. */ error CannotSelfApprove(address addr); /** * @notice Thrown when attempting to transfer a token to an address that does not satisfy IERC721Receiver requirements. * @param addr The address that cannot receive the tokens. */ error InvalidTransferRecipient(address addr); /** * @notice Thrown when attempting to specify an owner which is not valid (ex. the 0x00000... address) */ error InvalidOwner(address addr); /** * @notice Thrown when attempting to operate on a token id that does not exist. * @param id The token id that does not exist. */ error TokenDoesNotExist(uint256 id); /** * @notice Thrown when attempting to mint a token that already exists. * @param id The token id that already exists. */ error TokenAlreadyMinted(uint256 id); /** * @notice Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @notice Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @notice Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @notice Returns the number of tokens in ``owner``'s account. * * Requirements: * * - `owner` must be a valid address */ function balanceOf(address owner) external view returns (uint256 balance); /** * @notice Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @notice Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @notice Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @notice Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @notice Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @notice Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @notice Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @notice Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "./IERC721.sol"; /** * @title ERC721 extension with helper functions that allow the enumeration of NFT tokens. */ interface IERC721Enumerable is IERC721 { /** * @notice Thrown calling *ByIndex function with an index greater than the number of tokens existing * @param requestedIndex The index requested by the caller * @param length The length of the list that is being iterated, making the max index queryable length - 1 */ error IndexOverrun(uint requestedIndex, uint length); /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. * * Requirements: * - `owner` must be a valid address * - `index` must be less than the balance of the tokens for the owner */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. * * Requirements: * - `index` must be less than the total supply of the tokens */ function tokenByIndex(uint256 index) external view returns (uint256); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "./IERC165.sol"; /** * @title Additional metadata for IERC721 tokens. */ interface IERC721Metadata is IERC165 { /** * @notice Retrieves the name of the token, e.g. "Synthetix Account Token". * @return A string with the name of the token. */ function name() external view returns (string memory); /** * @notice Retrieves the symbol of the token, e.g. "SNX-ACC". * @return A string with the symbol of the token. */ function symbol() external view returns (string memory); /** * @notice Retrieves the off-chain URI where the specified token id may contain associated data, such as images, audio, etc. * @param tokenId The numeric id of the token in question. * @return The URI of the token in question. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title ERC721 extension that allows contracts to receive tokens with `safeTransferFrom`. */ interface IERC721Receiver { /** * @notice Function that will be called by ERC721 tokens implementing the `safeTransferFrom` function. * @dev The contract transferring the token will revert if the receiving contract does not implement this function. * @param operator The address that is executing the transfer. * @param from The address whose token is being transferred. * @param tokenId The numeric id of the token being transferred. * @param data Optional additional data that may be passed by the operator, and could be used by the implementing contract. * @return The selector of this function (IERC721Receiver.onERC721Received.selector). Caller will revert if not returned. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes memory data ) external returns (bytes4); }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "../errors/AccessError.sol"; library OwnableStorage { bytes32 private constant _SLOT_OWNABLE_STORAGE = keccak256(abi.encode("io.synthetix.core-contracts.Ownable")); struct Data { address owner; address nominatedOwner; } function load() internal pure returns (Data storage store) { bytes32 s = _SLOT_OWNABLE_STORAGE; assembly { store.slot := s } } function onlyOwner() internal view { if (msg.sender != getOwner()) { revert AccessError.Unauthorized(msg.sender); } } function getOwner() internal view returns (address) { return OwnableStorage.load().owner; } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "../interfaces/IERC721.sol"; import "../interfaces/IERC721Metadata.sol"; import "../interfaces/IERC721Receiver.sol"; import "../errors/AddressError.sol"; import "../errors/AccessError.sol"; import "../errors/InitError.sol"; import "../errors/ParameterError.sol"; import "./ERC721Storage.sol"; import "../utils/AddressUtil.sol"; import "../utils/StringUtil.sol"; /* * @title ERC721 non-fungible token (NFT) contract. * See IERC721. * * Reference implementations: * - OpenZeppelin - https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol */ contract ERC721 is IERC721, IERC721Metadata { /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == this.supportsInterface.selector || // ERC165 interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId; } /** * @inheritdoc IERC721 */ function balanceOf(address holder) public view virtual override returns (uint) { if (holder == address(0)) { revert InvalidOwner(holder); } return ERC721Storage.load().balanceOf[holder]; } /** * @inheritdoc IERC721 */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { if (!_exists(tokenId)) { revert TokenDoesNotExist(tokenId); } return ERC721Storage.load().ownerOf[tokenId]; } /** * @inheritdoc IERC721Metadata */ function name() external view virtual override returns (string memory) { return ERC721Storage.load().name; } /** * @inheritdoc IERC721Metadata */ function symbol() external view virtual override returns (string memory) { return ERC721Storage.load().symbol; } /** * @inheritdoc IERC721Metadata */ function tokenURI(uint256 tokenId) external view virtual override returns (string memory) { if (!_exists(tokenId)) { revert TokenDoesNotExist(tokenId); } string memory baseURI = ERC721Storage.load().baseTokenURI; return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, StringUtil.uintToString(tokenId))) : ""; } /** * @inheritdoc IERC721 */ function approve(address to, uint256 tokenId) public virtual override { ERC721Storage.Data storage store = ERC721Storage.load(); address holder = store.ownerOf[tokenId]; if (to == holder) { revert CannotSelfApprove(to); } if (msg.sender != holder && !isApprovedForAll(holder, msg.sender)) { revert AccessError.Unauthorized(msg.sender); } _approve(to, tokenId); } /** * @inheritdoc IERC721 */ function getApproved(uint256 tokenId) public view virtual override returns (address) { if (!_exists(tokenId)) { revert TokenDoesNotExist(tokenId); } return ERC721Storage.load().tokenApprovals[tokenId]; } /** * @inheritdoc IERC721 */ function setApprovalForAll(address operator, bool approved) public virtual override { if (msg.sender == operator) { revert CannotSelfApprove(operator); } ERC721Storage.load().operatorApprovals[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } /** * @inheritdoc IERC721 */ function isApprovedForAll( address holder, address operator ) public view virtual override returns (bool) { return ERC721Storage.load().operatorApprovals[holder][operator]; } /** * @inheritdoc IERC721 */ function transferFrom(address from, address to, uint256 tokenId) public virtual override { if (!_isApprovedOrOwner(msg.sender, tokenId)) { revert AccessError.Unauthorized(msg.sender); } _transfer(from, to, tokenId); } /** * @inheritdoc IERC721 */ function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @inheritdoc IERC721 */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory data ) public virtual override { if (!_isApprovedOrOwner(msg.sender, tokenId)) { revert AccessError.Unauthorized(msg.sender); } _transfer(from, to, tokenId); if (!_checkOnERC721Received(from, to, tokenId, data)) { revert InvalidTransferRecipient(to); } } function _exists(uint256 tokenId) internal view virtual returns (bool) { return ERC721Storage.load().ownerOf[tokenId] != address(0); } function _isApprovedOrOwner( address spender, uint256 tokenId ) internal view virtual returns (bool) { address holder = ownerOf(tokenId); // Not checking tokenId existence since it is checked in ownerOf() and getApproved() return (spender == holder || getApproved(tokenId) == spender || isApprovedForAll(holder, spender)); } function _mint(address to, uint256 tokenId) internal virtual { ERC721Storage.Data storage store = ERC721Storage.load(); if (to == address(0)) { revert AddressError.ZeroAddress(); } if (tokenId == 0) { revert ParameterError.InvalidParameter("tokenId", "cannot be zero"); } if (_exists(tokenId)) { revert TokenAlreadyMinted(tokenId); } _beforeTransfer(address(0), to, tokenId); store.balanceOf[to] += 1; store.ownerOf[tokenId] = to; _postTransfer(address(0), to, tokenId); emit Transfer(address(0), to, tokenId); } function _burn(uint256 tokenId) internal virtual { ERC721Storage.Data storage store = ERC721Storage.load(); address holder = store.ownerOf[tokenId]; _approve(address(0), tokenId); _beforeTransfer(holder, address(0), tokenId); store.balanceOf[holder] -= 1; delete store.ownerOf[tokenId]; _postTransfer(holder, address(0), tokenId); emit Transfer(holder, address(0), tokenId); } function _transfer(address from, address to, uint256 tokenId) internal virtual { ERC721Storage.Data storage store = ERC721Storage.load(); if (ownerOf(tokenId) != from) { revert AccessError.Unauthorized(from); } if (to == address(0)) { revert AddressError.ZeroAddress(); } _beforeTransfer(from, to, tokenId); // Clear approvals from the previous holder _approve(address(0), tokenId); store.balanceOf[from] -= 1; store.balanceOf[to] += 1; store.ownerOf[tokenId] = to; _postTransfer(from, to, tokenId); emit Transfer(from, to, tokenId); } function _approve(address to, uint256 tokenId) internal virtual { ERC721Storage.load().tokenApprovals[tokenId] = to; emit Approval(ERC721.ownerOf(tokenId), to, tokenId); } function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory data ) internal returns (bool) { if (AddressUtil.isContract(to)) { try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns ( bytes4 retval ) { return retval == IERC721Receiver.onERC721Received.selector; } catch { return false; } } else { return true; } } function _beforeTransfer( address from, address to, uint256 tokenId // solhint-disable-next-line no-empty-blocks ) internal virtual {} function _postTransfer( address from, address to, uint256 tokenId // solhint-disable-next-line no-empty-blocks ) internal virtual {} function _initialize( string memory tokenName, string memory tokenSymbol, string memory baseTokenURI ) internal virtual { ERC721Storage.Data storage store = ERC721Storage.load(); if ( bytes(store.name).length > 0 || bytes(store.symbol).length > 0 || bytes(store.baseTokenURI).length > 0 ) { revert InitError.AlreadyInitialized(); } if (bytes(tokenName).length == 0 || bytes(tokenSymbol).length == 0) { revert ParameterError.InvalidParameter("name/symbol", "must not be empty"); } store.name = tokenName; store.symbol = tokenSymbol; store.baseTokenURI = baseTokenURI; } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "./ERC721.sol"; import "./ERC721EnumerableStorage.sol"; import "../interfaces/IERC721Enumerable.sol"; /* * @title ERC721 extension with helper functions that allow the enumeration of NFT tokens. * See IERC721Enumerable * * Reference implementations: * - OpenZeppelin - https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721EnumerableStorage.sol */ abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { /** * @inheritdoc IERC165 */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return super.supportsInterface(interfaceId) || interfaceId == type(IERC721Enumerable).interfaceId; } /** * @inheritdoc IERC721Enumerable */ function tokenOfOwnerByIndex( address owner, uint256 index ) public view virtual override returns (uint256) { if (ERC721.balanceOf(owner) <= index) { revert IndexOverrun(index, ERC721.balanceOf(owner)); } return ERC721EnumerableStorage.load().ownedTokens[owner][index]; } /** * @inheritdoc IERC721Enumerable */ function totalSupply() public view virtual override returns (uint256) { return ERC721EnumerableStorage.load().allTokens.length; } /** * @dev Returns the total amount of tokens stored by the contract. */ function tokenByIndex(uint256 index) public view virtual override returns (uint256) { if (index >= ERC721Enumerable.totalSupply()) { revert IndexOverrun(index, ERC721Enumerable.totalSupply()); } return ERC721EnumerableStorage.load().allTokens[index]; } function _beforeTransfer(address from, address to, uint256 tokenId) internal virtual override { if (from == address(0)) { _addTokenToAllTokensEnumeration(tokenId); } else if (from != to) { _removeTokenFromOwnerEnumeration(from, tokenId); } if (to == address(0)) { _removeTokenFromAllTokensEnumeration(tokenId); } else if (to != from) { _addTokenToOwnerEnumeration(to, tokenId); } } function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { uint256 length = ERC721.balanceOf(to); ERC721EnumerableStorage.load().ownedTokens[to][length] = tokenId; ERC721EnumerableStorage.load().ownedTokensIndex[tokenId] = length; } /** * @dev Private function to add a token to this extension's token tracking data structures. * @param tokenId uint256 ID of the token to be added to the tokens list */ function _addTokenToAllTokensEnumeration(uint256 tokenId) private { ERC721EnumerableStorage.load().allTokensIndex[tokenId] = ERC721EnumerableStorage .load() .allTokens .length; ERC721EnumerableStorage.load().allTokens.push(tokenId); } /** * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for * gas optimizations e.g. when performing a transfer operation (avoiding double writes). * This has O(1) time complexity, but alters the order of the _ownedTokens array. * @param from address representing the previous owner of the given token ID * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address */ function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; uint256 tokenIndex = ERC721EnumerableStorage.load().ownedTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary if (tokenIndex != lastTokenIndex) { uint256 lastTokenId = ERC721EnumerableStorage.load().ownedTokens[from][lastTokenIndex]; ERC721EnumerableStorage.load().ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token ERC721EnumerableStorage.load().ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index } // This also deletes the contents at the last position of the array delete ERC721EnumerableStorage.load().ownedTokensIndex[tokenId]; delete ERC721EnumerableStorage.load().ownedTokens[from][lastTokenIndex]; } /** * @dev Private function to remove a token from this extension's token tracking data structures. * This has O(1) time complexity, but alters the order of the _allTokens array. * @param tokenId uint256 ID of the token to be removed from the tokens list */ function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = ERC721EnumerableStorage.load().allTokens.length - 1; uint256 tokenIndex = ERC721EnumerableStorage.load().allTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding // an 'if' statement (like in _removeTokenFromOwnerEnumeration) uint256 lastTokenId = ERC721EnumerableStorage.load().allTokens[lastTokenIndex]; ERC721EnumerableStorage.load().allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token ERC721EnumerableStorage.load().allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index // This also deletes the contents at the last position of the array delete ERC721EnumerableStorage.load().allTokensIndex[tokenId]; ERC721EnumerableStorage.load().allTokens.pop(); } function _initialize( string memory tokenName, string memory tokenSymbol, string memory baseTokenURI ) internal virtual override { super._initialize(tokenName, tokenSymbol, baseTokenURI); if (ERC721EnumerableStorage.load().allTokens.length > 0) { revert InitError.AlreadyInitialized(); } } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; library ERC721EnumerableStorage { bytes32 private constant _SLOT_ERC721_ENUMERABLE_STORAGE = keccak256(abi.encode("io.synthetix.core-contracts.ERC721Enumerable")); struct Data { mapping(uint256 => uint256) ownedTokensIndex; mapping(uint256 => uint256) allTokensIndex; mapping(address => mapping(uint256 => uint256)) ownedTokens; uint256[] allTokens; } function load() internal pure returns (Data storage store) { bytes32 s = _SLOT_ERC721_ENUMERABLE_STORAGE; assembly { store.slot := s } } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; library ERC721Storage { bytes32 private constant _SLOT_ERC721_STORAGE = keccak256(abi.encode("io.synthetix.core-contracts.ERC721")); struct Data { string name; string symbol; string baseTokenURI; mapping(uint256 => address) ownerOf; mapping(address => uint256) balanceOf; mapping(uint256 => address) tokenApprovals; mapping(address => mapping(address => bool)) operatorApprovals; } function load() internal pure returns (Data storage store) { bytes32 s = _SLOT_ERC721_STORAGE; assembly { store.slot := s } } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; library AddressUtil { function isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * Utilities that convert numeric types avoiding silent overflows. */ import "./SafeCast/SafeCastU32.sol"; import "./SafeCast/SafeCastI32.sol"; import "./SafeCast/SafeCastI24.sol"; import "./SafeCast/SafeCastU56.sol"; import "./SafeCast/SafeCastI56.sol"; import "./SafeCast/SafeCastU64.sol"; import "./SafeCast/SafeCastI128.sol"; import "./SafeCast/SafeCastI256.sol"; import "./SafeCast/SafeCastU128.sol"; import "./SafeCast/SafeCastU160.sol"; import "./SafeCast/SafeCastU256.sol"; import "./SafeCast/SafeCastAddress.sol"; import "./SafeCast/SafeCastBytes32.sol";
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastAddress { function toBytes32(address x) internal pure returns (bytes32) { return bytes32(uint256(uint160(x))); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastBytes32 { function toAddress(bytes32 x) internal pure returns (address) { return address(uint160(uint256(x))); } function toUint(bytes32 x) internal pure returns (uint) { return uint(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI128 { error OverflowInt128ToUint128(); error OverflowInt128ToInt32(); function toUint(int128 x) internal pure returns (uint128) { // ----------------<==============o==============>----------------- // ----------------xxxxxxxxxxxxxxxo===============>---------------- if (x < 0) { revert OverflowInt128ToUint128(); } return uint128(x); } function to256(int128 x) internal pure returns (int256) { return int256(x); } function to32(int128 x) internal pure returns (int32) { // ----------------<==============o==============>----------------- // ----------------xxxxxxxxxxxx<==o==>xxxxxxxxxxxx----------------- if (x < int(type(int32).min) || x > int(type(int32).max)) { revert OverflowInt128ToInt32(); } return int32(x); } function zero() internal pure returns (int128) { return int128(0); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI24 { function to256(int24 x) internal pure returns (int256) { return int256(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI256 { error OverflowInt256ToUint256(); error OverflowInt256ToInt128(); error OverflowInt256ToInt24(); function to128(int256 x) internal pure returns (int128) { // ----<==========================o===========================>---- // ----xxxxxxxxxxxx<==============o==============>xxxxxxxxxxxxx---- if (x < int256(type(int128).min) || x > int256(type(int128).max)) { revert OverflowInt256ToInt128(); } return int128(x); } function to24(int256 x) internal pure returns (int24) { // ----<==========================o===========================>---- // ----xxxxxxxxxxxxxxxxxxxx<======o=======>xxxxxxxxxxxxxxxxxxxx---- if (x < int256(type(int24).min) || x > int256(type(int24).max)) { revert OverflowInt256ToInt24(); } return int24(x); } function toUint(int256 x) internal pure returns (uint256) { // ----<==========================o===========================>---- // ----xxxxxxxxxxxxxxxxxxxxxxxxxxxo===============================> if (x < 0) { revert OverflowInt256ToUint256(); } return uint256(x); } function zero() internal pure returns (int256) { return int256(0); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI32 { error OverflowInt32ToUint32(); function toUint(int32 x) internal pure returns (uint32) { // ----------------------<========o========>---------------------- // ----------------------xxxxxxxxxo=========>---------------------- if (x < 0) { revert OverflowInt32ToUint32(); } return uint32(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastI56 { error OverflowInt56ToInt24(); function to24(int56 x) internal pure returns (int24) { // ----------------------<========o========>----------------------- // ----------------------xxx<=====o=====>xxx----------------------- if (x < int(type(int24).min) || x > int(type(int24).max)) { revert OverflowInt56ToInt24(); } return int24(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU128 { error OverflowUint128ToInt128(); function to256(uint128 x) internal pure returns (uint256) { return uint256(x); } function toInt(uint128 x) internal pure returns (int128) { // -------------------------------o===============>---------------- // ----------------<==============o==============>x---------------- if (x > uint128(type(int128).max)) { revert OverflowUint128ToInt128(); } return int128(x); } function toBytes32(uint128 x) internal pure returns (bytes32) { return bytes32(uint256(x)); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU160 { function to256(uint160 x) internal pure returns (uint256) { return uint256(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU256 { error OverflowUint256ToUint128(); error OverflowUint256ToInt256(); error OverflowUint256ToUint64(); error OverflowUint256ToUint32(); error OverflowUint256ToUint160(); function to128(uint256 x) internal pure returns (uint128) { // -------------------------------o===============================> // -------------------------------o===============>xxxxxxxxxxxxxxxx if (x > type(uint128).max) { revert OverflowUint256ToUint128(); } return uint128(x); } function to64(uint256 x) internal pure returns (uint64) { // -------------------------------o===============================> // -------------------------------o======>xxxxxxxxxxxxxxxxxxxxxxxxx if (x > type(uint64).max) { revert OverflowUint256ToUint64(); } return uint64(x); } function to32(uint256 x) internal pure returns (uint32) { // -------------------------------o===============================> // -------------------------------o===>xxxxxxxxxxxxxxxxxxxxxxxxxxxx if (x > type(uint32).max) { revert OverflowUint256ToUint32(); } return uint32(x); } function to160(uint256 x) internal pure returns (uint160) { // -------------------------------o===============================> // -------------------------------o==================>xxxxxxxxxxxxx if (x > type(uint160).max) { revert OverflowUint256ToUint160(); } return uint160(x); } function toBytes32(uint256 x) internal pure returns (bytes32) { return bytes32(x); } function toInt(uint256 x) internal pure returns (int256) { // -------------------------------o===============================> // ----<==========================o===========================>xxxx if (x > uint256(type(int256).max)) { revert OverflowUint256ToInt256(); } return int256(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU32 { error OverflowUint32ToInt32(); function toInt(uint32 x) internal pure returns (int32) { // -------------------------------o=========>---------------------- // ----------------------<========o========>x---------------------- if (x > uint32(type(int32).max)) { revert OverflowUint32ToInt32(); } return int32(x); } function to256(uint32 x) internal pure returns (uint256) { return uint256(x); } function to56(uint32 x) internal pure returns (uint56) { return uint56(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU56 { error OverflowUint56ToInt56(); function toInt(uint56 x) internal pure returns (int56) { // -------------------------------o=========>---------------------- // ----------------------<========o========>x---------------------- if (x > uint56(type(int56).max)) { revert OverflowUint56ToInt56(); } return int56(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /** * @title See SafeCast.sol. */ library SafeCastU64 { error OverflowUint64ToInt64(); function toInt(uint64 x) internal pure returns (int64) { // -------------------------------o=========>---------------------- // ----------------------<========o========>x---------------------- if (x > uint64(type(int64).max)) { revert OverflowUint64ToInt64(); } return int64(x); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "./SafeCast.sol"; library SetUtil { using SafeCastAddress for address; using SafeCastBytes32 for bytes32; using SafeCastU256 for uint256; // ---------------------------------------- // Uint support // ---------------------------------------- struct UintSet { Bytes32Set raw; } function add(UintSet storage set, uint value) internal { add(set.raw, value.toBytes32()); } function remove(UintSet storage set, uint value) internal { remove(set.raw, value.toBytes32()); } function replace(UintSet storage set, uint value, uint newValue) internal { replace(set.raw, value.toBytes32(), newValue.toBytes32()); } function contains(UintSet storage set, uint value) internal view returns (bool) { return contains(set.raw, value.toBytes32()); } function length(UintSet storage set) internal view returns (uint) { return length(set.raw); } function valueAt(UintSet storage set, uint position) internal view returns (uint) { return valueAt(set.raw, position).toUint(); } function positionOf(UintSet storage set, uint value) internal view returns (uint) { return positionOf(set.raw, value.toBytes32()); } function values(UintSet storage set) internal view returns (uint[] memory) { bytes32[] memory store = values(set.raw); uint[] memory result; assembly { result := store } return result; } // ---------------------------------------- // Address support // ---------------------------------------- struct AddressSet { Bytes32Set raw; } function add(AddressSet storage set, address value) internal { add(set.raw, value.toBytes32()); } function remove(AddressSet storage set, address value) internal { remove(set.raw, value.toBytes32()); } function replace(AddressSet storage set, address value, address newValue) internal { replace(set.raw, value.toBytes32(), newValue.toBytes32()); } function contains(AddressSet storage set, address value) internal view returns (bool) { return contains(set.raw, value.toBytes32()); } function length(AddressSet storage set) internal view returns (uint) { return length(set.raw); } function valueAt(AddressSet storage set, uint position) internal view returns (address) { return valueAt(set.raw, position).toAddress(); } function positionOf(AddressSet storage set, address value) internal view returns (uint) { return positionOf(set.raw, value.toBytes32()); } function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = values(set.raw); address[] memory result; assembly { result := store } return result; } // ---------------------------------------- // Core bytes32 support // ---------------------------------------- error PositionOutOfBounds(); error ValueNotInSet(); error ValueAlreadyInSet(); struct Bytes32Set { bytes32[] _values; mapping(bytes32 => uint) _positions; // Position zero is never used. } function add(Bytes32Set storage set, bytes32 value) internal { if (contains(set, value)) { revert ValueAlreadyInSet(); } set._values.push(value); set._positions[value] = set._values.length; } function remove(Bytes32Set storage set, bytes32 value) internal { uint position = set._positions[value]; if (position == 0) { revert ValueNotInSet(); } uint index = position - 1; uint lastIndex = set._values.length - 1; // If the element being deleted is not the last in the values, // move the last element to its position. if (index != lastIndex) { bytes32 lastValue = set._values[lastIndex]; set._values[index] = lastValue; set._positions[lastValue] = position; } // Remove the last element in the values. set._values.pop(); delete set._positions[value]; } function replace(Bytes32Set storage set, bytes32 value, bytes32 newValue) internal { if (!contains(set, value)) { revert ValueNotInSet(); } if (contains(set, newValue)) { revert ValueAlreadyInSet(); } uint position = set._positions[value]; delete set._positions[value]; uint index = position - 1; set._values[index] = newValue; set._positions[newValue] = position; } function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return set._positions[value] != 0; } function length(Bytes32Set storage set) internal view returns (uint) { return set._values.length; } function valueAt(Bytes32Set storage set, uint position) internal view returns (bytes32) { if (position == 0 || position > set._values.length) { revert PositionOutOfBounds(); } uint index = position - 1; return set._values[index]; } function positionOf(Bytes32Set storage set, bytes32 value) internal view returns (uint) { if (!contains(set, value)) { revert ValueNotInSet(); } return set._positions[value]; } function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { return set._values; } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; /* Reference implementations: * OpenZeppelin - https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol */ library StringUtil { function uintToString(uint value) internal pure returns (string memory) { if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; // solhint-disable-next-line numcast/safe-cast buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "@synthetixio/core-contracts/contracts/interfaces/IERC721Enumerable.sol"; /** * @title Module wrapping an ERC721 token implementation. */ interface INftModule is IERC721Enumerable { /** * @notice Returns wether the token has been initialized. * @return A boolean with the result of the query. */ function isInitialized() external returns (bool); /** * @notice Initializes the token with name, symbol, and uri. */ function initialize( string memory tokenName, string memory tokenSymbol, string memory uri ) external; /** * @notice Allows the owner to mint tokens. * @param to The address to receive the newly minted tokens. * @param tokenId The ID of the newly minted token */ function mint(address to, uint tokenId) external; /** * @notice Allows the owner to mint tokens. Verifies that the receiver can receive the token * @param to The address to receive the newly minted token. * @param tokenId The ID of the newly minted token * @param data any data which should be sent to the receiver */ function safeMint(address to, uint256 tokenId, bytes memory data) external; /** * @notice Allows the owner to burn tokens. * @param tokenId The token to burn */ function burn(uint tokenId) external; /** * @notice Allows an address that holds tokens to provide allowance to another. * @param tokenId The token which should be allowed to spender * @param spender The address that is given allowance. */ function setAllowance(uint tokenId, address spender) external; }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "@synthetixio/core-contracts/contracts/interfaces/IERC20.sol"; /** * @title Module wrapping an ERC20 token implementation. */ interface ITokenModule is IERC20 { /** * @notice Returns wether the token has been initialized. * @return A boolean with the result of the query. */ function isInitialized() external returns (bool); /** * @notice Initializes the token with name, symbol, and decimals. */ function initialize( string memory tokenName, string memory tokenSymbol, uint8 tokenDecimals ) external; /** * @notice Allows the owner to mint tokens. * @param to The address to receive the newly minted tokens. * @param amount The amount of tokens to mint. */ function mint(address to, uint amount) external; /** * @notice Allows the owner to burn tokens. * @param to The address whose tokens will be burnt. * @param amount The amount of tokens to burn. */ function burn(address to, uint amount) external; /** * @notice Allows an address that holds tokens to provide allowance to another. * @param from The address that is providing allowance. * @param spender The address that is given allowance. * @param amount The amount of allowance being given. */ function setAllowance(address from, address spender, uint amount) external; }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "@synthetixio/core-contracts/contracts/token/ERC721Enumerable.sol"; import "@synthetixio/core-contracts/contracts/utils/AddressUtil.sol"; import "@synthetixio/core-contracts/contracts/initializable/InitializableMixin.sol"; import "@synthetixio/core-contracts/contracts/ownership/OwnableStorage.sol"; import "@synthetixio/core-contracts/contracts/errors/AddressError.sol"; import "../storage/Initialized.sol"; import "../interfaces/INftModule.sol"; /** * @title Module wrapping an ERC721 token implementation. * See INftModule. */ contract NftModule is INftModule, ERC721Enumerable, InitializableMixin { bytes32 internal constant _INITIALIZED_NAME = "NftModule"; /** * @inheritdoc INftModule */ function isInitialized() external view returns (bool) { return _isInitialized(); } /** * @inheritdoc INftModule */ function initialize( string memory tokenName, string memory tokenSymbol, string memory uri ) public { OwnableStorage.onlyOwner(); _initialize(tokenName, tokenSymbol, uri); Initialized.load(_INITIALIZED_NAME).initialized = true; } /** * @inheritdoc INftModule */ function burn(uint256 tokenId) external override { OwnableStorage.onlyOwner(); _burn(tokenId); } /** * @inheritdoc INftModule */ function mint(address to, uint256 tokenId) external override { OwnableStorage.onlyOwner(); _mint(to, tokenId); } /** * @inheritdoc INftModule */ function safeMint(address to, uint256 tokenId, bytes memory data) external override { OwnableStorage.onlyOwner(); _mint(to, tokenId); if (!_checkOnERC721Received(address(0), to, tokenId, data)) { revert InvalidTransferRecipient(to); } } /** * @inheritdoc INftModule */ function setAllowance(uint tokenId, address spender) external override { OwnableStorage.onlyOwner(); ERC721Storage.load().tokenApprovals[tokenId] = spender; } function _isInitialized() internal view override returns (bool) { return Initialized.load(_INITIALIZED_NAME).initialized; } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; import "../interfaces/ITokenModule.sol"; import "../interfaces/INftModule.sol"; library AssociatedSystem { struct Data { address proxy; address impl; bytes32 kind; } error MismatchAssociatedSystemKind(bytes32 expected, bytes32 actual); function load(bytes32 id) internal pure returns (Data storage store) { bytes32 s = keccak256(abi.encode("io.synthetix.core-modules.AssociatedSystem", id)); assembly { store.slot := s } } bytes32 public constant KIND_ERC20 = "erc20"; bytes32 public constant KIND_ERC721 = "erc721"; bytes32 public constant KIND_UNMANAGED = "unmanaged"; function getAddress(Data storage self) internal view returns (address) { return self.proxy; } function asToken(Data storage self) internal view returns (ITokenModule) { expectKind(self, KIND_ERC20); return ITokenModule(self.proxy); } function asNft(Data storage self) internal view returns (INftModule) { expectKind(self, KIND_ERC721); return INftModule(self.proxy); } function set(Data storage self, address proxy, address impl, bytes32 kind) internal { self.proxy = proxy; self.impl = impl; self.kind = kind; } function expectKind(Data storage self, bytes32 kind) internal view { bytes32 actualKind = self.kind; if (actualKind != kind && actualKind != KIND_UNMANAGED) { revert MismatchAssociatedSystemKind(kind, actualKind); } } }
//SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; library Initialized { struct Data { bool initialized; } function load(bytes32 id) internal pure returns (Data storage store) { bytes32 s = keccak256(abi.encode("io.synthetix.code-modules.Initialized", id)); assembly { store.slot := s } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Balances related errors. */ library BalanceErrors { /** * @notice Error when a profile doesn't have enough balance to withdraw. * * Cases: * - `FundsModule.withdrawFunds()` * */ error InsufficientBalance(); /** * @notice Error when a profile want to subscribe or withdraw but will get insolvent. * * Cases: * - `FundsModule.withdrawFunds()` * - `SubscriptionsModule.subscribe()` * */ error InsolventUser(); /** * @notice Error when wanting to liquidate a profile subscription but is not liquidable. * * Cases: * - `LiquidationsModule.liquidate()` * */ error SolventUser(); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Input related errors. */ library InputErrors { /** * @notice Error when an input has unexpected zero uint256. * * Cases: * - `FundsModule.depositunds()` * - `FundsModule.withdrawFunds()` * */ error ZeroAmount(); /** * @notice Error when an input has unexpected zero address. * * Cases: * - `ProfilesModule.allowProfile()` * - `ProfilesModule.disallowProfile()` * - `VaultsModule.addVault()` * */ error ZeroAddress(); /** * @notice Error when an input has unexpected zero bytes32 ID. * * Cases: * - `FeesModule.initializeFeesModule()` * - `FeesModule.setGratefulFeeTreasury()` * - `VaultsModule.addVault()` * */ error ZeroId(); /** * @notice Error when an input has unexpected zero uint for time. * * Cases: * - `ConfigModule.initializeConfigModule()` * - `ConfigModule.setSolvencyTimeRequired()` * - `ConfigModule.setLiquidationTimeRequired()` * */ error ZeroTime(); /** * @notice Error when trying to initialize a module that has already been. * * Cases: * - `ConfigModule.initializeConfigModule()` * - `FeesModule.initializeFeesModule()` * - `VaultModule.addVault()` * */ error AlreadyInitialized(); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Profile related errors. */ library ProfileErrors { /** * @notice Error when providing a profile address that is not allowed by the system. * * Thrown in: * - `ProfileUtil.validateExistenceAndGetProfile()` * * Cases: * - `FundsModule.depositFunds()` * - `SubscriptionsModule.subscribe()` * - `SubscriptionsModule.unsubscribe()` * */ error InvalidProfile(); /** * @notice Error when a token ID from a profile has no owner. * * Thrown in: * - `ProfileUtil.validateExistenceAndGetProfile()` * * Cases: * - `FundsModule.depositFunds()` * - `SubscriptionsModule.subscribe()` * - `SubscriptionsModule.unsubscribe()` * */ error NonExistentProfile(); /** * @notice Error when a user is not approved or owner for a profile token ID. * * Thrown in: * - `ProfileUtil.validateAllowanceAndGetProfile()` * * Cases: * - `FundsModule.withdrawFunds()` * - `SubscriptionsModule.subscribe()` * - `SubscriptionsModule.unsubscribe()` * */ error UnauthorizedProfile(); /** * @notice Thrown when a profile attempts to renounce a permission that it didn't have. * * Cases: * - `ProfilesModules.renouncePermission()` * */ error PermissionNotGranted(); /** * @dev Thrown when the given target address does not have the given permission with the given profile. * * Thrown in: * - `Profile.loadProfileAndValidatePermission()` * * Cases: * - `FundsModule.withdrawFunds()` * - `SubscriptionsModule.subscribe()` * - `SubscriptionsModule.unsubscribe()` * */ error PermissionDenied(); /** * @dev Thrown when a profile cannot be found. * * Thrown in: * - `Profile.exists()` * * Cases: * - `FundsModule.depositFunds()` * - `SubscriptionsModule.subscribe()` * - `SubscriptionsModule.unsubscribe()` * */ error ProfileNotFound(); /** * @dev Thrown when a permission specified by a user does not exist or is invalid. * * Cases: * - `ProfilesModules.grantPermission()` * */ error InvalidPermission(); /** * @notice Thrown when the profile interacting with the system is expected to be the associated profile token, but is not. * * Cases: * - `ProfilesModules.notifyProfileTransfer()` * */ error OnlyGratefulProfileProxy(); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Subscriptions related errors. */ library SubscriptionErrors { /** * @notice Error when providing a creator that is similar to the giver or the treasury ID. * * Cases: * - `LiquidationsModule.liquidate()` * - `SubscriptionsModule.subscribe()` * - `SubscriptionsModule.unsubscribe()` * */ error InvalidCreator(); /** * @notice Error when providing a subscription rate that is not valid for the selected vault. * * Cases: * - `SubscriptionsModule.subscribe()` * */ error InvalidRate(); /** * @notice Error when trying to subscribe to a creator that already has an open subscription from the giver. * * Cases: * - `SubscriptionsModule.subscribe()` * */ error AlreadySubscribed(); /** * @notice Error when trying to unsubscribe from a creator that not has an open subscription from the giver. * * Cases: * - `LiquidationsModule.liquidate()` * - `SubscriptionsModule.unsubscribe()` * */ error NotSubscribed(); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Vault related errors. */ library VaultErrors { /** * @notice Error when trying to change a vault that has not been initialized. * * Cases: * - `VaultsModule._validateVaultPermissions()` * */ error VaultNotInitialized(); /** * @notice Error when trying to use a vault that is not active (not initialized or paused). * * Cases: * - `FundsModule.depositFunds()` * - `FundsModule.withdrawFunds()` * - `SubscriptionModule.subscribe()` * */ error InvalidVault(); /** * @notice Error when trying to deposit into a vault but the user has not allow the token to the system. * * Cases: * - `FundsModule.depositFunds()` * */ error InsufficientAllowance(); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Module for getting balances data. */ interface IBalancesModule { /************************************************************************** * View functions *************************************************************************/ /** * @notice Return the current profile balance from a vault * @param profileId The profile to return the balance * @param vaultId The vault from where return the balance * @return The current balance */ function balanceOf( bytes32 profileId, bytes32 vaultId ) external view returns (int256); /** * @notice Return the current profile flow from a vault * @param profileId The profile to return the flow * @param vaultId The vault from where return the flow * @return The current flow */ function getFlow( bytes32 profileId, bytes32 vaultId ) external view returns (int256); /** * @notice Check if a profile can be liquidated from a vault * @param profileId The profile to check the liquidation * @param vaultId The vault from where to check the liquidation * @return If profile/vault is liquidable */ function canBeLiquidated( bytes32 profileId, bytes32 vaultId ) external view returns (bool); /** * @notice Return the profile remaining time to zero balance from a vault * @param profileId The profile to return the remaining time * @param vaultId The vault from where return the remaining time * @return Time left */ function getRemainingTimeToZero( bytes32 profileId, bytes32 vaultId ) external view returns (uint256); /** * @notice Return the current profile balance data from a vault * @dev It bundle all balance data in one function * @param profileId The profile to return the current data * @param vaultId The vault from where return the current data * @return balance Current balance * @return flow Current flow * @return liquidable If profile/vault is liquidable * @return timeLeft Time left to zero balance */ function getBalanceCurrentData( bytes32 profileId, bytes32 vaultId ) external view returns ( int256 balance, int256 flow, bool liquidable, uint256 timeLeft ); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Module for depositing and withdrawing users funds. */ interface IFundsModule { /************************************************************************** * User functions *************************************************************************/ /** * @notice Deposit funds into Grateful. * * Requirements: * * - Only profiles NFTs allowed on the system * - Only existing profile ID * - Only vaults initialized into the system * - Emits `FundsDeposited` event * * @param profileId The profile ID to make a deposit * @param vaultId The vault ID to deposit into * @param amount The amount of the vault asset to deposit */ function depositFunds( bytes32 profileId, bytes32 vaultId, uint256 amount ) external; /** * @notice Withdraw funds from Grateful. * * Requirements: * * - Only profiles NFTs allowed on the system * - Sender must have withdraw role authorized * - Only vaults initialized into the system * - Profile ID must have enough vault balance to withdraw * - Profile ID must have enough vault balance to remain solvent for `solvencyTimeRequired` * - Emits `FundsWithdrawn` event * * @param profileId The profile ID to make a withdrawal * @param vaultId The vault ID to withdraw from * @param shares The amount of vault shares to withdraw */ function withdrawFunds( bytes32 profileId, bytes32 vaultId, uint256 shares ) external; /************************************************************************** * Events *************************************************************************/ /** * @notice Emits the funds deposited from a profile into a vault * @param profileId The profile ID that made the deposit * @param vaultId The vault that the deposit where made into * @param amount The vault asset amount that was deposited * @param shares The shares minted from the vault (normalized to 20 decimals) */ event FundsDeposited( bytes32 indexed profileId, bytes32 indexed vaultId, uint256 amount, uint256 shares ); /** * @notice Emits the funds withdrawn from a profile from a vault * @param profileId The profile ID that made the withdrawal * @param vaultId The vault that received the withdrawal * @param shares The vault shares amount that were withdrawn * @param amountWithdrawn The vault asset amount that was withdrawn */ event FundsWithdrawn( bytes32 indexed profileId, bytes32 indexed vaultId, uint256 shares, uint256 amountWithdrawn ); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Module with ERC721 logic for the grateful subscription. */ interface IGratefulSubscription { /************************************************************************** * Governance functions *************************************************************************/ /** * @notice Initialize the NFT * @dev Only owner / Token must not be initialized * @param tokenName The NFT token name * @param tokenSymbol The NFT token symbol * @param uri The NFT uri */ function initialize( string memory tokenName, string memory tokenSymbol, string memory uri ) external; /** * @notice Mint token to user and increment counter * @dev Only owner / Token must be initialized * @param to Address to mint the NFT */ function mint(address to) external; /************************************************************************** * View functions *************************************************************************/ /** * @notice Get the current subscriptions token ID * @return The current token ID */ function getCurrentTokenId() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Module for liquidating unsolvent suscriptions. */ interface ILiquidationsModule { /************************************************************************** * User functions *************************************************************************/ /** * @notice Liquidate subscription from giver to creator. * * Requirements: * * - Giver and creator cannot be the same * - Creator cannot be Grateful treasury * - Giver must be subscribed to creator * - Only vaults initialized into the system * - Giver vault balance must be liquidable (the balance to remain solvent is less then `liquidationTimeRequired`) * - Emits a `SubscriptionFinished` and `SubscriptionLiquidated` events * * @param giverId The giver ID from the subscription to liquidate * @param creatorId The creator ID from the subscription to liquidate * @param liquidatorId The liquidator ID to send the liquidation rewards * */ function liquidate( bytes32 giverId, bytes32 creatorId, bytes32 liquidatorId ) external; /************************************************************************** * Events *************************************************************************/ /** * @notice Emits the data from the liquidated subscription * @param giverId The ID from the profile that was subscribed * @param creatorId The ID from the profile that was receiving the subscription * @param liquidatorId The ID from the profile that liquidated the subscription * @param vaultId The vault being used in the subscription * @param subscriptionId The subscription ID from the Grateful Subscription NFT * @param reward The reward that the liquidator receive * @param surplus The surplus from the balance that was compensated (if any) */ event SubscriptionLiquidated( bytes32 indexed giverId, bytes32 indexed creatorId, bytes32 indexed liquidatorId, bytes32 vaultId, uint256 subscriptionId, uint256 reward, uint256 surplus ); // Note: Duplicated event until library events are exportable (https://github.com/ethereum/solidity/pull/10996) /** * @notice Emits the data from the finished subscription * @param giverId The ID from the profile that was subscribed * @param creatorId The ID from the profile that was receiving the subscription * @param vaultId The vault being used in the subscription * @param subscriptionId The subscription ID from the Grateful Subscription NFT * @param rate The subscription rate that was going to the creator (1e-20/second) * @param feeRate The fee rate that was going to the treasury (1e-20/second) */ event SubscriptionFinished( bytes32 indexed giverId, bytes32 indexed creatorId, bytes32 indexed vaultId, uint256 subscriptionId, uint256 rate, uint256 feeRate ); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {Subscription} from "../storage/Subscription.sol"; /** * @title Module for starting and finishing subscription. */ interface ISubscriptionsModule { /************************************************************************** * User functions *************************************************************************/ /** * @notice Start subscription from giver to creator. * * Requirements: * * - Only profiles NFTs allowed on the system * - Sender must have subscribe role authorized * - Only existing creator profile ID * - Giver and creator cannot be the same * - Creator cannot be Grateful treasury * - Giver cannot be already subscribed to creator * - Only vaults initialized into the system * - Rate must be valid (between min and max vault rate) * - Subscription NFT will be minted to current giver profile owner * - Giver profile ID must have enough vault balance to start a subscription * - Giver profile ID must have enough vault balance to remain solvent for `solvencyTimeRequired` after subscription starts * - Emits a `SubscriptionStarted` event * * @param giverId The giver profile ID * @param creatorId The creator profile ID * @param vaultId The vault ID from where start the subscription * @param subscriptionRate The subscription rate from giver balance to creator balance (1e-20/second) * */ function subscribe( bytes32 giverId, bytes32 creatorId, bytes32 vaultId, uint256 subscriptionRate ) external; /** * @notice End subscription from giver to creator. * * Requirements: * * - Only profiles NFTs allowed on the system * - Sender must have unsubscribe role authorized * - Only existing creator profile ID * - Giver and creator cannot be the same * - Creator cannot be Grateful treasury * - Giver must be subscribed to creator * - Emits `SubscriptionFinished` event * * @param giverId The giver profile ID * @param creatorId The creator profile ID */ function unsubscribe(bytes32 giverId, bytes32 creatorId) external; /************************************************************************** * View functions *************************************************************************/ /** * @notice Return subscription data from a subscription ID * @dev The subscription ID is a token ID from the Grateful Subcription NFT * @param subscriptionId The ID from where return the subscription data * @return subscription The subscription struct data */ function getSubscription( uint256 subscriptionId ) external pure returns (Subscription.Data memory subscription); /** * @notice Return subscription data from giver and creator IDs * @param giverId The ID from where the subscription was created * @param creatorId The ID from whom is receiving the subscription * @return subscription The subscription struct data */ function getSubscriptionFrom( bytes32 giverId, bytes32 creatorId ) external view returns (Subscription.Data memory subscription); /** * @notice Return the subscription ID from giver and creator IDs * @param giverId The ID from where the subscription was created * @param creatorId The ID from whom is receiving the subscription * @return The subscription ID */ function getSubscriptionId( bytes32 giverId, bytes32 creatorId ) external view returns (uint256); /** * @notice Return the subscription and fee rates from a subscription ID * @param subscriptionId The ID from where return the subscription data * @return Subscription rate and fee rate */ function getSubscriptionRates( uint256 subscriptionId ) external view returns (uint256, uint256); /** * @notice Return if a subscription is active from giver to creator * @param giverId The ID from where the subscription was created * @param creatorId The ID from whom is receiving the subscription * @return Subscribed flag */ function isSubscribed( bytes32 giverId, bytes32 creatorId ) external view returns (bool); /** * @notice Return the total subscription duration since it was created * @param subscriptionId The ID from where return the subscription data * @return The current duration */ function getSubscriptionDuration( uint256 subscriptionId ) external view returns (uint256); /************************************************************************** * Events *************************************************************************/ /** * @notice Emits the data from the started subscription * @param giverId The ID from the profile starting the subscription * @param creatorId The ID from the profile receiving the subscription * @param vaultId The vault using in the subscription * @param subscriptionId The subscription ID from the Grateful Subscription NFT * @param rate The subscription rate going to the creator (1e-20/second) * @param feeRate The fee rate going to the treasury (1e-20/second) */ event SubscriptionStarted( bytes32 indexed giverId, bytes32 indexed creatorId, bytes32 indexed vaultId, uint256 subscriptionId, uint256 rate, uint256 feeRate ); // Note: Duplicated event until library events are exportable (https://github.com/ethereum/solidity/pull/10996) /** * @notice Emits the data from the finished subscription * @param giverId The ID from the profile that was subscribed * @param creatorId The ID from the profile that was receiving the subscription * @param vaultId The vault being used in the subscription * @param subscriptionId The subscription ID from the Grateful Subscription NFT * @param rate The subscription rate that was going to the creator (1e-20/second) * @param feeRate The fee rate that was going to the treasury (1e-20/second) */ event SubscriptionFinished( bytes32 indexed giverId, bytes32 indexed creatorId, bytes32 indexed vaultId, uint256 subscriptionId, uint256 rate, uint256 feeRate ); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {IGratefulSubscription} from "../../interfaces/IGratefulSubscription.sol"; import {NftModule} from "@synthetixio/core-modules/contracts/modules/NftModule.sol"; import {ERC721} from "@synthetixio/core-contracts/contracts/token/ERC721.sol"; import {OwnableStorage} from "@synthetixio/core-contracts/contracts/ownership/OwnableStorage.sol"; import {SubscriptionNft} from "../../storage/SubscriptionNft.sol"; import {Base64} from "@openzeppelin/contracts/utils/Base64.sol"; /** * @title Module with ERC721 logic for the grateful subscription. * @dev See IGratefulSubscription */ contract GratefulSubscription is IGratefulSubscription, ERC721 { using SubscriptionNft for SubscriptionNft.Data; /// @inheritdoc IGratefulSubscription function initialize( string memory tokenName, string memory tokenSymbol, string memory uri ) public override { OwnableStorage.onlyOwner(); SubscriptionNft.load().incrementCounter(); _initialize(tokenName, tokenSymbol, uri); } /// @inheritdoc IGratefulSubscription function mint(address to) external override { OwnableStorage.onlyOwner(); SubscriptionNft.Data storage store = SubscriptionNft.load(); uint256 tokenId = store.tokenIdCounter; store.incrementCounter(); _mint(to, tokenId); } /// @inheritdoc IGratefulSubscription function getCurrentTokenId() external view override returns (uint256) { return SubscriptionNft.load().tokenIdCounter; } function tokenURI( uint256 tokenId ) external view virtual override returns (string memory) { string memory _image = Base64.encode(bytes(_generateImage())); return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( abi.encodePacked( '{"name":"', "Grateful Protocol Subscription", '", "description":"', "This NFT represents a subscription from giver to creator", '", "image": "data:image/svg+xml;base64,', _image, '"}' ) ) ) ) ); } function _generateImage() private pure returns (string memory) { return string.concat( '<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300">', "<style>.center{position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);}.pulse{width: 3.125rem;height: 3.125rem;background: linear-gradient(90deg, #ff6419 0%, #fa6b3e 36.98%, #ef7a93 74.48%, #eb80b5 100%);filter: blur(0.6rem);border-radius: 50%;animation: animate-pulse 1s linear infinite;}@keyframes animate-pulse {0% {box-shadow: 0 0 0 0 rgba(255, 109, 74, 0.7), 0 0 0 0 rgba(255, 109, 74, 0.7);}40% {box-shadow: 0 0 0 3.125rem rgba(255, 109, 74, 0), 0 0 0 0 rgba(255, 109, 74, 0.7);}80% {box-shadow: 0 0 0 3.125rem rgba(255, 109, 74, 0), 0 0 0 1.875rem rgba(255, 109, 74, 0);}100% {box-shadow: 0 0 0 0 rgba(255, 109, 74, 0), 0 0 0 1.875rem rgba(255, 109, 74, 0);}}</style>", '<div class="center"><div class="pulse"/></div>', "</svg>" ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {IBalancesModule} from "../interfaces/IBalancesModule.sol"; import {Balance} from "../storage/Balance.sol"; /** * @title Module for getting balances data. * @dev See IBalancesModule. */ contract BalancesModule is IBalancesModule { using Balance for Balance.Data; /// @inheritdoc IBalancesModule function balanceOf( bytes32 profileId, bytes32 vaultId ) external view override returns (int256) { return Balance.load(profileId, vaultId).balanceOf(); } /// @inheritdoc IBalancesModule function getFlow( bytes32 profileId, bytes32 vaultId ) external view override returns (int256) { return Balance.load(profileId, vaultId).flow; } /// @inheritdoc IBalancesModule function canBeLiquidated( bytes32 profileId, bytes32 vaultId ) external view override returns (bool) { return Balance.load(profileId, vaultId).canBeLiquidated(); } /// @inheritdoc IBalancesModule function getRemainingTimeToZero( bytes32 profileId, bytes32 vaultId ) external view override returns (uint256) { return Balance.load(profileId, vaultId).remainingTimeToZero(); } /// @inheritdoc IBalancesModule function getBalanceCurrentData( bytes32 profileId, bytes32 vaultId ) external view override returns (int256 balance, int256 flow, bool liquidable, uint256 timeLeft) { Balance.Data storage store = Balance.load(profileId, vaultId); balance = store.balanceOf(); flow = store.flow; liquidable = store.canBeLiquidated(); timeLeft = store.remainingTimeToZero(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {IFundsModule} from "../interfaces/IFundsModule.sol"; import {VaultUtil} from "../utils/VaultUtil.sol"; import {InputErrors} from "../errors/InputErrors.sol"; import {VaultErrors} from "../errors/VaultErrors.sol"; import {BalanceErrors} from "../errors/BalanceErrors.sol"; import {Balance} from "../storage/Balance.sol"; import {Profile} from "../storage/Profile.sol"; import {ProfileRBAC} from "../storage/ProfileRBAC.sol"; /** * @title Module for depositing and withdrawing users funds. * @dev See IFundsModule. */ contract FundsModule is IFundsModule { using Balance for Balance.Data; using Profile for Profile.Data; /// @inheritdoc IFundsModule function depositFunds( bytes32 profileId, bytes32 vaultId, uint256 amount ) external { if (amount == 0) revert InputErrors.ZeroAmount(); if (!VaultUtil.isVaultActive(vaultId)) revert VaultErrors.InvalidVault(); Profile.exists(profileId); uint256 shares = VaultUtil.deposit(vaultId, amount); Balance.load(profileId, vaultId).increase(shares); emit FundsDeposited(profileId, vaultId, amount, shares); } /// @inheritdoc IFundsModule function withdrawFunds( bytes32 profileId, bytes32 vaultId, uint256 shares ) external { if (shares == 0) revert InputErrors.ZeroAmount(); if (!VaultUtil.isVaultActive(vaultId)) revert VaultErrors.InvalidVault(); Profile.loadProfileAndValidatePermission( profileId, ProfileRBAC._WITHDRAW_PERMISSION ); Balance.Data storage store = Balance.load(profileId, vaultId); int256 balance = store.settle(); if (balance < 0 || uint256(balance) < shares) revert BalanceErrors.InsufficientBalance(); store.decrease(shares); if (!store.canWithdraw()) revert BalanceErrors.InsolventUser(); uint256 amountWithdrawn = VaultUtil.withdraw(vaultId, shares); emit FundsWithdrawn(profileId, vaultId, shares, amountWithdrawn); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {ILiquidationsModule} from "../interfaces/ILiquidationsModule.sol"; import {SubscriptionUtil} from "../utils/SubscriptionUtil.sol"; import {SubscriptionErrors} from "../errors/SubscriptionErrors.sol"; import {BalanceErrors} from "../errors/BalanceErrors.sol"; import {Balance} from "../storage/Balance.sol"; import {SubscriptionId} from "../storage/SubscriptionId.sol"; import {Subscription} from "../storage/Subscription.sol"; import {Fee} from "../storage/Fee.sol"; import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol"; /** * @title Module for liquidating unsolvent suscriptions. * @dev See ILiquidationsModule. */ contract LiquidationsModule is ILiquidationsModule { using SignedMath for int256; using Balance for Balance.Data; using SubscriptionId for SubscriptionId.Data; using Subscription for Subscription.Data; using Fee for Fee.Data; /// @inheritdoc ILiquidationsModule function liquidate( bytes32 giverId, bytes32 creatorId, bytes32 liquidatorId ) external override { bytes32 treasury = Fee.load().gratefulFeeTreasury; if (giverId == creatorId || creatorId == treasury) revert SubscriptionErrors.InvalidCreator(); if (!SubscriptionId.load(giverId, creatorId).isSubscribed()) revert SubscriptionErrors.NotSubscribed(); bytes32 vaultId = SubscriptionId .load(giverId, creatorId) .getSubscriptionData() .vaultId; if (!Balance.load(giverId, vaultId).canBeLiquidated()) revert BalanceErrors.SolventUser(); (uint256 subscriptionId, , , uint256 surplus) = _liquidateSubscription( giverId, creatorId, vaultId ); emit SubscriptionLiquidated( giverId, creatorId, liquidatorId, vaultId, subscriptionId, 0, surplus ); } function _liquidateSubscription( bytes32 giverId, bytes32 creatorId, bytes32 vaultId ) private returns ( uint256 subscriptionId, uint256 subscriptionRate, uint256 feeRate, uint256 surplus ) { // Get current flow int256 flow = Balance.load(giverId, vaultId).flow; // Finish subscription (subscriptionId, subscriptionRate, feeRate, ) = SubscriptionUtil .finishSubscription(giverId, creatorId); // Check if balance is negative and compensate if necessary if (Balance.load(giverId, vaultId).isNegative()) { surplus = _settleLostBalance( giverId, creatorId, vaultId, subscriptionRate, feeRate, flow ); } } function _settleLostBalance( bytes32 giverId, bytes32 creatorId, bytes32 vaultId, uint256 rate, uint256 feeRate, int256 flow ) private returns (uint256 surplus) { // Get absolute values uint256 absoluteBalance = Balance .load(giverId, vaultId) .balanceOf() .abs(); uint256 totalFlow = flow.abs(); // Decrease creator balance surplus uint256 subscriptionRateSurplus = (rate * absoluteBalance) / totalFlow; Balance.load(creatorId, vaultId).decrease(subscriptionRateSurplus); // Decrease treasury balance surplus uint256 feeRateSurplus = (feeRate * absoluteBalance) / totalFlow; bytes32 treasury = Fee.load().gratefulFeeTreasury; Balance.load(treasury, vaultId).decrease(feeRateSurplus); // Increase giver balance total surplus surplus = ((rate + feeRate) * absoluteBalance) / totalFlow; Balance.load(giverId, vaultId).increase(surplus); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {ISubscriptionsModule} from "../interfaces/ISubscriptionsModule.sol"; import {SubscriptionUtil} from "../utils/SubscriptionUtil.sol"; import {VaultUtil} from "../utils/VaultUtil.sol"; import {SubscriptionErrors} from "../errors/SubscriptionErrors.sol"; import {VaultErrors} from "../errors/VaultErrors.sol"; import {BalanceErrors} from "../errors/BalanceErrors.sol"; import {Balance} from "../storage/Balance.sol"; import {Subscription} from "../storage/Subscription.sol"; import {SubscriptionId} from "../storage/SubscriptionId.sol"; import {Fee} from "../storage/Fee.sol"; import {IGratefulSubscription} from "../interfaces/IGratefulSubscription.sol"; import {AssociatedSystem} from "@synthetixio/core-modules/contracts/storage/AssociatedSystem.sol"; import {Profile} from "../storage/Profile.sol"; import {ProfileRBAC} from "../storage/ProfileRBAC.sol"; /** * @title Module for starting and finishing subscriptions. * @dev See ISubscriptionsModule. */ contract SubscriptionsModule is ISubscriptionsModule { using Balance for Balance.Data; using Subscription for Subscription.Data; using SubscriptionId for SubscriptionId.Data; using Fee for Fee.Data; using AssociatedSystem for AssociatedSystem.Data; using Profile for Profile.Data; bytes32 private constant _GRATEFUL_SUBSCRIPTION_NFT = "gratefulSubscriptionNft"; /// @inheritdoc ISubscriptionsModule function subscribe( bytes32 giverId, bytes32 creatorId, bytes32 vaultId, uint256 subscriptionRate ) external { if (!VaultUtil.isVaultActive(vaultId)) revert VaultErrors.InvalidVault(); Profile.Data storage profile = Profile.loadProfileAndValidatePermission( giverId, ProfileRBAC._SUBSCRIBE_PERMISSION ); address owner = profile.rbac.owner; Profile.exists(creatorId); bytes32 treasury = Fee.load().gratefulFeeTreasury; if (giverId == creatorId || creatorId == treasury) revert SubscriptionErrors.InvalidCreator(); if (SubscriptionId.load(giverId, creatorId).isSubscribed()) revert SubscriptionErrors.AlreadySubscribed(); if (!VaultUtil.isRateValid(vaultId, subscriptionRate)) revert SubscriptionErrors.InvalidRate(); uint256 rate = VaultUtil.getCurrentRate(vaultId, subscriptionRate); ( uint256 subscriptionId, uint256 feeRate, uint256 totalRate ) = _startSubscription(giverId, creatorId, vaultId, rate, owner); if (!Balance.load(giverId, vaultId).canStartSubscription(totalRate)) revert BalanceErrors.InsolventUser(); emit SubscriptionStarted( giverId, creatorId, vaultId, subscriptionId, rate, feeRate ); } /** * @dev Starts a subscription. * * This function is used from a user. * * Updates the balances flows (giver, creator and treasury). * * If the subscription between giver and creator already exist, then the subscription is * updated to the new rate, else the subscription is created for the first time. * * To create a new subscription means to mint a new token ID from the Grateful subscription NFT. */ function _startSubscription( bytes32 giverId, bytes32 creatorId, bytes32 vaultId, uint256 subscriptionRate, address profileOwner ) private returns (uint256 subscriptionId, uint256 feeRate, uint256 totalRate) { // Calculate fee rate feeRate = Fee.load().getFeeRate(subscriptionRate); // Decrease giver flow totalRate = subscriptionRate + feeRate; Balance.load(giverId, vaultId).decreaseFlow(totalRate); // Increase creator flow Balance.load(creatorId, vaultId).increaseFlow(subscriptionRate); // Increase treasury flow with feeRate bytes32 treasury = Fee.load().gratefulFeeTreasury; Balance.load(treasury, vaultId).increaseFlow(feeRate); if (SubscriptionId.load(giverId, creatorId).exists()) { subscriptionId = _updateSubscription( giverId, creatorId, vaultId, subscriptionRate, feeRate ); } else { subscriptionId = _createSubscription( giverId, creatorId, vaultId, subscriptionRate, feeRate, profileOwner ); } } /** * @dev Creates a subscription. * * This function is used from a user. * * Gets the next token ID from the subscription NFT. * * Saves the subscription data from this ID, and links it with the giver and creator. * * A new token ID is minted to the giver profile owner. */ function _createSubscription( bytes32 giverId, bytes32 creatorId, bytes32 vaultId, uint256 subscriptionRate, uint256 feeRate, address profileOwner ) private returns (uint256 subscriptionId) { // Get subscription ID from subscription NFT IGratefulSubscription gs = IGratefulSubscription( AssociatedSystem.load(_GRATEFUL_SUBSCRIPTION_NFT).proxy ); subscriptionId = gs.getCurrentTokenId(); // Start subscription Subscription.Data storage subscription = Subscription.load( subscriptionId ); subscription.start(subscriptionRate, feeRate, creatorId, vaultId); // Link subscription ID with subscription data SubscriptionId.load(giverId, creatorId).set(subscriptionId); // Mint subscription NFT to giver profile owner gs.mint(profileOwner); } /** * @dev Updates a subscription. * * This function is used from a user. * * Loads the subscription data from the giver and creator. * * Updates the subscription data with the new rates and vault. * * No new subscription token is minted, the already created token is reused. */ function _updateSubscription( bytes32 giverId, bytes32 creatorId, bytes32 vaultId, uint256 subscriptionRate, uint256 feeRate ) private returns (uint256 subscriptionId) { // Get subscription data subscriptionId = SubscriptionId.load(giverId, creatorId).subscriptionId; Subscription.Data storage subscription = Subscription.load( subscriptionId ); // Update subscription subscription.update(subscriptionRate, feeRate, vaultId); } /// @inheritdoc ISubscriptionsModule function unsubscribe(bytes32 giverId, bytes32 creatorId) external { Profile.loadProfileAndValidatePermission( giverId, ProfileRBAC._UNSUBSCRIBE_PERMISSION ); Profile.exists(creatorId); bytes32 treasury = Fee.load().gratefulFeeTreasury; if (giverId == creatorId || creatorId == treasury) revert SubscriptionErrors.InvalidCreator(); if (!SubscriptionId.load(giverId, creatorId).isSubscribed()) revert SubscriptionErrors.NotSubscribed(); SubscriptionUtil.finishSubscription(giverId, creatorId); } /// @inheritdoc ISubscriptionsModule function getSubscription( uint256 subscriptionId ) external pure override returns (Subscription.Data memory subscription) { return Subscription.load(subscriptionId); } /// @inheritdoc ISubscriptionsModule function getSubscriptionFrom( bytes32 giverId, bytes32 creatorId ) external view override returns (Subscription.Data memory subscription) { return SubscriptionId.load(giverId, creatorId).getSubscriptionData(); } /// @inheritdoc ISubscriptionsModule function getSubscriptionId( bytes32 giverId, bytes32 creatorId ) external view override returns (uint256) { return SubscriptionId.load(giverId, creatorId).subscriptionId; } /// @inheritdoc ISubscriptionsModule function getSubscriptionRates( uint256 subscriptionId ) external view override returns (uint256, uint256) { return ( Subscription.load(subscriptionId).rate, Subscription.load(subscriptionId).feeRate ); } /// @inheritdoc ISubscriptionsModule function isSubscribed( bytes32 giverId, bytes32 creatorId ) external view override returns (bool) { return SubscriptionId.load(giverId, creatorId).isSubscribed(); } /// @inheritdoc ISubscriptionsModule function getSubscriptionDuration( uint256 subscriptionId ) external view override returns (uint256) { return Subscription.load(subscriptionId).getDuration(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol"; import {Config} from "../storage/Config.sol"; /** * @title Stores the balance and flow for a profile ID and vault ID tuple. * * Each profile has a different balance for each vault. */ library Balance { using SafeCast for uint256; using SafeCast for int256; using SignedMath for int256; using Config for Config.Data; struct Data { /** * @dev Amount of settled balance. * * Vault balance is normalized to 20 decimals. * * This can be increase or decrease during depositing or withdrawing funds, * or also after settling the elapsed time due to a flow change. * * The system accepts negative balances if the subscriptions were not liquidated at time. */ int256 balance; /** * @dev Current balance flow. * * Flow unit is 1e-20 per second. * * If the flow is positive, the balance is increasing each second. * If the flow is negative, the balance is decreasing each second. */ int216 flow; /** * @dev Last time balance was updated. * * This is used to calculate the current balance. */ uint40 lastUpdate; } /** * @dev Loads the balance for the profile/vault tuple. */ function load( bytes32 profileId, bytes32 vaultId ) internal pure returns (Data storage store) { bytes32 s = keccak256(abi.encode("Balance", profileId, vaultId)); assembly { store.slot := s } } /** * @dev Increases the settled balance by `amount`. */ function increase(Data storage self, uint256 amount) internal { self.balance += amount.toInt256(); } /** * @dev Decreases the settled balance by `amount`. */ function decrease(Data storage self, uint256 amount) internal { self.balance -= amount.toInt256(); } /** * @dev Calculates the current balance, stores it and returns it. */ function settle(Data storage self) internal returns (int256 newBalance) { newBalance = balanceOf(self); self.balance = newBalance; self.lastUpdate = (block.timestamp).toUint40(); } /** * @dev Increases the current flow by `amount`. */ function increaseFlow(Data storage self, uint256 amount) internal { settle(self); self.flow += (amount.toInt256()).toInt216(); } /** * @dev Decreases the current flow by `amount`. */ function decreaseFlow(Data storage self, uint256 amount) internal { settle(self); self.flow -= (amount.toInt256()).toInt216(); } /** * @dev Returns the current balance since last update. */ function balanceOf( Data storage self ) internal view returns (int256 balance) { uint256 elapsedTime = _getElapsedTime(self.lastUpdate); balance = _calculateBalance(self.balance, self.flow, elapsedTime); } /** * @dev Returns the elapsed time since `lastUpdate`. */ function _getElapsedTime( uint256 lastUpdate ) private view returns (uint256 elapsedTime) { if (lastUpdate == 0) return 0; if (block.timestamp <= lastUpdate) return 0; elapsedTime = block.timestamp - lastUpdate; } /** * @dev Calculates the current balance: `balance` + (`flow` * `time`) */ function _calculateBalance( int256 balance, int256 flow, uint256 time ) private pure returns (int256 currentBalance) { int256 totalFlow = flow * time.toInt256(); currentBalance = balance + totalFlow; } /** * @dev Returns if the balance is solvent for a given `time`. * * It adds the input time to the current elapsed time to calculate if the balance is positive in a future time. */ function _isSolvent( Data storage self, uint256 time ) private view returns (bool) { uint256 futureElapsedTime = _getElapsedTime(self.lastUpdate) + time; return _calculateBalance(self.balance, self.flow, futureElapsedTime) > 0; } /** * @dev Returns if the profile with the current balance can make a withdrawal. * * To make a withdrawal the balance must be solvent for the required time. * * Uses the solvency time required from the system to evaluate the solvency. */ function canWithdraw(Data storage self) internal view returns (bool) { uint256 time = Config.load().solvencyTimeRequired; return _isSolvent(self, time); } /** * @dev Returns if the profile with the current balance can make a new subscription. * * To make a new subscription the current balance must cover the required time of `rate` * and also must be solvent for that lapse. * * Uses the solvency time required from the system to evaluate the solvency. */ function canStartSubscription( Data storage self, uint256 rate ) internal view returns (bool) { uint256 time = Config.load().solvencyTimeRequired; int256 balance = balanceOf(self); int256 requiredBalance = (rate * time).toInt256(); bool hasEnoughBalance = balance > requiredBalance; return hasEnoughBalance && _isSolvent(self, time); } /** * @dev Returns if the profile with the current balance can be liquidated. * * To be liquidated the balance must have negative flow and also not be solvent * for the required time. * * Uses the liquidation time required from the system to evaluate the solvency. */ function canBeLiquidated(Data storage self) internal view returns (bool) { uint256 time = Config.load().liquidationTimeRequired; bool hasNegativeFlow = self.flow < 0; return hasNegativeFlow && !_isSolvent(self, time); } /** * @dev Returns if the balance is negative */ function isNegative(Data storage self) internal view returns (bool) { int256 balance = balanceOf(self); return balance < 0; } /** * @dev Returns the remaining time to zero balance. * * If the flow is positive (or zero) or the balance is already negative (or zero), the time is zero. * * remainingTime = currentBalance / flow */ function remainingTimeToZero( Data storage self ) internal view returns (uint256) { int256 balance = balanceOf(self); int256 flow = self.flow; if (flow >= 0 || balance <= 0) { return 0; } else { return uint256(balance) / flow.abs(); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Stores the system configuration. */ library Config { bytes32 private constant _CONFIG_STORAGE_SLOT = keccak256(abi.encode("Config")); struct Data { /** * @dev Time required to remain solvent. * * This is used to know if a profile is allow to open new subscriptions or making withdrawals. * * If the profile balance does not cover this future time, it is insolvent. */ uint256 solvencyTimeRequired; /** * @dev Time required to allow making liquidations. * * This is used to know if a profile is in a liquidation period. * * If the profile balance does not cover this future time, it can be liquidated. */ uint256 liquidationTimeRequired; } /** * @dev Loads the singleton storage info about the system. */ function load() internal pure returns (Data storage store) { bytes32 s = _CONFIG_STORAGE_SLOT; assembly { store.slot := s } } /** * @dev Sets the system solvency time. */ function setSolvencyTimeRequired( Data storage self, uint256 solvencyTime ) internal { self.solvencyTimeRequired = solvencyTime; } /** * @dev Sets the system liquidation time. */ function setLiquidationTimeRequired( Data storage self, uint256 liquidationTime ) internal { self.liquidationTimeRequired = liquidationTime; } /** * @dev Returns if the config storage is initialized. */ function isInitialized(Data storage self) internal view returns (bool) { return self.liquidationTimeRequired != 0; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; /** * @title Stores the system fees configuration. * * There can only be one fee treasury. */ library Fee { using Math for uint256; bytes32 private constant _FEE_STORAGE_SLOT = keccak256(abi.encode("Fee")); struct Data { /** * @dev Treasury ID where to receive the fees. * * The treasury must be a grateful profile because the fees are treated like a subscription. */ bytes32 gratefulFeeTreasury; /** * @dev Fee percentage to be taken from a subscription rate. * * Can be zero if wanted. */ uint256 feePercentage; } /** * @dev Loads the singleton storage info about the fees. */ function load() internal pure returns (Data storage store) { bytes32 s = _FEE_STORAGE_SLOT; assembly { store.slot := s } } /** * @dev Sets the grateful fee treasury where fees are collected. */ function setGratefulFeeTreasury( Data storage self, bytes32 gratefulFeeTreasury ) internal { self.gratefulFeeTreasury = gratefulFeeTreasury; } /** * @dev Sets the fee percentage taken from the rate subscription. */ function setFeePercentage( Data storage self, uint256 feePercentage ) internal { self.feePercentage = feePercentage; } /** * @dev Returns if the fee storage is initialized. */ function isInitialized(Data storage self) internal view returns (bool) { return self.gratefulFeeTreasury != bytes32(0); } /** * @dev Returns the fee rate from a subscription rate. * * The fee rate is calculated as a percentage from the `subscriptionRate`. * * feeRate = (subscriptionRate * feePercentage) / 100 */ function getFeeRate( Data storage self, uint256 subscriptionRate ) internal view returns (uint256) { return subscriptionRate.mulDiv(self.feePercentage, 100); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {ProfileRBAC} from "./ProfileRBAC.sol"; import {ProfileErrors} from "../errors/ProfileErrors.sol"; /** * @title Object for tracking profiles with access control. */ library Profile { using ProfileRBAC for ProfileRBAC.Data; struct Data { /** * @dev Hash identifier for the profile. Must be unique. */ bytes32 id; /** * @dev Role based access control data for the profile. */ ProfileRBAC.Data rbac; } /** * @dev Returns the profile stored at the specified profile id. */ function load(bytes32 id) internal pure returns (Data storage store) { bytes32 s = keccak256(abi.encode("Profile", id)); assembly { store.slot := s } } /** * @dev Creates a profile for the given id, and associates it to the given owner. * * Note: Will not fail if the profile already exists, and if so, will overwrite the existing owner. * Whatever calls this internal function must first check that the profile doesn't exist before re-creating it. */ function create( bytes32 id, address owner ) internal returns (Data storage profile) { profile = load(id); profile.id = id; profile.rbac.owner = owner; } /** * @dev Reverts if the profile does not exist with appropriate error. Otherwise, returns the profile. */ function exists(bytes32 id) internal view { if (load(id).rbac.owner == address(0)) { revert ProfileErrors.ProfileNotFound(); } } /** * @dev Loads the Profile object for the specified profileId, * and validates that sender has the specified permission. These * are different actions but they are merged in a single function * because loading a profile and checking for a permission is a very * common use case in other parts of the code. */ function loadProfileAndValidatePermission( bytes32 profileId, bytes32 permission ) internal view returns (Data storage profile) { profile = load(profileId); if (!profile.rbac.authorized(permission, msg.sender)) { revert ProfileErrors.PermissionDenied(); } } /** * @dev Returns a profile ID. * * It is the hash from the profile NFT address and a token ID. * * It must be unique for the system. */ function getProfileId( address profile, uint256 tokenId ) internal pure returns (bytes32 profileId) { profileId = keccak256(abi.encode(profile, tokenId)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {SetUtil} from "@synthetixio/core-contracts/contracts/utils/SetUtil.sol"; import {ProfileErrors} from "../errors/ProfileErrors.sol"; import {InputErrors} from "../errors/InputErrors.sol"; /** * @title Object for tracking an profiles permissions (role based access control). */ library ProfileRBAC { using SetUtil for SetUtil.Bytes32Set; using SetUtil for SetUtil.AddressSet; /** * @dev All permissions used by the system * need to be hardcoded here. */ bytes32 internal constant _ADMIN_PERMISSION = "ADMIN"; bytes32 internal constant _WITHDRAW_PERMISSION = "WITHDRAW"; bytes32 internal constant _SUBSCRIBE_PERMISSION = "SUBSCRIBE"; bytes32 internal constant _UNSUBSCRIBE_PERMISSION = "UNSUBSCRIBE"; bytes32 internal constant _EDIT_PERMISSION = "EDIT"; struct Data { /** * @dev The owner of the profile and admin of all permissions. */ address owner; /** * @dev Set of permissions for each address enabled by the profile. */ mapping(address => SetUtil.Bytes32Set) permissions; /** * @dev Array of addresses that this profile has given permissions to. */ SetUtil.AddressSet permissionAddresses; } /** * @dev Reverts if the specified permission is unknown to the profile RBAC system. */ function isPermissionValid(bytes32 permission) internal pure { if ( permission != _ADMIN_PERMISSION && permission != _WITHDRAW_PERMISSION && permission != _SUBSCRIBE_PERMISSION && permission != _UNSUBSCRIBE_PERMISSION && permission != _EDIT_PERMISSION ) { revert ProfileErrors.InvalidPermission(); } } /** * @dev Sets the owner of the profile. */ function setOwner(Data storage self, address owner) internal { self.owner = owner; } /** * @dev Grants a particular permission to the specified target address. */ function grantPermission( Data storage self, bytes32 permission, address target ) internal { if (target == address(0)) { revert InputErrors.ZeroAddress(); } if (permission == "") { revert ProfileErrors.InvalidPermission(); } if (!self.permissionAddresses.contains(target)) { self.permissionAddresses.add(target); } self.permissions[target].add(permission); } /** * @dev Revokes a particular permission from the specified target address. */ function revokePermission( Data storage self, bytes32 permission, address target ) internal { self.permissions[target].remove(permission); if (self.permissions[target].length() == 0) { self.permissionAddresses.remove(target); } } /** * @dev Revokes all permissions for the specified target address. * @notice only removes permissions for the given address, not for the entire profile */ function revokeAllPermissions(Data storage self, address target) internal { bytes32[] memory permissions = self.permissions[target].values(); if (permissions.length == 0) { return; } for (uint256 i = 0; i < permissions.length; i++) { self.permissions[target].remove(permissions[i]); } self.permissionAddresses.remove(target); } /** * @dev Returns wether the specified address has the given permission. */ function hasPermission( Data storage self, bytes32 permission, address target ) internal view returns (bool) { return target != address(0) && self.permissions[target].contains(permission); } /** * @dev Returns wether the specified target address has the given permission, or has the high level admin permission. */ function authorized( Data storage self, bytes32 permission, address target ) internal view returns (bool) { return ((target == self.owner) || hasPermission(self, _ADMIN_PERMISSION, target) || hasPermission(self, permission, target)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; /** * @title Stores the subscription data for each subscription. * * A subscription ID is a token ID minted in the Grateful subscription NFT. * * A subscription between a giver and a creator is unique. */ library Subscription { using SafeCast for uint256; struct Data { /** * @dev The subscription rate being streamed from giver to creator. * * Rate unit is 1e-20 per second. */ uint256 rate; /** * @dev The fee rate being streamed from giver to treasury. * * Fee rate unit is 1e-20 per second. */ uint176 feeRate; /** * @dev The last time the subscription was updated. * * This is stored when starting, updating or finishing it. */ uint40 lastUpdate; /** * @dev The subscription total duration since creation. */ uint40 duration; /** * @dev The creator ID who is receiving the subscription. */ bytes32 creatorId; /** * @dev The vault balance that is being used in the subscription. */ bytes32 vaultId; } /** * @dev Loads the subscription data from a subscription ID. */ function load( uint256 subscriptionId ) internal pure returns (Data storage store) { bytes32 s = keccak256(abi.encode("Subscription", subscriptionId)); assembly { store.slot := s } } /** * @dev Starts a subscription. * * This is used the first time a subscription is created. */ function start( Data storage self, uint256 rate, uint256 feeRate, bytes32 creatorId, bytes32 vaultId ) internal { self.rate = rate; self.feeRate = feeRate.toUint176(); self.lastUpdate = (block.timestamp).toUint40(); self.creatorId = creatorId; self.vaultId = vaultId; } /** * @dev Updates a subscription. * * This is used if the giver wants to update the rate or the vault being used. * * Also is called if restarting an already created subscription. */ function update( Data storage self, uint256 rate, uint256 feeRate, bytes32 vaultId ) internal { self.rate = rate; self.feeRate = feeRate.toUint176(); self.lastUpdate = (block.timestamp).toUint40(); self.vaultId = vaultId; } /** * @dev Finishes a subscription. * * This is used when the giver wants to unsubscribe. */ function finish(Data storage self) internal { uint256 elapsedTime = block.timestamp - self.lastUpdate; self.rate = 0; self.feeRate = 0; self.lastUpdate = (block.timestamp).toUint40(); self.duration += (elapsedTime).toUint40(); } /** * @dev Returns if the subscription is active. */ function isSubscribed(Data storage self) internal view returns (bool) { return self.rate != 0; } /** * @dev Returns the current subscription duration. * * It is the stored duration plus the elapsed time since last update. */ function getDuration( Data storage self ) internal view returns (uint256 duration) { uint256 lastUpdate = self.lastUpdate; if (lastUpdate == 0) return 0; uint256 elapsedTime = block.timestamp - lastUpdate; duration = self.duration + (elapsedTime).toUint128(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {Subscription} from "./Subscription.sol"; /** * @title Stores the relation from a giver/creator to a subscription ID. */ library SubscriptionId { using Subscription for Subscription.Data; struct Data { /** * @dev The subscription ID that represents the subscription from giver to creator. * * A subscription ID is a token ID minted in the Grateful subscription NFT. * * A subscription ID between a giver and a creator is unique. */ uint256 subscriptionId; } /** * @dev Loads the subscription ID from the giver/creator tuple. */ function load( bytes32 giverId, bytes32 creatorId ) internal pure returns (Data storage store) { bytes32 s = keccak256(abi.encode("SubscriptionId", giverId, creatorId)); assembly { store.slot := s } } /** * @dev Sets the subscription ID. */ function set(Data storage self, uint256 subscriptionId) internal { self.subscriptionId = subscriptionId; } /** * @dev Gets the subscription data stored in the subscription storage. */ function getSubscriptionData( Data storage self ) internal view returns (Subscription.Data storage subscriptionData) { return Subscription.load(self.subscriptionId); } /** * @dev Returns if the subscription is active. */ function isSubscribed(Data storage self) internal view returns (bool) { return Subscription.load(self.subscriptionId).isSubscribed(); } /** * @dev Returns if the subscription already exists. */ function exists(Data storage self) internal view returns (bool) { return self.subscriptionId != 0; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Stores the data of the Grateful subscription NFT. */ library SubscriptionNft { bytes32 private constant _SUBSCRIPTION_NFT_STORAGE_SLOT = keccak256(abi.encode("SubscriptionNft")); struct Data { /** * @dev The current token ID from the NFT. * * This is then used as a subscription ID. */ uint256 tokenIdCounter; } /** * @dev Loads the singleton storage info about the Grateful subscription NFT. */ function load() internal pure returns (Data storage store) { bytes32 s = _SUBSCRIPTION_NFT_STORAGE_SLOT; assembly { store.slot := s } } /** * @dev Increments the current token ID counter. */ function incrementCounter(Data storage self) internal { self.tokenIdCounter++; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /** * @title Stores the vaults data used by the system. */ library Vault { struct Data { /** * @dev The vault address. * * Must be an ERC4626. */ address impl; /** * @dev The extra decimals to be used to normalize all vaults. * * Normalized vaults have 20 decimals. * * This is used to minimize precision errors. */ uint256 decimalsNormalizer; /** * @dev The minimum rate accepted by the vault. * * It is verified when a subcription is starting. */ uint256 minRate; /** * @dev The maximum rate accepted by the vault. * * It is verified when a subcription is starting. */ uint256 maxRate; /** * @dev Flag to pause the vault. */ bool paused; } /** * @dev Loads the configuration for a vault. * * Vault ID is setup when initializing a vault. */ function load(bytes32 id) internal pure returns (Data storage store) { bytes32 s = keccak256(abi.encode("Vault", id)); assembly { store.slot := s } } /** * @dev Sets the data for a vault. */ function set( Data storage self, address impl, uint256 decimalsNormalizer, uint256 minRate, uint256 maxRate ) internal { self.impl = impl; self.decimalsNormalizer = decimalsNormalizer; self.minRate = minRate; self.maxRate = maxRate; } /** * @dev Sets the minimum rate for a vault. */ function setMinRate(Data storage self, uint256 minRate) internal { self.minRate = minRate; } /** * @dev Sets the maximum rate for a vault. */ function setMaxRate(Data storage self, uint256 maxRate) internal { self.maxRate = maxRate; } /** * @dev Pauses a vault. */ function pause(Data storage self) internal { self.paused = true; } /** * @dev Unpauses a vault. */ function unpause(Data storage self) internal { self.paused = false; } /** * @dev Returns the vault implementation address. */ function getVault(Data storage self) internal view returns (address) { return self.impl; } /** * @dev Returns if a vault has been initialized. */ function isInitialized(Data storage self) internal view returns (bool) { return self.impl != address(0); } /** * @dev Returns if a vault is active to be used. */ function isActive(Data storage self) internal view returns (bool) { return self.impl != address(0) && !self.paused; } /** * @dev Returns if a subscription rate is valid for the current vault. */ function isRateValid( Data storage self, uint256 rate ) internal view returns (bool) { return (rate >= self.minRate) && (rate <= self.maxRate); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {Balance} from "../storage/Balance.sol"; import {Subscription} from "../storage/Subscription.sol"; import {SubscriptionId} from "../storage/SubscriptionId.sol"; import {Fee} from "../storage/Fee.sol"; /** * @title Utils for reusing subscriptions interactions. * * Use case: reusing finishing subscription logic for Subscriptions and Liquidations modules */ library SubscriptionUtil { using Balance for Balance.Data; using Subscription for Subscription.Data; /** * @notice Emits the data from the finished subscription * @param giverId The ID from the profile that was subscribed * @param creatorId The ID from the profile that was receiving the subscription * @param vaultId The vault being used in the subscription * @param subscriptionId The subscription ID from the Grateful Subscription NFT * @param rate The subscription rate that was going to the creator (1e-20/second) * @param feeRate The fee rate that was going to the treasury (1e-20/second) */ event SubscriptionFinished( bytes32 indexed giverId, bytes32 indexed creatorId, bytes32 indexed vaultId, uint256 subscriptionId, uint256 rate, uint256 feeRate ); /** * @dev Finishes a subscription. * * This function is used from user or liquidator. * * Updates the balances flows (giver, creator and treasury). * * Updates the subscription state to be finished. * * Emit a event with the subscription data. */ function finishSubscription( bytes32 giverId, bytes32 creatorId ) internal returns ( uint256 subscriptionId, uint256 subscriptionRate, uint256 feeRate, bytes32 vaultId ) { // Get subscription data subscriptionId = SubscriptionId.load(giverId, creatorId).subscriptionId; Subscription.Data storage subscription = Subscription.load( subscriptionId ); subscriptionRate = subscription.rate; feeRate = subscription.feeRate; vaultId = subscription.vaultId; // Increase giver flow uint256 totalRate = subscriptionRate + feeRate; Balance.load(giverId, vaultId).increaseFlow(totalRate); // Decrease creator flow Balance.load(creatorId, vaultId).decreaseFlow(subscriptionRate); // Decrease treasury flow with feeRate bytes32 treasury = Fee.load().gratefulFeeTreasury; Balance.load(treasury, vaultId).decreaseFlow(feeRate); // Finish subscription subscription.finish(); // Emit event emit SubscriptionFinished( giverId, creatorId, vaultId, subscriptionId, subscriptionRate, feeRate ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; import {Vault} from "../storage/Vault.sol"; import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {VaultErrors} from "../errors/VaultErrors.sol"; /** * @title Utils for interacting with ERC4626 vaults. */ library VaultUtil { using SafeERC20 for IERC20; using Vault for Vault.Data; /************************************************************************** * Vault interaction functions *************************************************************************/ /** * @dev Makes a user deposit into a vault. * * The vault must be a ERC4626. * * The `amount` corresponds to vault assets. The `shares` to vault shares. * * The user must have allowed the amount of the vault asset to the system. * * The assets are first transfer to the system and the system makes the deposit. * * The vault shares are normalized to 20 decimals after the deposit is made. */ function deposit( bytes32 vaultId, uint256 amount ) internal returns (uint256 shares) { Vault.Data storage store = Vault.load(vaultId); IERC4626 vault = IERC4626(store.impl); _checkUserAllowance(vault, amount); IERC20(vault.asset()).safeTransferFrom( msg.sender, address(this), amount ); IERC20(vault.asset()).approve(address(vault), amount); shares = vault.deposit({assets: amount, receiver: address(this)}) * store.decimalsNormalizer; } /** * @dev Makes a user withdrawal from a vault. * * The vault must be a ERC4626. * * The `shares` corresponds to vault shares. The `amountWithdrawn` to vault assets. * * The assets are directly transferred to the user. * * The user shares are normalized to original decimals before the redeem is made. */ function withdraw( bytes32 vaultId, uint256 shares ) internal returns (uint256 amountWithdrawn) { Vault.Data storage store = Vault.load(vaultId); IERC4626 vault = IERC4626(store.impl); uint256 normalizedShares = shares / store.decimalsNormalizer; amountWithdrawn = vault.redeem({ shares: normalizedShares, receiver: msg.sender, owner: address(this) }); } /************************************************************************** * View functions *************************************************************************/ /** * @dev Checks user allowande to the system. * * The allowance check is made with the vault asset. */ function _checkUserAllowance(IERC4626 vault, uint256 amount) private view { uint256 allowance = IERC20(vault.asset()).allowance( msg.sender, address(this) ); if (allowance < amount) revert VaultErrors.InsufficientAllowance(); } /** * @dev Returns if a vault is active. */ function isVaultActive(bytes32 id) internal view returns (bool) { return Vault.load(id).isActive(); } /** * @dev Returns if a subscription rate is valid. */ function isRateValid( bytes32 id, uint256 rate ) internal view returns (bool) { return Vault.load(id).isRateValid(rate); } /** * @dev Converts the rate from assets to shares. * * Receives a subscription rate denominated in assets. * * Returns a subscription rate denominated in shares. * * This is used because the relation between asset/share in a vault is changing. */ function getCurrentRate( bytes32 id, uint256 subscriptionRate ) internal view returns (uint256) { Vault.Data storage store = Vault.load(id); IERC4626 vault = IERC4626(store.impl); return vault.convertToShares(subscriptionRate); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"CannotSelfApprove","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"InvalidOwner","type":"error"},{"inputs":[{"internalType":"string","name":"parameter","type":"string"},{"internalType":"string","name":"reason","type":"string"}],"name":"InvalidParameter","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"InvalidTransferRecipient","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"TokenAlreadyMinted","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"TokenDoesNotExist","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"string","name":"uri","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50611b0e806100206000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80636a62784211610097578063a6487c5311610066578063a6487c5314610212578063b88d4fde14610225578063c87b56dd14610238578063e985e9c51461024b57600080fd5b80636a627842146101d157806370a08231146101e457806395d89b41146101f7578063a22cb465146101ff57600080fd5b806323b872dd116100d357806323b872dd1461018257806342842e0e1461019557806356189236146101a85780636352211e146101be57600080fd5b806301ffc9a71461010557806306fdde031461012d578063081812fc14610142578063095ea7b31461016d575b600080fd5b6101186101133660046113c6565b61025e565b60405190151581526020015b60405180910390f35b6101356102b0565b604051610124919061143a565b61015561015036600461144d565b610348565b6040516001600160a01b039091168152602001610124565b61018061017b366004611482565b61039d565b005b6101806101903660046114ac565b610444565b6101806101a33660046114ac565b61047d565b6101b0610498565b604051908152602001610124565b6101556101cc36600461144d565b6104a8565b6101806101df3660046114e8565b6104f8565b6101b06101f23660046114e8565b610522565b610135610580565b61018061020d366004611503565b610598565b6101806102203660046115eb565b61064f565b610180610233366004611673565b610672565b61013561024636600461144d565b6106da565b6101186102593660046116ef565b61073f565b60006001600160e01b031982166301ffc9a760e01b148061028f57506001600160e01b031982166380ac58cd60e01b145b806102aa57506001600160e01b03198216635b5e139f60e01b145b92915050565b60606102ba61077c565b80546102c590611722565b80601f01602080910402602001604051908101604052809291908181526020018280546102f190611722565b801561033e5780601f106103135761010080835404028352916020019161033e565b820191906000526020600020905b81548152906001019060200180831161032157829003601f168201915b5050505050905090565b6000610353826107e9565b6103785760405163c927e5bf60e01b8152600481018390526024015b60405180910390fd5b61038061077c565b60009283526005016020525060409020546001600160a01b031690565b60006103a761077c565b60008381526003820160205260409020549091506001600160a01b039081169084168190036103f45760405163132d05df60e31b81526001600160a01b038516600482015260240161036f565b336001600160a01b038216148015906104145750610412813361073f565b155b156104345760405163472511eb60e11b815233600482015260240161036f565b61043e8484610817565b50505050565b61044e3382610892565b61046d5760405163472511eb60e11b815233600482015260240161036f565b6104788383836108f1565b505050565b61047883838360405180602001604052806000815250610672565b60006104a2610a44565b54919050565b60006104b3826107e9565b6104d35760405163c927e5bf60e01b81526004810183905260240161036f565b6104db61077c565b60009283526003016020525060409020546001600160a01b031690565b610500610a7a565b600061050a610a44565b805490915061051882610ab7565b6104788382610acd565b60006001600160a01b0382166105565760405163b20f76e360e01b81526001600160a01b038316600482015260240161036f565b61055e61077c565b6001600160a01b03909216600090815260049290920160205250604090205490565b606061058a61077c565b60010180546102c590611722565b6001600160a01b03821633036105cc5760405163132d05df60e31b81526001600160a01b038316600482015260240161036f565b806105d561077c565b336000818152600692909201602090815260408084206001600160a01b038816808652925292839020805494151560ff199095169490941790935590517f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c319061064390851515815260200190565b60405180910390a35050565b610657610a7a565b610667610662610a44565b610ab7565b610478838383610c22565b61067c3383610892565b61069b5760405163472511eb60e11b815233600482015260240161036f565b6106a68484846108f1565b6106b284848484610d38565b61043e576040516307d7841560e21b81526001600160a01b038416600482015260240161036f565b606060006106ee6106e9610ddf565b6111f1565b905061071881604051602001610704919061175c565b6040516020818303038152906040526111f1565b604051602001610728919061185d565b604051602081830303815290604052915050919050565b600061074961077c565b6001600160a01b039384166000908152600691909101602090815260408083209490951682529290925250205460ff1690565b6000806040516020016107cb9060208082526022908201527f696f2e73796e7468657469782e636f72652d636f6e7472616374732e45524337604082015261323160f01b606082015260800190565b60408051601f19818403018152919052805160209091012092915050565b6000806107f461077c565b60009384526003016020526040909220546001600160a01b031690911415919050565b8161082061077c565b60008381526005919091016020526040902080546001600160a01b0319166001600160a01b0392831617905581908316610859826104a8565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60008061089e836104a8565b9050806001600160a01b0316846001600160a01b031614806108d95750836001600160a01b03166108ce84610348565b6001600160a01b0316145b806108e957506108e9818561073f565b949350505050565b60006108fb61077c565b9050836001600160a01b0316610910836104a8565b6001600160a01b0316146109425760405163472511eb60e11b81526001600160a01b038516600482015260240161036f565b6001600160a01b0383166109695760405163d92e233d60e01b815260040160405180910390fd5b610974600083610817565b6001600160a01b0384166000908152600482016020526040812080546001929061099f9084906118b8565b90915550506001600160a01b038316600090815260048201602052604081208054600192906109cf9084906118cb565b90915550506000828152600382016020526040902080546001600160a01b0319166001600160a01b03851617905581836001600160a01b0316856001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a450505050565b6000806040516020016107cb906020808252600f908201526e14dd589cd8dc9a5c1d1a5bdb93999d608a1b604082015260600190565b610a82611344565b6001600160a01b0316336001600160a01b031614610ab55760405163472511eb60e11b815233600482015260240161036f565b565b8054816000610ac5836118de565b919050555050565b6000610ad761077c565b90506001600160a01b038316610b005760405163d92e233d60e01b815260040160405180910390fd5b81600003610b625760408051634bab873760e11b8152600481019190915260076044820152661d1bdad95b925960ca1b606482015260806024820152600e60848201526d63616e6e6f74206265207a65726f60901b60a482015260c40161036f565b610b6b826107e9565b15610b8c576040516322d1d39560e21b81526004810183905260240161036f565b6001600160a01b03831660009081526004820160205260408120805460019290610bb79084906118cb565b90915550506000828152600382016020526040902080546001600160a01b0319166001600160a01b03851617905560405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a4505050565b6000610c2c61077c565b90506000816000018054610c3f90611722565b90501180610c5d57506000816001018054610c5990611722565b9050115b80610c7857506000816002018054610c7490611722565b9050115b15610c955760405162dc149f60e41b815260040160405180910390fd5b83511580610ca257508251155b15610d085760408051634bab873760e11b81526004810191909152600b60448201526a1b985b594bdcde5b589bdb60aa1b60648201526080602482015260116084820152706d757374206e6f7420626520656d70747960781b60a482015260c40161036f565b80610d138582611945565b5060018101610d228482611945565b5060028101610d318382611945565b5050505050565b6000833b15610dd457604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290610d73903390899088908890600401611a05565b6020604051808303816000875af1925050508015610dae575060408051601f3d908101601f19168201909252610dab91810190611a42565b60015b610dba575060006108e9565b6001600160e01b031916630a85bd0160e11b1490506108e9565b506001949350505050565b60606040516020016111dd907f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d2233303022206865696768743d22333030226020820152601f60f91b60408201527f3c7374796c653e2e63656e7465727b706f736974696f6e3a206162736f6c757460418201527f653b746f703a203530253b6c6566743a203530253b7472616e73666f726d3a2060618201527f7472616e736c617465282d3530252c202d353025293b7d2e70756c73657b776960818201527f6474683a20332e31323572656d3b6865696768743a20332e31323572656d3b6260a18201527f61636b67726f756e643a206c696e6561722d6772616469656e7428393064656760c18201527f2c20236666363431392030252c20236661366233652033362e3938252c20236560e18201527f66376139332037342e3438252c20236562383062352031303025293b66696c746101018201527f65723a20626c757228302e3672656d293b626f726465722d7261646975733a206101218201527f3530253b616e696d6174696f6e3a20616e696d6174652d70756c7365203173206101418201527f6c696e65617220696e66696e6974653b7d406b65796672616d657320616e696d6101618201527f6174652d70756c7365207b3025207b626f782d736861646f773a2030203020306101818201527f20302072676261283235352c203130392c2037342c20302e37292c20302030206101a18201527f3020302072676261283235352c203130392c2037342c20302e37293b7d3430256101c18201527f207b626f782d736861646f773a20302030203020332e31323572656d207267626101e18201527f61283235352c203130392c2037342c2030292c203020302030203020726762616102018201527f283235352c203130392c2037342c20302e37293b7d383025207b626f782d73686102218201527f61646f773a20302030203020332e31323572656d2072676261283235352c20316102418201527f30392c2037342c2030292c20302030203020312e38373572656d2072676261286102618201527f3235352c203130392c2037342c2030293b7d31303025207b626f782d736861646102818201527f6f773a20302030203020302072676261283235352c203130392c2037342c20306102a18201527f292c20302030203020312e38373572656d2072676261283235352c203130392c6102c182015271101b9a161018149dbebe9e17b9ba3cb6329f60711b6102e18201527f3c64697620636c6173733d2263656e746572223e3c64697620636c6173733d226102f38201526d383ab639b291179f1e17b234bb1f60911b610313820152651e17b9bb339f60d11b6103218201526103270190565b604051602081830303815290604052905090565b6060815160000361121057505060408051602081019091526000815290565b6000604051806060016040528060408152602001611a99604091399050600060038451600261123f91906118cb565b6112499190611a5f565b611254906004611a81565b67ffffffffffffffff81111561126c5761126c61153f565b6040519080825280601f01601f191660200182016040528015611296576020820181803683370190505b509050600182016020820185865187015b80821015611302576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f81168501518453506001830192506112a7565b505060038651066001811461131e576002811461133157611339565b603d6001830353603d6002830353611339565b603d60018303535b509195945050505050565b600061134e61135d565b546001600160a01b0316919050565b6000806040516020016107cb9060208082526023908201527f696f2e73796e7468657469782e636f72652d636f6e7472616374732e4f776e61604082015262626c6560e81b606082015260800190565b6001600160e01b0319811681146113c357600080fd5b50565b6000602082840312156113d857600080fd5b81356113e3816113ad565b9392505050565b60005b838110156114055781810151838201526020016113ed565b50506000910152565b600081518084526114268160208601602086016113ea565b601f01601f19169290920160200192915050565b6020815260006113e3602083018461140e565b60006020828403121561145f57600080fd5b5035919050565b80356001600160a01b038116811461147d57600080fd5b919050565b6000806040838503121561149557600080fd5b61149e83611466565b946020939093013593505050565b6000806000606084860312156114c157600080fd5b6114ca84611466565b92506114d860208501611466565b9150604084013590509250925092565b6000602082840312156114fa57600080fd5b6113e382611466565b6000806040838503121561151657600080fd5b61151f83611466565b91506020830135801515811461153457600080fd5b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156115705761157061153f565b604051601f8501601f19908116603f011681019082821181831017156115985761159861153f565b816040528093508581528686860111156115b157600080fd5b858560208301376000602087830101525050509392505050565b600082601f8301126115dc57600080fd5b6113e383833560208501611555565b60008060006060848603121561160057600080fd5b833567ffffffffffffffff8082111561161857600080fd5b611624878388016115cb565b9450602086013591508082111561163a57600080fd5b611646878388016115cb565b9350604086013591508082111561165c57600080fd5b50611669868287016115cb565b9150509250925092565b6000806000806080858703121561168957600080fd5b61169285611466565b93506116a060208601611466565b925060408501359150606085013567ffffffffffffffff8111156116c357600080fd5b8501601f810187136116d457600080fd5b6116e387823560208401611555565b91505092959194509250565b6000806040838503121561170257600080fd5b61170b83611466565b915061171960208401611466565b90509250929050565b600181811c9082168061173657607f821691505b60208210810361175657634e487b7160e01b600052602260045260246000fd5b50919050565b683d913730b6b2911d1160b91b81527f477261746566756c2050726f746f636f6c20537562736372697074696f6e0000600982015271111610113232b9b1b934b83a34b7b7111d1160711b60278201527f54686973204e465420726570726573656e74732061207375627363726970746960398201527f6f6e2066726f6d20676976657220746f2063726561746f72000000000000000060598201527f222c2022696d616765223a2022646174613a696d6167652f7376672b786d6c3b60718201526618985cd94d8d0b60ca1b609182015281516000906118448160988501602087016113ea565b61227d60f01b6098939091019283015250609a01919050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161189581601d8501602087016113ea565b91909101601d0192915050565b634e487b7160e01b600052601160045260246000fd5b818103818111156102aa576102aa6118a2565b808201808211156102aa576102aa6118a2565b6000600182016118f0576118f06118a2565b5060010190565b601f82111561047857600081815260208120601f850160051c8101602086101561191e5750805b601f850160051c820191505b8181101561193d5782815560010161192a565b505050505050565b815167ffffffffffffffff81111561195f5761195f61153f565b6119738161196d8454611722565b846118f7565b602080601f8311600181146119a857600084156119905750858301515b600019600386901b1c1916600185901b17855561193d565b600085815260208120601f198616915b828110156119d7578886015182559484019460019091019084016119b8565b50858210156119f55787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611a389083018461140e565b9695505050505050565b600060208284031215611a5457600080fd5b81516113e3816113ad565b600082611a7c57634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176102aa576102aa6118a256fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212204c3e3c8cc945409ff3c80c1fde0b39c95900f8de6241c75f84bf7acf6e7addca64736f6c63430008110033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|