ERC-20
Overview
Max Total Supply
191,878.495954218085443475 fSUSHI
Holders
15
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Balance
288.608928937932197076 fSUSHIValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
This contract contains unverified libraries: __CACHE_BREAKER__
Contract Name:
FSushi
Compiler Version
v0.8.17+commit.8df45f5f
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "./interfaces/IFSushi.sol"; import "./libraries/DateUtils.sol"; contract FSushi is Ownable, ERC20, IFSushi { using DateUtils for uint256; bytes32 private constant _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; address private immutable _CACHED_THIS; bytes32 private immutable _HASHED_NAME; bytes32 private immutable _HASHED_VERSION; bytes32 private immutable _TYPE_HASH; uint256 public immutable override startWeek; mapping(address => bool) public override isMinter; bool public override mintersLocked; mapping(address => uint256) public override nonces; /** * @return minimum number of minted total supply during the whole week (only available >= startWeek) */ mapping(uint256 => uint256) public override totalSupplyDuring; /** * @notice totalSupplyDuring is guaranteed to be correct before this week (exclusive) */ uint256 public override lastCheckpoint; modifier onlyMinter() { if (!isMinter[msg.sender]) revert Forbidden(); _; } constructor() ERC20("Flash Sushi Token", "fSUSHI") { uint256 nextWeek = block.timestamp.toWeekNumber() + 1; startWeek = nextWeek; lastCheckpoint = nextWeek; bytes32 hashedName = keccak256(bytes("Flash Sushi Token")); bytes32 hashedVersion = keccak256(bytes("1")); bytes32 typeHash = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); _HASHED_NAME = hashedName; _HASHED_VERSION = hashedVersion; _CACHED_CHAIN_ID = block.chainid; _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); _CACHED_THIS = address(this); _TYPE_HASH = typeHash; } function DOMAIN_SEPARATOR() external view override returns (bytes32) { return _domainSeparatorV4(); } function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { return _CACHED_DOMAIN_SEPARATOR; } else { return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); } } function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash ) private view returns (bytes32) { return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); } function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } function setMinter(address account, bool _isMinter) external override onlyOwner { if (mintersLocked) revert MintersLocked(); isMinter[account] = _isMinter; emit SetMinter(account, _isMinter); } function lockMinters() external onlyOwner { mintersLocked = true; emit LockMinters(); } function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override { if (block.timestamp > deadline) revert Expired(); bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)); bytes32 hash = _hashTypedDataV4(structHash); address signer = ECDSA.recover(hash, v, r, s); if (signer != owner) revert InvalidSignature(); _approve(owner, spender, value); } function mint(address to, uint256 amount) external onlyMinter { _mint(to, amount); checkpoint(); } function checkpointedTotalSupplyDuring(uint256 week) external override returns (uint256) { checkpoint(); return totalSupplyDuring[week]; } /** * @dev if this function doesn't get called for 512 weeks (around 9.8 years) this contract breaks */ function checkpoint() public { uint256 from = lastCheckpoint; uint256 until = block.timestamp.toWeekNumber(); if (until < from) return; for (uint256 i; i < 512; ) { uint256 week = from + i; uint256 old = totalSupplyDuring[week]; if (week == until) { uint256 current = totalSupply(); if (current > old) { totalSupplyDuring[week] = current; } break; } else if (startWeek < week && old == 0) { totalSupplyDuring[week] = totalSupplyDuring[week - 1]; } unchecked { ++i; } } lastCheckpoint = until; emit Checkpoint(until); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IFSushi is IERC20Metadata { error Forbidden(); error Expired(); error MintersLocked(); error InvalidSignature(); event SetMinter(address indexed account, bool indexed isMinter); event LockMinters(); event Checkpoint(uint256 lastCheckpoint); function DOMAIN_SEPARATOR() external view returns (bytes32); function startWeek() external view returns (uint256); function isMinter(address account) external view returns (bool); function mintersLocked() external view returns (bool); function nonces(address account) external view returns (uint256); function totalSupplyDuring(uint256 time) external view returns (uint256); function lastCheckpoint() external view returns (uint256); function setMinter(address account, bool _isMinter) external; function lockMinters() external; function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function mint(address to, uint256 amount) external; function checkpointedTotalSupplyDuring(uint256 week) external returns (uint256); function checkpoint() external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; uint256 constant WEEK = 1 weeks; library DateUtils { function toWeekNumber(uint256 timestamp) internal pure returns (uint256) { return timestamp / WEEK; } function toTimestamp(uint256 weekNumber) internal pure returns (uint256) { return weekNumber * WEEK; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts 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 v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.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. If 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)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 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) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol) pragma solidity ^0.8.0; import "../ERC20.sol"; import "../../../utils/Context.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { _spendAllowance(account, _msgSender(), amount); _burn(account, amount); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol) pragma solidity ^0.8.0; import "./IERC721.sol"; import "./IERC721Receiver.sol"; import "./extensions/IERC721Metadata.sol"; import "../../utils/Address.sol"; import "../../utils/Context.sol"; import "../../utils/Strings.sol"; import "../../utils/introspection/ERC165.sol"; /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension, but not including the Enumerable extension, which is available separately as * {ERC721Enumerable}. */ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { using Address for address; using Strings for uint256; // Token name string private _name; // Token symbol string private _symbol; // Mapping from token ID to owner address mapping(uint256 => address) private _owners; // Mapping owner address to token count mapping(address => uint256) private _balances; // Mapping from token ID to approved address mapping(uint256 => address) private _tokenApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual override returns (uint256) { require(owner != address(0), "ERC721: address zero is not a valid owner"); return _balances[owner]; } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { address owner = _ownerOf(tokenId); require(owner != address(0), "ERC721: invalid token ID"); return owner; } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireMinted(tokenId); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ""; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual override { address owner = ERC721.ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require( _msgSender() == owner || isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not token owner or approved for all" ); _approve(to, tokenId); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual override returns (address) { _requireMinted(tokenId); return _tokenApprovals[tokenId]; } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom( address from, address to, uint256 tokenId ) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId ) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory data ) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); _safeTransfer(from, to, tokenId, data); } /** * @dev 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. * * `data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer( address from, address to, uint256 tokenId, bytes memory data ) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist */ function _ownerOf(uint256 tokenId) internal view virtual returns (address) { return _owners[tokenId]; } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _ownerOf(tokenId) != address(0); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { address owner = ERC721.ownerOf(tokenId); return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); } /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: * * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint( address to, uint256 tokenId, bytes memory data ) internal virtual { _mint(to, tokenId); require( _checkOnERC721Received(address(0), to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer" ); } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId, 1); // Check that tokenId was not minted by `_beforeTokenTransfer` hook require(!_exists(tokenId), "ERC721: token already minted"); unchecked { // Will not overflow unless all 2**256 token ids are minted to the same owner. // Given that tokens are minted one by one, it is impossible in practice that // this ever happens. Might change if we allow batch minting. // The ERC fails to describe this case. _balances[to] += 1; } _owners[tokenId] = to; emit Transfer(address(0), to, tokenId); _afterTokenTransfer(address(0), to, tokenId, 1); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * This is an internal function that does not check if the sender is authorized to operate on the token. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal virtual { address owner = ERC721.ownerOf(tokenId); _beforeTokenTransfer(owner, address(0), tokenId, 1); // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook owner = ERC721.ownerOf(tokenId); // Clear approvals delete _tokenApprovals[tokenId]; unchecked { // Cannot overflow, as that would require more tokens to be burned/transferred // out than the owner initially received through minting and transferring in. _balances[owner] -= 1; } delete _owners[tokenId]; emit Transfer(owner, address(0), tokenId); _afterTokenTransfer(owner, address(0), tokenId, 1); } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer( address from, address to, uint256 tokenId ) internal virtual { require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); require(to != address(0), "ERC721: transfer to the zero address"); _beforeTokenTransfer(from, to, tokenId, 1); // Check that tokenId was not transferred by `_beforeTokenTransfer` hook require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); // Clear approvals from the previous owner delete _tokenApprovals[tokenId]; unchecked { // `_balances[from]` cannot overflow for the same reason as described in `_burn`: // `from`'s balance is the number of token held, which is at least one before the current // transfer. // `_balances[to]` could overflow in the conditions described in `_mint`. That would require // all 2**256 token ids to be minted, which in practice is impossible. _balances[from] -= 1; _balances[to] += 1; } _owners[tokenId] = to; emit Transfer(from, to, tokenId); _afterTokenTransfer(from, to, tokenId, 1); } /** * @dev Approve `to` to operate on `tokenId` * * Emits an {Approval} event. */ function _approve(address to, uint256 tokenId) internal virtual { _tokenApprovals[tokenId] = to; emit Approval(ERC721.ownerOf(tokenId), to, tokenId); } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Emits an {ApprovalForAll} event. */ function _setApprovalForAll( address owner, address operator, bool approved ) internal virtual { require(owner != operator, "ERC721: approve to caller"); _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Reverts if the `tokenId` has not been minted yet. */ function _requireMinted(uint256 tokenId) internal view virtual { require(_exists(tokenId), "ERC721: invalid token ID"); } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory data ) private returns (bool) { if (to.isContract()) { try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert("ERC721: transfer to non ERC721Receiver implementer"); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } } /** * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`. * - When `from` is zero, the tokens will be minted for `to`. * - When `to` is zero, ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * - `batchSize` is non-zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256, /* firstTokenId */ uint256 batchSize ) internal virtual { if (batchSize > 1) { if (from != address(0)) { _balances[from] -= batchSize; } if (to != address(0)) { _balances[to] += batchSize; } } } /** * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`. * - When `from` is zero, the tokens were minted for `to`. * - When `to` is zero, ``from``'s tokens were burned. * - `from` and `to` are never both zero. * - `batchSize` is non-zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 firstTokenId, uint256 batchSize ) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.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 functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev 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; /** * @dev 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; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * 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; /** * @dev 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; /** * @dev 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; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev 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 // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721URIStorage.sol) pragma solidity ^0.8.0; import "../ERC721.sol"; /** * @dev ERC721 token with storage based token URI management. */ abstract contract ERC721URIStorage is ERC721 { using Strings for uint256; // Optional mapping for token URIs mapping(uint256 => string) private _tokenURIs; /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { _requireMinted(tokenId); string memory _tokenURI = _tokenURIs[tokenId]; string memory base = _baseURI(); // If there is no base URI, return the token URI. if (bytes(base).length == 0) { return _tokenURI; } // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). if (bytes(_tokenURI).length > 0) { return string(abi.encodePacked(base, _tokenURI)); } return super.tokenURI(tokenId); } /** * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); _tokenURIs[tokenId] = _tokenURI; } /** * @dev See {ERC721-_burn}. This override additionally checks to see if a * token-specific URI was set for the token, and if so, it deletes the token URI from * the storage mapping. */ function _burn(uint256 tokenId) internal virtual override { super._burn(tokenId); if (bytes(_tokenURIs[tokenId]).length != 0) { delete _tokenURIs[tokenId]; } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract FlashNFT is ERC721URIStorage, Ownable { using Counters for Counters.Counter; Counters.Counter private tokenIds; constructor() ERC721("Flashstake NFT", "FLASHNFT") {} function contractURI() public pure returns (string memory) { return "https://nft.flashstake.io/metadata"; } function _baseURI() internal pure virtual override returns (string memory) { return "https://nft.flashstake.io/"; } function exists(uint256 _tokenId) public view returns (bool) { return _exists(_tokenId); } // Only the FlashV3 protocol (owner) can burn function burn(uint256 _tokenId) public onlyOwner returns (bool) { _burn(_tokenId); return true; } // Only the FlashV3 protocol (owner) can mint function mint(address _recipientAddress) public onlyOwner returns (uint256) { tokenIds.increment(); _mint(_recipientAddress, tokenIds.current()); return tokenIds.current(); } function totalSupply() public view returns (uint256) { return tokenIds.current(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) pragma solidity ^0.8.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` */ library Counters { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { unchecked { counter._value += 1; } } function decrement(Counter storage counter) internal { uint256 value = counter._value; require(value > 0, "Counter: decrement overflow"); unchecked { counter._value = value - 1; } } function reset(Counter storage counter) internal { counter._value = 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol) pragma solidity ^0.8.0; import "./draft-IERC20Permit.sol"; import "../ERC20.sol"; import "../../../utils/cryptography/ECDSA.sol"; import "../../../utils/cryptography/EIP712.sol"; import "../../../utils/Counters.sol"; /** * @dev Implementation 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. * * _Available since v3.4._ */ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // solhint-disable-next-line var-name-mixedcase bytes32 private constant _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); /** * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`. * However, to ensure consistency with the upgradeable transpiler, we will continue * to reserve a slot. * @custom:oz-renamed-from _PERMIT_TYPEHASH */ // solhint-disable-next-line var-name-mixedcase bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT; /** * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. * * It's a good idea to use the same `name` that is defined as the ERC20 token name. */ constructor(string memory name) EIP712(name, "1") {} /** * @dev See {IERC20Permit-permit}. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); bytes32 hash = _hashTypedDataV4(structHash); address signer = ECDSA.recover(hash, v, r, s); require(signer == owner, "ERC20Permit: invalid signature"); _approve(owner, spender, value); } /** * @dev See {IERC20Permit-nonces}. */ function nonces(address owner) public view virtual override returns (uint256) { return _nonces[owner].current(); } /** * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view override returns (bytes32) { return _domainSeparatorV4(); } /** * @dev "Consume a nonce": return the current value and increment. * * _Available since v4.1._ */ function _useNonce(address owner) internal virtual returns (uint256 current) { Counters.Counter storage nonce = _nonces[owner]; current = nonce.current(); nonce.increment(); } }
// 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 (last updated v4.8.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.0; import "./ECDSA.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding * they need in their contracts using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * _Available since v3.4._ */ abstract contract EIP712 { /* solhint-disable var-name-mixedcase */ // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to // invalidate the cached domain separator if the chain id changes. bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; uint256 private immutable _CACHED_CHAIN_ID; address private immutable _CACHED_THIS; bytes32 private immutable _HASHED_NAME; bytes32 private immutable _HASHED_VERSION; bytes32 private immutable _TYPE_HASH; /* solhint-enable var-name-mixedcase */ /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ constructor(string memory name, string memory version) { bytes32 hashedName = keccak256(bytes(name)); bytes32 hashedVersion = keccak256(bytes(version)); bytes32 typeHash = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); _HASHED_NAME = hashedName; _HASHED_VERSION = hashedVersion; _CACHED_CHAIN_ID = block.chainid; _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); _CACHED_THIS = address(this); _TYPE_HASH = typeHash; } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { return _CACHED_DOMAIN_SEPARATOR; } else { return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); } } function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash ) private view returns (bytes32) { return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.0; import "../ERC20.sol"; import "../utils/SafeERC20.sol"; import "../../../interfaces/IERC4626.sol"; import "../../../utils/math/Math.sol"; /** * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * CAUTION: Deposits and withdrawals may incur unexpected slippage. Users should verify that the amount received of * shares or assets is as expected. EOAs should operate through a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * _Available since v4.7._ */ abstract contract ERC4626 is ERC20, IERC4626 { using Math for uint256; IERC20 private immutable _asset; uint8 private immutable _decimals; /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). */ constructor(IERC20 asset_) { (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); _decimals = success ? assetDecimals : super.decimals(); _asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20 asset_) private returns (bool, uint8) { (bool success, bytes memory encodedDecimals) = address(asset_).call( abi.encodeWithSelector(IERC20Metadata.decimals.selector) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are read from the underlying asset in the constructor and cached. If this fails (e.g., the asset * has not been created yet), the cached value is set to a default obtained by `super.decimals()` (which depends on * inheritance but is most likely 18). Override this function in order to set a guaranteed hardcoded value. * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) { return _decimals; } /** @dev See {IERC4626-asset}. */ function asset() public view virtual override returns (address) { return address(_asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual override returns (uint256) { return _asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) { return _convertToShares(assets, Math.Rounding.Down); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) { return _convertToAssets(shares, Math.Rounding.Down); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual override returns (uint256) { return _isVaultCollateralized() ? type(uint256).max : 0; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual override returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual override returns (uint256) { return _convertToAssets(balanceOf(owner), Math.Rounding.Down); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual override returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual override returns (uint256) { return _convertToShares(assets, Math.Rounding.Down); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual override returns (uint256) { return _convertToAssets(shares, Math.Rounding.Up); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { return _convertToShares(assets, Math.Rounding.Up); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual override returns (uint256) { return _convertToAssets(shares, Math.Rounding.Down); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max"); uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. */ function mint(uint256 shares, address receiver) public virtual override returns (uint256) { require(shares <= maxMint(receiver), "ERC4626: mint more than max"); uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw( uint256 assets, address receiver, address owner ) public virtual override returns (uint256) { require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max"); uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem( uint256 shares, address receiver, address owner ) public virtual override returns (uint256) { require(shares <= maxRedeem(owner), "ERC4626: redeem more than max"); uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. * * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset * would represent an infinite amount of shares. */ function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) { uint256 supply = totalSupply(); return (assets == 0 || supply == 0) ? _initialConvertToShares(assets, rounding) : assets.mulDiv(supply, totalAssets(), rounding); } /** * @dev Internal conversion function (from assets to shares) to apply when the vault is empty. * * NOTE: Make sure to keep this function consistent with {_initialConvertToAssets} when overriding it. */ function _initialConvertToShares( uint256 assets, Math.Rounding /*rounding*/ ) internal view virtual returns (uint256 shares) { return assets; } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) { uint256 supply = totalSupply(); return (supply == 0) ? _initialConvertToAssets(shares, rounding) : shares.mulDiv(totalAssets(), supply, rounding); } /** * @dev Internal conversion function (from shares to assets) to apply when the vault is empty. * * NOTE: Make sure to keep this function consistent with {_initialConvertToShares} when overriding it. */ function _initialConvertToAssets( uint256 shares, Math.Rounding /*rounding*/ ) internal view virtual returns (uint256 assets) { return shares; } /** * @dev Deposit/mint common workflow. */ function _deposit( address caller, address receiver, uint256 assets, uint256 shares ) internal virtual { // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20.safeTransfer(_asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } function _isVaultCollateralized() private view returns (bool) { return totalAssets() > 0 || totalSupply() == 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.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.8.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 sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, 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 (last updated v4.8.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/Address.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Internal function that returns the initialized version. Returns `_initialized` */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Internal function that returns the initialized version. Returns `_initializing` */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; abstract contract BaseERC20 is Initializable, IERC20Metadata { error Expired(); error InvalidSignature(); error InvalidAccount(); error InvalidSender(); error InvalidReceiver(); error InvalidOwner(); error InvalidSpender(); error NotEnoughBalance(); uint8 public constant override decimals = 18; bytes32 private _CACHED_DOMAIN_SEPARATOR; uint256 private _CACHED_CHAIN_ID; address private _CACHED_THIS; bytes32 private _HASHED_NAME; bytes32 private _HASHED_VERSION; bytes32 private _TYPE_HASH; string public override name; string public override symbol; uint256 internal _totalSupply; mapping(address => uint256) internal _balanceOf; mapping(address => mapping(address => uint256)) internal _allowance; mapping(address => uint256) public nonces; bytes32 private constant _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); function BaseERC20_initialize( string memory _name, string memory _symbol, string memory _version ) internal onlyInitializing { name = _name; symbol = _symbol; bytes32 hashedName = keccak256(bytes(_name)); bytes32 hashedVersion = keccak256(bytes(_version)); bytes32 typeHash = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); _HASHED_NAME = hashedName; _HASHED_VERSION = hashedVersion; _CACHED_CHAIN_ID = block.chainid; _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); _CACHED_THIS = address(this); _TYPE_HASH = typeHash; } function DOMAIN_SEPARATOR() external view returns (bytes32) { return _domainSeparatorV4(); } function totalSupply() external view virtual returns (uint256) { return _totalSupply; } function balanceOf(address account) external view virtual returns (uint256) { return _balanceOf[account]; } function allowance(address owner, address spender) external view virtual returns (uint256) { return _allowance[owner][spender]; } function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { return _CACHED_DOMAIN_SEPARATOR; } else { return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); } } function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash ) private view returns (bytes32) { return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); } function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { if (block.timestamp > deadline) revert Expired(); bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)); bytes32 hash = _hashTypedDataV4(structHash); address signer = ECDSA.recover(hash, v, r, s); if (signer != owner) revert InvalidSignature(); _approve(owner, spender, value); } function transferFrom( address from, address to, uint256 amount ) external returns (bool) { if (_allowance[from][msg.sender] != type(uint256).max) { _allowance[from][msg.sender] -= amount; } _transfer(from, to, amount); return true; } function transfer(address to, uint256 amount) external override returns (bool) { _transfer(msg.sender, to, amount); return true; } function _transfer( address from, address to, uint256 amount ) internal virtual returns (uint256 balanceOfFrom, uint256 balanceOfTo) { if (from == address(0)) revert InvalidSender(); if (to == address(0)) revert InvalidReceiver(); uint256 balance = _balanceOf[from]; if (balance < amount) revert NotEnoughBalance(); unchecked { balanceOfFrom = balance - amount; balanceOfTo = _balanceOf[to] + amount; _balanceOf[from] = balanceOfFrom; _balanceOf[to] = balanceOfTo; } emit Transfer(from, to, amount); } function approve(address spender, uint256 amount) external override returns (bool) { _approve(msg.sender, spender, amount); return true; } function _approve( address owner, address spender, uint256 amount ) internal virtual { if (owner == address(0)) revert InvalidOwner(); if (spender == address(0)) revert InvalidSpender(); _allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } function _mint(address account, uint256 amount) internal virtual { if (account == address(0)) revert InvalidAccount(); _totalSupply += amount; unchecked { _balanceOf[account] += amount; } emit Transfer(address(0), account, amount); } function _burn(address account, uint256 amount) internal virtual { if (account == address(0)) revert InvalidAccount(); uint256 balance = _balanceOf[account]; if (balance < amount) revert NotEnoughBalance(); unchecked { _balanceOf[account] = balance - amount; _totalSupply -= amount; } emit Transfer(account, address(0), amount); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/interfaces/IERC4626.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Pair.sol"; import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Router02.sol"; import "./interfaces/IFarmingLPToken.sol"; import "./interfaces/IFarmingLPTokenFactory.sol"; import "./interfaces/IFarmingLPTokenMigrator.sol"; import "./interfaces/IMasterChef.sol"; import "./libraries/UniswapV2Utils.sol"; import "./base/BaseERC20.sol"; contract FarmingLPToken is BaseERC20, ReentrancyGuard, IFarmingLPToken { using SafeERC20 for IERC20; using SafeCast for uint256; using SafeCast for int256; uint128 internal constant POINTS_MULTIPLIER = type(uint128).max; address public override factory; address public override router; address public override masterChef; uint256 public override pid; address public override sushi; address public override lpToken; address public override token0; address public override token1; uint256 public override withdrawableTotalLPs; uint256 internal _pointsPerShare; mapping(address => int256) internal _pointsCorrection; mapping(address => uint256) internal _withdrawnVaultBalanceOf; function initialize( address _router, address _masterChef, uint256 _pid ) external override initializer { if (_router == address(0)) return; factory = msg.sender; (address _lpToken, , , ) = IMasterChef(_masterChef).poolInfo(_pid); address _token0 = IUniswapV2Pair(_lpToken).token0(); address _token1 = IUniswapV2Pair(_lpToken).token1(); router = _router; masterChef = _masterChef; pid = _pid; sushi = IMasterChef(_masterChef).sushi(); lpToken = _lpToken; token0 = _token0; token1 = _token1; BaseERC20_initialize( string.concat( "Farming LP Token (", IERC20Metadata(_token0).name(), "-", IERC20Metadata(_token1).name(), ")" ), string.concat("fLP:", IERC20Metadata(_token0).symbol(), "-", IERC20Metadata(_token1).symbol()), "1" ); approveMax(); } function withdrawableLPsOf(address account) external view override returns (uint256) { uint256 total = totalShares(); if (total == 0) return 0; return (sharesOf(account) * withdrawableTotalLPs) / total; } /** * @return Sum of (shares in SUSHI) + (withdrawable total SUSHI) */ function totalSupply() public view override(BaseERC20, IERC20) returns (uint256) { return totalShares() + withdrawableTotalYield(); } /** * @return Sum of (shares in SUSHI by depositd account) + (SUSHI withdrawable by account) */ function balanceOf(address account) public view override(BaseERC20, IERC20) returns (uint256) { return sharesOf(account) + withdrawableYieldOf(account); } /** * @return total shares in SUSHI currently being depositd */ function totalShares() public view override returns (uint256) { return _totalSupply; } /** * @return shares in SUSHI currently being depositd by account */ function sharesOf(address account) public view override returns (uint256) { return _balanceOf[account]; } /** * @dev Returns the total amount of SUSHI if every holder wants to withdraw at once * @return A uint256 representing the total SUSHI */ function withdrawableTotalYield() public view override returns (uint256) { address yieldVault = IFarmingLPTokenFactory(factory).yieldVault(); uint256 pendingSushi = IMasterChef(masterChef).pendingSushi(pid, address(this)); return pendingSushi + IERC4626(yieldVault).maxWithdraw(address(this)); } /** * @dev Returns the amount of SUSHI a given address is able to withdraw. * @param account Address of a reward recipient * @return A uint256 representing the SUSHI `account` can withdraw */ function withdrawableYieldOf(address account) public view override returns (uint256) { address yieldVault = IFarmingLPTokenFactory(factory).yieldVault(); return IERC4626(yieldVault).convertToAssets((_withdrawableVaultBalanceOf(account, true))); } /** * @dev Vault balance is used to record reward debt for account. * @param account Address of a reward recipient * @param preview if true, it adds the amount of MasterChef.pendingSushi() * @return A uint256 representing the SUSHI `account` can withdraw */ function _withdrawableVaultBalanceOf(address account, bool preview) internal view returns (uint256) { return _cumulativeVaultBalanceOf(account, preview) - _withdrawnVaultBalanceOf[account]; } /** * @notice View the amount of vault balance that an address has earned in total. * @dev cumulativeVaultBalanceOf(account) = withdrawableVaultBalanceOf(account) + withdrawnVaultBalanceOf(account) * = (pointsPerShare * sharesOf(account) + pointsCorrection[account]) / POINTS_MULTIPLIER * @param account The address of a token holder. * @param preview if true, it adds the amount of MasterChef.pendingSushi() * @return The amount of SUSHI that `account` has earned in total. */ function _cumulativeVaultBalanceOf(address account, bool preview) internal view returns (uint256) { uint256 pointsPerShare = _pointsPerShare; if (preview) { uint256 total = totalShares(); if (total > 0) { address yieldVault = IFarmingLPTokenFactory(factory).yieldVault(); uint256 pendingSushi = IMasterChef(masterChef).pendingSushi(pid, address(this)); pointsPerShare += (IERC4626(yieldVault).previewDeposit(pendingSushi) * POINTS_MULTIPLIER) / total; } } return ((pointsPerShare * sharesOf(account)).toInt256() + _pointsCorrection[account]).toUint256() / POINTS_MULTIPLIER; } function approveMax() public override { IERC20(lpToken).approve(masterChef, type(uint256).max); IERC20(sushi).approve(IFarmingLPTokenFactory(factory).yieldVault(), type(uint256).max); IERC20(sushi).approve(router, type(uint256).max); IERC20(token0).approve(router, type(uint256).max); IERC20(token1).approve(router, type(uint256).max); } /** * @dev amount of sushi that LPs converted to is added to sharesOf(account) and aLP is minted * user signature is needed for IUniswapV2Pair.permit() */ function depositSigned( uint256 amountLP, address[] calldata path0, address[] calldata path1, uint256 amountMin, address beneficiary, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override nonReentrant { IUniswapV2Pair(lpToken).permit(msg.sender, address(this), amountLP, deadline, v, r, s); _deposit(amountLP, path0, path1, amountMin, beneficiary); } /** * @dev amount of sushi that LPs converted to is added to sharesOf(account) and aLP is minted */ function deposit( uint256 amountLP, address[] calldata path0, address[] calldata path1, uint256 amountMin, address beneficiary, uint256 deadline ) external override nonReentrant { if (block.timestamp > deadline) revert Expired(); _deposit(amountLP, path0, path1, amountMin, beneficiary); } function _deposit( uint256 amountLP, address[] calldata path0, address[] calldata path1, uint256 amountMin, address beneficiary ) internal { if (path0[0] != token0 || path0[path0.length - 1] != sushi) revert InvalidPath(); if (path1[0] != token1 || path1[path1.length - 1] != sushi) revert InvalidPath(); IERC20(lpToken).safeTransferFrom(msg.sender, address(this), amountLP); uint256 total = IUniswapV2Pair(lpToken).totalSupply(); (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(lpToken).getReserves(); uint256 amount = UniswapV2Utils.quote(router, (reserve0 * amountLP) / total, path0) + UniswapV2Utils.quote(router, (reserve1 * amountLP) / total, path1); if (amount < amountMin) revert InsufficientAmount(); IMasterChef(masterChef).deposit(pid, amountLP); _depositSushi(); _mint(beneficiary, amount); withdrawableTotalLPs += amountLP; emit Deposit(amount, amountLP, beneficiary); } /** * @dev amount is added to sharesOf(account) and same amount of aLP is minted * provided SUSHI is swapped then added as liquidity which results in LP tokens depositd */ function depositWithSushi( uint256 amount, address[] calldata path0, address[] calldata path1, uint256 amountLPMin, address beneficiary, uint256 deadline ) external override { if (path0[0] != sushi || path0[path0.length - 1] != token0) revert InvalidPath(); if (path1[0] != sushi || path1[path1.length - 1] != token1) revert InvalidPath(); IERC20(sushi).safeTransferFrom(msg.sender, address(this), amount); uint256 amountLP = UniswapV2Utils.addLiquidityWithSingleToken(router, amount, path0, path1, deadline); if (amountLP < amountLPMin) revert InsufficientAmount(); IMasterChef(masterChef).deposit(pid, amountLP); _depositSushi(); _mint(beneficiary, amount); withdrawableTotalLPs += amountLP; emit Deposit(amount, amountLP, beneficiary); } /** * @dev when unstaking, the user's share of LP tokens are returned and pro-rata SUSHI yield is return as well */ function withdraw(uint256 shares, address beneficiary) external override nonReentrant { uint256 amountLP = (shares * withdrawableTotalLPs) / totalShares(); IMasterChef(masterChef).withdraw(pid, amountLP); _claimSushi(shares, beneficiary); IERC20(lpToken).safeTransfer(beneficiary, amountLP); _burn(msg.sender, shares); withdrawableTotalLPs -= amountLP; emit Withdraw(shares, amountLP, beneficiary); } function _claimSushi(uint256 shares, address beneficiary) internal { _depositSushi(); uint256 sharesMax = sharesOf(msg.sender); if (shares > sharesMax) revert InsufficientAmount(); address yieldVault = IFarmingLPTokenFactory(factory).yieldVault(); uint256 withdrawable = _withdrawableVaultBalanceOf(msg.sender, false); if (withdrawable == 0) revert InsufficientYield(); uint256 yieldShares = (withdrawable * shares) / sharesMax; _withdrawnVaultBalanceOf[msg.sender] += yieldShares; uint256 yield = IERC4626(yieldVault).redeem(yieldShares, beneficiary, address(this)); emit ClaimSushi(shares, yield, beneficiary); } /** * @dev withdraw without caring about rewards. EMERGENCY ONLY */ function emergencyWithdraw(address beneficiary) external override nonReentrant { uint256 shares = sharesOf(msg.sender); uint256 amountLP = (shares * withdrawableTotalLPs) / totalShares(); IMasterChef(masterChef).withdraw(pid, amountLP); IERC20(lpToken).safeTransfer(beneficiary, amountLP); _burn(msg.sender, shares); withdrawableTotalLPs -= amountLP; emit EmergencyWithdraw(shares, amountLP, beneficiary); } /** * @dev migrate to a new version of fLP */ function migrate(address beneficiary, bytes calldata params) external nonReentrant { address migrator = IFarmingLPTokenFactory(factory).migrator(); if (migrator == address(0)) revert NoMigratorSet(); uint256 shares = sharesOf(msg.sender); uint256 amountLP = (shares * withdrawableTotalLPs) / totalShares(); IMasterChef(masterChef).withdraw(pid, amountLP); _claimSushi(shares, beneficiary); _burn(msg.sender, shares); withdrawableTotalLPs -= amountLP; address _lpToken = lpToken; IERC20(_lpToken).approve(migrator, amountLP); IFarmingLPTokenMigrator(migrator).onMigrate(msg.sender, pid, _lpToken, shares, amountLP, beneficiary, params); emit Migrate(shares, amountLP, beneficiary); } /** * @dev migrate to a new version of fLP without caring about rewards. EMERGENCY ONLY */ function emergencyMigrate(address beneficiary, bytes calldata params) external nonReentrant { address migrator = IFarmingLPTokenFactory(factory).migrator(); if (migrator == address(0)) revert NoMigratorSet(); uint256 shares = sharesOf(msg.sender); uint256 amountLP = (shares * withdrawableTotalLPs) / totalShares(); IMasterChef(masterChef).withdraw(pid, amountLP); _burn(msg.sender, shares); withdrawableTotalLPs -= amountLP; address _lpToken = lpToken; IERC20(_lpToken).approve(migrator, amountLP); IFarmingLPTokenMigrator(migrator).onMigrate(msg.sender, pid, _lpToken, shares, amountLP, beneficiary, params); emit EmergencyMigrate(shares, amountLP, beneficiary); } /** * @dev withdraws pending SUSHI from MasterChef and add it to the balance */ function checkpoint() external override nonReentrant { uint256 balance = IERC20(lpToken).balanceOf(address(this)); if (balance > withdrawableTotalLPs) { withdrawableTotalLPs = balance; } IMasterChef(masterChef).deposit(pid, 0); _depositSushi(); } function _depositSushi() internal { uint256 balance = IERC20(sushi).balanceOf(address(this)); if (balance > 0) { address yieldVault = IFarmingLPTokenFactory(factory).yieldVault(); uint256 yieldBalance = IERC4626(yieldVault).deposit(balance, address(this)); uint256 total = totalShares(); if (total > 0) { _pointsPerShare += (yieldBalance * POINTS_MULTIPLIER) / total; } } } function _transfer( address from, address to, uint256 amount ) internal override returns (uint256 balanceOfFrom, uint256 balanceOfTo) { uint256 balance = balanceOf(from); uint256 shares = balance == 0 ? 0 : (amount * sharesOf(from)) / balance; (balanceOfFrom, balanceOfTo) = super._transfer(from, to, shares); int256 _magCorrection = (_pointsPerShare * shares).toInt256(); _pointsCorrection[from] += _magCorrection; _pointsCorrection[to] += _magCorrection; } function _mint(address account, uint256 shares) internal override { super._mint(account, shares); _correctPoints(account, -int256(shares)); } function _burn(address account, uint256 shares) internal override { super._burn(account, shares); _correctPoints(account, int256(shares)); } function _correctPoints(address account, int256 amount) internal { _pointsCorrection[account] += amount * int256(_pointsPerShare); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IFarmingLPToken is IERC20 { error InvalidPath(); error InsufficientYield(); error InsufficientAmount(); error NoMigratorSet(); event Deposit(uint256 shares, uint256 amountLP, address indexed beneficiary); event Withdraw(uint256 shares, uint256 amountLP, address indexed beneficiary); event EmergencyWithdraw(uint256 shares, uint256 amountLP, address indexed beneficiary); event ClaimSushi(uint256 shares, uint256 yield, address indexed beneficiary); event Migrate(uint256 shares, uint256 amountLP, address indexed beneficiary); event EmergencyMigrate(uint256 shares, uint256 amountLP, address indexed beneficiary); function initialize( address _router, address _masterChef, uint256 _pid ) external; function factory() external view returns (address); function router() external view returns (address); function masterChef() external view returns (address); function sushi() external view returns (address); function pid() external view returns (uint256); function lpToken() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function withdrawableTotalLPs() external view returns (uint256); function withdrawableLPsOf(address account) external view returns (uint256); function withdrawableTotalYield() external view returns (uint256); function withdrawableYieldOf(address account) external view returns (uint256); function totalShares() external view returns (uint256); function sharesOf(address account) external view returns (uint256); function approveMax() external; function depositSigned( uint256 amountLP, address[] calldata path0, address[] calldata path1, uint256 amountMin, address beneficiary, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function deposit( uint256 amountLP, address[] calldata path0, address[] calldata path1, uint256 amountMin, address beneficiary, uint256 deadline ) external; function depositWithSushi( uint256 amount, address[] calldata path0, address[] calldata path1, uint256 amountLPMin, address beneficiary, uint256 deadline ) external; function withdraw(uint256 shares, address beneficiary) external; function emergencyWithdraw(address beneficiary) external; function migrate(address beneficiary, bytes calldata params) external; function emergencyMigrate(address beneficiary, bytes calldata params) external; function checkpoint() external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface IFarmingLPTokenFactory { error InvalidAddress(); error MigratorSet(); error TokenCreated(); event UpdateVault(address indexed vault); event UpdateMigrator(address indexed migrator); event CreateFarmingLPToken(uint256 indexed pid, address indexed token); function router() external view returns (address); function masterChef() external view returns (address); function yieldVault() external view returns (address); function migrator() external view returns (address); function getFarmingLPToken(uint256 pid) external view returns (address); function predictFarmingLPTokenAddress(uint256 pid) external view returns (address token); function updateYieldVault(address vault) external; function updateMigrator(address vault) external; function createFarmingLPToken(uint256 pid) external returns (address token); }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface IFarmingLPTokenMigrator { /** * @dev address of legacy FarmingLPTokenFactory */ function factoryLegacy() external view returns (address); /** * @dev msg.sender MUST be factoryLegacy.getFarmingLPToken(lpToken) */ function onMigrate( address account, uint256 pid, address lpToken, uint256 shares, uint256 amountLP, address beneficiary, bytes calldata params ) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface IMasterChef { function sushi() external view returns (address); function poolInfo(uint256 _pid) external view returns ( address lpToken, uint256 allocPoint, uint256 lastRewardBlock, uint256 accSushiPerShare ); function userInfo(uint256 _pid, address user) external view returns (uint256 amount, uint256 rewardDebt); function pendingSushi(uint256 _pid, address _user) external view returns (uint256); function deposit(uint256 _pid, uint256 _amount) external; function withdraw(uint256 _pid, uint256 _amount) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Router02.sol"; library UniswapV2Utils { using SafeERC20 for IERC20; error InsufficientAmountLP(); function quote( address router, uint256 amountIn, address[] memory path ) internal view returns (uint256 amountOut) { if (path.length < 2) return amountIn; uint256[] memory amountsOut = IUniswapV2Router02(router).getAmountsOut(amountIn, path); return amountsOut[amountsOut.length - 1]; } function addLiquidityWithSingleToken( address router, uint256 amount, address[] memory path0, address[] memory path1, uint256 deadline ) internal returns (uint256 amountLP) { uint256 amountOut0 = swap(router, amount / 2, path0, deadline); uint256 amountOut1 = swap(router, amount / 2, path1, deadline); (address token0, address token1) = (path0[path0.length - 1], path1[path1.length - 1]); (, , uint256 _amountLP) = IUniswapV2Router02(router).addLiquidity( token0, token1, amountOut0, amountOut1, 0, 0, address(this), deadline ); uint256 balance0 = IERC20(token0).balanceOf(address(this)); if (balance0 > 0) { IERC20(token0).safeTransfer(msg.sender, balance0); } uint256 balance1 = IERC20(token1).balanceOf(address(this)); if (balance1 > 0) { IERC20(token1).safeTransfer(msg.sender, balance1); } return _amountLP; } function swap( address router, uint256 amountIn, address[] memory path, uint256 deadline ) internal returns (uint256 amountOut) { if (path.length < 2) return amountIn; uint256[] memory amountsOut = IUniswapV2Router02(router).swapExactTokensForTokens( amountIn, 0, path, address(this), deadline ); return amountsOut[amountsOut.length - 1]; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. 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 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @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 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @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 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @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 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @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 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @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 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @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 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @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 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @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 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @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 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @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 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @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 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @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 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @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 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @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 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @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 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @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 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @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 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @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 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @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 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @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 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @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 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @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 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @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 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @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 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @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 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @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 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @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 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @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 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @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 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @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 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @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: GPL-3.0 pragma solidity >=0.5.0; interface IUniswapV2Pair { event Approval(address indexed owner, address indexed spender, uint value); event Transfer(address indexed from, address indexed to, uint value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint); function balanceOf(address owner) external view returns (uint); function allowance(address owner, address spender) external view returns (uint); function approve(address spender, uint value) external returns (bool); function transfer(address to, uint value) external returns (bool); function transferFrom(address from, address to, uint value) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint); function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; event Mint(address indexed sender, uint amount0, uint amount1); event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); event Swap( address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); function price0CumulativeLast() external view returns (uint); function price1CumulativeLast() external view returns (uint); function kLast() external view returns (uint); function mint(address to) external returns (uint liquidity); function burn(address to) external returns (uint amount0, uint amount1); function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; function skim(address to) external; function sync() external; function initialize(address, address) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.2; import './IUniswapV2Router01.sol'; interface IUniswapV2Router02 is IUniswapV2Router01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint amountOutMin, address[] calldata path, address to, uint deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.2; interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountToken, uint amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB); function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./interfaces/IFarmingLPTokenFactory.sol"; import "./FarmingLPToken.sol"; contract FarmingLPTokenFactory is Ownable, IFarmingLPTokenFactory { address public immutable override router; address public immutable override masterChef; address internal immutable _implementation; address public override yieldVault; address public override migrator; mapping(uint256 => address) public override getFarmingLPToken; constructor( address _router, address _masterChef, address _yieldVault ) { router = _router; masterChef = _masterChef; yieldVault = _yieldVault; FarmingLPToken token = new FarmingLPToken(); token.initialize(address(0), address(0), 0); _implementation = address(token); } function predictFarmingLPTokenAddress(uint256 pid) external view override returns (address token) { token = Clones.predictDeterministicAddress(_implementation, bytes32(pid)); } function updateYieldVault(address vault) external override onlyOwner { if (vault == address(0)) revert InvalidAddress(); yieldVault = vault; emit UpdateVault(vault); } function updateMigrator(address _migrator) external override onlyOwner { if (_migrator != address(0)) revert MigratorSet(); migrator = _migrator; emit UpdateMigrator(_migrator); } function createFarmingLPToken(uint256 pid) external override returns (address token) { if (getFarmingLPToken[pid] != address(0)) revert TokenCreated(); token = Clones.cloneDeterministic(_implementation, bytes32(pid)); FarmingLPToken(token).initialize(router, masterChef, pid); getFarmingLPToken[pid] = token; emit CreateFarmingLPToken(pid, token); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol) pragma solidity ^0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "./interfaces/ISousChef.sol"; import "./interfaces/IFSushi.sol"; import "./interfaces/IFlashStrategySushiSwapFactory.sol"; import "./interfaces/IFlashStrategySushiSwap.sol"; import "./FSushiBill.sol"; contract SousChef is Ownable, ISousChef { using SafeERC20 for IERC20; using SafeCast for uint256; using SafeCast for int256; using DateUtils for uint256; uint256 public constant override BONUS_MULTIPLIER = 10; uint256 public constant override REWARDS_FOR_INITIAL_WEEK = BONUS_MULTIPLIER * 30000e18; address public immutable override fSushi; address public immutable override flashStrategyFactory; uint256 public immutable override startWeek; address internal immutable _implementation; /** * @notice address of IFSushiRestaurant */ address public override restaurant; /** * @notice address of IFSushiKitchen */ address public override kitchen; mapping(uint256 => address) public override getBill; /** * @notice how much rewards to be minted at the week */ mapping(uint256 => uint256) public override weeklyRewards; // week => amount /** * @notice weeklyRewards is guaranteed to be correct before this week (exclusive) */ uint256 public override lastCheckpoint; // week constructor( address _fSushi, address _restaurant, address _kitchen, address _flashStrategyFactory ) { fSushi = _fSushi; restaurant = _restaurant; kitchen = _kitchen; flashStrategyFactory = _flashStrategyFactory; uint256 nextWeek = block.timestamp.toWeekNumber() + 1; startWeek = nextWeek; lastCheckpoint = nextWeek + 1; weeklyRewards[nextWeek] = REWARDS_FOR_INITIAL_WEEK; FSushiBill bill = new FSushiBill(); bill.initialize(0, address(0)); _implementation = address(bill); } function predictBillAddress(uint256 pid) external view override returns (address bill) { bill = Clones.predictDeterministicAddress(_implementation, bytes32(pid)); } function updateRestaurant(address _restaurant) external override onlyOwner { if (_restaurant == address(0)) revert InvalidRestaurant(); restaurant = _restaurant; emit UpdateRestaurant(_restaurant); } function updateKitchen(address _kitchen) external override onlyOwner { if (_kitchen == address(0)) revert InvalidKitchen(); kitchen = _kitchen; emit UpdateKitchen(_kitchen); } function createBill(uint256 pid) external override returns (address bill) { if (getBill[pid] != address(0)) revert BillCreated(); address strategy = IFlashStrategySushiSwapFactory(flashStrategyFactory).getFlashStrategySushiSwap(pid); if (strategy == address(0)) strategy = IFlashStrategySushiSwapFactory(flashStrategyFactory).createFlashStrategySushiSwap(pid); bill = Clones.cloneDeterministic(_implementation, bytes32(pid)); FSushiBill(bill).initialize(pid, IFlashStrategySushiSwap(strategy).fToken()); getBill[pid] = bill; emit CreateBill(pid, bill); } /** * @dev if this function doesn't get called for 512 weeks (around 9.8 years) this contract breaks */ function checkpoint() external override { uint256 from = lastCheckpoint; uint256 until = block.timestamp.toWeekNumber() + 1; if (until <= from) return; for (uint256 i; i < 512; ) { uint256 week = from + i; if (until <= week) break; // last week's circulating supply becomes the total rewards in this week uint256 circulatingSupply = IFSushi(fSushi).checkpointedTotalSupplyDuring(week - 1) - IFSushiRestaurant(restaurant).checkpointedLockedTotalBalanceDuring(week - 1); // emission rate decreases 1% every week uint256 rewards = (circulatingSupply * 99) / 100; // 10x bonus is given only for the first week if (week == startWeek + 1) { rewards /= BONUS_MULTIPLIER; } weeklyRewards[week] = rewards; unchecked { ++i; } } lastCheckpoint = until; } function mintFSushi( uint256 pid, address to, uint256 amount ) external override { if (getBill[pid] != msg.sender) revert Forbidden(); IFSushi(fSushi).mint(to, amount); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface ISousChef { error BillCreated(); error InvalidRestaurant(); error InvalidKitchen(); error Forbidden(); event UpdateRestaurant(address indexed restaurant); event UpdateKitchen(address indexed kitchen); event CreateBill(uint256 indexed pid, address indexed bill); event Checkpoint(); function BONUS_MULTIPLIER() external view returns (uint256); function REWARDS_FOR_INITIAL_WEEK() external view returns (uint256); function fSushi() external view returns (address); function flashStrategyFactory() external view returns (address); function startWeek() external view returns (uint256); function restaurant() external view returns (address); function kitchen() external view returns (address); function getBill(uint256 pid) external view returns (address); function weeklyRewards(uint256 week) external view returns (uint256); function lastCheckpoint() external view returns (uint256); function predictBillAddress(uint256 pid) external view returns (address bill); function updateRestaurant(address _restaurant) external; function updateKitchen(address _kitchen) external; function createBill(uint256 pid) external returns (address strategy); function checkpoint() external; function mintFSushi( uint256 pid, address to, uint256 amount ) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface IFlashStrategySushiSwapFactory { error InvalidFee(); error InvalidFeeRecipient(); error FlashStrategySushiSwapCreated(); event UpdateStakeFeeBPS(uint256 fee); event UpdateFlashStakeFeeBPS(uint256 fee); event UpdateFeeRecipient(address feeRecipient); event CreateFlashStrategySushiSwap(uint256 pid, address strategy); function flashProtocol() external view returns (address); function flpTokenFactory() external view returns (address); function feeRecipient() external view returns (address); function getFlashStrategySushiSwap(uint256 pid) external view returns (address); function predictFlashStrategySushiSwapAddress(uint256 pid) external view returns (address strategy); function updateFeeRecipient(address _feeRecipient) external; function createFlashStrategySushiSwap(uint256 pid) external returns (address strategy); }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; import "./IFlashStrategy.sol"; interface IFlashStrategySushiSwap is IFlashStrategy { error Forbidden(); error InvalidVault(); error AmountTooLow(); error InsufficientYield(); error InsufficientTotalSupply(); function factory() external view returns (address); function flashProtocol() external view returns (address); function fToken() external view returns (address); function sushi() external view returns (address); function flpToken() external view returns (address); function initialize(address _flashProtocol, address _flpToken) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./base/BaseERC20.sol"; import "./interfaces/IFSushiBill.sol"; import "./interfaces/ISousChef.sol"; import "./interfaces/IFSushiRestaurant.sol"; import "./interfaces/IFSushiKitchen.sol"; import "./interfaces/IFSushi.sol"; import "./libraries/DateUtils.sol"; contract FSushiBill is BaseERC20, IFSushiBill { using SafeERC20 for IERC20; using DateUtils for uint256; uint256 internal constant TOKENLESS_PRODUCTION = 40; address public override sousChef; uint256 public override pid; address public override fToken; uint256 public override workingSupply; // amount /** * @notice points = ∫W(t)dt, where W(t) is the working supply at the week */ mapping(uint256 => uint256) public override points; // week => points /** * @notice points are guaranteed to be correct before this time's week start */ uint256 public override lastCheckpoint; // timestamp mapping(address => uint256) public override workingBalanceOf; // account => amount /** * @notice userPoints = ∫w(t)dt, where a(t) is the working balance of account at the week */ mapping(address => mapping(uint256 => uint256)) public override userPoints; // account => week => points /** * @notice userPoints of account is guaranteed to be correct before this week */ mapping(address => uint256) public override userLastCheckpoint; // account => timestamp /** * @notice how much rewards were claimed in total for account */ mapping(address => uint256) public override claimedRewards; // account => amount /** * @notice in the next claim, rewards will be accumulated from this week */ mapping(address => uint256) public override nextClaimableWeek; // account => week function initialize(uint256 _pid, address _fToken) external override initializer { if (_fToken == address(0)) return; BaseERC20_initialize( string.concat("Flash Sushi Bill for ", IERC20Metadata(_fToken).name()), string.concat("x", IERC20Metadata(_fToken).symbol()), "1" ); sousChef = msg.sender; pid = _pid; fToken = _fToken; } function deposit(uint256 amount, address beneficiary) external override { _userCheckpoint(msg.sender); if (amount > 0) { IERC20(fToken).safeTransferFrom(msg.sender, address(this), amount); uint256 balance = _balanceOf[msg.sender] + amount; _balanceOf[msg.sender] = balance; uint256 totalSupply = _totalSupply + amount; _totalSupply = totalSupply; _updateWorkingBalance(msg.sender, balance, totalSupply); _mint(beneficiary, amount); } emit Deposit(msg.sender, amount, beneficiary); } function withdraw(uint256 amount, address beneficiary) external override { _userCheckpoint(msg.sender); if (amount > 0) { uint256 balance = _balanceOf[msg.sender] - amount; _balanceOf[msg.sender] = balance; uint256 totalSupply = _totalSupply - amount; _totalSupply = totalSupply; _updateWorkingBalance(msg.sender, balance, totalSupply); _burn(msg.sender, amount); IERC20(fToken).safeTransfer(beneficiary, amount); } emit Withdraw(msg.sender, amount, beneficiary); } /** * @dev if this function doesn't get called for 512 weeks (around 9.8 years) this contract breaks */ function checkpoint() public override { ISousChef(sousChef).checkpoint(); uint256 prevCheckpoint = lastCheckpoint; _updatePoints(points, workingSupply, prevCheckpoint); if (prevCheckpoint < block.timestamp) { lastCheckpoint = block.timestamp; } emit Checkpoint(); } function userCheckpoint(address account) external override { _userCheckpoint(account); _updateWorkingBalance(account, _balanceOf[account], _totalSupply); } function _userCheckpoint(address account) internal { checkpoint(); uint256 prevCheckpoint = userLastCheckpoint[account]; _updatePoints(userPoints[account], workingBalanceOf[account], prevCheckpoint); if (prevCheckpoint < block.timestamp) { userLastCheckpoint[account] = block.timestamp; } emit UserCheckpoint(account); } function _updatePoints( mapping(uint256 => uint256) storage _points, uint256 workingBalance, uint256 lastTime ) internal { if (workingBalance == 0) return; if (lastTime == 0) { uint256 startWeek = ISousChef(sousChef).startWeek(); lastTime = startWeek.toTimestamp(); } uint256 from = lastTime.toWeekNumber(); for (uint256 i; i < 512; ) { uint256 week = from + i; uint256 weekStart = week.toTimestamp(); uint256 weekEnd = weekStart + WEEK; if (block.timestamp <= weekStart) break; if (block.timestamp < weekEnd) { _points[week] += workingBalance * (block.timestamp - Math.max(lastTime, weekStart)); break; } if (i == 0) { _points[week] += workingBalance * (weekEnd - lastTime); } else { _points[week] += workingBalance * WEEK; } unchecked { ++i; } } } function _updateWorkingBalance( address account, uint256 balance, uint256 supply ) internal { address restaurant = ISousChef(sousChef).restaurant(); IFSushiRestaurant(restaurant).userCheckpoint(account); uint256 week = block.timestamp.toWeekNumber(); uint256 lockedBalance = IFSushiRestaurant(restaurant).lockedUserBalanceDuring(account, week - 1); uint256 lockedTotal = IFSushiRestaurant(restaurant).lockedTotalBalanceDuring(week - 1); uint256 workingBalance = (balance * TOKENLESS_PRODUCTION) / 100; if (lockedTotal > 0) { workingBalance += (((supply * lockedBalance) / lockedTotal) * (100 - TOKENLESS_PRODUCTION)) / 100; } workingBalance = Math.min(workingBalance, balance); uint256 prevBalance = workingBalanceOf[account]; workingBalanceOf[account] = workingBalance; uint256 _workingSupply = workingSupply + workingBalance - prevBalance; workingSupply = _workingSupply; emit UpdateWorkingBalance(account, workingBalance, _workingSupply); } function claimRewards(address beneficiary) external { _userCheckpoint(msg.sender); uint256 prevWeek = nextClaimableWeek[msg.sender]; if (prevWeek == block.timestamp) return; if (prevWeek == 0) { uint256 startWeek = ISousChef(sousChef).startWeek(); prevWeek = startWeek.toTimestamp(); } (address _sousChef, uint256 _pid) = (sousChef, pid); address kitchen = ISousChef(_sousChef).kitchen(); IFSushiKitchen(kitchen).checkpoint(_pid); // add week-by-week rewards until the last week uint256 totalRewards; uint256 from = prevWeek.toWeekNumber(); uint256 to = block.timestamp.toWeekNumber(); // exclusive last index for (uint256 i; i < 512; ) { uint256 week = from + i; if (to <= week) break; uint256 weeklyRewards = ISousChef(_sousChef).weeklyRewards(week); uint256 weight = IFSushiKitchen(kitchen).relativeWeightAt(_pid, week.toTimestamp()); uint256 rewards = (weeklyRewards * weight * userPoints[msg.sender][week]) / points[week] / 1e18; totalRewards += rewards; unchecked { ++i; } } nextClaimableWeek[msg.sender] = to; if (totalRewards > 0) { claimedRewards[msg.sender] += totalRewards; ISousChef(sousChef).mintFSushi(_pid, beneficiary, totalRewards); emit ClaimRewards(msg.sender, beneficiary, totalRewards); } } function _transfer( address from, address to, uint256 amount ) internal override returns (uint256 balanceOfFrom, uint256 balanceOfTo) { _userCheckpoint(from); _userCheckpoint(to); (balanceOfFrom, balanceOfTo) = super._transfer(from, to, amount); uint256 totalSupply = _totalSupply; _updateWorkingBalance(msg.sender, balanceOfFrom, totalSupply); _updateWorkingBalance(msg.sender, balanceOfTo, totalSupply); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface IFlashStrategy { event BurnedFToken(address indexed _address, uint256 _tokenAmount, uint256 _yieldReturned); // This is how principal will be deposited into the contract // The Flash protocol allows the strategy to specify how much // should be registered. This allows the strategy to manipulate (eg take fee) // on the principal if the strategy requires function depositPrincipal(uint256 _tokenAmount) external returns (uint256); // This is how principal will be returned from the contract function withdrawPrincipal(uint256 _tokenAmount) external; // Responsible for instant upfront yield. Takes fERC20 tokens specific to this // strategy. The strategy is responsible for returning some amount of principal tokens function burnFToken( uint256 _tokenAmount, uint256 _minimumReturned, address _yieldTo ) external returns (uint256); // This should return the current total of all principal within the contract function getPrincipalBalance() external view returns (uint256); // This should return the current total of all yield generated to date (including bootstrapped tokens) function getYieldBalance() external view returns (uint256); // This should return the principal token address (eg DAI) function getPrincipalAddress() external view returns (address); // View function which quotes how many principal tokens would be returned if x // fERC20 tokens are burned function quoteMintFToken(uint256 _tokenAmount, uint256 duration) external view returns (uint256); // View function which quotes how many principal tokens would be returned if x // fERC20 tokens are burned // IMPORTANT NOTE: This should utilise bootstrap tokens if they exist // bootstrapped tokens are any principal tokens that exist within the smart contract function quoteBurnFToken(uint256 _tokenAmount) external view returns (uint256); // The function to set the fERC20 address within the strategy function setFTokenAddress(address _fTokenAddress) external; // This should return what the maximum stake duration is function getMaxStakeDuration() external view returns (uint256); }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IFSushiBill is IERC20Metadata { error NoClaimableRewards(); event Deposit(address indexed account, uint256 amount, address indexed beneficiary); event Withdraw(address indexed account, uint256 amount, address indexed beneficiary); event Checkpoint(); event UserCheckpoint(address indexed account); event UpdateWorkingBalance(address indexed account, uint256 workingBalance, uint256 workingSupply); event ClaimRewards(address indexed account, address indexed beneficiary, uint256 amount); function sousChef() external view returns (address); function pid() external view returns (uint256); function fToken() external view returns (address); function workingSupply() external view returns (uint256); function points(uint256 week) external view returns (uint256); function lastCheckpoint() external view returns (uint256 timestamp); function workingBalanceOf(address account) external view returns (uint256); function userPoints(address account, uint256 week) external view returns (uint256); function userLastCheckpoint(address account) external view returns (uint256 timestamp); function claimedRewards(address account) external view returns (uint256); function nextClaimableWeek(address account) external view returns (uint256); function initialize(uint256 _pid, address _fToken) external; function deposit(uint256 amount, address beneficiary) external; function withdraw(uint256 amount, address beneficiary) external; function claimRewards(address beneficiary) external; function checkpoint() external; function userCheckpoint(address account) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface IFSushiRestaurant { function startWeek() external view returns (uint256); function lastDeposit(address account) external view returns (uint256); function lockedTotalBalanceDuring(uint256 week) external view returns (uint256); function lockedUserBalanceDuring(address account, uint256 week) external view returns (uint256); function lastCheckpoint() external view returns (uint256); function lastUserCheckpoint(address account) external view returns (uint256); function checkpointedLockedTotalBalanceDuring(uint256 week) external returns (uint256); function checkpointedLockedUserBalanceDuring(address account, uint256 week) external returns (uint256); function checkpoint() external; function userCheckpoint(address account) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface IFSushiKitchen { error InvalidPid(); event AddPool(uint256 indexed pid); event UpdateWeight(uint256 indexed pid, uint256 weightPoints, uint256 totalWeightPoints); function flashStrategyFactory() external view returns (address); function totalWeightPointsLength() external view returns (uint256); function weightPointsLength(uint256 pid) external view returns (uint256); function totalWeightPoints() external view returns (uint256); function weightPoints(uint256 pid) external view returns (uint256); function totalWeightPointsAt(uint256 timestamp) external view returns (uint256); function weightPointsAt(uint256 pid, uint256 timestamp) external view returns (uint256); function relativeWeight(uint256 pid) external view returns (uint256); function relativeWeightAt(uint256 pid, uint256 timestamp) external view returns (uint256); function addPool(uint256 pid, uint256 points) external; function updateWeight(uint256 pid, uint256 points) external; function checkpoint(uint256 pid) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; // WARNING: There is a known vuln contained within this contract related to vote delegation, // it's NOT recommmended to use this in production. // SushiToken with Governance. contract SushiToken is ERC20("SushiToken", "SUSHI"), Ownable { using SafeMath for uint256; /// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterChef). function mint(address _to, uint256 _amount) public onlyOwner { _mint(_to, _amount); _moveDelegates(address(0), _delegates[_to], _amount); } // Copied and modified from YAM code: // https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernanceStorage.sol // https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernance.sol // Which is copied and modified from COMPOUND: // https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/Comp.sol /// @notice A record of each accounts delegate mapping(address => address) internal _delegates; /// @notice A checkpoint for marking number of votes from a given block struct Checkpoint { uint32 fromBlock; uint256 votes; } /// @notice A record of votes checkpoints for each account, by index mapping(address => mapping(uint32 => Checkpoint)) public checkpoints; /// @notice The number of checkpoints for each account mapping(address => uint32) public numCheckpoints; /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the delegation struct used by the contract bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); /// @notice A record of states for signing / validating signatures mapping(address => uint256) public nonces; /// @notice An event thats emitted when an account changes its delegate event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); /// @notice An event thats emitted when a delegate account's vote balance changes event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); /** * @notice Delegate votes from `msg.sender` to `delegatee` * @param delegator The address to get delegatee for */ function delegates(address delegator) external view returns (address) { return _delegates[delegator]; } /** * @notice Delegate votes from `msg.sender` to `delegatee` * @param delegatee The address to delegate votes to */ function delegate(address delegatee) external { return _delegate(msg.sender, delegatee); } /** * @notice Delegates votes from signatory to `delegatee` * @param delegatee The address to delegate votes to * @param nonce The contract state required to match the signature * @param expiry The time at which to expire the signature * @param v The recovery byte of the signature * @param r Half of the ECDSA signature pair * @param s Half of the ECDSA signature pair */ function delegateBySig( address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s ) external { bytes32 domainSeparator = keccak256( abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), getChainId(), address(this)) ); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "SUSHI::delegateBySig: invalid signature"); require(nonce == nonces[signatory]++, "SUSHI::delegateBySig: invalid nonce"); require(block.timestamp <= expiry, "SUSHI::delegateBySig: signature expired"); return _delegate(signatory, delegatee); } /** * @notice Gets the current votes balance for `account` * @param account The address to get votes balance * @return The number of current votes for `account` */ function getCurrentVotes(address account) external view returns (uint256) { uint32 nCheckpoints = numCheckpoints[account]; return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; } /** * @notice Determine the prior number of votes for an account as of a block number * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. * @param account The address of the account to check * @param blockNumber The block number to get the vote balance at * @return The number of votes the account had as of the given block */ function getPriorVotes(address account, uint256 blockNumber) external view returns (uint256) { require(blockNumber < block.number, "SUSHI::getPriorVotes: not yet determined"); uint32 nCheckpoints = numCheckpoints[account]; if (nCheckpoints == 0) { return 0; } // First check most recent balance if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { return checkpoints[account][nCheckpoints - 1].votes; } // Next check implicit zero balance if (checkpoints[account][0].fromBlock > blockNumber) { return 0; } uint32 lower = 0; uint32 upper = nCheckpoints - 1; while (upper > lower) { uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Checkpoint memory cp = checkpoints[account][center]; if (cp.fromBlock == blockNumber) { return cp.votes; } else if (cp.fromBlock < blockNumber) { lower = center; } else { upper = center - 1; } } return checkpoints[account][lower].votes; } function _delegate(address delegator, address delegatee) internal { address currentDelegate = _delegates[delegator]; uint256 delegatorBalance = balanceOf(delegator); // balance of underlying SUSHIs (not scaled); _delegates[delegator] = delegatee; emit DelegateChanged(delegator, currentDelegate, delegatee); _moveDelegates(currentDelegate, delegatee, delegatorBalance); } function _moveDelegates( address srcRep, address dstRep, uint256 amount ) internal { if (srcRep != dstRep && amount > 0) { if (srcRep != address(0)) { // decrease old representative uint32 srcRepNum = numCheckpoints[srcRep]; uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; uint256 srcRepNew = srcRepOld.sub(amount); _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); } if (dstRep != address(0)) { // increase new representative uint32 dstRepNum = numCheckpoints[dstRep]; uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; uint256 dstRepNew = dstRepOld.add(amount); _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); } } } function _writeCheckpoint( address delegatee, uint32 nCheckpoints, uint256 oldVotes, uint256 newVotes ) internal { uint32 blockNumber = safe32(block.number, "SUSHI::_writeCheckpoint: block number exceeds 32 bits"); if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; } else { checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); numCheckpoints[delegatee] = nCheckpoints + 1; } emit DelegateVotesChanged(delegatee, oldVotes, newVotes); } function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) { require(n < 2**32, errorMessage); return uint32(n); } function getChainId() internal view returns (uint256) { return block.chainid; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./SushiToken.sol"; interface IMigratorChef { // Perform LP token migration from legacy UniswapV2 to SushiSwap. // Take the current LP token address and return the new LP token address. // Migrator should have full access to the caller's LP token. // Return the new LP token address. // // XXX Migrator must have allowance access to UniswapV2 LP tokens. // SushiSwap must mint EXACTLY the same amount of SushiSwap LP tokens or // else something bad will happen. Traditional UniswapV2 does not // do that so be careful! function migrate(IERC20 token) external returns (IERC20); } // MasterChef is the master of Sushi. He can make Sushi and he is a fair guy. // // Note that it's ownable and the owner wields tremendous power. The ownership // will be transferred to a governance smart contract once SUSHI is sufficiently // distributed and the community can show to govern itself. // // Have fun reading it. Hopefully it's bug-free. God bless. contract MasterChef is Ownable { using SafeMath for uint256; using SafeERC20 for IERC20; // Info of each user. struct UserInfo { uint256 amount; // How many LP tokens the user has provided. uint256 rewardDebt; // Reward debt. See explanation below. // // We do some fancy math here. Basically, any point in time, the amount of SUSHIs // entitled to a user but is pending to be distributed is: // // pending reward = (user.amount * pool.accSushiPerShare) - user.rewardDebt // // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens: // 1. The pool's `accSushiPerShare` (and `lastRewardBlock`) gets updated. // 2. User receives the pending reward sent to his/her address. // 3. User's `amount` gets updated. // 4. User's `rewardDebt` gets updated. } // Info of each pool. struct PoolInfo { IERC20 lpToken; // Address of LP token contract. uint256 allocPoint; // How many allocation points assigned to this pool. SUSHIs to distribute per block. uint256 lastRewardBlock; // Last block number that SUSHIs distribution occurs. uint256 accSushiPerShare; // Accumulated SUSHIs per share, times 1e12. See below. } // The SUSHI TOKEN! SushiToken public sushi; // Dev address. address public devaddr; // Block number when bonus SUSHI period ends. uint256 public bonusEndBlock; // SUSHI tokens created per block. uint256 public sushiPerBlock; // Bonus muliplier for early sushi makers. uint256 public constant BONUS_MULTIPLIER = 10; // The migrator contract. It has a lot of power. Can only be set through governance (owner). IMigratorChef public migrator; // Info of each pool. PoolInfo[] public poolInfo; // Info of each user that stakes LP tokens. mapping(uint256 => mapping(address => UserInfo)) public userInfo; // Total allocation poitns. Must be the sum of all allocation points in all pools. uint256 public totalAllocPoint = 0; // The block number when SUSHI mining starts. uint256 public startBlock; event Deposit(address indexed user, uint256 indexed pid, uint256 amount); event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); constructor( SushiToken _sushi, address _devaddr, uint256 _sushiPerBlock, uint256 _startBlock, uint256 _bonusEndBlock ) { sushi = _sushi; devaddr = _devaddr; sushiPerBlock = _sushiPerBlock; bonusEndBlock = _bonusEndBlock; startBlock = _startBlock; } function poolLength() external view returns (uint256) { return poolInfo.length; } // Add a new lp to the pool. Can only be called by the owner. // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do. function add( uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate ) public onlyOwner { if (_withUpdate) { massUpdatePools(); } uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; totalAllocPoint = totalAllocPoint.add(_allocPoint); poolInfo.push( PoolInfo({ lpToken: _lpToken, allocPoint: _allocPoint, lastRewardBlock: lastRewardBlock, accSushiPerShare: 0 }) ); } // Update the given pool's SUSHI allocation point. Can only be called by the owner. function set( uint256 _pid, uint256 _allocPoint, bool _withUpdate ) public onlyOwner { if (_withUpdate) { massUpdatePools(); } totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); poolInfo[_pid].allocPoint = _allocPoint; } // Set the migrator contract. Can only be called by the owner. function setMigrator(IMigratorChef _migrator) public onlyOwner { migrator = _migrator; } // Migrate lp token to another lp contract. Can be called by anyone. We trust that migrator contract is good. function migrate(uint256 _pid) public { require(address(migrator) != address(0), "migrate: no migrator"); PoolInfo storage pool = poolInfo[_pid]; IERC20 lpToken = pool.lpToken; uint256 bal = lpToken.balanceOf(address(this)); lpToken.safeApprove(address(migrator), bal); IERC20 newLpToken = migrator.migrate(lpToken); require(bal == newLpToken.balanceOf(address(this)), "migrate: bad"); pool.lpToken = newLpToken; } // Return reward multiplier over the given _from to _to block. function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) { if (_to <= bonusEndBlock) { return _to.sub(_from).mul(BONUS_MULTIPLIER); } else if (_from >= bonusEndBlock) { return _to.sub(_from); } else { return bonusEndBlock.sub(_from).mul(BONUS_MULTIPLIER).add(_to.sub(bonusEndBlock)); } } // View function to see pending SUSHIs on frontend. function pendingSushi(uint256 _pid, address _user) external view returns (uint256) { PoolInfo storage pool = poolInfo[_pid]; UserInfo storage user = userInfo[_pid][_user]; uint256 accSushiPerShare = pool.accSushiPerShare; uint256 lpSupply = pool.lpToken.balanceOf(address(this)); if (block.number > pool.lastRewardBlock && lpSupply != 0) { uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); uint256 sushiReward = multiplier.mul(sushiPerBlock).mul(pool.allocPoint).div(totalAllocPoint); accSushiPerShare = accSushiPerShare.add(sushiReward.mul(1e12).div(lpSupply)); } return user.amount.mul(accSushiPerShare).div(1e12).sub(user.rewardDebt); } // Update reward vairables for all pools. Be careful of gas spending! function massUpdatePools() public { uint256 length = poolInfo.length; for (uint256 pid = 0; pid < length; ++pid) { updatePool(pid); } } // Update reward variables of the given pool to be up-to-date. function updatePool(uint256 _pid) public { PoolInfo storage pool = poolInfo[_pid]; if (block.number <= pool.lastRewardBlock) { return; } uint256 lpSupply = pool.lpToken.balanceOf(address(this)); if (lpSupply == 0) { pool.lastRewardBlock = block.number; return; } uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number); uint256 sushiReward = multiplier.mul(sushiPerBlock).mul(pool.allocPoint).div(totalAllocPoint); sushi.mint(devaddr, sushiReward.div(10)); sushi.mint(address(this), sushiReward); pool.accSushiPerShare = pool.accSushiPerShare.add(sushiReward.mul(1e12).div(lpSupply)); pool.lastRewardBlock = block.number; } // Deposit LP tokens to MasterChef for SUSHI allocation. function deposit(uint256 _pid, uint256 _amount) public { PoolInfo storage pool = poolInfo[_pid]; UserInfo storage user = userInfo[_pid][msg.sender]; updatePool(_pid); if (user.amount > 0) { uint256 pending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.rewardDebt); safeSushiTransfer(msg.sender, pending); } pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); user.amount = user.amount.add(_amount); user.rewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12); emit Deposit(msg.sender, _pid, _amount); } // Withdraw LP tokens from MasterChef. function withdraw(uint256 _pid, uint256 _amount) public { PoolInfo storage pool = poolInfo[_pid]; UserInfo storage user = userInfo[_pid][msg.sender]; require(user.amount >= _amount, "withdraw: not good"); updatePool(_pid); uint256 pending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.rewardDebt); safeSushiTransfer(msg.sender, pending); user.amount = user.amount.sub(_amount); user.rewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12); pool.lpToken.safeTransfer(address(msg.sender), _amount); emit Withdraw(msg.sender, _pid, _amount); } // Withdraw without caring about rewards. EMERGENCY ONLY. function emergencyWithdraw(uint256 _pid) public { PoolInfo storage pool = poolInfo[_pid]; UserInfo storage user = userInfo[_pid][msg.sender]; pool.lpToken.safeTransfer(address(msg.sender), user.amount); emit EmergencyWithdraw(msg.sender, _pid, user.amount); user.amount = 0; user.rewardDebt = 0; } // Safe sushi transfer function, just in case if rounding error causes pool to not have enough SUSHIs. function safeSushiTransfer(address _to, uint256 _amount) internal { uint256 sushiBal = sushi.balanceOf(address(this)); if (_amount > sushiBal) { sushi.transfer(_to, sushiBal); } else { sushi.transfer(_to, _amount); } } // Update dev address by the previous dev. function dev(address _devaddr) public { require(msg.sender == devaddr, "dev: wut?"); devaddr = _devaddr; } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../interfaces/IFlashStrategy.sol"; import "../interfaces/IFlashFToken.sol"; import "../interfaces/IFlashNFT.sol"; import "../interfaces/IFlashFTokenFactory.sol"; contract FlashProtocol is Ownable, ReentrancyGuard { using SafeERC20 for IERC20; address public immutable flashNFTAddress; address immutable flashFTokenFactoryAddress; // Define the structure for each strategy struct StrategyInformation { address fTokenAddress; address principalTokenAddress; } mapping(address => StrategyInformation) strategies; // This will store the NFT ID to StakeID mapping mapping(uint256 => uint256) nftIdMappingsToStakeIds; // This will store how many stakes we have uint256 stakeCount = 0; // The global fToken mint fee uint96 globalMintFee = 0; address globalMintFeeRecipient = 0x5089722613C2cCEe071C39C59e9889641f435F15; // This defines the structure of the Stake information we store struct StakeStruct { address stakerAddress; // Address of staker address strategyAddress; // Address of strategy being used uint256 stakeStartTs; // Unix timestamp of when stake started uint256 stakeDuration; // Time in seconds from start time until stake ends uint256 stakedAmount; // The amount of tokens staked bool active; // Stake has been removed/unstaked uint256 nftId; // NFT id if set uint256 fTokensToUser; // How many fERC20 tokens were minted uint256 fTokensFee; // How many fERC20 tokens were taken as fee uint256 totalFTokenBurned; uint256 totalStakedWithdrawn; } mapping(uint256 => StakeStruct) stakes; // Define events event StrategyRegistered( address indexed _strategyAddress, address indexed _principalTokenAddress, address indexed _fTokenAddress ); event Staked(uint256 _stakeId); event Unstaked(uint256 _stakeId, uint256 _tokensReturned, uint256 _fTokensBurned, bool _stakeFinished); event NFTIssued(uint256 _stakeId, uint256 nftId); constructor(address _flashNFTAddress, address _flashFTokenFactoryAddress) { flashNFTAddress = _flashNFTAddress; flashFTokenFactoryAddress = _flashFTokenFactoryAddress; } function registerStrategy( address _strategyAddress, address _principalTokenAddress, string calldata _fTokenName, string calldata _fTokenSymbol ) external { require( strategies[_strategyAddress].principalTokenAddress == address(0) && _strategyAddress != address(0) && _principalTokenAddress != address(0) ); address flashFToken = IFlashFTokenFactory(flashFTokenFactoryAddress).createFToken(_fTokenName, _fTokenSymbol); // Store the appropriate information strategies[_strategyAddress].fTokenAddress = flashFToken; strategies[_strategyAddress].principalTokenAddress = _principalTokenAddress; IFlashStrategy(_strategyAddress).setFTokenAddress(flashFToken); emit StrategyRegistered(_strategyAddress, _principalTokenAddress, flashFToken); } function stake( address _strategyAddress, uint256 _tokenAmount, uint256 _stakeDuration, address _fTokensTo, bool _issueNFT ) public nonReentrant returns (StakeStruct memory _stake) { require(strategies[_strategyAddress].principalTokenAddress != address(0)); require( _stakeDuration >= 60 && _stakeDuration <= IFlashStrategy(_strategyAddress).getMaxStakeDuration(), "ISD" ); // Transfer the tokens from caller to the strategy contract IERC20(strategies[_strategyAddress].principalTokenAddress).safeTransferFrom( msg.sender, address(_strategyAddress), _tokenAmount ); // Determine how many fERC20 tokens to mint (ask strategy) uint256 tokensToMint = IFlashStrategy(_strategyAddress).quoteMintFToken(_tokenAmount, _stakeDuration); // Deposit into the strategy uint256 principalAfterDeductions = IFlashStrategy(_strategyAddress).depositPrincipal(_tokenAmount); // Calculate fee and if this is more than 0, transfer fee uint256 fee = (tokensToMint * globalMintFee) / 10000; if (fee > 0) { IFlashFToken(strategies[_strategyAddress].fTokenAddress).mint(globalMintFeeRecipient, fee); } // Mint fERC20 tokens to the user IFlashFToken(strategies[_strategyAddress].fTokenAddress).mint(_fTokensTo, (tokensToMint - fee)); // Save the stake details stakeCount = stakeCount + 1; stakes[stakeCount] = StakeStruct( msg.sender, _strategyAddress, block.timestamp, _stakeDuration, principalAfterDeductions, true, 0, (tokensToMint - fee), fee, 0, 0 ); // Mint NFT if requested if (_issueNFT) { issueNFT(stakeCount); } emit Staked(stakeCount); return stakes[stakeCount]; } function unstake( uint256 _id, bool _isNFT, uint256 _fTokenToBurn ) external nonReentrant returns (uint256 _principalReturned, uint256 _fTokensBurned) { StakeStruct memory p; uint256 stakeId; address returnAddress; if (_isNFT) { stakeId = nftIdMappingsToStakeIds[_id]; p = stakes[stakeId]; returnAddress = msg.sender; require(p.nftId == _id, "SNM"); require(IFlashNFT(flashNFTAddress).ownerOf(_id) == msg.sender, "NNO"); } else { stakeId = _id; p = stakes[stakeId]; returnAddress = p.stakerAddress; require(p.nftId == 0, "NTR"); require(p.stakerAddress == msg.sender, "NSO"); } require(p.active == true, "SNE"); bool stakeFinished; uint256 principalToReturn; uint256 percentageIntoStake = (((block.timestamp - p.stakeStartTs) * (10**18)) / p.stakeDuration); if (percentageIntoStake >= (10**18)) { // Stake has ended, simply return principal principalToReturn = p.stakedAmount - p.totalStakedWithdrawn; _fTokenToBurn = 0; stakeFinished = true; } else { require(block.timestamp >= (p.stakeStartTs + 3600), "MIN DUR 1HR"); // Stake has not ended yet, user is trying to withdraw early uint256 fTokenBurnForFullUnstake = ((((10**18) - percentageIntoStake) * (p.fTokensToUser + p.fTokensFee)) / (10**18)); if (p.totalFTokenBurned > fTokenBurnForFullUnstake) { // The total number of fTokens burned is greater than the amount required, no burn required fTokenBurnForFullUnstake = 0; } else { fTokenBurnForFullUnstake = fTokenBurnForFullUnstake - p.totalFTokenBurned; } // Ensure the user cannot burn more fTokens than required if (_fTokenToBurn > fTokenBurnForFullUnstake) { _fTokenToBurn = fTokenBurnForFullUnstake; } // Is the user trying to withdraw everything early? if (_fTokenToBurn == fTokenBurnForFullUnstake) { // Yes, return all principal principalToReturn = p.stakedAmount - p.totalStakedWithdrawn; stakeFinished = true; } else { // No - only a partial withdraw principalToReturn = (((_fTokenToBurn * (10**18)) / (p.fTokensToUser + p.fTokensFee)) * p.stakedAmount) / (10**18); } // Burn these fTokens IFlashFToken(strategies[p.strategyAddress].fTokenAddress).burnFrom(msg.sender, _fTokenToBurn); // Update stake information stakes[stakeId].totalFTokenBurned = p.totalFTokenBurned + _fTokenToBurn; stakes[stakeId].totalStakedWithdrawn = p.totalStakedWithdrawn + principalToReturn; } require(principalToReturn > 0); require(p.stakedAmount >= stakes[stakeId].totalStakedWithdrawn); // if the stake is finished, delete all data related to it (nice to have) if (stakeFinished) { delete stakes[stakeId]; } // if the stake finished and it was NFT based, remove the mapping (nice to have) if (stakeFinished && _isNFT) { delete nftIdMappingsToStakeIds[_id]; } emit Unstaked(stakeId, principalToReturn, _fTokenToBurn, stakeFinished); // Remove tokens from Strategy and transfer to user IFlashStrategy(p.strategyAddress).withdrawPrincipal(principalToReturn); IERC20(strategies[p.strategyAddress].principalTokenAddress).safeTransfer(returnAddress, principalToReturn); return (principalToReturn, _fTokenToBurn); } function issueNFT(uint256 _stakeId) public returns (uint256 _nftId) { StakeStruct memory p = stakes[_stakeId]; require(p.active == true && p.nftId == 0 && p.stakerAddress == msg.sender); // Mint the NFT uint256 nftId = IFlashNFT(flashNFTAddress).mint(msg.sender); // Store the NFT ID stakes[_stakeId].nftId = nftId; // Update the NFT Mapping so we can look it up later nftIdMappingsToStakeIds[nftId] = _stakeId; emit NFTIssued(_stakeId, nftId); return nftId; } function setMintFeeInfo(address _feeRecipient, uint96 _feePercentageBasis) external onlyOwner { require(_feePercentageBasis <= 2000); globalMintFeeRecipient = _feeRecipient; globalMintFee = _feePercentageBasis; } function getStakeInfo(uint256 _id, bool _isNFT) external view returns (StakeStruct memory _stake) { uint256 stakeId; if (_isNFT) { stakeId = nftIdMappingsToStakeIds[_id]; require(stakes[stakeId].nftId == _id); } else { stakeId = _id; } return stakes[stakeId]; } function flashStake( address _strategyAddress, uint256 _tokenAmount, uint256 _stakeDuration, uint256 _minimumReceived, address _yieldTo, bool _mintNFT ) external { // Stake (re-direct fTokens to this contract) uint256 fTokensToUser = stake(_strategyAddress, _tokenAmount, _stakeDuration, address(this), _mintNFT) .fTokensToUser; IERC20 fToken = IERC20(strategies[_strategyAddress].fTokenAddress); // Approve, burn and send yield to specified address fToken.approve(_strategyAddress, fTokensToUser); IFlashStrategy(_strategyAddress).burnFToken(fTokensToUser, _minimumReceived, _yieldTo); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface IFlashFToken { function mint(address account, uint256 amount) external; function burnFrom(address from, uint256 amount) external; function decimals() external returns (uint8); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface IFlashNFT { function mint(address _recipientAddress) external returns (uint256); function burn(uint256 _tokenId) external returns (bool); function ownerOf(uint256 tokenId) external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface IFlashFTokenFactory { function createFToken(string calldata _fTokenName, string calldata _fTokenSymbol) external returns (address); }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./interfaces/IFlashStrategySushiSwap.sol"; import "./interfaces/IFlashStrategySushiSwapFactory.sol"; import "./interfaces/IFlashFToken.sol"; import "./interfaces/IFarmingLPTokenFactory.sol"; import "./interfaces/IFarmingLPToken.sol"; import "./interfaces/IERC20Receiver.sol"; contract FlashStrategySushiSwap is Initializable, ReentrancyGuard, IFlashStrategySushiSwap { using SafeERC20 for IERC20; address public override factory; /** * @notice address of FlashProtocol */ address public override flashProtocol; /** * @notice address of SUSHI token */ address public override sushi; /** * @notice address of FarmingLPToken */ address public override flpToken; uint256 internal _balancePrincipal; /** * @notice address of fERC20 for this strategy */ address public override fToken; function initialize(address _flashProtocol, address _flpToken) external override initializer { if (_flashProtocol == address(0)) return; factory = msg.sender; flashProtocol = _flashProtocol; sushi = IFarmingLPToken(_flpToken).sushi(); flpToken = _flpToken; } modifier onlyAuthorised() { if (msg.sender != flashProtocol && msg.sender != address(this)) revert Forbidden(); _; } /** * @return amount of principal tokens that are currently deposited */ function getPrincipalBalance() external view override returns (uint256) { return _balancePrincipal; } /** * @return amount of yield tokens that can be rewarded in SUSHI */ function getYieldBalance() public view override returns (uint256) { return IFarmingLPToken(flpToken).withdrawableYieldOf(address(this)); } /** * @return address of LP Token */ function getPrincipalAddress() external view override returns (address) { return flpToken; } /** * @dev called by flashProtocol * @return amountFToken how many fTokens should be minted for a given _amount and _duration (in seconds) */ function quoteMintFToken(uint256 _amount, uint256 _duration) external pure override returns (uint256 amountFToken) { // 1 fToken per 1 year uint256 amountToMint = (_amount * _duration) / 365 days; if (amountToMint == 0) revert AmountTooLow(); return amountToMint; } /** * @return how many aLP rewards should be returned if _amount fERC20 tokens are burned */ function quoteBurnFToken(uint256 _amount) public view override returns (uint256) { uint256 totalSupply = IERC20(fToken).totalSupply(); if (totalSupply == 0) revert InsufficientTotalSupply(); if (_amount > totalSupply) { _amount = totalSupply; } return (getYieldBalance() * _amount) / totalSupply; } function getMaxStakeDuration() public pure override returns (uint256) { return 4 * 365 days; } /** * @dev called by flashProtocol */ function setFTokenAddress(address _fTokenAddress) external override { if (msg.sender != flashProtocol) revert Forbidden(); fToken = _fTokenAddress; } /** * @notice This function will be called whenever a user stakes via the Flash Protocol. * @dev The Strategy owner can choose to implement a fee but the resulting "locked" principal the user should expect * after the stake has ended must be returned. */ function depositPrincipal(uint256 _amount) external override onlyAuthorised returns (uint256) { uint256 fee = _amount / 400; // charge 0.25% _transferFee(flpToken, fee); uint256 amount = _amount - fee; _balancePrincipal += amount; return amount; } /** * @notice This function should withdraw principal from the underlying strategy. */ function withdrawPrincipal(uint256 _amount) external override onlyAuthorised { address _flpToken = flpToken; IFarmingLPToken(_flpToken).checkpoint(); IERC20(_flpToken).safeTransfer(msg.sender, _amount); _balancePrincipal -= _amount; } /** * @notice This is the function the user will be calling when performing a FlashBurn. * @dev It is responsible for burning the fToken supplied by the user and returning yield to the user. */ function burnFToken( uint256 _amount, uint256 _minimumReturned, address _yieldTo ) external override nonReentrant returns (uint256) { uint256 yield = quoteBurnFToken(_amount); if (yield == 0 || yield < _minimumReturned) revert InsufficientYield(); IFlashFToken(fToken).burnFrom(msg.sender, _amount); address _flpToken = flpToken; IFarmingLPToken(_flpToken).withdraw(yield, address(this)); address lpToken = IFarmingLPToken(_flpToken).lpToken(); uint256 balanceLPToken = IERC20(lpToken).balanceOf(address(this)); _transfer(lpToken, _yieldTo, balanceLPToken); address _sushi = sushi; uint256 balanceSushi = IERC20(_sushi).balanceOf(address(this)); _transfer(_sushi, _yieldTo, balanceSushi); emit BurnedFToken(msg.sender, _amount, yield); return yield; } function _transfer( address token, address to, uint256 amount ) internal { uint256 fee = amount / 100; // charge 1% IERC20(token).safeTransfer(to, amount - fee); _transferFee(token, fee); } function _transferFee(address token, uint256 amount) internal { if (amount > 0) { address feeRecipient = IFlashStrategySushiSwapFactory(factory).feeRecipient(); IERC20(token).safeTransfer(feeRecipient, amount); if (feeRecipient.code.length > 0) { try IERC20Receiver(feeRecipient).onReceiveERC20(token, address(this), amount) {} catch {} } } } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; interface IERC20Receiver { function onReceiveERC20( address token, address from, uint256 amount ) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./interfaces/IFlashStrategySushiSwapFactory.sol"; import "./FlashStrategySushiSwap.sol"; interface IFlashProtocol { function registerStrategy( address _strategyAddress, address _principalTokenAddress, string calldata _fTokenName, string calldata _fTokenSymbol ) external; } contract FlashStrategySushiSwapFactory is Ownable, IFlashStrategySushiSwapFactory { bytes16 private constant _SYMBOLS = "0123456789abcdef"; /** * @notice address of FlashProtocol */ address public immutable override flashProtocol; /** * @notice address of FarmingLPTokenFactory */ address public immutable override flpTokenFactory; address internal immutable _implementation; /** * @notice fee recipient */ address public override feeRecipient; mapping(uint256 => address) public override getFlashStrategySushiSwap; constructor( address _flashProtocol, address _flpTokenFactory, address _feeRecipient ) { flashProtocol = _flashProtocol; flpTokenFactory = _flpTokenFactory; updateFeeRecipient(_feeRecipient); FlashStrategySushiSwap strategy = new FlashStrategySushiSwap(); strategy.initialize(address(0), address(0)); _implementation = address(strategy); } function predictFlashStrategySushiSwapAddress(uint256 pid) external view override returns (address token) { token = Clones.predictDeterministicAddress(_implementation, bytes32(pid)); } function updateFeeRecipient(address _feeRecipient) public override onlyOwner { if (_feeRecipient == address(0)) revert InvalidFeeRecipient(); feeRecipient = _feeRecipient; emit UpdateFeeRecipient(_feeRecipient); } function createFlashStrategySushiSwap(uint256 pid) external override returns (address strategy) { if (getFlashStrategySushiSwap[pid] != address(0)) revert FlashStrategySushiSwapCreated(); address flpToken = IFarmingLPTokenFactory(flpTokenFactory).getFarmingLPToken(pid); if (flpToken == address(0)) flpToken = IFarmingLPTokenFactory(flpTokenFactory).createFarmingLPToken(pid); strategy = Clones.cloneDeterministic(_implementation, bytes32(pid)); FlashStrategySushiSwap(strategy).initialize(flashProtocol, flpToken); getFlashStrategySushiSwap[pid] = strategy; string memory name = string.concat("fToken for ", IERC20Metadata(flpToken).name()); string memory symbol = string.concat( "f", IERC20Metadata(flpToken).symbol(), "-", _toHexString(uint160(flpToken) >> 144, 4) ); IFlashProtocol(flashProtocol).registerStrategy(strategy, flpToken, name, symbol); emit CreateFlashStrategySushiSwap(pid, strategy); } function _toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(length); for (uint256 i = 0; i < length; ) { buffer[length - i - 1] = _SYMBOLS[value & 0xf]; value >>= 4; unchecked { ++i; } } return string(buffer); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/access/Ownable.sol"; import "./interfaces/IFSushiKitchen.sol"; import "./interfaces/IFlashStrategySushiSwapFactory.sol"; import "./libraries/Snapshots.sol"; contract FSushiKitchen is Ownable, IFSushiKitchen { using Snapshots for Snapshots.Snapshot[]; uint256 internal constant BASE = 1e18; address public immutable override flashStrategyFactory; Snapshots.Snapshot[] internal _totalWeightPoints; mapping(uint256 => Snapshots.Snapshot[]) internal _weightPoints; // pid -> snapshots constructor(address _flashStrategyFactory) { flashStrategyFactory = _flashStrategyFactory; } function totalWeightPointsLength() external view override returns (uint256) { return _totalWeightPoints.size(); } function weightPointsLength(uint256 pid) external view override returns (uint256) { return _weightPoints[pid].size(); } function totalWeightPoints() external view override returns (uint256) { return _totalWeightPoints.lastValue(); } function weightPoints(uint256 pid) external view override returns (uint256) { return _weightPoints[pid].lastValue(); } function totalWeightPointsAt(uint256 timestamp) external view override returns (uint256) { return _totalWeightPoints.valueAt(timestamp); } function weightPointsAt(uint256 pid, uint256 timestamp) external view override returns (uint256) { return _weightPoints[pid].valueAt(timestamp); } function relativeWeight(uint256 pid) external view override returns (uint256) { uint256 totalPoints = _totalWeightPoints.lastValue(); if (totalPoints == 0) return 0; uint256 points = _weightPoints[pid].lastValue(); return (points * BASE) / totalPoints; } function relativeWeightAt(uint256 pid, uint256 timestamp) external view override returns (uint256) { uint256 totalPoints = _totalWeightPoints.valueAt(timestamp); if (totalPoints == 0) return 0; uint256 points = _weightPoints[pid].valueAt(timestamp); return (points * BASE) / totalPoints; } function addPool(uint256 pid, uint256 points) external override { address strategy = IFlashStrategySushiSwapFactory(flashStrategyFactory).getFlashStrategySushiSwap(pid); if (strategy == address(0)) revert InvalidPid(); emit AddPool(pid); _updateWeight(pid, points); } function updateWeight(uint256 pid, uint256 points) external override onlyOwner { if (_weightPoints[pid].size() == 0) revert InvalidPid(); _updateWeight(pid, points); } function _updateWeight(uint256 pid, uint256 points) internal { uint256 prev = _weightPoints[pid].lastValue(); _weightPoints[pid].append(points); uint256 totalPoints = _totalWeightPoints.lastValue() + points - prev; _totalWeightPoints.append(totalPoints); emit UpdateWeight(pid, points, totalPoints); } function checkpoint(uint256) external override { // Empty: for future compatibility } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; library Snapshots { error InvalidTimestamp(); struct Snapshot { uint256 value; uint256 timestamp; } function size(Snapshot[] storage snapshots) internal view returns (uint256) { return snapshots.length; } function lastValue(Snapshot[] storage snapshots) internal view returns (uint256) { uint256 _length = snapshots.length; return _length > 0 ? snapshots[_length - 1].value : 0; } function valueAt(Snapshot[] storage snapshots, uint256 timestamp) internal view returns (uint256) { uint256 _now = block.timestamp; if (_now < timestamp) revert InvalidTimestamp(); uint256 _length = snapshots.length; if (_length == 0) { return 0; } // First check most recent balance if (snapshots[_length - 1].timestamp <= timestamp) { return snapshots[_length - 1].value; } // Next check implicit zero balance if (timestamp < snapshots[0].timestamp) { return 0; } unchecked { uint256 lower = 0; uint256 upper = _length - 1; while (upper > lower) { uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow Snapshot memory snapshot = snapshots[center]; if (snapshot.timestamp == _now) { return snapshot.value; } else if (snapshot.timestamp < _now) { lower = center; } else { upper = center - 1; } } return snapshots[lower].value; } } function append(Snapshot[] storage snapshots, uint256 value) internal { snapshots.push(Snapshot(value, block.timestamp)); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import "./interfaces/IFSushiBar.sol"; import "./interfaces/IFSushi.sol"; import "./libraries/DateUtils.sol"; contract FSushiBar is ERC4626, IFSushiBar { using DateUtils for uint256; uint256 internal constant MINIMUM_PERIOD = 1 weeks; uint256 public immutable override startWeek; /** * @notice timestamp when users lastly deposited */ mapping(address => uint256) public override lastDeposit; // timestamp /** * @dev this is guaranteed to be correct up until the last week * @return minimum number of staked total assets during the whole week */ mapping(uint256 => uint256) public override lockedTotalBalanceDuring; /** * @notice lockedTotalBalanceDuring is guaranteed to be correct before this week */ uint256 public override lastCheckpoint; // week /** * @dev this is guaranteed to be correct up until the last week * @return minimum number of staked assets of account during the whole week */ mapping(address => mapping(uint256 => uint256)) public override lockedUserBalanceDuring; /** * @notice lockedUserBalanceDuring is guaranteed to be correct before this week (exclusive) */ mapping(address => uint256) public override lastUserCheckpoint; // week constructor(address fSushi) ERC4626(IERC20(fSushi)) ERC20("Flash SushiBar", "xfSUSHI") { uint256 nextWeek = block.timestamp.toWeekNumber() + 1; startWeek = nextWeek; lastCheckpoint = nextWeek; } function checkpointedLockedTotalBalanceDuring(uint256 week) external override returns (uint256) { checkpoint(); return lockedTotalBalanceDuring[week]; } function checkpointedLockedUserBalanceDuring(address account, uint256 week) external override returns (uint256) { checkpoint(); return lockedUserBalanceDuring[account][week]; } /** * @dev if this function doesn't get called for 512 weeks (around 9.8 years) this contract breaks */ function checkpoint() public override { uint256 from = lastCheckpoint; uint256 until = block.timestamp.toWeekNumber(); if (until <= from) return; for (uint256 i; i < 512; ) { uint256 week = from + i; if (until <= week) break; lockedTotalBalanceDuring[week + 1] = lockedTotalBalanceDuring[week]; unchecked { ++i; } } lastCheckpoint = until; } /** * @dev if this function doesn't get called for 512 weeks (around 9.8 years) this contract breaks */ function userCheckpoint(address account) public override { checkpoint(); uint256 from = lastUserCheckpoint[account]; if (from == 0) { from = startWeek; } uint256 until = block.timestamp.toWeekNumber(); if (until <= from) return; for (uint256 i; i < 512; ) { uint256 week = from + i; if (until <= week) break; lockedUserBalanceDuring[account][week + 1] = lockedUserBalanceDuring[account][week]; unchecked { ++i; } } lastUserCheckpoint[account] = until; } function depositSigned( uint256 assets, address receiver, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override returns (uint256) { IFSushi(asset()).permit(msg.sender, address(this), assets, deadline, v, r, s); return deposit(assets, receiver); } function mintSigned( uint256 shares, address receiver, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override returns (uint256) { IFSushi(asset()).permit(msg.sender, address(this), previewMint(shares), deadline, v, r, s); return mint(shares, receiver); } function _deposit( address caller, address receiver, uint256 assets, uint256 shares ) internal override { if (block.timestamp < startWeek.toTimestamp()) revert TooEarly(); super._deposit(caller, receiver, assets, shares); userCheckpoint(msg.sender); uint256 week = block.timestamp.toWeekNumber(); lockedTotalBalanceDuring[week] += assets; lockedUserBalanceDuring[msg.sender][week] += assets; lastDeposit[caller] = block.timestamp; } /** * @dev Users can withdraw only when 1 week have passed after `lastDeposit` of their account */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal override { if (block.timestamp < lastDeposit[owner] + MINIMUM_PERIOD) revert TooEarly(); super._withdraw(caller, receiver, owner, assets, shares); userCheckpoint(msg.sender); uint256 week = block.timestamp.toWeekNumber(); lockedTotalBalanceDuring[week] -= assets; lockedUserBalanceDuring[msg.sender][week] -= assets; } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.0; import "@openzeppelin/contracts/interfaces/IERC4626.sol"; import "./IFSushiRestaurant.sol"; interface IFSushiBar is IERC4626, IFSushiRestaurant { error TooEarly(); function depositSigned( uint256 assets, address receiver, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns (uint256); function mintSigned( uint256 shares, address receiver, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external returns (uint256); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "./interfaces/IFSushi.sol"; contract FSushiAirdrops is Ownable { bytes32 private constant CLAIM_TYPEHASH = keccak256("Claim(uint256 chainId,address contract,uint256 id,address account,uint256 amount)"); address public immutable fSushi; address public signer; string[] public airdrops; mapping(uint256 => mapping(address => bool)) public hasClaimed; error NotEOA(); error InvalidName(); error InvalidId(); error InvalidSignature(); error Expired(); error Claimed(); event UpdateSigner(address indexed signer); event AddAirdrop(uint256 indexed id, string name); event Claim(uint256 indexed id, string name, address indexed account, uint256 amount, address indexed beneficiary); constructor(address _fSushi) { fSushi = _fSushi; } function updateSigner(address _signer) external onlyOwner { if (_signer.code.length > 0) revert NotEOA(); signer = _signer; emit UpdateSigner(_signer); } function addAirdrop(string memory name) external onlyOwner { if (bytes(name).length == 0) revert InvalidName(); uint256 id = airdrops.length; airdrops.push(name); emit AddAirdrop(id, name); } function claim( uint256 id, uint256 amount, address beneficiary, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { if (block.timestamp > deadline) revert Expired(); string memory name = airdrops[id]; if (bytes(name).length == 0) revert InvalidId(); if (hasClaimed[id][msg.sender]) revert Claimed(); hasClaimed[id][msg.sender] = true; bytes32 hash = keccak256( abi.encodePacked(block.chainid, address(this), id, msg.sender, amount, beneficiary, deadline) ); address _signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(hash), v, r, s); if (_signer != signer) revert InvalidSignature(); IFSushi(fSushi).mint(beneficiary, amount); emit Claim(id, name, msg.sender, amount, beneficiary); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../interfaces/IERC20Receiver.sol"; contract FeeVault is IERC20Receiver { using SafeERC20 for IERC20; mapping(address => uint256) claimableAmount; function onReceiveERC20( address token, address, uint256 ) external { claimableAmount[token] = IERC20(token).balanceOf(address(this)); } function claim(address token) external { uint256 amount = claimableAmount[token]; claimableAmount[token] = 0; IERC20(token).safeTransfer(msg.sender, amount); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/access/Ownable.sol"; import "../interfaces/IFlashFTokenFactory.sol"; import "./FlashFToken.sol"; contract FlashFTokenFactory is Ownable, IFlashFTokenFactory { event FTokenCreated(address _tokenAddress); constructor() {} function createFToken(string calldata _fTokenName, string calldata _fTokenSymbol) external onlyOwner returns (address) { FlashFToken flashFToken = new FlashFToken(_fTokenName, _fTokenSymbol); flashFToken.transferOwnership(msg.sender); emit FTokenCreated(address(flashFToken)); return address(flashFToken); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; contract FlashFToken is ERC20, ERC20Burnable, ERC20Permit, Ownable { constructor(string memory _tokenName, string memory _tokenSymbol) ERC20(_tokenName, _tokenSymbol) ERC20Permit(_tokenName) {} function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface ISushiBar is IERC20 { function sushi() external view returns (IERC20); function enter(uint256 _amount) external; function leave(uint256 _share) external; }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import "./interfaces/ISushiBar.sol"; contract SushiBarVault is ERC4626 { using SafeERC20 for IERC20; address internal immutable _sushi; address internal immutable _sushiBar; constructor(address sushi, address sushiBar) ERC4626(IERC20(sushi)) ERC20("SushiBar Yield Vault", "yxSUSHI") { _sushi = sushi; _sushiBar = sushiBar; approveMax(); } function totalAssets() public view override returns (uint256) { uint256 total = IERC20(_sushiBar).totalSupply(); return total == 0 ? 0 : (IERC20(_sushi).balanceOf(address(_sushiBar)) * IERC20(_sushiBar).balanceOf(address(this))) / total; } function _initialConvertToShares( uint256 assets, Math.Rounding /*rounding*/ ) internal view override returns (uint256 shares) { uint256 balance = IERC20(_sushi).balanceOf(address(_sushiBar)); return balance == 0 ? assets : (assets * IERC20(_sushiBar).totalSupply()) / balance; } function _initialConvertToAssets( uint256 shares, Math.Rounding /*rounding*/ ) internal view override returns (uint256 assets) { uint256 total = IERC20(_sushiBar).totalSupply(); return total == 0 ? shares : (shares * IERC20(_sushi).balanceOf(address(_sushiBar))) / total; } function approveMax() public { IERC20(_sushi).approve(_sushiBar, type(uint256).max); } function _deposit( address caller, address receiver, uint256 assets, uint256 shares ) internal override { IERC20(_sushi).safeTransferFrom(msg.sender, address(this), assets); ISushiBar(_sushiBar).enter(assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal override { if (caller != owner) { _spendAllowance(owner, caller, shares); } _burn(owner, shares); ISushiBar(_sushiBar).leave(shares); IERC20(_sushi).safeTransfer(receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./libraries/SafeMath.sol"; contract SushiBar is ERC20("SushiBar", "xSUSHI") { using SafeMath for uint256; IERC20 public sushi; constructor(IERC20 _sushi) { sushi = _sushi; } // Enter the bar. Pay some SUSHIs. Earn some shares. function enter(uint256 _amount) public { uint256 totalSushi = sushi.balanceOf(address(this)); uint256 totalShares = totalSupply(); if (totalShares == 0 || totalSushi == 0) { _mint(msg.sender, _amount); } else { uint256 what = _amount.mul(totalShares).div(totalSushi); _mint(msg.sender, what); } sushi.transferFrom(msg.sender, address(this), _amount); } // Leave the bar. Claim back your SUSHIs. function leave(uint256 _share) public { uint256 totalShares = totalSupply(); uint256 what = _share.mul(sushi.balanceOf(address(this))).div(totalShares); _burn(msg.sender, _share); sushi.transfer(msg.sender, what); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; // a library for performing overflow-safe math, updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math) library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256 c) { require((c = a + b) >= b, "SafeMath: Add Overflow"); } function sub(uint256 a, uint256 b) internal pure returns (uint256 c) { require((c = a - b) <= a, "SafeMath: Underflow"); } function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { require(b == 0 || (c = a * b) / b == a, "SafeMath: Mul Overflow"); } function div(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a / b; } function to128(uint256 a) internal pure returns (uint128 c) { require(a <= type(uint128).max, "SafeMath: uint128 Overflow"); c = uint128(a); } } library SafeMath128 { function add(uint128 a, uint128 b) internal pure returns (uint128 c) { require((c = a + b) >= b, "SafeMath: Add Overflow"); } function sub(uint128 a, uint128 b) internal pure returns (uint128 c) { require((c = a - b) <= a, "SafeMath: Underflow"); } }
// SPDX-License-Identifier: BSL-1.1 pragma solidity ^0.8.17; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract ERC20Mock is ERC20 { uint8 internal immutable _decimals; constructor( string memory name, string memory symbol, uint8 __decimals ) ERC20(name, symbol) { _decimals = __decimals; } function decimals() public view override returns (uint8) { return _decimals; } function mint(address account, uint256 amount) public { _mint(account, amount); } function burn(address account, uint256 amount) public { _burn(account, amount); } function transferInternal( address from, address to, uint256 value ) public { _transfer(from, to, value); } function approveInternal( address owner, address spender, uint256 value ) public { _approve(owner, spender, value); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0; interface IERC20Uniswap { event Approval(address indexed owner, address indexed spender, uint256 value); event Transfer(address indexed from, address indexed to, uint256 value); function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0; interface IWETH { function deposit() external payable; function transfer(address to, uint256 value) external returns (bool); function withdraw(uint256) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0; interface IUniswapV2Factory { event PairCreated(address indexed token0, address indexed token1, address pair, uint256); function feeTo() external view returns (address); function feeToSetter() external view returns (address); function migrator() external view returns (address); function getPair(address tokenA, address tokenB) external view returns (address pair); function allPairs(uint256) external view returns (address pair); function allPairsLength() external view returns (uint256); function createPair(address tokenA, address tokenB) external returns (address pair); function setFeeTo(address) external; function setFeeToSetter(address) external; function setMigrator(address) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0; interface IUniswapV2Callee { function uniswapV2Call( address sender, uint256 amount0, uint256 amount1, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0; interface IUniswapV2Pair { event Approval(address indexed owner, address indexed spender, uint256 value); event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; event Mint(address indexed sender, uint256 amount0, uint256 amount1); event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); event Swap( address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint256); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast ); function price0CumulativeLast() external view returns (uint256); function price1CumulativeLast() external view returns (uint256); function kLast() external view returns (uint256); function mint(address to) external returns (uint256 liquidity); function burn(address to) external returns (uint256 amount0, uint256 amount1); function swap( uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data ) external; function skim(address to) external; function sync() external; function initialize(address, address) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.2; import "./IUniswapV2Router01.sol"; interface IUniswapV2Router02 is IUniswapV2Router01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.2; interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns ( uint256 amountA, uint256 amountB, uint256 liquidity ); function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns ( uint256 amountToken, uint256 amountETH, uint256 liquidity ); function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETH( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external returns (uint256 amountToken, uint256 amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountA, uint256 amountB); function removeLiquidityETHWithPermit( address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint256 amountToken, uint256 amountETH); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactETHForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function swapTokensForExactETH( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapETHForExactTokens( uint256 amountOut, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountOut); function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountIn); function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.0; // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false library TransferHelper { function safeApprove( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('approve(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: APPROVE_FAILED"); } function safeTransfer( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FAILED"); } function safeTransferFrom( address token, address from, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED"); } function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, "TransferHelper: ETH_TRANSFER_FAILED"); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "../interfaces/IERC20.sol"; library SafeERC20 { function safeSymbol(IERC20Uniswap token) internal view returns (string memory) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x95d89b41)); return success && data.length > 0 ? abi.decode(data, (string)) : "???"; } function safeName(IERC20Uniswap token) internal view returns (string memory) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x06fdde03)); return success && data.length > 0 ? abi.decode(data, (string)) : "???"; } function safeDecimals(IERC20Uniswap token) public view returns (uint8) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(0x313ce567)); return success && data.length == 32 ? abi.decode(data, (uint8)) : 18; } function safeTransfer( IERC20Uniswap token, address to, uint256 amount ) internal { (bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(0xa9059cbb, to, amount)); require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeERC20: Transfer failed"); } function safeTransferFrom( IERC20Uniswap token, address from, uint256 amount ) internal { (bool success, bytes memory data) = address(token).call( abi.encodeWithSelector(0x23b872dd, from, address(this), amount) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "SafeERC20: TransferFrom failed"); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0; interface IUniswapV2ERC20 { event Approval(address indexed owner, address indexed spender, uint256 value); event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; }
{ "optimizer": { "enabled": true, "runs": 200 }, "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": { "": { "__CACHE_BREAKER__": "0x00000000d41867734bbee4c6863d9255b2b06ac1" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Expired","type":"error"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"MintersLocked","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lastCheckpoint","type":"uint256"}],"name":"Checkpoint","type":"event"},{"anonymous":false,"inputs":[],"name":"LockMinters","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bool","name":"isMinter","type":"bool"}],"name":"SetMinter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"week","type":"uint256"}],"name":"checkpointedTotalSupplyDuring","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastCheckpoint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockMinters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintersLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"_isMinter","type":"bool"}],"name":"setMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startWeek","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalSupplyDuring","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
610160604090808252346200048e57620000198162000493565b6011815270233630b9b41029bab9b434902a37b5b2b760791b6020918183820152835192620000488462000493565b600684526566535553484960d01b8185015260008054336001600160a01b0319821681178355919291906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08480a38251956001600160401b03958688116200047a576004978854906001968783811c931680156200046f575b868410146200045c578190601f9384811162000409575b508690848311600114620003a557889262000399575b5050600019600383901b1c191690871b1789555b815190888211620003865760059283548881811c911680156200037b575b8782101462000368579081838594931162000315575b5086918311600114620002b1578792620002a5575b5050600019600383901b1c191690861b1790555b62093a80420494848601809611620002925760119061014096808852600a55838351620001958162000493565b838152019081522093828251620001ac8162000493565b82815201603160f81b815220928460e052610100978489524660a0528251938401947f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f968787528486015260608501524660808501523060a085015260a0845260c0840197848910908911176200027f5750508590525190206080523060c0526101209081526114519384620004c6853960805184611037015260a05184611103015260c05184611001015260e05184611086015251836110ac0152518261106301525181818161022901526113aa0152f35b634e487b7160e01b825260419052602490fd5b634e487b7160e01b845260118852602484fd5b01519050388062000154565b8488528688208994509190601f198416895b89828210620002fe5750508411620002e4575b505050811b01905562000168565b015160001960f88460031b161c19169055388080620002d6565b8385015186558c97909501949384019301620002c3565b9091925084885286882083808601871c8201928987106200035e575b9186958c9295949301881c01915b8281106200034f5750506200013f565b8a81558695508b91016200033f565b9250819262000331565b634e487b7160e01b885260228c52602488fd5b90607f169062000129565b634e487b7160e01b865260418a52602486fd5b015190503880620000f7565b8c89528789208a94509190601f1984168a5b8a828210620003f25750508411620003d8575b505050811b0189556200010b565b015160001960f88460031b161c19169055388080620003ca565b8385015186558d97909501949384019301620003b7565b9091508b88528688208480850160051c82019289861062000452575b918b91869594930160051c01915b82811062000443575050620000e1565b8a81558594508b910162000433565b9250819262000425565b634e487b7160e01b875260228b52602487fd5b92607f1692620000ca565b634e487b7160e01b84526041600452602484fd5b600080fd5b604081019081106001600160401b03821117620004af57604052565b634e487b7160e01b600052604160045260246000fdfe6080604081815260048036101561001557600080fd5b600092833560e01c90816306fdde0314610b6b57508063095ea7b314610b415780630fd3da3814610af457806318160ddd14610ad55780632381cc6414610aae57806323b872dd146109e2578063313ce567146109c65780633644e515146109a2578063395093511461095257806340c10f191461086a57806370a0823114610832578063715018a6146107d85780637ecebe00146107a05780637fe290f71461077c5780638da5cb5b1461075457806395d89b41146106525780639ebb3ffc14610623578063a457c2d71461057f578063a9059cbb1461054e578063aa271e1a14610510578063c2c4c5c1146104f4578063cf456ae71461045f578063d32e81a514610440578063d505accf14610299578063dd62ed3e14610250578063eddf2d21146102115763f2fde38b1461014c57600080fd5b3461020d57602036600319011261020d57610165610ca8565b9061016e610cd9565b6001600160a01b039182169283156101bb57505082546001600160a01b0319811683178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b906020608492519162461bcd60e51b8352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152fd5b8280fd5b50503461024c578160031936011261024c57602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b5080fd5b50503461024c578060031936011261024c578060209261026e610ca8565b610276610cc3565b6001600160a01b0391821683526002865283832091168252845220549051908152f35b5091903461024c5760e036600319011261024c576102b5610ca8565b6102bd610cc3565b90604435926064356084359060ff8216820361043c5780421161042c576001600160a01b03848116808952600860205284892080549194929392919060001982146104195760018201905585519260208401917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528688860152858a1660608601528a608086015260a085015260c084015260c0835260e083019267ffffffffffffffff93818110858211176104065787525190209061037c610ffe565b92865192602084019461190160f01b865260228501526042840152604283526080830190838210908211176103f357916103cf93916103c793885260c4359260a43592519020611243565b919091611129565b16036103e457506103e1939450610efc565b80f35b51638baa579f60e01b81528590fd5b634e487b7160e01b8b5260418c5260248bfd5b634e487b7160e01b8c5260418d5260248cfd5b634e487b7160e01b8b5260118c5260248bfd5b8251630407b05b60e31b81528890fd5b8680fd5b50503461024c578160031936011261024c57602090600a549051908152f35b50903461020d578060031936011261020d57610479610ca8565b602435928315158094036104f05761048f610cd9565b60ff600754166104e257506001600160a01b03168084526006602052908320805460ff191660ff84161790557f1f96bc657d385fd83da973a43f2ad969e6d96b6779b779571a7306db7ca1cd008380a380f35b825163815eb75760e01b8152fd5b8480fd5b833461050d578060031936011261050d576103e16112d2565b80fd5b50503461024c57602036600319011261024c5760209160ff9082906001600160a01b0361053b610ca8565b1681526006855220541690519015158152f35b50503461024c578060031936011261024c5760209061057861056e610ca8565b6024359033610d8c565b5160018152f35b50823461050d578260031936011261050d57610599610ca8565b918360243592338152600260205281812060018060a01b03861682526020522054908282106105d2576020856105788585038733610efc565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152fd5b503461020d57602036600319011261020d5760209282916106426112d2565b3581526009845220549051908152f35b50903461020d578260031936011261020d57805191836005549060019082821c92828116801561074a575b6020958686108214610737575084885290811561071557506001146106bc575b6106b886866106ae828b0383610d31565b5191829182610c5f565b0390f35b929550600583527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b82841061070257505050826106b8946106ae92820101943861069d565b80548685018801529286019281016106e5565b60ff191687860152505050151560051b83010192506106ae826106b83861069d565b634e487b7160e01b845260229052602483fd5b93607f169361067d565b50503461024c578160031936011261024c57905490516001600160a01b039091168152602090f35b50503461024c578160031936011261024c5760209060ff6007541690519015158152f35b50503461024c57602036600319011261024c5760209181906001600160a01b036107c8610ca8565b1681526008845220549051908152f35b833461050d578060031936011261050d576107f1610cd9565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50503461024c57602036600319011261024c5760209181906001600160a01b0361085a610ca8565b1681526001845220549051908152f35b50903461020d578060031936011261020d57610884610ca8565b9060243591338552600660205260ff828620541615610942576001600160a01b031692831561090057506020827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef926108e08795600354610d69565b6003558585526001835280852082815401905551908152a36103e16112d2565b6020606492519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b8151631dd2188d60e31b81528490fd5b50503461024c578060031936011261024c5761057860209261099b610975610ca8565b338352600286528483206001600160a01b03821684528652918490205460243590610d69565b9033610efc565b50503461024c578160031936011261024c576020906109bf610ffe565b9051908152f35b50503461024c578160031936011261024c576020905160128152f35b5082903461024c57606036600319011261024c576109fe610ca8565b610a06610cc3565b6001600160a01b03821684526002602090815285852033865290529284902054604435939260018201610a42575b602086610578878787610d8c565b848210610a6b5750918391610a606020969561057895033383610efc565b919394819350610a34565b606490602087519162461bcd60e51b8352820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b503461020d57602036600319011261020d5760209282913581526009845220549051908152f35b50503461024c578160031936011261024c576020906003549051908152f35b833461050d578060031936011261050d57610b0d610cd9565b600160ff1960075416176007557f83b471c54c2c3e053b0eaff5194120062ed80b9c544a643eed228777fccde9a38180a180f35b50503461024c578060031936011261024c57602090610578610b61610ca8565b6024359033610efc565b905082843461050d578060031936011261050d57809380549160019083821c92828516948515610c55575b6020958686108114610c4257858952908115610c1e5750600114610bc6575b6106b887876106ae828c0383610d31565b81529295507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b828410610c0b57505050826106b8946106ae92820101948680610bb5565b8054868501880152928601928101610bed565b60ff19168887015250505050151560051b83010192506106ae826106b88680610bb5565b634e487b7160e01b845260228352602484fd5b93607f1693610b96565b6020808252825181830181905290939260005b828110610c9457505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610c72565b600435906001600160a01b0382168203610cbe57565b600080fd5b602435906001600160a01b0382168203610cbe57565b6000546001600160a01b03163303610ced57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b90601f8019910116810190811067ffffffffffffffff821117610d5357604052565b634e487b7160e01b600052604160045260246000fd5b91908201809211610d7657565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b03908116918215610ea95716918215610e585760008281526001602052604081205491808310610e0457604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef95876020965260018652038282205586815220818154019055604051908152a3565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b6001600160a01b03908116918215610fad5716918215610f5d5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260028252604060002085600052825280604060002055604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480611100575b15611059577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f000000000000000000000000000000000000000000000000000000000000000082527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260a0815260c0810181811067ffffffffffffffff821117610d535760405251902090565b507f00000000000000000000000000000000000000000000000000000000000000004614611030565b600581101561122d578061113a5750565b600181036111875760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606490fd5b600281036111d45760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606490fd5b6003146111dd57565b60405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608490fd5b634e487b7160e01b600052602160045260246000fd5b9291907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116112c65791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa156112b95781516001600160a01b038116156112b3579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b600a5462093a80420490808210611417576000905b6102008210611324575b50506020817fde5ae8a37da230f7df39b8ea385fa1ab48e7caa55f1c25eaaef1ed8690f3699892600a55604051908152a1565b61132e8282610d69565b806000526009906020908282526040938460002054958783146000146113a1575050917fde5ae8a37da230f7df39b8ea385fa1ab48e7caa55f1c25eaaef1ed8690f369989593916020959360035494851161138f575b5050505050916112f1565b60005252600020553880808080611384565b939095829395927f000000000000000000000000000000000000000000000000000000000000000010908161140e575b506113e5575b5050506001915001906112e7565b600019830194838611610d765760019560005252806000205491600052600020553880806113d7565b905015386113d1565b505056fea264697066735822122085dafce05f79bc15599ab574e95727745a072d439dd4bd15c87eaabdefd3c8e164736f6c63430008110033
Deployed Bytecode
0x6080604081815260048036101561001557600080fd5b600092833560e01c90816306fdde0314610b6b57508063095ea7b314610b415780630fd3da3814610af457806318160ddd14610ad55780632381cc6414610aae57806323b872dd146109e2578063313ce567146109c65780633644e515146109a2578063395093511461095257806340c10f191461086a57806370a0823114610832578063715018a6146107d85780637ecebe00146107a05780637fe290f71461077c5780638da5cb5b1461075457806395d89b41146106525780639ebb3ffc14610623578063a457c2d71461057f578063a9059cbb1461054e578063aa271e1a14610510578063c2c4c5c1146104f4578063cf456ae71461045f578063d32e81a514610440578063d505accf14610299578063dd62ed3e14610250578063eddf2d21146102115763f2fde38b1461014c57600080fd5b3461020d57602036600319011261020d57610165610ca8565b9061016e610cd9565b6001600160a01b039182169283156101bb57505082546001600160a01b0319811683178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b906020608492519162461bcd60e51b8352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152fd5b8280fd5b50503461024c578160031936011261024c57602090517f0000000000000000000000000000000000000000000000000000000000000ace8152f35b5080fd5b50503461024c578060031936011261024c578060209261026e610ca8565b610276610cc3565b6001600160a01b0391821683526002865283832091168252845220549051908152f35b5091903461024c5760e036600319011261024c576102b5610ca8565b6102bd610cc3565b90604435926064356084359060ff8216820361043c5780421161042c576001600160a01b03848116808952600860205284892080549194929392919060001982146104195760018201905585519260208401917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528688860152858a1660608601528a608086015260a085015260c084015260c0835260e083019267ffffffffffffffff93818110858211176104065787525190209061037c610ffe565b92865192602084019461190160f01b865260228501526042840152604283526080830190838210908211176103f357916103cf93916103c793885260c4359260a43592519020611243565b919091611129565b16036103e457506103e1939450610efc565b80f35b51638baa579f60e01b81528590fd5b634e487b7160e01b8b5260418c5260248bfd5b634e487b7160e01b8c5260418d5260248cfd5b634e487b7160e01b8b5260118c5260248bfd5b8251630407b05b60e31b81528890fd5b8680fd5b50503461024c578160031936011261024c57602090600a549051908152f35b50903461020d578060031936011261020d57610479610ca8565b602435928315158094036104f05761048f610cd9565b60ff600754166104e257506001600160a01b03168084526006602052908320805460ff191660ff84161790557f1f96bc657d385fd83da973a43f2ad969e6d96b6779b779571a7306db7ca1cd008380a380f35b825163815eb75760e01b8152fd5b8480fd5b833461050d578060031936011261050d576103e16112d2565b80fd5b50503461024c57602036600319011261024c5760209160ff9082906001600160a01b0361053b610ca8565b1681526006855220541690519015158152f35b50503461024c578060031936011261024c5760209061057861056e610ca8565b6024359033610d8c565b5160018152f35b50823461050d578260031936011261050d57610599610ca8565b918360243592338152600260205281812060018060a01b03861682526020522054908282106105d2576020856105788585038733610efc565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152fd5b503461020d57602036600319011261020d5760209282916106426112d2565b3581526009845220549051908152f35b50903461020d578260031936011261020d57805191836005549060019082821c92828116801561074a575b6020958686108214610737575084885290811561071557506001146106bc575b6106b886866106ae828b0383610d31565b5191829182610c5f565b0390f35b929550600583527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b82841061070257505050826106b8946106ae92820101943861069d565b80548685018801529286019281016106e5565b60ff191687860152505050151560051b83010192506106ae826106b83861069d565b634e487b7160e01b845260229052602483fd5b93607f169361067d565b50503461024c578160031936011261024c57905490516001600160a01b039091168152602090f35b50503461024c578160031936011261024c5760209060ff6007541690519015158152f35b50503461024c57602036600319011261024c5760209181906001600160a01b036107c8610ca8565b1681526008845220549051908152f35b833461050d578060031936011261050d576107f1610cd9565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50503461024c57602036600319011261024c5760209181906001600160a01b0361085a610ca8565b1681526001845220549051908152f35b50903461020d578060031936011261020d57610884610ca8565b9060243591338552600660205260ff828620541615610942576001600160a01b031692831561090057506020827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef926108e08795600354610d69565b6003558585526001835280852082815401905551908152a36103e16112d2565b6020606492519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b8151631dd2188d60e31b81528490fd5b50503461024c578060031936011261024c5761057860209261099b610975610ca8565b338352600286528483206001600160a01b03821684528652918490205460243590610d69565b9033610efc565b50503461024c578160031936011261024c576020906109bf610ffe565b9051908152f35b50503461024c578160031936011261024c576020905160128152f35b5082903461024c57606036600319011261024c576109fe610ca8565b610a06610cc3565b6001600160a01b03821684526002602090815285852033865290529284902054604435939260018201610a42575b602086610578878787610d8c565b848210610a6b5750918391610a606020969561057895033383610efc565b919394819350610a34565b606490602087519162461bcd60e51b8352820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b503461020d57602036600319011261020d5760209282913581526009845220549051908152f35b50503461024c578160031936011261024c576020906003549051908152f35b833461050d578060031936011261050d57610b0d610cd9565b600160ff1960075416176007557f83b471c54c2c3e053b0eaff5194120062ed80b9c544a643eed228777fccde9a38180a180f35b50503461024c578060031936011261024c57602090610578610b61610ca8565b6024359033610efc565b905082843461050d578060031936011261050d57809380549160019083821c92828516948515610c55575b6020958686108114610c4257858952908115610c1e5750600114610bc6575b6106b887876106ae828c0383610d31565b81529295507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b828410610c0b57505050826106b8946106ae92820101948680610bb5565b8054868501880152928601928101610bed565b60ff19168887015250505050151560051b83010192506106ae826106b88680610bb5565b634e487b7160e01b845260228352602484fd5b93607f1693610b96565b6020808252825181830181905290939260005b828110610c9457505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610c72565b600435906001600160a01b0382168203610cbe57565b600080fd5b602435906001600160a01b0382168203610cbe57565b6000546001600160a01b03163303610ced57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b90601f8019910116810190811067ffffffffffffffff821117610d5357604052565b634e487b7160e01b600052604160045260246000fd5b91908201809211610d7657565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b03908116918215610ea95716918215610e585760008281526001602052604081205491808310610e0457604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef95876020965260018652038282205586815220818154019055604051908152a3565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b6001600160a01b03908116918215610fad5716918215610f5d5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260028252604060002085600052825280604060002055604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b307f000000000000000000000000a24390c62186a8d265344e914f0fd962b81b5f136001600160a01b03161480611100575b15611059577f88b9f6414ab4603c98f91b898f4cd398d7111bb6b5cb05418e5337c9328189ea90565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f2b1a24063c1de52ccbc434e0c03af1ccd7cc467cf79deaf837718a78d08d2e1c60408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c0810181811067ffffffffffffffff821117610d535760405251902090565b507f00000000000000000000000000000000000000000000000000000000000000014614611030565b600581101561122d578061113a5750565b600181036111875760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606490fd5b600281036111d45760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606490fd5b6003146111dd57565b60405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608490fd5b634e487b7160e01b600052602160045260246000fd5b9291907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116112c65791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa156112b95781516001600160a01b038116156112b3579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b600a5462093a80420490808210611417576000905b6102008210611324575b50506020817fde5ae8a37da230f7df39b8ea385fa1ab48e7caa55f1c25eaaef1ed8690f3699892600a55604051908152a1565b61132e8282610d69565b806000526009906020908282526040938460002054958783146000146113a1575050917fde5ae8a37da230f7df39b8ea385fa1ab48e7caa55f1c25eaaef1ed8690f369989593916020959360035494851161138f575b5050505050916112f1565b60005252600020553880808080611384565b939095829395927f0000000000000000000000000000000000000000000000000000000000000ace10908161140e575b506113e5575b5050506001915001906112e7565b600019830194838611610d765760019560005252806000205491600052600020553880806113d7565b905015386113d1565b505056fea264697066735822122085dafce05f79bc15599ab574e95727745a072d439dd4bd15c87eaabdefd3c8e164736f6c63430008110033
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.