diff --git a/.gitmodules b/.gitmodules index 888d42d..0f07815 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/solady"] + path = lib/solady + url = https://github.com/vectorized/solady diff --git a/_imagine/Enjoy.sol b/_imagine/Enjoy.sol deleted file mode 100644 index a911014..0000000 --- a/_imagine/Enjoy.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -/* - - - - - - ░░░░░░░░░░░░░░ - ░░▒▒░░░░░░░░░░░░░░░░░░░░ - ░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░ - ░░▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░ - ░▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░ - ░▓▓▓▒▒▒▒░░░░░░░░░░░░ ░░░░░░░░ - ░▓▓▓▒▒▒▒░░░░░░░░░░░░░░ ░░░░░░░░░░ - ░▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░ - ░▓▓▓▓▓▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ - ░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░ - ░░▓▓▓▓▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░ - ░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒░░░░░░░░░▒▒▒▒▒░░ - ░░▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░ - ░░▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░ - - OURS TRULY, - - - - - - - - - - - - */ - - interface Enjoy { } \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index b29abc1..9982a72 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,7 +2,7 @@ auto_detect_solc = true fs_permissions = [{access = "read", path = "./addresses"}, {access = "read", path = "./package.json"}] fuzz_runs = 500 -libs = ['lib', '_imagine'] +libs = ['lib'] optimizer = true optimizer_runs = 500000 out = 'out' diff --git a/lib/solady b/lib/solady new file mode 160000 index 0000000..6c54795 --- /dev/null +++ b/lib/solady @@ -0,0 +1 @@ +Subproject commit 6c54795ef69838e233020e9ab29f3f6288efdf06 diff --git a/remappings.txt b/remappings.txt index 83fbb69..e1e5d7f 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1 +1,2 @@ -forge-std/=lib/forge-std/src/ \ No newline at end of file +forge-std/=lib/forge-std/src/ +solady/=lib/solady/src/ \ No newline at end of file diff --git a/src/ProtocolRewards.sol b/src/ProtocolRewards.sol index ca2b1c2..06ef037 100644 --- a/src/ProtocolRewards.sol +++ b/src/ProtocolRewards.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {Enjoy} from "../_imagine/Enjoy.sol"; -import {EIP712} from "./lib/EIP712.sol"; +import {EIP712} from "solady/utils/EIP712.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; import {IProtocolRewards} from "./interfaces/IProtocolRewards.sol"; /// @title ProtocolRewards /// @notice Manager of deposits & withdrawals for protocol rewards -contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { +contract ProtocolRewards is IProtocolRewards, EIP712, Ownable { /// @notice The EIP-712 typehash for gasless withdraws bytes32 public constant WITHDRAW_TYPEHASH = keccak256("Withdraw(address from,address to,uint256 amount,uint256 nonce,uint256 deadline)"); @@ -17,7 +17,21 @@ contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { /// @notice An account's nonce for gasless withdraws mapping(address => uint256) public nonces; - constructor() payable EIP712("ProtocolRewards", "1") {} + /// @notice Total Balance across all accounts + uint256 public totalBalance; + + constructor() payable EIP712() {} + + function _domainNameAndVersion() + internal + pure + virtual + override + returns (string memory name, string memory version) + { + name = "ProtocolRewards"; + version = "1"; + } /// @notice The total amount of ETH held in the contract function totalSupply() external view returns (uint256) { @@ -33,11 +47,32 @@ contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { revert ADDRESS_ZERO(); } - balanceOf[to] += msg.value; + _increaseBalance(to, msg.value); emit Deposit(msg.sender, to, reason, msg.value, comment); } + /// @notice Allow admin to increase balance of an address only up the amount of excess ETH in the contract + /// @dev TODO we will probably need a batch version of this function + /// @param to The address to increase the balance of + /// @param amount The amount to increase the balance by + function increaseBalance(address to, uint256 amount) external onlyOwner { + if (amount > address(this).balance - totalBalance) { + revert INVALID_AMOUNT(); + } + _increaseBalance(to, amount); + } + + function _increaseBalance(address to, uint256 amount) internal { + balanceOf[to] += amount; + totalBalance += amount; + } + + function _decreaseBalance(address to, uint256 amount) internal { + balanceOf[to] -= amount; + totalBalance -= amount; + } + /// @notice Generic function to deposit ETH for multiple recipients, with an optional comment /// @param recipients recipients to send the amount to, array aligns with amounts /// @param amounts amounts to send to each recipient, array aligns with recipients @@ -75,7 +110,7 @@ contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { revert ADDRESS_ZERO(); } - balanceOf[currentRecipient] += currentAmount; + _increaseBalance(currentRecipient, currentAmount); emit Deposit(msg.sender, currentRecipient, reasons[i], currentAmount, comment); @@ -85,66 +120,6 @@ contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { } } - /// @notice Used by Zora ERC-721 & ERC-1155 contracts to deposit protocol rewards - /// @param creator Creator for NFT rewards - /// @param creatorReward Creator reward amount - /// @param createReferral Creator referral - /// @param createReferralReward Creator referral reward - /// @param mintReferral Mint referral user - /// @param mintReferralReward Mint referral amount - /// @param firstMinter First minter reward - /// @param firstMinterReward First minter reward amount - /// @param zora ZORA recipient - /// @param zoraReward ZORA amount - function depositRewards( - address creator, - uint256 creatorReward, - address createReferral, - uint256 createReferralReward, - address mintReferral, - uint256 mintReferralReward, - address firstMinter, - uint256 firstMinterReward, - address zora, - uint256 zoraReward - ) external payable { - if (msg.value != (creatorReward + createReferralReward + mintReferralReward + firstMinterReward + zoraReward)) { - revert INVALID_DEPOSIT(); - } - - unchecked { - if (creator != address(0)) { - balanceOf[creator] += creatorReward; - } - if (createReferral != address(0)) { - balanceOf[createReferral] += createReferralReward; - } - if (mintReferral != address(0)) { - balanceOf[mintReferral] += mintReferralReward; - } - if (firstMinter != address(0)) { - balanceOf[firstMinter] += firstMinterReward; - } - if (zora != address(0)) { - balanceOf[zora] += zoraReward; - } - } - - emit RewardsDeposit( - creator, - createReferral, - mintReferral, - firstMinter, - zora, - msg.sender, - creatorReward, - createReferralReward, - mintReferralReward, - firstMinterReward, - zoraReward - ); - } - /// @notice Withdraw protocol rewards /// @param to Withdraws from msg.sender to this address /// @param amount Amount to withdraw (0 for total balance) @@ -163,7 +138,7 @@ contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { amount = balanceOf[owner]; } - balanceOf[owner] -= amount; + _decreaseBalance(owner, amount); emit Withdraw(owner, to, amount); @@ -220,7 +195,7 @@ contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { withdrawHash = keccak256(abi.encode(WITHDRAW_TYPEHASH, from, to, amount, nonces[from]++, deadline)); } - bytes32 digest = _hashTypedDataV4(withdrawHash); + bytes32 digest = _hashTypedData(withdrawHash); address recoveredAddress = ecrecover(digest, v, r, s); @@ -240,7 +215,7 @@ contract ProtocolRewards is Enjoy, IProtocolRewards, EIP712 { amount = balanceOf[from]; } - balanceOf[from] -= amount; + _decreaseBalance(from, amount); emit Withdraw(from, to, amount); diff --git a/src/abstract/ERC1155/ERC1155Rewards.sol b/src/abstract/ERC1155/ERC1155Rewards.sol deleted file mode 100644 index c2cf40a..0000000 --- a/src/abstract/ERC1155/ERC1155Rewards.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import {RewardSplits} from "../RewardSplits.sol"; - -/// @notice The base logic for handling Zora ERC-1155 protocol rewards -/// @dev Used in https://github.com/ourzora/zora-1155-contracts/blob/main/src/nft/ZoraCreator1155Impl.sol -abstract contract ERC1155Rewards is RewardSplits { - constructor(address _protocolRewards, address _zoraRewardRecipient) payable RewardSplits(_protocolRewards, _zoraRewardRecipient) {} - - function _handleRewardsAndGetValueSent( - uint256 msgValue, - uint256 numTokens, - address creator, - address createReferral, - address mintReferral - ) internal returns (uint256) { - uint256 totalReward = computeTotalReward(numTokens); - - if (msgValue < totalReward) { - revert INVALID_ETH_AMOUNT(); - } else if (msgValue == totalReward) { - _depositFreeMintRewards(totalReward, numTokens, creator, createReferral, mintReferral); - - return 0; - } else { - _depositPaidMintRewards(totalReward, numTokens, creator, createReferral, mintReferral); - - unchecked { - return msgValue - totalReward; - } - } - } -} diff --git a/src/abstract/ERC1155/ERC1155RewardsStorageV1.sol b/src/abstract/ERC1155/ERC1155RewardsStorageV1.sol deleted file mode 100644 index f2007bd..0000000 --- a/src/abstract/ERC1155/ERC1155RewardsStorageV1.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -contract ERC1155RewardsStorageV1 { - mapping(uint256 => address) public createReferrals; -} diff --git a/src/abstract/ERC721/ERC721Rewards.sol b/src/abstract/ERC721/ERC721Rewards.sol deleted file mode 100644 index 5bed832..0000000 --- a/src/abstract/ERC721/ERC721Rewards.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import {RewardSplits} from "../RewardSplits.sol"; - -/// @notice The base logic for handling Zora ERC-721 protocol rewards -/// @dev Used in https://github.com/ourzora/zora-drops-contracts/blob/main/src/ERC721Drop.sol -abstract contract ERC721Rewards is RewardSplits { - constructor(address _protocolRewards, address _zoraRewardRecipient) payable RewardSplits(_protocolRewards, _zoraRewardRecipient) {} - - function _handleRewards(uint256 msgValue, uint256 numTokens, uint256 salePrice, address creator, address createReferral, address mintReferral) internal { - uint256 totalReward = computeTotalReward(numTokens); - - if (salePrice == 0) { - if (msgValue != totalReward) { - revert INVALID_ETH_AMOUNT(); - } - - _depositFreeMintRewards(totalReward, numTokens, creator, createReferral, mintReferral); - } else { - uint256 totalSale = numTokens * salePrice; - - if (msgValue != (totalReward + totalSale)) { - revert INVALID_ETH_AMOUNT(); - } - - _depositPaidMintRewards(totalReward, numTokens, creator, createReferral, mintReferral); - } - } -} diff --git a/src/abstract/ERC721/ERC721RewardsStorageV1.sol b/src/abstract/ERC721/ERC721RewardsStorageV1.sol deleted file mode 100644 index 0e352e5..0000000 --- a/src/abstract/ERC721/ERC721RewardsStorageV1.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -abstract contract ERC721RewardsStorageV1 { - address public createReferral; -} diff --git a/src/abstract/RewardSplits.sol b/src/abstract/RewardSplits.sol deleted file mode 100644 index 9a4196e..0000000 --- a/src/abstract/RewardSplits.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import {IProtocolRewards} from "../interfaces/IProtocolRewards.sol"; - -struct RewardsSettings { - uint256 creatorReward; - uint256 createReferralReward; - uint256 mintReferralReward; - uint256 firstMinterReward; - uint256 zoraReward; -} - -/// @notice Common logic for between Zora ERC-721 & ERC-1155 contracts for protocol reward splits & deposits -abstract contract RewardSplits { - error CREATOR_FUNDS_RECIPIENT_NOT_SET(); - error INVALID_ADDRESS_ZERO(); - error INVALID_ETH_AMOUNT(); - error ONLY_CREATE_REFERRAL(); - - uint256 internal constant TOTAL_REWARD_PER_MINT = 0.000777 ether; - - uint256 internal constant CREATOR_REWARD = 0.000333 ether; - uint256 internal constant FIRST_MINTER_REWARD = 0.000111 ether; - - uint256 internal constant CREATE_REFERRAL_FREE_MINT_REWARD = 0.000111 ether; - uint256 internal constant MINT_REFERRAL_FREE_MINT_REWARD = 0.000111 ether; - uint256 internal constant ZORA_FREE_MINT_REWARD = 0.000111 ether; - - uint256 internal constant MINT_REFERRAL_PAID_MINT_REWARD = 0.000222 ether; - uint256 internal constant CREATE_REFERRAL_PAID_MINT_REWARD = 0.000222 ether; - uint256 internal constant ZORA_PAID_MINT_REWARD = 0.000222 ether; - - address internal immutable zoraRewardRecipient; - IProtocolRewards internal immutable protocolRewards; - - constructor(address _protocolRewards, address _zoraRewardRecipient) payable { - if (_protocolRewards == address(0) || _zoraRewardRecipient == address(0)) { - revert INVALID_ADDRESS_ZERO(); - } - - protocolRewards = IProtocolRewards(_protocolRewards); - zoraRewardRecipient = _zoraRewardRecipient; - } - - function computeTotalReward(uint256 numTokens) public pure returns (uint256) { - return numTokens * TOTAL_REWARD_PER_MINT; - } - - function computeFreeMintRewards(uint256 numTokens) public pure returns (RewardsSettings memory) { - return - RewardsSettings({ - creatorReward: numTokens * CREATOR_REWARD, - createReferralReward: numTokens * CREATE_REFERRAL_FREE_MINT_REWARD, - mintReferralReward: numTokens * MINT_REFERRAL_FREE_MINT_REWARD, - firstMinterReward: numTokens * FIRST_MINTER_REWARD, - zoraReward: numTokens * ZORA_FREE_MINT_REWARD - }); - } - - function computePaidMintRewards(uint256 numTokens) public pure returns (RewardsSettings memory) { - return - RewardsSettings({ - creatorReward: 0, - createReferralReward: numTokens * CREATE_REFERRAL_PAID_MINT_REWARD, - mintReferralReward: numTokens * MINT_REFERRAL_PAID_MINT_REWARD, - firstMinterReward: numTokens * FIRST_MINTER_REWARD, - zoraReward: numTokens * ZORA_PAID_MINT_REWARD - }); - } - - function _depositFreeMintRewards(uint256 totalReward, uint256 numTokens, address creator, address createReferral, address mintReferral) internal { - RewardsSettings memory settings = computeFreeMintRewards(numTokens); - - if (createReferral == address(0)) { - createReferral = zoraRewardRecipient; - } - - if (mintReferral == address(0)) { - mintReferral = zoraRewardRecipient; - } - - protocolRewards.depositRewards{value: totalReward}( - creator, - settings.creatorReward, - createReferral, - settings.createReferralReward, - mintReferral, - settings.mintReferralReward, - creator, - settings.firstMinterReward, - zoraRewardRecipient, - settings.zoraReward - ); - } - - function _depositPaidMintRewards(uint256 totalReward, uint256 numTokens, address creator, address createReferral, address mintReferral) internal { - RewardsSettings memory settings = computePaidMintRewards(numTokens); - - if (createReferral == address(0)) { - createReferral = zoraRewardRecipient; - } - - if (mintReferral == address(0)) { - mintReferral = zoraRewardRecipient; - } - - protocolRewards.depositRewards{value: totalReward}( - address(0), - 0, - createReferral, - settings.createReferralReward, - mintReferral, - settings.mintReferralReward, - creator, - settings.firstMinterReward, - zoraRewardRecipient, - settings.zoraReward - ); - } -} diff --git a/src/interfaces/IProtocolRewards.sol b/src/interfaces/IProtocolRewards.sol index c68ba9a..fff9353 100644 --- a/src/interfaces/IProtocolRewards.sol +++ b/src/interfaces/IProtocolRewards.sol @@ -65,6 +65,8 @@ interface IProtocolRewards { /// @notice Low-level ETH transfer has failed error TRANSFER_FAILED(); + error INVALID_AMOUNT(); + /// @notice Generic function to deposit ETH for a recipient, with an optional comment /// @param to Address to deposit to /// @param to Reason system reason for deposit (used for indexing) @@ -78,30 +80,6 @@ interface IProtocolRewards { /// @param comment Optional comment to include with mint function depositBatch(address[] calldata recipients, uint256[] calldata amounts, bytes4[] calldata reasons, string calldata comment) external payable; - /// @notice Used by Zora ERC-721 & ERC-1155 contracts to deposit protocol rewards - /// @param creator Creator for NFT rewards - /// @param creatorReward Creator reward amount - /// @param createReferral Creator referral - /// @param createReferralReward Creator referral reward - /// @param mintReferral Mint referral user - /// @param mintReferralReward Mint referral amount - /// @param firstMinter First minter reward - /// @param firstMinterReward First minter reward amount - /// @param zora ZORA recipient - /// @param zoraReward ZORA amount - function depositRewards( - address creator, - uint256 creatorReward, - address createReferral, - uint256 createReferralReward, - address mintReferral, - uint256 mintReferralReward, - address firstMinter, - uint256 firstMinterReward, - address zora, - uint256 zoraReward - ) external payable; - /// @notice Withdraw protocol rewards /// @param to Withdraws from msg.sender to this address /// @param amount amount to withdraw diff --git a/src/lib/ECDSA.sol b/src/lib/ECDSA.sol deleted file mode 100644 index 9d0eecc..0000000 --- a/src/lib/ECDSA.sol +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) - -pragma solidity ^0.8.8; - -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 message) { - // 32 is the length in bytes of hash, - // enforced by the type signature above - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, "\x19Ethereum Signed Message:\n32") - mstore(0x1c, hash) - message := keccak256(0x00, 0x3c) - } - } - - /** - * @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 data) { - /// @solidity memory-safe-assembly - assembly { - let ptr := mload(0x40) - mstore(ptr, "\x19\x01") - mstore(add(ptr, 0x02), domainSeparator) - mstore(add(ptr, 0x22), structHash) - data := keccak256(ptr, 0x42) - } - } - - /** - * @dev Returns an Ethereum Signed Data with intended validator, created from a - * `validator` and `data` according to the version 0 of EIP-191. - * - * See {recover}. - */ - function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x00", validator, data)); - } -} diff --git a/src/lib/EIP712.sol b/src/lib/EIP712.sol deleted file mode 100644 index fc46fae..0000000 --- a/src/lib/EIP712.sol +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol) - -pragma solidity ^0.8.8; - -import "./ECDSA.sol"; -import "./IERC5267.sol"; -import "./ShortStrings.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]. - * - * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain - * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the - * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. - * - * _Available since v3.4._ - * - * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment - */ -abstract contract EIP712 is IERC5267 { - using ShortStrings for *; - - bytes32 private constant _TYPE_HASH = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - // 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 _cachedDomainSeparator; - uint256 private immutable _cachedChainId; - address private immutable _cachedThis; - - bytes32 private immutable _hashedName; - bytes32 private immutable _hashedVersion; - - ShortString private immutable _name; - ShortString private immutable _version; - string private _nameFallback; - string private _versionFallback; - - /** - * @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) { - _name = name.toShortStringWithFallback(_nameFallback); - _version = version.toShortStringWithFallback(_versionFallback); - _hashedName = keccak256(bytes(name)); - _hashedVersion = keccak256(bytes(version)); - - _cachedChainId = block.chainid; - _cachedDomainSeparator = _buildDomainSeparator(); - _cachedThis = address(this); - } - - /** - * @dev Returns the domain separator for the current chain. - */ - function _domainSeparatorV4() internal view returns (bytes32) { - if (address(this) == _cachedThis && block.chainid == _cachedChainId) { - return _cachedDomainSeparator; - } else { - return _buildDomainSeparator(); - } - } - - function _buildDomainSeparator() private view returns (bytes32) { - return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, 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); - } - - /** - * @dev See {EIP-5267}. - * - * _Available since v4.9._ - */ - function eip712Domain() - public - view - virtual - override - returns ( - bytes1 fields, - string memory name, - string memory version, - uint256 chainId, - address verifyingContract, - bytes32 salt, - uint256[] memory extensions - ) - { - return ( - hex"0f", // 01111 - _name.toStringWithFallback(_nameFallback), - _version.toStringWithFallback(_versionFallback), - block.chainid, - address(this), - bytes32(0), - new uint256[](0) - ); - } -} diff --git a/src/lib/IERC5267.sol b/src/lib/IERC5267.sol deleted file mode 100644 index c76d113..0000000 --- a/src/lib/IERC5267.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol) - -pragma solidity ^0.8.8; - -interface IERC5267 { - /** - * @dev MAY be emitted to signal that the domain could have changed. - */ - event EIP712DomainChanged(); - - /** - * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 - * signature. - */ - function eip712Domain() - external - view - returns ( - bytes1 fields, - string memory name, - string memory version, - uint256 chainId, - address verifyingContract, - bytes32 salt, - uint256[] memory extensions - ); -} diff --git a/src/lib/Math.sol b/src/lib/Math.sol deleted file mode 100644 index 3134948..0000000 --- a/src/lib/Math.sol +++ /dev/null @@ -1,339 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) - -pragma solidity ^0.8.8; - -/** - * @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) { - // Solidity will revert if denominator == 0, unlike the div opcode on its own. - // The surrounding unchecked block does not change this fact. - // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. - return prod0 / denominator; - } - - // Make sure the result is less than 2^256. Also prevents denominator == 0. - require(denominator > prod1, "Math: mulDiv overflow"); - - /////////////////////////////////////////////// - // 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 256, 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 << 3) < value ? 1 : 0); - } - } -} diff --git a/src/lib/ShortStrings.sol b/src/lib/ShortStrings.sol deleted file mode 100644 index 90a0549..0000000 --- a/src/lib/ShortStrings.sol +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol) - -pragma solidity ^0.8.8; - -import "./StorageSlot.sol"; - -// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | -// | length | 0x BB | -type ShortString is bytes32; - -/** - * @dev This library provides functions to convert short memory strings - * into a `ShortString` type that can be used as an immutable variable. - * - * Strings of arbitrary length can be optimized using this library if - * they are short enough (up to 31 bytes) by packing them with their - * length (1 byte) in a single EVM word (32 bytes). Additionally, a - * fallback mechanism can be used for every other case. - * - * Usage example: - * - * ```solidity - * contract Named { - * using ShortStrings for *; - * - * ShortString private immutable _name; - * string private _nameFallback; - * - * constructor(string memory contractName) { - * _name = contractName.toShortStringWithFallback(_nameFallback); - * } - * - * function name() external view returns (string memory) { - * return _name.toStringWithFallback(_nameFallback); - * } - * } - * ``` - */ -library ShortStrings { - // Used as an identifier for strings longer than 31 bytes. - bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF; - - error StringTooLong(string str); - error InvalidShortString(); - - /** - * @dev Encode a string of at most 31 chars into a `ShortString`. - * - * This will trigger a `StringTooLong` error is the input string is too long. - */ - function toShortString(string memory str) internal pure returns (ShortString) { - bytes memory bstr = bytes(str); - if (bstr.length > 31) { - revert StringTooLong(str); - } - return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length)); - } - - /** - * @dev Decode a `ShortString` back to a "normal" string. - */ - function toString(ShortString sstr) internal pure returns (string memory) { - uint256 len = byteLength(sstr); - // using `new string(len)` would work locally but is not memory safe. - string memory str = new string(32); - /// @solidity memory-safe-assembly - assembly { - mstore(str, len) - mstore(add(str, 0x20), sstr) - } - return str; - } - - /** - * @dev Return the length of a `ShortString`. - */ - function byteLength(ShortString sstr) internal pure returns (uint256) { - uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF; - if (result > 31) { - revert InvalidShortString(); - } - return result; - } - - /** - * @dev Encode a string into a `ShortString`, or write it to storage if it is too long. - */ - function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) { - if (bytes(value).length < 32) { - return toShortString(value); - } else { - StorageSlot.getStringSlot(store).value = value; - return ShortString.wrap(_FALLBACK_SENTINEL); - } - } - - /** - * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}. - */ - function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) { - if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) { - return toString(value); - } else { - return store; - } - } - - /** - * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}. - * - * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of - * actual characters as the UTF-8 encoding of a single character can span over multiple bytes. - */ - function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) { - if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) { - return byteLength(value); - } else { - return bytes(store).length; - } - } -} diff --git a/src/lib/SignedMath.sol b/src/lib/SignedMath.sol deleted file mode 100644 index b1940c7..0000000 --- a/src/lib/SignedMath.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) - -pragma solidity ^0.8.8; - -/** - * @dev Standard signed math utilities missing in the Solidity language. - */ -library SignedMath { - /** - * @dev Returns the largest of two signed numbers. - */ - function max(int256 a, int256 b) internal pure returns (int256) { - return a > b ? a : b; - } - - /** - * @dev Returns the smallest of two signed numbers. - */ - function min(int256 a, int256 b) internal pure returns (int256) { - return a < b ? a : b; - } - - /** - * @dev Returns the average of two signed numbers without overflow. - * The result is rounded towards zero. - */ - function average(int256 a, int256 b) internal pure returns (int256) { - // Formula from the book "Hacker's Delight" - int256 x = (a & b) + ((a ^ b) >> 1); - return x + (int256(uint256(x) >> 255) & (a ^ b)); - } - - /** - * @dev Returns the absolute unsigned value of a signed value. - */ - function abs(int256 n) internal pure returns (uint256) { - unchecked { - // must be unchecked in order to support `n = type(int256).min` - return uint256(n >= 0 ? n : -n); - } - } -} diff --git a/src/lib/StorageSlot.sol b/src/lib/StorageSlot.sol deleted file mode 100644 index 0dd1c05..0000000 --- a/src/lib/StorageSlot.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol) -// This file was procedurally generated from scripts/generate/templates/StorageSlot.js. - -pragma solidity ^0.8.8; - -/** - * @dev Library for reading and writing primitive types to specific storage slots. - * - * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. - * This library helps with reading and writing to such slots without the need for inline assembly. - * - * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. - * - * Example usage to set ERC1967 implementation slot: - * ```solidity - * contract ERC1967 { - * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - * - * function _getImplementation() internal view returns (address) { - * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - * } - * - * function _setImplementation(address newImplementation) internal { - * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - * } - * } - * ``` - * - * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._ - * _Available since v4.9 for `string`, `bytes`._ - */ -library StorageSlot { - struct AddressSlot { - address value; - } - - struct BooleanSlot { - bool value; - } - - struct Bytes32Slot { - bytes32 value; - } - - struct Uint256Slot { - uint256 value; - } - - struct StringSlot { - string value; - } - - struct BytesSlot { - bytes value; - } - - /** - * @dev Returns an `AddressSlot` with member `value` located at `slot`. - */ - function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `BooleanSlot` with member `value` located at `slot`. - */ - function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. - */ - function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Uint256Slot` with member `value` located at `slot`. - */ - function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `StringSlot` with member `value` located at `slot`. - */ - function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `StringSlot` representation of the string storage pointer `store`. - */ - function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := store.slot - } - } - - /** - * @dev Returns an `BytesSlot` with member `value` located at `slot`. - */ - function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. - */ - function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := store.slot - } - } -} diff --git a/src/lib/Strings.sol b/src/lib/Strings.sol deleted file mode 100644 index f0e9091..0000000 --- a/src/lib/Strings.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) - -pragma solidity ^0.8.8; - -import "./Math.sol"; -import "./SignedMath.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 `int256` to its ASCII `string` decimal representation. - */ - function toString(int256 value) internal pure returns (string memory) { - return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); - } - - /** - * @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); - } - - /** - * @dev Returns true if the two strings are equal. - */ - function equal(string memory a, string memory b) internal pure returns (bool) { - return keccak256(bytes(a)) == keccak256(bytes(b)); - } -} diff --git a/test/ProtocolRewardsTest.sol b/test/ProtocolRewardsTest.sol index 1e1ddcd..338e71e 100644 --- a/test/ProtocolRewardsTest.sol +++ b/test/ProtocolRewardsTest.sol @@ -5,8 +5,6 @@ import "forge-std/Test.sol"; import "../src/ProtocolRewards.sol"; -import "./utils/MockNFTs.sol"; - contract ProtocolRewardsTest is Test { uint256 internal constant ETH_SUPPLY = 120_200_000 ether; diff --git a/test/unit/Deposit.t.sol b/test/unit/Deposit.t.sol deleted file mode 100644 index b9cb2eb..0000000 --- a/test/unit/Deposit.t.sol +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import "../ProtocolRewardsTest.sol"; -import "../../src/abstract/RewardSplits.sol"; - -contract DepositTest is ProtocolRewardsTest { - function setUp() public override { - super.setUp(); - } - - function testDeposit(uint256 amount, address to) public { - vm.assume(amount < ETH_SUPPLY); - vm.assume(to != address(0)); - - vm.deal(collector, amount); - - vm.prank(collector); - protocolRewards.deposit{value: amount}(to, bytes4(0), "test"); - - assertEq(protocolRewards.balanceOf(to), amount); - } - - function testRevert_CannotDepositToAddressZero(uint256 amount) public { - vm.assume(amount < ETH_SUPPLY); - - vm.deal(collector, amount); - - vm.prank(collector); - vm.expectRevert(abi.encodeWithSignature("ADDRESS_ZERO()")); - protocolRewards.deposit{value: amount}(address(0), bytes4(0), "test"); - } - - function testDepositBatch(uint8 numRecipients) public { - address[] memory recipients = new address[](numRecipients); - uint256[] memory amounts = new uint256[](numRecipients); - bytes4[] memory reasons = new bytes4[](numRecipients); - - uint256 totalValue; - - for (uint256 i; i < numRecipients; ++i) { - recipients[i] = makeAddr(vm.toString(i + 1)); - amounts[i] = i + 1 ether; - - totalValue += amounts[i]; - } - - vm.deal(collector, totalValue); - vm.prank(collector); - protocolRewards.depositBatch{value: totalValue}(recipients, amounts, reasons, "test"); - - for (uint256 i; i < numRecipients; ++i) { - assertEq(protocolRewards.balanceOf(recipients[i]), amounts[i]); - } - } - - function testRevert_RecipientsAndAmountsLengthMismatch(uint8 numRecipients, uint8 numAmounts) public { - vm.assume(numRecipients != numAmounts); - - address[] memory recipients = new address[](numRecipients); - uint256[] memory amounts = new uint256[](numAmounts); - bytes4[] memory reasons = new bytes4[](numAmounts); - - uint256 totalValue; - - for (uint256 i; i < numAmounts; ++i) { - amounts[i] = i + 1 ether; - - totalValue += amounts[i]; - } - - for (uint256 i; i < numRecipients; ++i) { - recipients[i] = makeAddr(vm.toString(i + 1)); - } - - vm.deal(collector, totalValue); - - vm.prank(collector); - vm.expectRevert(abi.encodeWithSignature("ARRAY_LENGTH_MISMATCH()")); - protocolRewards.depositBatch{value: totalValue}(recipients, amounts, reasons, "test"); - } - - function testRevert_InvalidDepositMsgValue(uint8 numRecipients) public { - vm.assume(numRecipients > 0); - - address[] memory recipients = new address[](numRecipients); - uint256[] memory amounts = new uint256[](numRecipients); - bytes4[] memory reasons = new bytes4[](numRecipients); - - uint256 totalValue; - - for (uint256 i; i < numRecipients; ++i) { - recipients[i] = makeAddr(vm.toString(i + 1)); - amounts[i] = i + 1 ether; - - totalValue += amounts[i]; - } - - vm.deal(collector, totalValue); - - vm.prank(collector); - vm.expectRevert(abi.encodeWithSignature("INVALID_DEPOSIT()")); - protocolRewards.depositBatch{value: 0}(recipients, amounts, reasons, "test"); - } - - function testRevert_RecipientCannotBeAddressZero(uint8 numRecipients) public { - vm.assume(numRecipients > 0); - - address[] memory recipients = new address[](numRecipients); - uint256[] memory amounts = new uint256[](numRecipients); - bytes4[] memory reasons = new bytes4[](numRecipients); - - uint256 totalValue; - - for (uint256 i; i < numRecipients; ++i) { - recipients[i] = makeAddr(vm.toString(i + 1)); - amounts[i] = i + 1 ether; - - totalValue += amounts[i]; - } - - recipients[0] = address(0); - - vm.deal(collector, totalValue); - - vm.prank(collector); - vm.expectRevert(abi.encodeWithSignature("ADDRESS_ZERO()")); - protocolRewards.depositBatch{value: totalValue}(recipients, amounts, reasons, "test"); - } -} diff --git a/test/unit/ERC1155Rewards.t.sol b/test/unit/ERC1155Rewards.t.sol deleted file mode 100644 index 8f54973..0000000 --- a/test/unit/ERC1155Rewards.t.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import "../ProtocolRewardsTest.sol"; -import {RewardsSettings} from "../../src/abstract/RewardSplits.sol"; - -contract ERC1155RewardsTest is ProtocolRewardsTest { - MockERC1155 internal mockERC1155; - - function setUp() public override { - super.setUp(); - - mockERC1155 = new MockERC1155(creator, createReferral, address(protocolRewards), zora); - - vm.label(address(mockERC1155), "MOCK_ERC1155"); - } - - function test1155FreeMintDeposit(uint16 numTokens) public { - vm.assume(numTokens > 0); - - uint256 totalReward = mockERC1155.computeTotalReward(numTokens); - - vm.deal(collector, totalReward); - - vm.prank(collector); - mockERC1155.mintWithRewards{value: totalReward}(collector, 0, numTokens, mintReferral); - - RewardsSettings memory settings = mockERC1155.computeFreeMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.creatorReward + settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(createReferral), settings.createReferralReward); - assertEq(protocolRewards.balanceOf(mintReferral), settings.mintReferralReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward); - } - - function test1155PaidMintDeposit(uint16 numTokens, uint256 pricePerToken) public { - vm.assume(numTokens > 0); - vm.assume(pricePerToken > 0 && pricePerToken < 100 ether); - - mockERC1155.setSalePrice(pricePerToken); - - uint256 totalReward = mockERC1155.computeTotalReward(numTokens); - uint256 totalSale = numTokens * pricePerToken; - uint256 totalValue = totalReward + totalSale; - - vm.deal(collector, totalValue); - - vm.prank(collector); - mockERC1155.mintWithRewards{value: totalValue}(collector, 0, numTokens, mintReferral); - - RewardsSettings memory settings = mockERC1155.computePaidMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(createReferral), settings.createReferralReward); - assertEq(protocolRewards.balanceOf(mintReferral), settings.mintReferralReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward); - } - - function test1155FreeMintNullReferralRecipients(uint16 numTokens) public { - vm.assume(numTokens > 0); - - mockERC1155 = new MockERC1155(creator, address(0), address(protocolRewards), zora); - - uint256 totalReward = mockERC1155.computeTotalReward(numTokens); - - vm.deal(collector, totalReward); - - vm.prank(collector); - mockERC1155.mintWithRewards{value: totalReward}(collector, 0, numTokens, address(0)); - - RewardsSettings memory settings = mockERC1155.computeFreeMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.creatorReward + settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward + settings.mintReferralReward + settings.createReferralReward); - } - - function test1155PaidMintNullReferralRecipient(uint16 numTokens, uint256 pricePerToken) public { - vm.assume(numTokens > 0); - vm.assume(pricePerToken > 0 && pricePerToken < 100 ether); - - mockERC1155 = new MockERC1155(creator, address(0), address(protocolRewards), zora); - - mockERC1155.setSalePrice(pricePerToken); - - uint256 totalReward = mockERC1155.computeTotalReward(numTokens); - uint256 totalSale = numTokens * pricePerToken; - uint256 totalValue = totalReward + totalSale; - - vm.deal(collector, totalValue); - - vm.prank(collector); - mockERC1155.mintWithRewards{value: totalValue}(collector, 0, numTokens, address(0)); - - RewardsSettings memory settings = mockERC1155.computePaidMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward + settings.mintReferralReward + settings.createReferralReward); - } - - function testRevert1155FreeMintInvalidEth(uint16 numTokens) public { - vm.assume(numTokens > 0); - - vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); - mockERC1155.mintWithRewards(collector, 0, numTokens, mintReferral); - } - - function testRevert1155PaidMintInvalidEth(uint16 numTokens, uint256 pricePerToken) public { - vm.assume(numTokens > 0); - vm.assume(pricePerToken > 0 && pricePerToken < 100 ether); - - mockERC1155.setSalePrice(pricePerToken); - - vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); - mockERC1155.mintWithRewards(collector, 0, numTokens, mintReferral); - } - - function testRevert1155PaidMintInvalidEthRemaining(uint16 numTokens, uint256 pricePerToken) public { - vm.assume(numTokens > 0); - vm.assume(pricePerToken > 0 && pricePerToken < 100 ether); - - mockERC1155.setSalePrice(pricePerToken); - - uint256 totalReward = mockERC1155.computeTotalReward(numTokens); - uint256 totalSale = numTokens * pricePerToken; - uint256 totalValue = totalReward + totalSale; - - vm.deal(collector, totalValue); - - vm.prank(collector); - vm.expectRevert(abi.encodeWithSignature("MOCK_ERC1155_INVALID_REMAINING_VALUE()")); - mockERC1155.mintWithRewards{value: totalValue - 1}(collector, 0, numTokens, mintReferral); - } -} diff --git a/test/unit/ERC721Rewards.t.sol b/test/unit/ERC721Rewards.t.sol deleted file mode 100644 index f2c0d03..0000000 --- a/test/unit/ERC721Rewards.t.sol +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import "../ProtocolRewardsTest.sol"; -import {RewardsSettings} from "../../src/abstract/RewardSplits.sol"; - -contract ERC721RewardsTest is ProtocolRewardsTest { - MockERC721 internal mockERC721; - - function setUp() public override { - super.setUp(); - - mockERC721 = new MockERC721(creator, createReferral, address(protocolRewards), zora); - - vm.label(address(mockERC721), "MOCK_ERC721"); - } - - function testValidateFreeMintTotalComputation(uint16 numTokens) public { - uint256 expectedTotal = mockERC721.computeTotalReward(numTokens); - - RewardsSettings memory settings = mockERC721.computeFreeMintRewards(numTokens); - - uint256 actualTotal = settings.creatorReward + - settings.createReferralReward + - settings.mintReferralReward + - settings.firstMinterReward + - settings.zoraReward; - - assertEq(expectedTotal, actualTotal); - } - - function testValidatePaidMintTotalComputation(uint32 numTokens) public { - uint256 expectedTotal = mockERC721.computeTotalReward(numTokens); - - RewardsSettings memory settings = mockERC721.computePaidMintRewards(numTokens); - - uint256 actualTotal = settings.mintReferralReward + settings.createReferralReward + settings.firstMinterReward + settings.zoraReward; - - assertEq(expectedTotal, actualTotal); - } - - function test721FreeMintDeposit(uint16 numTokens) public { - vm.assume(numTokens > 0 && numTokens < 10_000); - - uint256 totalReward = mockERC721.computeTotalReward(numTokens); - - vm.deal(collector, totalReward); - - vm.prank(collector); - mockERC721.mintWithRewards{value: totalReward}(collector, numTokens, mintReferral); - - RewardsSettings memory settings = mockERC721.computeFreeMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.creatorReward + settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(createReferral), settings.createReferralReward); - assertEq(protocolRewards.balanceOf(mintReferral), settings.mintReferralReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward); - } - - function test721PaidMintDeposit(uint16 numTokens, uint256 pricePerToken) public { - vm.assume(numTokens > 0 && numTokens < 10_000); - vm.assume(pricePerToken > 0 && pricePerToken < 100 ether); - - mockERC721.setSalePrice(pricePerToken); - - uint256 totalReward = mockERC721.computeTotalReward(numTokens); - uint256 totalSale = numTokens * pricePerToken; - uint256 totalValue = totalReward + totalSale; - - vm.deal(collector, totalValue); - - vm.prank(collector); - mockERC721.mintWithRewards{value: totalValue}(collector, numTokens, mintReferral); - - RewardsSettings memory settings = mockERC721.computePaidMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(createReferral), settings.createReferralReward); - assertEq(protocolRewards.balanceOf(mintReferral), settings.mintReferralReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward); - } - - function test721FreeMintNullReferralRecipients(uint16 numTokens) public { - vm.assume(numTokens > 0 && numTokens < 10_000); - - mockERC721 = new MockERC721(creator, address(0), address(protocolRewards), zora); - - uint256 totalReward = mockERC721.computeTotalReward(numTokens); - - vm.deal(collector, totalReward); - - vm.prank(collector); - mockERC721.mintWithRewards{value: totalReward}(collector, numTokens, address(0)); - - RewardsSettings memory settings = mockERC721.computeFreeMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.creatorReward + settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward + settings.mintReferralReward + settings.createReferralReward); - } - - function test721PaidMintNullReferralRecipient(uint16 numTokens, uint256 pricePerToken) public { - vm.assume(numTokens > 0 && numTokens < 10_000); - vm.assume(pricePerToken > 0 && pricePerToken < 100 ether); - - mockERC721 = new MockERC721(creator, address(0), address(protocolRewards), zora); - - mockERC721.setSalePrice(pricePerToken); - - uint256 totalReward = mockERC721.computeTotalReward(numTokens); - uint256 totalSale = numTokens * pricePerToken; - uint256 totalValue = totalReward + totalSale; - - vm.deal(collector, totalValue); - - vm.prank(collector); - mockERC721.mintWithRewards{value: totalValue}(collector, numTokens, address(0)); - - RewardsSettings memory settings = mockERC721.computePaidMintRewards(numTokens); - - assertEq(protocolRewards.totalSupply(), totalReward); - assertEq(protocolRewards.balanceOf(creator), settings.firstMinterReward); - assertEq(protocolRewards.balanceOf(zora), settings.zoraReward + settings.mintReferralReward + settings.createReferralReward); - } - - function testSet721CreatorFundsRecipientAsContractIfNotSet(uint16 numTokens) public { - vm.assume(numTokens > 0); - - mockERC721 = new MockERC721(address(0), createReferral, address(protocolRewards), zora); - - uint256 totalValue = mockERC721.computeTotalReward(numTokens); - - RewardsSettings memory settings = mockERC721.computeFreeMintRewards(numTokens); - - mockERC721.mintWithRewards{value: totalValue}(collector, numTokens, mintReferral); - - assertEq(protocolRewards.balanceOf(address(mockERC721)), settings.creatorReward + settings.firstMinterReward); - } - - function testRevert721FreeMintInvalidEth(uint16 numTokens) public { - vm.assume(numTokens > 0); - - vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); - mockERC721.mintWithRewards(collector, numTokens, mintReferral); - } - - function testRevert721PaidMintInvalidEth(uint16 numTokens, uint256 pricePerToken) public { - vm.assume(numTokens > 0); - vm.assume(pricePerToken > 0 && pricePerToken < 100 ether); - - mockERC721.setSalePrice(pricePerToken); - - vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); - mockERC721.mintWithRewards(collector, numTokens, mintReferral); - } -} diff --git a/test/unit/Withdraw.t.sol b/test/unit/Withdraw.t.sol index 99c906f..5774ec9 100644 --- a/test/unit/Withdraw.t.sol +++ b/test/unit/Withdraw.t.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.17; import "../ProtocolRewardsTest.sol"; -import "../../src/abstract/RewardSplits.sol"; contract WithdrawTest is ProtocolRewardsTest { function setUp() public override { diff --git a/test/utils/ERC1155.sol b/test/utils/ERC1155.sol deleted file mode 100644 index 9eb8223..0000000 --- a/test/utils/ERC1155.sol +++ /dev/null @@ -1,897 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/ERC1155.sol) - -pragma solidity ^0.8.0; - -// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol) - -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - -/** - * @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); -} - -/** - * @dev Required interface of an ERC1155 compliant contract, as defined in the - * https://eips.ethereum.org/EIPS/eip-1155[EIP]. - * - * _Available since v3.1._ - */ -interface IERC1155 is IERC165 { - /** - * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. - */ - event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); - - /** - * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all - * transfers. - */ - event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values); - - /** - * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to - * `approved`. - */ - event ApprovalForAll(address indexed account, address indexed operator, bool approved); - - /** - * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. - * - * If an {URI} event was emitted for `id`, the standard - * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value - * returned by {IERC1155MetadataURI-uri}. - */ - event URI(string value, uint256 indexed id); - - /** - * @dev Returns the amount of tokens of token type `id` owned by `account`. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function balanceOf(address account, uint256 id) external view returns (uint256); - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. - * - * Requirements: - * - * - `accounts` and `ids` must have the same length. - */ - function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); - - /** - * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, - * - * Emits an {ApprovalForAll} event. - * - * Requirements: - * - * - `operator` cannot be the caller. - */ - function setApprovalForAll(address operator, bool approved) external; - - /** - * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. - * - * See {setApprovalForAll}. - */ - function isApprovedForAll(address account, address operator) external view returns (bool); - - /** - * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. - * - * Emits a {TransferSingle} event. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. - * - `from` must have a balance of tokens of type `id` of at least `amount`. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the - * acceptance magic value. - */ - function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. - * - * Emits a {TransferBatch} event. - * - * Requirements: - * - * - `ids` and `amounts` must have the same length. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the - * acceptance magic value. - */ - function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external; -} - -// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) - -/** - * @dev _Available since v3.1._ - */ -interface IERC1155Receiver is IERC165 { - /** - * @dev Handles the receipt of a single ERC1155 token type. This function is - * called at the end of a `safeTransferFrom` after the balance has been updated. - * - * NOTE: To accept the transfer, this must return - * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` - * (i.e. 0xf23a6e61, or its own function selector). - * - * @param operator The address which initiated the transfer (i.e. msg.sender) - * @param from The address which previously owned the token - * @param id The ID of the token being transferred - * @param value The amount of tokens being transferred - * @param data Additional data with no specified format - * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed - */ - function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data) external returns (bytes4); - - /** - * @dev Handles the receipt of a multiple ERC1155 token types. This function - * is called at the end of a `safeBatchTransferFrom` after the balances have - * been updated. - * - * NOTE: To accept the transfer(s), this must return - * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` - * (i.e. 0xbc197c81, or its own function selector). - * - * @param operator The address which initiated the batch transfer (i.e. msg.sender) - * @param from The address which previously owned the token - * @param ids An array containing ids of each token being transferred (order and length must match values array) - * @param values An array containing amounts of each token being transferred (order and length must match ids array) - * @param data Additional data with no specified format - * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed - */ - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4); -} - -// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol) - -/** - * @dev Interface of the optional ERC1155MetadataExtension interface, as defined - * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. - * - * _Available since v3.1._ - */ -interface IERC1155MetadataURI is IERC1155 { - /** - * @dev Returns the URI for token type `id`. - * - * If the `\{id\}` substring is present in the URI, it must be replaced by - * clients with the actual token type ID. - */ - function uri(uint256 id) external view returns (string memory); -} - -// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * - * Furthermore, `isContract` will also return true if the target contract within - * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, - * which only has an effect at the end of a transaction. - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling - * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. - * - * _Available since v4.8._ - */ - function verifyCallResultFromTarget( - address target, - bool success, - bytes memory returndata, - string memory errorMessage - ) internal view returns (bytes memory) { - if (success) { - if (returndata.length == 0) { - // only check isContract if the call was successful and the return data is empty - // otherwise we already know that it was a contract - require(isContract(target), "Address: call to non-contract"); - } - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - /** - * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason or using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - function _revert(bytes memory returndata, string memory errorMessage) private pure { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } -} - -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -/** - * @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; - } -} - -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.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; - } -} - -/** - * @dev Implementation of the basic standard multi-token. - * See https://eips.ethereum.org/EIPS/eip-1155 - * Originally based on code by Enjin: https://github.com/enjin/erc-1155 - * - * _Available since v3.1._ - */ -contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { - using Address for address; - - // Mapping from token ID to account balances - mapping(uint256 => mapping(address => uint256)) private _balances; - - // Mapping from account to operator approvals - mapping(address => mapping(address => bool)) private _operatorApprovals; - - // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json - string private _uri; - - /** - * @dev See {_setURI}. - */ - constructor(string memory uri_) { - _setURI(uri_); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return interfaceId == type(IERC1155).interfaceId || interfaceId == type(IERC1155MetadataURI).interfaceId || super.supportsInterface(interfaceId); - } - - /** - * @dev See {IERC1155MetadataURI-uri}. - * - * This implementation returns the same URI for *all* token types. It relies - * on the token type ID substitution mechanism - * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. - * - * Clients calling this function must replace the `\{id\}` substring with the - * actual token type ID. - */ - function uri(uint256) public view virtual override returns (string memory) { - return _uri; - } - - /** - * @dev See {IERC1155-balanceOf}. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { - require(account != address(0), "ERC1155: address zero is not a valid owner"); - return _balances[id][account]; - } - - /** - * @dev See {IERC1155-balanceOfBatch}. - * - * Requirements: - * - * - `accounts` and `ids` must have the same length. - */ - function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view virtual override returns (uint256[] memory) { - require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); - - uint256[] memory batchBalances = new uint256[](accounts.length); - - for (uint256 i = 0; i < accounts.length; ++i) { - batchBalances[i] = balanceOf(accounts[i], ids[i]); - } - - return batchBalances; - } - - /** - * @dev See {IERC1155-setApprovalForAll}. - */ - function setApprovalForAll(address operator, bool approved) public virtual override { - _setApprovalForAll(_msgSender(), operator, approved); - } - - /** - * @dev See {IERC1155-isApprovedForAll}. - */ - function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { - return _operatorApprovals[account][operator]; - } - - /** - * @dev See {IERC1155-safeTransferFrom}. - */ - function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) public virtual override { - require(from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: caller is not token owner or approved"); - _safeTransferFrom(from, to, id, amount, data); - } - - /** - * @dev See {IERC1155-safeBatchTransferFrom}. - */ - function safeBatchTransferFrom(address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public virtual override { - require(from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: caller is not token owner or approved"); - _safeBatchTransferFrom(from, to, ids, amounts, data); - } - - /** - * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. - * - * Emits a {TransferSingle} event. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - `from` must have a balance of tokens of type `id` of at least `amount`. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the - * acceptance magic value. - */ - function _safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) internal virtual { - require(to != address(0), "ERC1155: transfer to the zero address"); - - address operator = _msgSender(); - uint256[] memory ids = _asSingletonArray(id); - uint256[] memory amounts = _asSingletonArray(amount); - - _beforeTokenTransfer(operator, from, to, ids, amounts, data); - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - _balances[id][to] += amount; - - emit TransferSingle(operator, from, to, id, amount); - - _afterTokenTransfer(operator, from, to, ids, amounts, data); - - _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); - } - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. - * - * Emits a {TransferBatch} event. - * - * Requirements: - * - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the - * acceptance magic value. - */ - function _safeBatchTransferFrom(address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual { - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - require(to != address(0), "ERC1155: transfer to the zero address"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, from, to, ids, amounts, data); - - for (uint256 i = 0; i < ids.length; ++i) { - uint256 id = ids[i]; - uint256 amount = amounts[i]; - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - _balances[id][to] += amount; - } - - emit TransferBatch(operator, from, to, ids, amounts); - - _afterTokenTransfer(operator, from, to, ids, amounts, data); - - _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); - } - - /** - * @dev Sets a new URI for all token types, by relying on the token type ID - * substitution mechanism - * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. - * - * By this mechanism, any occurrence of the `\{id\}` substring in either the - * URI or any of the amounts in the JSON file at said URI will be replaced by - * clients with the token type ID. - * - * For example, the `https://token-cdn-domain/\{id\}.json` URI would be - * interpreted by clients as - * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` - * for token type ID 0x4cce0. - * - * See {uri}. - * - * Because these URIs cannot be meaningfully represented by the {URI} event, - * this function emits no events. - */ - function _setURI(string memory newuri) internal virtual { - _uri = newuri; - } - - /** - * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. - * - * Emits a {TransferSingle} event. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the - * acceptance magic value. - */ - function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual { - require(to != address(0), "ERC1155: mint to the zero address"); - - address operator = _msgSender(); - uint256[] memory ids = _asSingletonArray(id); - uint256[] memory amounts = _asSingletonArray(amount); - - _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); - - _balances[id][to] += amount; - emit TransferSingle(operator, address(0), to, id, amount); - - _afterTokenTransfer(operator, address(0), to, ids, amounts, data); - - _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); - } - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. - * - * Emits a {TransferBatch} event. - * - * Requirements: - * - * - `ids` and `amounts` must have the same length. - * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the - * acceptance magic value. - */ - function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual { - require(to != address(0), "ERC1155: mint to the zero address"); - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); - - for (uint256 i = 0; i < ids.length; i++) { - _balances[ids[i]][to] += amounts[i]; - } - - emit TransferBatch(operator, address(0), to, ids, amounts); - - _afterTokenTransfer(operator, address(0), to, ids, amounts, data); - - _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); - } - - /** - * @dev Destroys `amount` tokens of token type `id` from `from` - * - * Emits a {TransferSingle} event. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `from` must have at least `amount` tokens of token type `id`. - */ - function _burn(address from, uint256 id, uint256 amount) internal virtual { - require(from != address(0), "ERC1155: burn from the zero address"); - - address operator = _msgSender(); - uint256[] memory ids = _asSingletonArray(id); - uint256[] memory amounts = _asSingletonArray(amount); - - _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - - emit TransferSingle(operator, from, address(0), id, amount); - - _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); - } - - /** - * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. - * - * Emits a {TransferBatch} event. - * - * Requirements: - * - * - `ids` and `amounts` must have the same length. - */ - function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual { - require(from != address(0), "ERC1155: burn from the zero address"); - require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); - - address operator = _msgSender(); - - _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); - - for (uint256 i = 0; i < ids.length; i++) { - uint256 id = ids[i]; - uint256 amount = amounts[i]; - - uint256 fromBalance = _balances[id][from]; - require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); - unchecked { - _balances[id][from] = fromBalance - amount; - } - } - - emit TransferBatch(operator, from, address(0), ids, amounts); - - _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); - } - - /** - * @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, "ERC1155: setting approval status for self"); - _operatorApprovals[owner][operator] = approved; - emit ApprovalForAll(owner, operator, approved); - } - - /** - * @dev Hook that is called before any token transfer. This includes minting - * and burning, as well as batched variants. - * - * The same hook is called on both single and batched variants. For single - * transfers, the length of the `ids` and `amounts` arrays will be 1. - * - * Calling conditions (for each `id` and `amount` pair): - * - * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * of token type `id` will be transferred to `to`. - * - When `from` is zero, `amount` tokens of token type `id` will be minted - * for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` - * will be burned. - * - `from` and `to` are never both zero. - * - `ids` and `amounts` have the same, non-zero length. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual {} - - /** - * @dev Hook that is called after any token transfer. This includes minting - * and burning, as well as batched variants. - * - * The same hook is called on both single and batched variants. For single - * transfers, the length of the `id` and `amount` arrays will be 1. - * - * Calling conditions (for each `id` and `amount` pair): - * - * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * of token type `id` will be transferred to `to`. - * - When `from` is zero, `amount` tokens of token type `id` will be minted - * for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` - * will be burned. - * - `from` and `to` are never both zero. - * - `ids` and `amounts` have the same, non-zero length. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _afterTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual {} - - function _doSafeTransferAcceptanceCheck(address operator, address from, address to, uint256 id, uint256 amount, bytes memory data) private { - if (to.isContract()) { - try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { - if (response != IERC1155Receiver.onERC1155Received.selector) { - revert("ERC1155: ERC1155Receiver rejected tokens"); - } - } catch Error(string memory reason) { - revert(reason); - } catch { - revert("ERC1155: transfer to non-ERC1155Receiver implementer"); - } - } - } - - function _doSafeBatchTransferAcceptanceCheck( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) private { - if (to.isContract()) { - try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (bytes4 response) { - if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { - revert("ERC1155: ERC1155Receiver rejected tokens"); - } - } catch Error(string memory reason) { - revert(reason); - } catch { - revert("ERC1155: transfer to non-ERC1155Receiver implementer"); - } - } - } - - function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { - uint256[] memory array = new uint256[](1); - array[0] = element; - - return array; - } -} diff --git a/test/utils/ERC721.sol b/test/utils/ERC721.sol deleted file mode 100644 index 775d09b..0000000 --- a/test/utils/ERC721.sol +++ /dev/null @@ -1,1364 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol) - -pragma solidity ^0.8.0; - -// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol) - -// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) - -/** - * @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); -} - -/** - * @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); -} - -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) - -/** - * @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); -} - -// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.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); -} - -// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * - * Furthermore, `isContract` will also return true if the target contract within - * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, - * which only has an effect at the end of a transaction. - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling - * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. - * - * _Available since v4.8._ - */ - function verifyCallResultFromTarget( - address target, - bool success, - bytes memory returndata, - string memory errorMessage - ) internal view returns (bytes memory) { - if (success) { - if (returndata.length == 0) { - // only check isContract if the call was successful and the return data is empty - // otherwise we already know that it was a contract - require(isContract(target), "Address: call to non-contract"); - } - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - /** - * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason or using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - function _revert(bytes memory returndata, string memory errorMessage) private pure { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } -} - -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -/** - * @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; - } -} - -// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) - -// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) - -/** - * @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) { - // Solidity will revert if denominator == 0, unlike the div opcode on its own. - // The surrounding unchecked block does not change this fact. - // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. - return prod0 / denominator; - } - - // Make sure the result is less than 2^256. Also prevents denominator == 0. - require(denominator > prod1, "Math: mulDiv overflow"); - - /////////////////////////////////////////////// - // 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 256, 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 << 3) < value ? 1 : 0); - } - } -} - -// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) - -/** - * @dev Standard signed math utilities missing in the Solidity language. - */ -library SignedMath { - /** - * @dev Returns the largest of two signed numbers. - */ - function max(int256 a, int256 b) internal pure returns (int256) { - return a > b ? a : b; - } - - /** - * @dev Returns the smallest of two signed numbers. - */ - function min(int256 a, int256 b) internal pure returns (int256) { - return a < b ? a : b; - } - - /** - * @dev Returns the average of two signed numbers without overflow. - * The result is rounded towards zero. - */ - function average(int256 a, int256 b) internal pure returns (int256) { - // Formula from the book "Hacker's Delight" - int256 x = (a & b) + ((a ^ b) >> 1); - return x + (int256(uint256(x) >> 255) & (a ^ b)); - } - - /** - * @dev Returns the absolute unsigned value of a signed value. - */ - function abs(int256 n) internal pure returns (uint256) { - unchecked { - // must be unchecked in order to support `n = type(int256).min` - return uint256(n >= 0 ? n : -n); - } - } -} - -/** - * @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 `int256` to its ASCII `string` decimal representation. - */ - function toString(int256 value) internal pure returns (string memory) { - return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); - } - - /** - * @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); - } - - /** - * @dev Returns true if the two strings are equal. - */ - function equal(string memory a, string memory b) internal pure returns (bool) { - return keccak256(bytes(a)) == keccak256(bytes(b)); - } -} - -// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.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; - } -} - -/** - * @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 {} - - /** - * @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 {} - - /** - * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override. - * - * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant - * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such - * that `ownerOf(tokenId)` is `a`. - */ - // solhint-disable-next-line func-name-mixedcase - function __unsafe_increaseBalance(address account, uint256 amount) internal { - _balances[account] += amount; - } -} diff --git a/test/utils/MockNFTs.sol b/test/utils/MockNFTs.sol deleted file mode 100644 index 554a0ac..0000000 --- a/test/utils/MockNFTs.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import {ERC721} from "./ERC721.sol"; -import {ERC1155} from "./ERC1155.sol"; - -import {ERC721RewardsStorageV1} from "../../src/abstract/ERC721/ERC721RewardsStorageV1.sol"; -import {ERC721Rewards} from "../../src/abstract/ERC721/ERC721Rewards.sol"; -import {ERC1155Rewards} from "../../src/abstract/ERC1155/ERC1155Rewards.sol"; -import {ERC1155RewardsStorageV1} from "../../src/abstract/ERC1155/ERC1155RewardsStorageV1.sol"; - -contract MockERC721 is ERC721, ERC721Rewards, ERC721RewardsStorageV1 { - address public creator; - uint256 public salePrice; - uint256 public currentTokenId; - - constructor( - address _creator, - address _createReferral, - address _protocolRewards, - address _zoraRewardRecipient - ) ERC721("Mock ERC721", "MOCK") ERC721Rewards(_protocolRewards, _zoraRewardRecipient) { - creator = _creator; - createReferral = _createReferral; - } - - function setSalePrice(uint256 _salePrice) external { - salePrice = _salePrice; - } - - function mintWithRewards(address to, uint256 numTokens, address mintReferral) external payable { - _handleRewards(msg.value, numTokens, salePrice, creator != address(0) ? creator : address(this), createReferral, mintReferral); - - for (uint256 i; i < numTokens; ++i) { - _mint(to, currentTokenId++); - } - } -} - -contract MockERC1155 is ERC1155, ERC1155Rewards, ERC1155RewardsStorageV1 { - error MOCK_ERC1155_INVALID_REMAINING_VALUE(); - - address public creator; - uint256 public salePrice; - - constructor( - address _creator, - address _createReferral, - address _protocolRewards, - address _zoraRewardRecipient - ) ERC1155("Mock ERC1155 URI") ERC1155Rewards(_protocolRewards, _zoraRewardRecipient) { - creator = _creator; - createReferrals[0] = _createReferral; - } - - function setSalePrice(uint256 _salePrice) external { - salePrice = _salePrice; - } - - function mintWithRewards(address to, uint256 tokenId, uint256 numTokens, address mintReferral) external payable { - uint256 remainingValue = _handleRewardsAndGetValueSent(msg.value, numTokens, creator, createReferrals[tokenId], mintReferral); - - uint256 expectedRemainingValue = salePrice * numTokens; - - if (remainingValue != expectedRemainingValue) revert MOCK_ERC1155_INVALID_REMAINING_VALUE(); - - _mint(to, tokenId, numTokens, ""); - } -}