diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..94c0c3a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,18 @@ +on: [push] + +name: Test + +jobs: + check: + name: Transient Labs Sol Tools + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Run unit tests + run: make compiler_test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3269660..6ee213e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ out/ # Dotenv file .env + +# Docs +docs/ \ No newline at end of file diff --git a/Makefile b/Makefile index 7ef5b38..c43796f 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,13 @@ clean: # Remove the modules remove: - rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib + rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib && touch .gitmodules # Install the modules install: forge install foundry-rs/forge-std --no-commit - forge install OpenZeppelin/openzeppelin-contracts@v4.8.3 --no-commit - forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v4.8.3 --no-commit + forge install OpenZeppelin/openzeppelin-contracts@v5.0.1 --no-commit + forge install OpenZeppelin/openzeppelin-contracts-upgradeable@v5.0.1 --no-commit forge install manifoldxyz/royalty-registry-solidity --no-commit # Updatee the modules @@ -26,13 +26,12 @@ build: # Tests compiler_test: - forge test --use 0.8.17 - forge test --use 0.8.18 - forge test --use 0.8.19 forge test --use 0.8.20 + forge test --use 0.8.21 + forge test --use 0.8.22 quick_test: - forge test --fuzz-runs 512 + forge test gas_test: forge test --gas-report diff --git a/README.md b/README.md index afee23a..8c9b6d3 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,15 @@ You should have no trouble inheriting from this library if you install with foun When cloning, you must use either `make remove && make install` or `make update` to install/update the required modules, such as `forge-std` or OpenZeppelin contracts. -We use OpenZeppelin contracts version 4.8.3 in this codebase. +We use OpenZeppelin contracts version 5.0.1 in this codebase. ## Testing -You should run the test suite with `make test_suite`. +You should run the test suites in the Makefile. This loops through the following solidity versions: -- 0.8.17 -- 0.8.18 -- 0.8.19 - 0.8.20 +- 0.8.21 +- 0.8.22 ## Disclaimer This codebase is provided on an "as is" and "as available" basis. diff --git a/foundry.toml b/foundry.toml index 709186c..482fd67 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,5 +8,7 @@ optimizer = true optimizer_runs = 20000 verbosity = 3 wrap_comments = true -gas_reports = ["OwnableAccessControl", "EIP2981TL", "OwnableAccessControlUpgradeable", "EIP2981TLUpgradeable", "TransferHelper", "RoyaltyPayoutHelper", "RoyaltyPayoutHelperUpgradeable"] -fs_permissions = [{ access = "read", path = "./"}] \ No newline at end of file +fs_permissions = [{ access = "read", path = "./"}] + +[fuzz] +runs = 1024 \ No newline at end of file diff --git a/lib/forge-std b/lib/forge-std index 1d9650e..155d547 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 1d9650e951204a0ddce9ff89c32f1997984cef4d +Subproject commit 155d547c449afa8715f538d69454b83944117811 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 0a25c19..01ef448 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 0a25c1940ca220686588c4af3ec526f725fe2582 +Subproject commit 01ef448981be9d20ca85f2faf6ebdf591ce409f3 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 58fa0f8..fbdb824 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 58fa0f81c4036f1a3b616fdffad2fd27e5d5ce21 +Subproject commit fbdb824a735891908d5588b28e0da5852d7ed7ba diff --git a/remappings.txt b/remappings.txt index 43c8980..b903d32 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,14 +1,12 @@ @manifoldxyz/libraries-solidity/=lib/royalty-registry-solidity/lib/libraries-solidity/ -@openzeppelin/contracts-upgradeable/=lib/royalty-registry-solidity/lib/openzeppelin-contracts-upgradeable/contracts/ -@openzeppelin/contracts/=lib/royalty-registry-solidity/lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ create2-helpers/=lib/royalty-registry-solidity/lib/create2-helpers/ create2-scripts/=lib/royalty-registry-solidity/lib/create2-helpers/script/ ds-test/=lib/forge-std/lib/ds-test/src/ +erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/ forge-std/=lib/forge-std/src/ libraries-solidity/=lib/royalty-registry-solidity/lib/libraries-solidity/contracts/ -openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ -openzeppelin-contracts/=lib/openzeppelin-contracts/ openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ openzeppelin/=lib/openzeppelin-contracts/contracts/ -royalty-registry-solidity/=lib/royalty-registry-solidity/contracts -tl-sol-tools/=src/ \ No newline at end of file +royalty-registry-solidity/=lib/royalty-registry-solidity/contracts/ \ No newline at end of file diff --git a/src/access/OwnableAccessControl.sol b/src/access/OwnableAccessControl.sol index 0b709a0..fc715f3 100644 --- a/src/access/OwnableAccessControl.sol +++ b/src/access/OwnableAccessControl.sol @@ -1,30 +1,16 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; -import {EnumerableSet} from "openzeppelin/utils/structs/EnumerableSet.sol"; import {Ownable} from "openzeppelin/access/Ownable.sol"; - -/*////////////////////////////////////////////////////////////////////////// - Custom Errors -//////////////////////////////////////////////////////////////////////////*/ - -/// @dev does not have specified role -error NotSpecifiedRole(bytes32 role); - -/// @dev is not specified role or owner -error NotRoleOrOwner(bytes32 role); - -/*////////////////////////////////////////////////////////////////////////// - OwnableAccessControl -//////////////////////////////////////////////////////////////////////////*/ +import {EnumerableSet} from "openzeppelin/utils/structs/EnumerableSet.sol"; /// @title OwnableAccessControl.sol -/// @notice single owner, flexible access control mechanics -/// @dev can easily be extended by inheriting and applying additional roles -/// @dev by default, only the owner can grant roles but by inheriting, but you +/// @notice Single owner, flexible access control mechanics +/// @dev Can easily be extended by inheriting and applying additional roles +/// @dev By default, only the owner can grant roles but by inheriting, but you /// may allow other roles to grant roles by using the internal helper. /// @author transientlabs.xyz -/// @custom:last-updated 2.2.2 +/// @custom:version 3.0.0 abstract contract OwnableAccessControl is Ownable { /*////////////////////////////////////////////////////////////////////////// State Variables @@ -37,20 +23,30 @@ abstract contract OwnableAccessControl is Ownable { mapping(uint256 => mapping(bytes32 => EnumerableSet.AddressSet)) private _roleMembers; /*////////////////////////////////////////////////////////////////////////// - Events + Events //////////////////////////////////////////////////////////////////////////*/ - /// @param from - address that authorized the role change - /// @param user - the address who's role has been changed - /// @param approved - boolean indicating the user's status in role - /// @param role - the bytes32 role created in the inheriting contract + /// @param from Address that authorized the role change + /// @param user The address who's role has been changed + /// @param approved Boolean indicating the user's status in role + /// @param role The bytes32 role created in the inheriting contract event RoleChange(address indexed from, address indexed user, bool indexed approved, bytes32 role); /// @param from - address that authorized the revoke event AllRolesRevoked(address indexed from); /*////////////////////////////////////////////////////////////////////////// - Modifiers + Errors + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Does not have specified role + error NotSpecifiedRole(bytes32 role); + + /// @dev Is not specified role or owner + error NotRoleOrOwner(bytes32 role); + + /*////////////////////////////////////////////////////////////////////////// + Modifiers //////////////////////////////////////////////////////////////////////////*/ modifier onlyRole(bytes32 role) { @@ -71,34 +67,34 @@ abstract contract OwnableAccessControl is Ownable { Constructor //////////////////////////////////////////////////////////////////////////*/ - constructor() Ownable() {} + constructor() Ownable(msg.sender) {} /*////////////////////////////////////////////////////////////////////////// - External Role Functions + External Role Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice function to revoke all roles currently present - /// @dev increments the `_c` variables - /// @dev requires owner privileges + /// @notice Function to revoke all roles currently present + /// @dev Increments the `_c` variables + /// @dev Requires owner privileges function revokeAllRoles() external onlyOwner { _c++; emit AllRolesRevoked(msg.sender); } - /// @notice function to renounce role - /// @param role - bytes32 role created in inheriting contracts + /// @notice Function to renounce role + /// @param role Bytes32 role created in inheriting contracts function renounceRole(bytes32 role) external { address[] memory members = new address[](1); members[0] = msg.sender; _setRole(role, members, false); } - /// @notice function to grant/revoke a role to an address - /// @dev requires owner to call this function but this may be further + /// @notice Function to grant/revoke a role to an address + /// @dev Requires owner to call this function but this may be further /// extended using the internal helper function in inheriting contracts - /// @param role - bytes32 role created in inheriting contracts - /// @param roleMembers - list of addresses that should have roles attached to them based on `status` - /// @param status - bool whether to remove or add `roleMembers` to the `role` + /// @param role Bytes32 role created in inheriting contracts + /// @param roleMembers List of addresses that should have roles attached to them based on `status` + /// @param status Bool whether to remove or add `roleMembers` to the `role` function setRole(bytes32 role, address[] memory roleMembers, bool status) external onlyOwner { _setRole(role, roleMembers, status); } @@ -107,15 +103,15 @@ abstract contract OwnableAccessControl is Ownable { External View Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice function to see if an address is the owner - /// @param role - bytes32 role created in inheriting contracts - /// @param potentialRoleMember - address to check for role membership + /// @notice Function to see if an address is the owner + /// @param role Bytes32 role created in inheriting contracts + /// @param potentialRoleMember Address to check for role membership function hasRole(bytes32 role, address potentialRoleMember) public view returns (bool) { return _roleStatus[_c][role][potentialRoleMember]; } - /// @notice function to get role members - /// @param role - bytes32 role created in inheriting contracts + /// @notice Function to get role members + /// @param role Bytes32 role created in inheriting contracts function getRoleMembers(bytes32 role) public view returns (address[] memory) { return _roleMembers[_c][role].values(); } @@ -124,10 +120,10 @@ abstract contract OwnableAccessControl is Ownable { Internal Helper Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice helper function to set addresses for a role - /// @param role - bytes32 role created in inheriting contracts - /// @param roleMembers - list of addresses that should have roles attached to them based on `status` - /// @param status - bool whether to remove or add `roleMembers` to the `role` + /// @notice Helper function to set addresses for a role + /// @param role Bytes32 role created in inheriting contracts + /// @param roleMembers List of addresses that should have roles attached to them based on `status` + /// @param status Bool whether to remove or add `roleMembers` to the `role` function _setRole(bytes32 role, address[] memory roleMembers, bool status) internal { for (uint256 i = 0; i < roleMembers.length; i++) { _roleStatus[_c][role][roleMembers[i]] = status; diff --git a/src/payments/IChainalysisSanctionsOracle.sol b/src/payments/IChainalysisSanctionsOracle.sol new file mode 100644 index 0000000..6fff193 --- /dev/null +++ b/src/payments/IChainalysisSanctionsOracle.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IChainalysisSanctionsOracle { + function isSanctioned(address addr) external view returns (bool); +} diff --git a/src/payments/IWETH.sol b/src/payments/IWETH.sol index f38b643..320bec3 100644 --- a/src/payments/IWETH.sol +++ b/src/payments/IWETH.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; diff --git a/src/payments/RoyaltyPayoutHelper.sol b/src/payments/RoyaltyPayoutHelper.sol index 419d990..e7a7638 100644 --- a/src/payments/RoyaltyPayoutHelper.sol +++ b/src/payments/RoyaltyPayoutHelper.sol @@ -1,19 +1,15 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; -import {TransferHelper} from "./TransferHelper.sol"; -import {SanctionsCompliance} from "./SanctionsCompliance.sol"; import {IRoyaltyEngineV1} from "royalty-registry-solidity/IRoyaltyEngineV1.sol"; - -/*////////////////////////////////////////////////////////////////////////// - Royalty Payout Helper -//////////////////////////////////////////////////////////////////////////*/ +import {SanctionsCompliance} from "src/payments/SanctionsCompliance.sol"; +import {TransferHelper} from "src/payments/TransferHelper.sol"; /// @title Royalty Payout Helper /// @notice Abstract contract to help payout royalties using the Royalty Registry /// @dev Does not manage updating the sanctions oracle and expects the child contract to implement /// @author transientlabs.xyz -/// @custom:last-updated 2.5.0 +/// @custom:last-updated 3.0.0 abstract contract RoyaltyPayoutHelper is TransferHelper, SanctionsCompliance { /*////////////////////////////////////////////////////////////////////////// State Variables @@ -29,7 +25,9 @@ abstract contract RoyaltyPayoutHelper is TransferHelper, SanctionsCompliance { /// @param sanctionsOracle - the init sanctions oracle /// @param wethAddress - the init weth address /// @param royaltyEngineAddress - the init royalty engine address - constructor(address sanctionsOracle, address wethAddress, address royaltyEngineAddress) SanctionsCompliance(sanctionsOracle) { + constructor(address sanctionsOracle, address wethAddress, address royaltyEngineAddress) + SanctionsCompliance(sanctionsOracle) + { weth = wethAddress; royaltyEngine = IRoyaltyEngineV1(royaltyEngineAddress); } @@ -59,7 +57,7 @@ abstract contract RoyaltyPayoutHelper is TransferHelper, SanctionsCompliance { /// @notice Function to payout royalties from the contract balance based on sale price /// @dev if the call to the royalty engine reverts or if the return values are invalid, no payments are made /// @dev if the sum of the royalty payouts is greater than the salePrice, the loop exits early for gas savings (this shouldn't happen in reality) - /// @dev if this is used in a call where tokens should be transferred from a sender, it is advisable to + /// @dev if this is used in a call where tokens should be transferred from a sender, it is advisable to /// first transfer the required amount to the contract and then call this function, as it will save on gas /// @param token The contract address for the token /// @param tokenId The token id diff --git a/src/payments/SanctionsCompliance.sol b/src/payments/SanctionsCompliance.sol index 5903130..a456fc7 100644 --- a/src/payments/SanctionsCompliance.sol +++ b/src/payments/SanctionsCompliance.sol @@ -1,44 +1,35 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; -/*////////////////////////////////////////////////////////////////////////// - Chainalysis Sanctions Oracle -//////////////////////////////////////////////////////////////////////////*/ - -interface ChainalysisSanctionsOracle { - function isSanctioned(address addr) external view returns (bool); -} - -/*////////////////////////////////////////////////////////////////////////// - Errors -//////////////////////////////////////////////////////////////////////////*/ - -error SanctionedAddress(); - -/*////////////////////////////////////////////////////////////////////////// - Sanctions Compliance -//////////////////////////////////////////////////////////////////////////*/ +import {IChainalysisSanctionsOracle} from "src/payments/IChainalysisSanctionsOracle.sol"; /// @title Sanctions Compliance /// @notice Abstract contract to comply with U.S. sanctioned addresses /// @dev Uses the Chainalysis Sanctions Oracle for checking sanctions /// @author transientlabs.xyz -/// @custom:last-updated 2.5.0 +/// @custom:version 3.0.0 contract SanctionsCompliance { /*////////////////////////////////////////////////////////////////////////// State Variables //////////////////////////////////////////////////////////////////////////*/ - ChainalysisSanctionsOracle public oracle; + IChainalysisSanctionsOracle public oracle; + + /*////////////////////////////////////////////////////////////////////////// + Errors + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Sanctioned address by OFAC + error SanctionedAddress(); /*////////////////////////////////////////////////////////////////////////// - Events + Events //////////////////////////////////////////////////////////////////////////*/ event SanctionsOracleUpdated(address indexed prevOracle, address indexed newOracle); /*////////////////////////////////////////////////////////////////////////// - Constructor + Constructor //////////////////////////////////////////////////////////////////////////*/ constructor(address initOracle) { @@ -53,7 +44,7 @@ contract SanctionsCompliance { /// @param newOracle The new sanctions oracle address function _updateSanctionsOracle(address newOracle) internal { address prevOracle = address(oracle); - oracle = ChainalysisSanctionsOracle(newOracle); + oracle = IChainalysisSanctionsOracle(newOracle); emit SanctionsOracleUpdated(prevOracle, newOracle); } diff --git a/src/payments/TransferHelper.sol b/src/payments/TransferHelper.sol index 1c8cefa..7d3a520 100644 --- a/src/payments/TransferHelper.sol +++ b/src/payments/TransferHelper.sol @@ -1,35 +1,31 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol"; -import {IWETH, IERC20} from "./IWETH.sol"; - -/*////////////////////////////////////////////////////////////////////////// - Custom Errors -//////////////////////////////////////////////////////////////////////////*/ - -/// @dev ETH transfer failed -error ETHTransferFailed(); - -/// @dev Transferred too few ERC-20 tokens -error InsufficentERC20Transfer(); - -/*////////////////////////////////////////////////////////////////////////// - Transfer Helper -//////////////////////////////////////////////////////////////////////////*/ +import {IWETH, IERC20} from "src/payments/IWETH.sol"; /// @title Transfer Helper /// @notice Abstract contract that has helper function for sending ETH and ERC20's safely /// @author transientlabs.xyz -/// @custom:last-updated 2.6.0 +/// @custom:version 3.0.0 abstract contract TransferHelper { /*////////////////////////////////////////////////////////////////////////// - State Variables + Types //////////////////////////////////////////////////////////////////////////*/ using SafeERC20 for IERC20; using SafeERC20 for IWETH; + /*////////////////////////////////////////////////////////////////////////// + Errors + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev ETH transfer failed + error ETHTransferFailed(); + + /// @dev Transferred too few ERC-20 tokens + error InsufficentERC20Transfer(); + /*////////////////////////////////////////////////////////////////////////// ETH Functions //////////////////////////////////////////////////////////////////////////*/ diff --git a/src/royalties/EIP2981TL.sol b/src/royalties/EIP2981TL.sol index c4f82fc..bb056f8 100644 --- a/src/royalties/EIP2981TL.sol +++ b/src/royalties/EIP2981TL.sol @@ -1,33 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import {ERC165} from "openzeppelin/utils/introspection/ERC165.sol"; -import {IEIP2981} from "./IEIP2981.sol"; - -/*////////////////////////////////////////////////////////////////////////// - Custom Errors -//////////////////////////////////////////////////////////////////////////*/ - -/// @dev error if the recipient is set to address(0) -error ZeroAddressError(); - -/// @dev error if the royalty percentage is greater than to 100% -error MaxRoyaltyError(); - -/*////////////////////////////////////////////////////////////////////////// - EIP2981TL -//////////////////////////////////////////////////////////////////////////*/ +import {IEIP2981} from "src/royalties/IEIP2981.sol"; /// @title EIP2981TL.sol -/// @notice abstract contract to define a default royalty spec +/// @notice Abstract contract to define a default royalty spec /// while allowing for specific token overrides -/// @dev follows EIP-2981 (https://eips.ethereum.org/EIPS/eip-2981) +/// @dev Follows EIP-2981 (https://eips.ethereum.org/EIPS/eip-2981) /// @author transientlabs.xyz -/// https://github.com/Transient-Labs/tl-sol-tools -/// @custom:last-updated 2.2.2 +/// @custom:version 3.0.0 abstract contract EIP2981TL is IEIP2981, ERC165 { /*////////////////////////////////////////////////////////////////////////// - Royalty Struct + Types //////////////////////////////////////////////////////////////////////////*/ struct RoyaltySpec { @@ -39,16 +24,27 @@ abstract contract EIP2981TL is IEIP2981, ERC165 { State Variables //////////////////////////////////////////////////////////////////////////*/ + uint256 public constant BASIS = 10_000; address private _defaultRecipient; uint256 private _defaultPercentage; mapping(uint256 => RoyaltySpec) private _tokenOverrides; + /*////////////////////////////////////////////////////////////////////////// + Errors + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev error if the recipient is set to address(0) + error ZeroAddressError(); + + /// @dev error if the royalty percentage is greater than to 100% + error MaxRoyaltyError(); + /*////////////////////////////////////////////////////////////////////////// Constructor //////////////////////////////////////////////////////////////////////////*/ - /// @param defaultRecipient - the default royalty payout address - /// @param defaultPercentage - the deafult royalty percentage, out of 10,000 + /// @param defaultRecipient The default royalty payout address + /// @param defaultPercentage The deafult royalty percentage, out of 10,000 constructor(address defaultRecipient, uint256 defaultPercentage) { _setDefaultRoyaltyInfo(defaultRecipient, defaultPercentage); } @@ -57,9 +53,9 @@ abstract contract EIP2981TL is IEIP2981, ERC165 { Royalty Changing Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice function to set default royalty info - /// @param newRecipient - the new default royalty payout address - /// @param newPercentage - the new default royalty percentage, out of 10,000 + /// @notice Function to set default royalty info + /// @param newRecipient The new default royalty payout address + /// @param newPercentage The new default royalty percentage, out of 10,000 function _setDefaultRoyaltyInfo(address newRecipient, uint256 newPercentage) internal { if (newRecipient == address(0)) revert ZeroAddressError(); if (newPercentage > 10_000) revert MaxRoyaltyError(); @@ -67,10 +63,10 @@ abstract contract EIP2981TL is IEIP2981, ERC165 { _defaultPercentage = newPercentage; } - /// @notice function to override royalty spec on a specific token - /// @param tokenId - the token id to override royalty for - /// @param newRecipient - the new royalty payout address - /// @param newPercentage - the new royalty percentage, out of 10,000 + /// @notice Function to override royalty spec on a specific token + /// @param tokenId The token id to override royalty for + /// @param newRecipient The new royalty payout address + /// @param newPercentage The new royalty percentage, out of 10,000 function _overrideTokenRoyaltyInfo(uint256 tokenId, address newRecipient, uint256 newPercentage) internal { if (newRecipient == address(0)) revert ZeroAddressError(); if (newPercentage > 10_000) revert MaxRoyaltyError(); @@ -94,7 +90,7 @@ abstract contract EIP2981TL is IEIP2981, ERC165 { recipient = _tokenOverrides[tokenId].recipient; percentage = _tokenOverrides[tokenId].percentage; } - return (recipient, salePrice / 10_000 * percentage); // divide first to avoid overflow + return (recipient, salePrice * percentage / BASIS); } /*////////////////////////////////////////////////////////////////////////// diff --git a/src/royalties/IEIP2981.sol b/src/royalties/IEIP2981.sol index cf33c1c..d9373c0 100644 --- a/src/royalties/IEIP2981.sol +++ b/src/royalties/IEIP2981.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; /// /// @dev Interface for the NFT Royalty Standard @@ -12,10 +12,10 @@ interface IEIP2981 { /// @notice Called with the sale price to determine how much royalty // is owed and to whom. - /// @param tokenId - the NFT asset queried for royalty information - /// @param salePrice - the sale price of the NFT asset specified by tokenId - /// @return receiver - address of who should be sent the royalty payment - /// @return royaltyAmount - the royalty payment amount for salePrice + /// @param tokenId The NFT asset queried for royalty information + /// @param salePrice The sale price of the NFT asset specified by tokenId + /// @return receiver Address of who should be sent the royalty payment + /// @return royaltyAmount The royalty payment amount for salePrice function royaltyInfo(uint256 tokenId, uint256 salePrice) external view diff --git a/src/upgradeable/access/OwnableAccessControlUpgradeable.sol b/src/upgradeable/access/OwnableAccessControlUpgradeable.sol index 229c18b..874cb60 100644 --- a/src/upgradeable/access/OwnableAccessControlUpgradeable.sol +++ b/src/upgradeable/access/OwnableAccessControlUpgradeable.sol @@ -1,57 +1,69 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; -import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol"; -import {EnumerableSetUpgradeable} from "openzeppelin-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import {OwnableUpgradeable} from "openzeppelin-upgradeable/access/OwnableUpgradeable.sol"; +import {EnumerableSet} from "openzeppelin/utils/structs/EnumerableSet.sol"; -/*////////////////////////////////////////////////////////////////////////// - Custom Errors -//////////////////////////////////////////////////////////////////////////*/ - -/// @dev does not have specified role -error NotSpecifiedRole(bytes32 role); - -/// @dev is not specified role or owner -error NotRoleOrOwner(bytes32 role); - -/*////////////////////////////////////////////////////////////////////////// - OwnableAccessControlUpgradeable -//////////////////////////////////////////////////////////////////////////*/ - -/// @title OwnableAccessControl.sol -/// @notice single owner, flexible access control mechanics -/// @dev can easily be extended by inheriting and applying additional roles -/// @dev by default, only the owner can grant roles but by inheriting, but you +/// @title OwnableAccessControlUpgradeable.sol +/// @notice Single owner, flexible access control mechanics +/// @dev Can easily be extended by inheriting and applying additional roles +/// @dev By default, only the owner can grant roles but by inheriting, but you /// may allow other roles to grant roles by using the internal helper. /// @author transientlabs.xyz -/// @custom:last-updated 2.2.2 -abstract contract OwnableAccessControlUpgradeable is Initializable, OwnableUpgradeable { +/// @custom:version 3.0.0 +abstract contract OwnableAccessControlUpgradeable is OwnableUpgradeable { /*////////////////////////////////////////////////////////////////////////// - State Variables + Types //////////////////////////////////////////////////////////////////////////*/ - using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; + using EnumerableSet for EnumerableSet.AddressSet; - uint256 private _c; // counter to be able to revoke all priviledges - mapping(uint256 => mapping(bytes32 => mapping(address => bool))) private _roleStatus; - mapping(uint256 => mapping(bytes32 => EnumerableSetUpgradeable.AddressSet)) private _roleMembers; + /*////////////////////////////////////////////////////////////////////////// + Storage + //////////////////////////////////////////////////////////////////////////*/ + + /// @custom:storage-location erc7201:transientlabs.storage.OwnableAccessControl + struct OwnableAccessControlStorage { + uint256 c; // counter to be able to revoke all priviledges + mapping(uint256 => mapping(bytes32 => mapping(address => bool))) roleStatus; + mapping(uint256 => mapping(bytes32 => EnumerableSet.AddressSet)) roleMembers; + } + + // keccak256(abi.encode(uint256(keccak256("transientlabs.storage.OwnableAccessControl")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant OwnableAccessControlStorageLocation = + 0x0d0469b3d32e63681b9fc586a5627ad5e70b3d1ad20f31767e4b6c4141c7e300; + + function _getOwnableAccessControlStorage() private pure returns (OwnableAccessControlStorage storage $) { + assembly { + $.slot := OwnableAccessControlStorageLocation + } + } /*////////////////////////////////////////////////////////////////////////// - Events + Events //////////////////////////////////////////////////////////////////////////*/ - /// @param from - address that authorized the role change - /// @param user - the address who's role has been changed - /// @param approved - boolean indicating the user's status in role - /// @param role - the bytes32 role created in the inheriting contract + /// @param from Address that authorized the role change + /// @param user The address who's role has been changed + /// @param approved Boolean indicating the user's status in role + /// @param role The bytes32 role created in the inheriting contract event RoleChange(address indexed from, address indexed user, bool indexed approved, bytes32 role); - /// @param from - address that authorized the revoke + /// @param from Address that authorized the revoke event AllRolesRevoked(address indexed from); /*////////////////////////////////////////////////////////////////////////// - Modifiers + Errors + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Does not have specified role + error NotSpecifiedRole(bytes32 role); + + /// @dev Is not specified role or owner + error NotRoleOrOwner(bytes32 role); + + /*////////////////////////////////////////////////////////////////////////// + Modifiers //////////////////////////////////////////////////////////////////////////*/ modifier onlyRole(bytes32 role) { @@ -69,13 +81,12 @@ abstract contract OwnableAccessControlUpgradeable is Initializable, OwnableUpgra } /*////////////////////////////////////////////////////////////////////////// - Initializer + Initializer //////////////////////////////////////////////////////////////////////////*/ - /// @param initOwner - the address of the initial owner + /// @param initOwner The address of the initial owner function __OwnableAccessControl_init(address initOwner) internal onlyInitializing { - __Ownable_init(); - _transferOwnership(initOwner); + __Ownable_init(initOwner); __OwnableAccessControl_init_unchained(); } @@ -85,28 +96,29 @@ abstract contract OwnableAccessControlUpgradeable is Initializable, OwnableUpgra External Role Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice function to revoke all roles currently present - /// @dev increments the `_c` variables - /// @dev requires owner privileges + /// @notice Function to revoke all roles currently present + /// @dev Increments the `_c` variables + /// @dev Requires owner privileges function revokeAllRoles() external onlyOwner { - _c++; + OwnableAccessControlStorage storage $ = _getOwnableAccessControlStorage(); + $.c++; emit AllRolesRevoked(msg.sender); } - /// @notice function to renounce role - /// @param role - bytes32 role created in inheriting contracts + /// @notice Function to renounce role + /// @param role Bytes32 role created in inheriting contracts function renounceRole(bytes32 role) external { address[] memory members = new address[](1); members[0] = msg.sender; _setRole(role, members, false); } - /// @notice function to grant/revoke a role to an address - /// @dev requires owner to call this function but this may be further + /// @notice Function to grant/revoke a role to an address + /// @dev Requires owner to call this function but this may be further /// extended using the internal helper function in inheriting contracts - /// @param role - bytes32 role created in inheriting contracts - /// @param roleMembers - list of addresses that should have roles attached to them based on `status` - /// @param status - bool whether to remove or add `roleMembers` to the `role` + /// @param role Bytes32 role created in inheriting contracts + /// @param roleMembers List of addresses that should have roles attached to them based on `status` + /// @param status Bool whether to remove or add `roleMembers` to the `role` function setRole(bytes32 role, address[] memory roleMembers, bool status) external onlyOwner { _setRole(role, roleMembers, status); } @@ -115,43 +127,39 @@ abstract contract OwnableAccessControlUpgradeable is Initializable, OwnableUpgra External View Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice function to see if an address is the owner - /// @param role - bytes32 role created in inheriting contracts - /// @param potentialRoleMember - address to check for role membership + /// @notice Function to see if an address is the owner + /// @param role Bytes32 role created in inheriting contracts + /// @param potentialRoleMember Address to check for role membership function hasRole(bytes32 role, address potentialRoleMember) public view returns (bool) { - return _roleStatus[_c][role][potentialRoleMember]; + OwnableAccessControlStorage storage $ = _getOwnableAccessControlStorage(); + return $.roleStatus[$.c][role][potentialRoleMember]; } - /// @notice function to get role members - /// @param role - bytes32 role created in inheriting contracts + /// @notice Function to get role members + /// @param role Bytes32 role created in inheriting contracts function getRoleMembers(bytes32 role) public view returns (address[] memory) { - return _roleMembers[_c][role].values(); + OwnableAccessControlStorage storage $ = _getOwnableAccessControlStorage(); + return $.roleMembers[$.c][role].values(); } /*////////////////////////////////////////////////////////////////////////// Internal Helper Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice helper function to set addresses for a role - /// @param role - bytes32 role created in inheriting contracts - /// @param roleMembers - list of addresses that should have roles attached to them based on `status` - /// @param status - bool whether to remove or add `roleMembers` to the `role` + /// @notice Helper function to set addresses for a role + /// @param role Bytes32 role created in inheriting contracts + /// @param roleMembers List of addresses that should have roles attached to them based on `status` + /// @param status Bool whether to remove or add `roleMembers` to the `role` function _setRole(bytes32 role, address[] memory roleMembers, bool status) internal { + OwnableAccessControlStorage storage $ = _getOwnableAccessControlStorage(); for (uint256 i = 0; i < roleMembers.length; i++) { - _roleStatus[_c][role][roleMembers[i]] = status; + $.roleStatus[$.c][role][roleMembers[i]] = status; if (status) { - _roleMembers[_c][role].add(roleMembers[i]); + $.roleMembers[$.c][role].add(roleMembers[i]); } else { - _roleMembers[_c][role].remove(roleMembers[i]); + $.roleMembers[$.c][role].remove(roleMembers[i]); } emit RoleChange(msg.sender, roleMembers[i], status, role); } } - - /*////////////////////////////////////////////////////////////////////////// - Upgradeability Gap - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev gap variable - see https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - uint256[50] private _gap; } diff --git a/src/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.sol b/src/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.sol index 64cf6bf..451fca0 100644 --- a/src/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.sol +++ b/src/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.sol @@ -1,27 +1,35 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; -import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol"; -import {TransferHelper} from "../../payments/TransferHelper.sol"; -import {SanctionsComplianceUpgradeable} from "./SanctionsComplianceUpgradeable.sol"; import {IRoyaltyEngineV1} from "royalty-registry-solidity/IRoyaltyEngineV1.sol"; - -/*////////////////////////////////////////////////////////////////////////// - Royalty Payout Helper -//////////////////////////////////////////////////////////////////////////*/ +import {TransferHelper} from "src/payments/TransferHelper.sol"; +import {SanctionsComplianceUpgradeable} from "src/upgradeable/payments/SanctionsComplianceUpgradeable.sol"; /// @title Royalty Payout Helper /// @notice Abstract contract to help payout royalties using the Royalty Registry /// @dev Does not manage updating the sanctions oracle and expects the child contract to implement /// @author transientlabs.xyz -/// @custom:last-updated 2.4.0 -abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelper, SanctionsComplianceUpgradeable { +/// @custom:version 3.0.0 +abstract contract RoyaltyPayoutHelperUpgradeable is SanctionsComplianceUpgradeable, TransferHelper { /*////////////////////////////////////////////////////////////////////////// - State Variables + Storage //////////////////////////////////////////////////////////////////////////*/ - address public weth; - IRoyaltyEngineV1 public royaltyEngine; + /// @custom:storage-location erc7201:transientlabs.storage.RoyaltyPayoutHelper + struct RoyaltyPayoutHelperStorage { + address weth; + IRoyaltyEngineV1 royaltyEngine; + } + + // keccak256(abi.encode(uint256(keccak256("transientlabs.storage.RoyaltyPayoutHelper")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant RoyaltyPayoutHelperStorageLocation = + 0x9ab1d1ca9bfa2c669468b724939724262b3f2887db3df18c90168701d6422700; + + function _getRoyaltyPayoutHelperStorage() private pure returns (RoyaltyPayoutHelperStorage storage $) { + assembly { + $.slot := RoyaltyPayoutHelperStorageLocation + } + } /*////////////////////////////////////////////////////////////////////////// Initializer @@ -31,7 +39,10 @@ abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelpe /// @param sanctionsOracle - the init sanctions oracle /// @param wethAddress - the init weth address /// @param royaltyEngineAddress - the init royalty engine address - function __RoyaltyPayoutHelper_init(address sanctionsOracle, address wethAddress, address royaltyEngineAddress) internal onlyInitializing { + function __RoyaltyPayoutHelper_init(address sanctionsOracle, address wethAddress, address royaltyEngineAddress) + internal + onlyInitializing + { __RoyaltyPayoutHelper_init_unchained(wethAddress, royaltyEngineAddress); __SanctionsCompliance_init(sanctionsOracle); } @@ -43,8 +54,9 @@ abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelpe internal onlyInitializing { - weth = wethAddress; - royaltyEngine = IRoyaltyEngineV1(royaltyEngineAddress); + RoyaltyPayoutHelperStorage storage $ = _getRoyaltyPayoutHelperStorage(); + $.weth = wethAddress; + $.royaltyEngine = IRoyaltyEngineV1(royaltyEngineAddress); } /*////////////////////////////////////////////////////////////////////////// @@ -55,14 +67,16 @@ abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelpe /// @dev Care should be taken to ensure proper access control for this function /// @param wethAddress The new WETH token address function _setWethAddress(address wethAddress) internal { - weth = wethAddress; + RoyaltyPayoutHelperStorage storage $ = _getRoyaltyPayoutHelperStorage(); + $.weth = wethAddress; } /// @notice Function to update the royalty engine address /// @dev Care should be taken to ensure proper access control for this function /// @param royaltyEngineAddress The new royalty engine address function _setRoyaltyEngineAddress(address royaltyEngineAddress) internal { - royaltyEngine = IRoyaltyEngineV1(royaltyEngineAddress); + RoyaltyPayoutHelperStorage storage $ = _getRoyaltyPayoutHelperStorage(); + $.royaltyEngine = IRoyaltyEngineV1(royaltyEngineAddress); } /*////////////////////////////////////////////////////////////////////////// @@ -72,7 +86,7 @@ abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelpe /// @notice Function to payout royalties from the contract balance based on sale price /// @dev if the call to the royalty engine reverts or if the return values are invalid, no payments are made /// @dev if the sum of the royalty payouts is greater than the salePrice, the loop exits early for gas savings (this shouldn't happen in reality) - /// @dev if this is used in a call where tokens should be transferred from a sender, it is advisable to + /// @dev if this is used in a call where tokens should be transferred from a sender, it is advisable to /// first transfer the required amount to the contract and then call this function, as it will save on gas /// @param token The contract address for the token /// @param tokenId The token id @@ -83,9 +97,10 @@ abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelpe internal returns (uint256 remainingSale) { + RoyaltyPayoutHelperStorage storage $ = _getRoyaltyPayoutHelperStorage(); remainingSale = salePrice; - if (address(royaltyEngine).code.length == 0) return remainingSale; - try royaltyEngine.getRoyalty(token, tokenId, salePrice) returns ( + if (address($.royaltyEngine).code.length == 0) return remainingSale; + try $.royaltyEngine.getRoyalty(token, tokenId, salePrice) returns ( address payable[] memory recipients, uint256[] memory amounts ) { if (recipients.length != amounts.length) return remainingSale; @@ -95,7 +110,7 @@ abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelpe if (amounts[i] > remainingSale) break; remainingSale -= amounts[i]; if (currency == address(0)) { - _safeTransferETH(recipients[i], amounts[i], weth); + _safeTransferETH(recipients[i], amounts[i], $.weth); } else { _safeTransferERC20(recipients[i], currency, amounts[i]); } @@ -108,9 +123,18 @@ abstract contract RoyaltyPayoutHelperUpgradeable is Initializable, TransferHelpe } /*////////////////////////////////////////////////////////////////////////// - Upgradeability Gap + Public View Functions //////////////////////////////////////////////////////////////////////////*/ - /// @dev gap variable - see https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - uint256[50] private _gap; + /// @notice Function to get the current WETH address + function weth() public view returns (address) { + RoyaltyPayoutHelperStorage storage $ = _getRoyaltyPayoutHelperStorage(); + return $.weth; + } + + /// @notice Function to get the royalty registry + function royaltyEngine() public view returns (IRoyaltyEngineV1) { + RoyaltyPayoutHelperStorage storage $ = _getRoyaltyPayoutHelperStorage(); + return $.royaltyEngine; + } } diff --git a/src/upgradeable/payments/SanctionsComplianceUpgradeable.sol b/src/upgradeable/payments/SanctionsComplianceUpgradeable.sol index 0f48d86..9084570 100644 --- a/src/upgradeable/payments/SanctionsComplianceUpgradeable.sol +++ b/src/upgradeable/payments/SanctionsComplianceUpgradeable.sol @@ -2,36 +2,32 @@ pragma solidity ^0.8.17; import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol"; - -/*////////////////////////////////////////////////////////////////////////// - Chainalysis Sanctions Oracle -//////////////////////////////////////////////////////////////////////////*/ - -interface ChainalysisSanctionsOracle { - function isSanctioned(address addr) external view returns (bool); -} - -/*////////////////////////////////////////////////////////////////////////// - Errors -//////////////////////////////////////////////////////////////////////////*/ - -error SanctionedAddress(); - -/*////////////////////////////////////////////////////////////////////////// - Sanctions Compliance -//////////////////////////////////////////////////////////////////////////*/ +import {IChainalysisSanctionsOracle} from "src/payments/IChainalysisSanctionsOracle.sol"; /// @title Sanctions Compliance /// @notice Abstract contract to comply with U.S. sanctioned addresses /// @dev Uses the Chainalysis Sanctions Oracle for checking sanctions /// @author transientlabs.xyz -/// @custom:last-updated 2.5.0 +/// @custom:version 3.0.0 contract SanctionsComplianceUpgradeable is Initializable { /*////////////////////////////////////////////////////////////////////////// - State Variables + Storage //////////////////////////////////////////////////////////////////////////*/ - ChainalysisSanctionsOracle public oracle; + /// @custom:storage-location erc7201:transientlabs.storage.SanctionsCompliance + struct SanctionComplianceStorage { + IChainalysisSanctionsOracle oracle; + } + + // keccak256(abi.encode(uint256(keccak256("transientlabs.storage.SanctionsCompliance")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant SanctionComplianceStorageLocation = + 0xd66684c5a7747baca4a45cbf84c01526f3b53186fc4aea64a4c6e2fa4447c700; + + function _getSanctionsComplianceStorage() private pure returns (SanctionComplianceStorage storage $) { + assembly { + $.slot := SanctionComplianceStorageLocation + } + } /*////////////////////////////////////////////////////////////////////////// Events @@ -39,6 +35,13 @@ contract SanctionsComplianceUpgradeable is Initializable { event SanctionsOracleUpdated(address indexed prevOracle, address indexed newOracle); + /*////////////////////////////////////////////////////////////////////////// + Errors + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Sanctioned address by OFAC + error SanctionedAddress(); + /*////////////////////////////////////////////////////////////////////////// Initializer //////////////////////////////////////////////////////////////////////////*/ @@ -51,10 +54,7 @@ contract SanctionsComplianceUpgradeable is Initializable { /// @notice unchained function to initialize the contract /// @param initOracle The initial oracle address - function __SanctionsCompliance_init_unchained(address initOracle) - internal - onlyInitializing - { + function __SanctionsCompliance_init_unchained(address initOracle) internal onlyInitializing { _updateSanctionsOracle(initOracle); } @@ -65,8 +65,9 @@ contract SanctionsComplianceUpgradeable is Initializable { /// @notice Internal function to change the sanctions oracle /// @param newOracle The new sanctions oracle address function _updateSanctionsOracle(address newOracle) internal { - address prevOracle = address(oracle); - oracle = ChainalysisSanctionsOracle(newOracle); + SanctionComplianceStorage storage $ = _getSanctionsComplianceStorage(); + address prevOracle = address($.oracle); + $.oracle = IChainalysisSanctionsOracle(newOracle); emit SanctionsOracleUpdated(prevOracle, newOracle); } @@ -77,11 +78,22 @@ contract SanctionsComplianceUpgradeable is Initializable { /// @param shouldRevertIfSanctioned A flag indicating if the call should revert if the sender is sanctioned. Set to false if wanting to get a result. /// @return isSanctioned Boolean indicating if the sender is sanctioned function _isSanctioned(address sender, bool shouldRevertIfSanctioned) internal view returns (bool isSanctioned) { - if (address(oracle) == address(0)) { + SanctionComplianceStorage storage $ = _getSanctionsComplianceStorage(); + if (address($.oracle) == address(0)) { return false; } - isSanctioned = oracle.isSanctioned(sender); + isSanctioned = $.oracle.isSanctioned(sender); if (shouldRevertIfSanctioned && isSanctioned) revert SanctionedAddress(); return isSanctioned; } + + /*////////////////////////////////////////////////////////////////////////// + Public View Functions + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Function to get chainalysis oracle + function oracle() public view returns (IChainalysisSanctionsOracle) { + SanctionComplianceStorage storage $ = _getSanctionsComplianceStorage(); + return $.oracle; + } } diff --git a/src/upgradeable/royalties/EIP2981TLUpgradeable.sol b/src/upgradeable/royalties/EIP2981TLUpgradeable.sol index 443ab9d..b8d4a6a 100644 --- a/src/upgradeable/royalties/EIP2981TLUpgradeable.sol +++ b/src/upgradeable/royalties/EIP2981TLUpgradeable.sol @@ -1,33 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; -import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol"; import {ERC165Upgradeable} from "openzeppelin-upgradeable/utils/introspection/ERC165Upgradeable.sol"; -import {IEIP2981} from "../../royalties/IEIP2981.sol"; - -/*////////////////////////////////////////////////////////////////////////// - Custom Errors -//////////////////////////////////////////////////////////////////////////*/ - -/// @dev error if the recipient is set to address(0) -error ZeroAddressError(); - -/// @dev error if the royalty percentage is greater than to 100% -error MaxRoyaltyError(); - -/*////////////////////////////////////////////////////////////////////////// - EIP2981TL -//////////////////////////////////////////////////////////////////////////*/ +import {IEIP2981} from "src/royalties/IEIP2981.sol"; /// @title EIP2981TLUpgradeable.sol -/// @notice abstract contract to define a default royalty spec +/// @notice Abstract contract to define a default royalty spec /// while allowing for specific token overrides -/// @dev follows EIP-2981 (https://eips.ethereum.org/EIPS/eip-2981) +/// @dev Follows EIP-2981 (https://eips.ethereum.org/EIPS/eip-2981) /// @author transientlabs.xyz -/// @custom:last-updated 2.2.2 -abstract contract EIP2981TLUpgradeable is IEIP2981, Initializable, ERC165Upgradeable { +/// @custom:version 3.0.0 +abstract contract EIP2981TLUpgradeable is IEIP2981, ERC165Upgradeable { /*////////////////////////////////////////////////////////////////////////// - Royalty Struct + Types //////////////////////////////////////////////////////////////////////////*/ struct RoyaltySpec { @@ -36,27 +21,56 @@ abstract contract EIP2981TLUpgradeable is IEIP2981, Initializable, ERC165Upgrade } /*////////////////////////////////////////////////////////////////////////// - State Variables + Storage + //////////////////////////////////////////////////////////////////////////*/ + + /// @custom:storage-location erc7201:transientlabs.storage.EIP2981TLStorage + struct EIP2981TLStorage { + address defaultRecipient; + uint256 defaultPercentage; + mapping(uint256 => RoyaltySpec) tokenOverrides; + } + + // keccak256(abi.encode(uint256(keccak256("transientlabs.storage.EIP2981TLStorage")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant EIP2981TLStorageLocation = + 0xe9db8e9b56f2e28e12956850f386d9a4c1e886a4f584b61a10a9d0cacee70700; + + function _getEIP2981TLStorage() private pure returns (EIP2981TLStorage storage $) { + assembly { + $.slot := EIP2981TLStorageLocation + } + } + + /*////////////////////////////////////////////////////////////////////////// + Constants //////////////////////////////////////////////////////////////////////////*/ - address private _defaultRecipient; - uint256 private _defaultPercentage; - mapping(uint256 => RoyaltySpec) private _tokenOverrides; + uint256 public constant BASIS = 10_000; + + /*////////////////////////////////////////////////////////////////////////// + Errors + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev error if the recipient is set to address(0) + error ZeroAddressError(); + + /// @dev error if the royalty percentage is greater than to 100% + error MaxRoyaltyError(); /*////////////////////////////////////////////////////////////////////////// Initializer //////////////////////////////////////////////////////////////////////////*/ - /// @notice function to initialize the contract - /// @param defaultRecipient - the default royalty payout address - /// @param defaultPercentage - the deafult royalty percentage, out of 10,000 + /// @notice Function to initialize the contract + /// @param defaultRecipient The default royalty payout address + /// @param defaultPercentage The deafult royalty percentage, out of 10,000 function __EIP2981TL_init(address defaultRecipient, uint256 defaultPercentage) internal onlyInitializing { __EIP2981TL_init_unchained(defaultRecipient, defaultPercentage); } - /// @notice unchained function to initialize the contract - /// @param defaultRecipient - the default royalty payout address - /// @param defaultPercentage - the deafult royalty percentage, out of 10,000 + /// @notice Unchained function to initialize the contract + /// @param defaultRecipient The default royalty payout address + /// @param defaultPercentage The deafult royalty percentage, out of 10,000 function __EIP2981TL_init_unchained(address defaultRecipient, uint256 defaultPercentage) internal onlyInitializing @@ -68,25 +82,27 @@ abstract contract EIP2981TLUpgradeable is IEIP2981, Initializable, ERC165Upgrade Royalty Changing Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice function to set default royalty info - /// @param newRecipient - the new default royalty payout address - /// @param newPercentage - the new default royalty percentage, out of 10,000 + /// @notice Function to set default royalty info + /// @param newRecipient The new default royalty payout address + /// @param newPercentage The new default royalty percentage, out of 10,000 function _setDefaultRoyaltyInfo(address newRecipient, uint256 newPercentage) internal { + EIP2981TLStorage storage $ = _getEIP2981TLStorage(); if (newRecipient == address(0)) revert ZeroAddressError(); if (newPercentage > 10_000) revert MaxRoyaltyError(); - _defaultRecipient = newRecipient; - _defaultPercentage = newPercentage; + $.defaultRecipient = newRecipient; + $.defaultPercentage = newPercentage; } - /// @notice function to override royalty spec on a specific token - /// @param tokenId - the token id to override royalty for - /// @param newRecipient - the new royalty payout address - /// @param newPercentage - the new royalty percentage, out of 10,000 + /// @notice Function to override royalty spec on a specific token + /// @param tokenId The token id to override royalty for + /// @param newRecipient The new royalty payout address + /// @param newPercentage The new royalty percentage, out of 10,000 function _overrideTokenRoyaltyInfo(uint256 tokenId, address newRecipient, uint256 newPercentage) internal { + EIP2981TLStorage storage $ = _getEIP2981TLStorage(); if (newRecipient == address(0)) revert ZeroAddressError(); if (newPercentage > 10_000) revert MaxRoyaltyError(); - _tokenOverrides[tokenId].recipient = newRecipient; - _tokenOverrides[tokenId].percentage = newPercentage; + $.tokenOverrides[tokenId].recipient = newRecipient; + $.tokenOverrides[tokenId].percentage = newPercentage; } /*////////////////////////////////////////////////////////////////////////// @@ -99,13 +115,14 @@ abstract contract EIP2981TLUpgradeable is IEIP2981, Initializable, ERC165Upgrade view returns (address receiver, uint256 royaltyAmount) { - address recipient = _defaultRecipient; - uint256 percentage = _defaultPercentage; - if (_tokenOverrides[tokenId].recipient != address(0)) { - recipient = _tokenOverrides[tokenId].recipient; - percentage = _tokenOverrides[tokenId].percentage; + EIP2981TLStorage storage $ = _getEIP2981TLStorage(); + address recipient = $.defaultRecipient; + uint256 percentage = $.defaultPercentage; + if ($.tokenOverrides[tokenId].recipient != address(0)) { + recipient = $.tokenOverrides[tokenId].recipient; + percentage = $.tokenOverrides[tokenId].percentage; } - return (recipient, salePrice / 10_000 * percentage); // divide first to avoid overflow + return (recipient, salePrice * percentage / BASIS); } /*////////////////////////////////////////////////////////////////////////// @@ -124,13 +141,7 @@ abstract contract EIP2981TLUpgradeable is IEIP2981, Initializable, ERC165Upgrade /// @notice Query the default royalty receiver and percentage. /// @return Tuple containing the default royalty recipient and percentage out of 10_000 function getDefaultRoyaltyRecipientAndPercentage() external view returns (address, uint256) { - return (_defaultRecipient, _defaultPercentage); + EIP2981TLStorage storage $ = _getEIP2981TLStorage(); + return ($.defaultRecipient, $.defaultPercentage); } - - /*////////////////////////////////////////////////////////////////////////// - Upgradeability Gap - //////////////////////////////////////////////////////////////////////////*/ - - /// @dev gap variable - see https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - uint256[50] private _gap; } diff --git a/test/access/OwnableAccessControl.t.sol b/test/access/OwnableAccessControl.t.sol index d927b40..54abe1a 100644 --- a/test/access/OwnableAccessControl.t.sol +++ b/test/access/OwnableAccessControl.t.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT - -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import {MockOwnableAccessControl} from "../utils/MockOwnableAccessControl.sol"; -import {OwnableAccessControl, NotRoleOrOwner, NotSpecifiedRole} from "tl-sol-tools/access/OwnableAccessControl.sol"; +import {MockOwnableAccessControl} from "test/utils/MockOwnableAccessControl.sol"; +import {OwnableAccessControl, Ownable} from "src/access/OwnableAccessControl.sol"; contract TestOwnableAccessControl is Test { MockOwnableAccessControl public mockContract; @@ -12,14 +11,14 @@ contract TestOwnableAccessControl is Test { event RoleChange(address indexed from, address indexed user, bool indexed approved, bytes32 role); event AllRolesRevoked(address indexed from); - function testInitialValues() public { + function test_InitialValues() public { mockContract = new MockOwnableAccessControl(); // expect default owner and number assertEq(mockContract.owner(), address(this)); assertEq(mockContract.number(), 0); } - function testOwnerRole() public { + function test_OwnerRole() public { mockContract = new MockOwnableAccessControl(); // expect owner can change the number mockContract.onlyOwnerFunction(1); @@ -58,14 +57,18 @@ contract TestOwnableAccessControl is Test { mockContract.revokeAllRoles(); // expect reverts on other access controlled functions - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(3); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.MINTER_ROLE()) + ); mockContract.onlyMinterFunction(3); } - function testAdminRole(address admin, address minter, uint256 newNumberOne, uint256 newNumberTwo) public { + function test_AdminRole(address admin, address minter, uint256 newNumberOne, uint256 newNumberTwo) public { mockContract = new MockOwnableAccessControl(); address[] memory admins = new address[](1); admins[0] = admin; @@ -90,18 +93,20 @@ contract TestOwnableAccessControl is Test { // expect reverts on other role locked functions if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, admin)); mockContract.onlyOwnerFunction(newNumberOne); } if (admin != minter) { - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.MINTER_ROLE()) + ); mockContract.onlyMinterFunction(newNumberOne); } // expect can't revoke all roles if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, admin)); mockContract.revokeAllRoles(); } @@ -114,28 +119,32 @@ contract TestOwnableAccessControl is Test { // check reverts happen for all functions vm.startPrank(admin, admin); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert(abi.encodeWithSelector(OwnableAccessControl.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); mockContract.setMinterRole(minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(newNumberOne); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert(abi.encodeWithSelector(OwnableAccessControl.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); mockContract.onlyAdminOrOwnerFunction(newNumberTwo); if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, admin)); mockContract.onlyOwnerFunction(newNumberOne); } if (admin != minter) { - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.MINTER_ROLE()) + ); mockContract.onlyMinterFunction(newNumberOne); } // expect can't revoke all roles if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, admin)); mockContract.revokeAllRoles(); } @@ -151,7 +160,7 @@ contract TestOwnableAccessControl is Test { vm.stopPrank(); } - function testMinterRole(address minter, uint256 newNumber) public { + function test_MinterRole(address minter, uint256 newNumber) public { mockContract = new MockOwnableAccessControl(); // grant minter role and expect proper event log address[] memory minters = new address[](1); @@ -168,23 +177,25 @@ contract TestOwnableAccessControl is Test { assertEq(mockContract.number(), newNumber); // expect reverts on all other functions - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert(abi.encodeWithSelector(OwnableAccessControl.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); mockContract.setMinterRole(minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(newNumber); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert(abi.encodeWithSelector(OwnableAccessControl.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); mockContract.onlyAdminOrOwnerFunction(newNumber); if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, minter)); mockContract.onlyOwnerFunction(newNumber); } // expect can't revoke all roles if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, minter)); mockContract.revokeAllRoles(); } @@ -199,26 +210,30 @@ contract TestOwnableAccessControl is Test { // expect reverts on all functions vm.startPrank(minter, minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.MINTER_ROLE()) + ); mockContract.onlyMinterFunction(newNumber); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert(abi.encodeWithSelector(OwnableAccessControl.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); mockContract.setMinterRole(minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControl.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(newNumber); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert(abi.encodeWithSelector(OwnableAccessControl.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); mockContract.onlyAdminOrOwnerFunction(newNumber); if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, minter)); mockContract.onlyOwnerFunction(newNumber); } // expect can't revoke all roles if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, minter)); mockContract.revokeAllRoles(); } diff --git a/test/payments/RoyaltyPayoutHelper.t.sol b/test/payments/RoyaltyPayoutHelper.t.sol index f67eeab..3952afd 100644 --- a/test/payments/RoyaltyPayoutHelper.t.sol +++ b/test/payments/RoyaltyPayoutHelper.t.sol @@ -1,17 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import {Receiver, RevertingReceiver} from "../utils/Receivers.sol"; -import {WETH9} from "../utils/WETH9.sol"; -import {MockERC20, MockERC20WithFee} from "../utils/MockERC20.sol"; -import {RoyaltyPayoutHelper, IRoyaltyEngineV1} from "tl-sol-tools/payments/RoyaltyPayoutHelper.sol"; -import {ChainalysisSanctionsOracle} from "tl-sol-tools/payments/SanctionsCompliance.sol"; import {Strings} from "openzeppelin/utils/Strings.sol"; +import {Receiver, RevertingReceiver} from "test/utils/Receivers.sol"; +import {WETH9} from "test/utils/WETH9.sol"; +import {MockERC20, MockERC20WithFee} from "test/utils/MockERC20.sol"; +import {RoyaltyPayoutHelper, IRoyaltyEngineV1} from "src/payments/RoyaltyPayoutHelper.sol"; +import {IChainalysisSanctionsOracle} from "src/payments/IChainalysisSanctionsOracle.sol"; contract ExternalRoyaltyPayoutHelper is RoyaltyPayoutHelper { - - constructor(address sanctionsAddress, address wethAddress, address royaltyEngineAddress) RoyaltyPayoutHelper(sanctionsAddress, wethAddress, royaltyEngineAddress) {} + constructor(address sanctionsAddress, address wethAddress, address royaltyEngineAddress) + RoyaltyPayoutHelper(sanctionsAddress, wethAddress, royaltyEngineAddress) + {} function setWethAddress(address wethAddress) external { _setWethAddress(wethAddress); @@ -21,7 +22,10 @@ contract ExternalRoyaltyPayoutHelper is RoyaltyPayoutHelper { _setRoyaltyEngineAddress(royaltyEngineAddress); } - function payoutRoyalties(address token, uint256 tokenId, address currency, uint256 salePrice) external returns(uint256) { + function payoutRoyalties(address token, uint256 tokenId, address currency, uint256 salePrice) + external + returns (uint256) + { return _payoutRoyalties(token, tokenId, currency, salePrice); } @@ -31,7 +35,6 @@ contract ExternalRoyaltyPayoutHelper is RoyaltyPayoutHelper { } contract TestRoyaltyPayoutHelper is Test { - using Strings for uint256; ExternalRoyaltyPayoutHelper rph; @@ -57,32 +60,28 @@ contract TestRoyaltyPayoutHelper is Test { rph = new ExternalRoyaltyPayoutHelper(address(0), weth, royaltyEngine); } - function testInit() public view { + function test_Init() public view { assert(rph.weth() == weth); assert(address(rph.royaltyEngine()) == royaltyEngine); } - function testUpdateWethAddress(address newWeth) public { + function test_UpdateWethAddress(address newWeth) public { rph.setWethAddress(newWeth); assert(rph.weth() == newWeth); } - function testUpdateRoyaltyEngine(address newRoyaltyEngine) public { + function test_UpdateRoyaltyEngine(address newRoyaltyEngine) public { rph.setRoyaltyEngineAddress(newRoyaltyEngine); assert(address(rph.royaltyEngine()) == newRoyaltyEngine); } - function testPayoutRoyaltiesEOA(uint256 salePrice) public { + function test_PayoutRoyaltiesEOA(uint256 salePrice) public { uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); assert(remainingSale == salePrice); } - function testPayoutRoyaltiesRevertingQuery(uint256 salePrice) public { - vm.mockCallRevert( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - "fail fail" - ); + function test_PayoutRoyaltiesRevertingQuery(uint256 salePrice) public { + vm.mockCallRevert(royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), "fail fail"); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); assert(remainingSale == salePrice); @@ -90,15 +89,12 @@ contract TestRoyaltyPayoutHelper is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesUnequalLengthArrays(uint256 salePrice) public { - + function test_PayoutRoyaltiesUnequalLengthArrays(uint256 salePrice) public { address[] memory recipients = new address[](1); recipients[0] = address(1); uint256[] memory amounts = new uint256[](0); vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); @@ -107,14 +103,11 @@ contract TestRoyaltyPayoutHelper is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesZeroLengthArrays(uint256 salePrice) public { - + function test_PayoutRoyaltiesZeroLengthArrays(uint256 salePrice) public { address[] memory recipients = new address[](0); uint256[] memory amounts = new uint256[](0); vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); @@ -123,7 +116,7 @@ contract TestRoyaltyPayoutHelper is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesMoreThanSalePrice() public { + function test_PayoutRoyaltiesMoreThanSalePrice() public { uint256 price = 1 ether; address[] memory recipients = new address[](2); recipients[0] = address(100); @@ -135,9 +128,7 @@ contract TestRoyaltyPayoutHelper is Test { vm.deal(address(rph), price); vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), price); @@ -147,7 +138,7 @@ contract TestRoyaltyPayoutHelper is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesETH(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { + function test_PayoutRoyaltiesETH(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { vm.assume(salePrice > 4); vm.assume(numRecipients > 0); vm.assume(salePrice >= numRecipients); @@ -162,18 +153,14 @@ contract TestRoyaltyPayoutHelper is Test { } vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); if (sanctionsCompliance) { address newOracle = makeAddr("Sanctions compliance is soooooo fun"); rph.updateSanctionsOracle(newOracle); vm.mockCall( - newOracle, - abi.encodeWithSelector(ChainalysisSanctionsOracle.isSanctioned.selector), - abi.encode(true) + newOracle, abi.encodeWithSelector(IChainalysisSanctionsOracle.isSanctioned.selector), abi.encode(true) ); } @@ -196,7 +183,7 @@ contract TestRoyaltyPayoutHelper is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesERC20(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { + function test_PayoutRoyaltiesERC20(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { vm.assume(salePrice > 4); vm.assume(numRecipients > 0); vm.assume(salePrice >= numRecipients); @@ -211,18 +198,14 @@ contract TestRoyaltyPayoutHelper is Test { } vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); if (sanctionsCompliance) { address newOracle = makeAddr("Sanctions compliance is soooooo fun"); rph.updateSanctionsOracle(newOracle); vm.mockCall( - newOracle, - abi.encodeWithSelector(ChainalysisSanctionsOracle.isSanctioned.selector), - abi.encode(true) + newOracle, abi.encodeWithSelector(IChainalysisSanctionsOracle.isSanctioned.selector), abi.encode(true) ); } @@ -246,7 +229,7 @@ contract TestRoyaltyPayoutHelper is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesERC20WithFee(uint8 numRecipients, uint128 salePrice) public { + function test_PayoutRoyaltiesERC20WithFee(uint8 numRecipients, uint128 salePrice) public { vm.assume(salePrice > 4); vm.assume(numRecipients > 0); vm.assume(salePrice >= numRecipients); @@ -261,21 +244,18 @@ contract TestRoyaltyPayoutHelper is Test { } vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); vm.prank(ben); - erc20fee.transfer(address(rph), uint256(salePrice)+1); + erc20fee.transfer(address(rph), uint256(salePrice) + 1); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(erc20fee), uint256(salePrice)); assert(remainingAmount == remainingSale); for (uint256 i = 0; i < numRecipients; i++) { - assert(erc20fee.balanceOf(recipients[i]) == price-1); + assert(erc20fee.balanceOf(recipients[i]) == price - 1); } vm.clearMockedCalls(); } - -} \ No newline at end of file +} diff --git a/test/payments/SanctionsCompliance.t.sol b/test/payments/SanctionsCompliance.t.sol index e2a54f3..af94f36 100644 --- a/test/payments/SanctionsCompliance.t.sol +++ b/test/payments/SanctionsCompliance.t.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {SanctionsCompliance, ChainalysisSanctionsOracle, SanctionedAddress} from "tl-sol-tools/payments/SanctionsCompliance.sol"; +import {SanctionsCompliance} from "src/payments/SanctionsCompliance.sol"; +import {IChainalysisSanctionsOracle} from "src/payments/IChainalysisSanctionsOracle.sol"; contract SanctionsComplianceTest is Test, SanctionsCompliance { - constructor() SanctionsCompliance(address(0)) {} function test_init(address sender) public view { @@ -21,7 +21,7 @@ contract SanctionsComplianceTest is Test, SanctionsCompliance { assert(address(oracle) == newOracle); } - function isSanctioned(address sender, bool shouldRevert) external view returns(bool) { + function isSanctioned(address sender, bool shouldRevert) external view returns (bool) { return _isSanctioned(sender, shouldRevert); } @@ -31,7 +31,11 @@ contract SanctionsComplianceTest is Test, SanctionsCompliance { vm.assume(newOracle != address(0)); _updateSanctionsOracle(newOracle); - vm.mockCall(newOracle, abi.encodeWithSelector(ChainalysisSanctionsOracle.isSanctioned.selector), abi.encode(isSanctioned_)); + vm.mockCall( + newOracle, + abi.encodeWithSelector(IChainalysisSanctionsOracle.isSanctioned.selector), + abi.encode(isSanctioned_) + ); if (isSanctioned_ && shouldRevert) { vm.expectRevert(SanctionedAddress.selector); @@ -42,4 +46,4 @@ contract SanctionsComplianceTest is Test, SanctionsCompliance { vm.clearMockedCalls(); } -} \ No newline at end of file +} diff --git a/test/payments/TransferHelper.t.sol b/test/payments/TransferHelper.t.sol index 312fe29..90fab8c 100644 --- a/test/payments/TransferHelper.t.sol +++ b/test/payments/TransferHelper.t.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import {Receiver, RevertingReceiver, GriefingReceiver} from "../utils/Receivers.sol"; -import {WETH9} from "../utils/WETH9.sol"; -import {MockERC20, MockERC20WithFee} from "../utils/MockERC20.sol"; -import {TransferHelper, ETHTransferFailed, InsufficentERC20Transfer} from "tl-sol-tools/payments/TransferHelper.sol"; import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; +import {Receiver, RevertingReceiver, GriefingReceiver} from "test/utils/Receivers.sol"; +import {WETH9} from "test/utils/WETH9.sol"; +import {MockERC20, MockERC20WithFee} from "test/utils/MockERC20.sol"; +import {TransferHelper} from "src/payments/TransferHelper.sol"; contract ExternalTransferHelper is TransferHelper { - function safeTransferETH(address recipient, uint256 amount, address weth) external { _safeTransferETH(recipient, amount, weth); } @@ -50,11 +49,8 @@ contract TestTransferHelper is Test { erc20fee = new MockERC20WithFee(ben); } - function testSafeTransferETH(address recipient, uint256 amount) public { - - vm.assume( - recipient.code.length == 0 && recipient > address(100) - ); + function test_SafeTransferETH(address recipient, uint256 amount) public { + vm.assume(recipient.code.length == 0 && recipient > address(100)); vm.assume(amount < 1_000_000_000_000_000_000 ether); @@ -83,10 +79,8 @@ contract TestTransferHelper is Test { assert(IERC20(weth).balanceOf(griefingReceiver) - b4 == amount); } - function testSafeTransferETHWithGasLimit(address recipient, uint256 amount) public { - vm.assume( - recipient.code.length == 0 && recipient > address(100) - ); + function test_SafeTransferETHWithGasLimit(address recipient, uint256 amount) public { + vm.assume(recipient.code.length == 0 && recipient > address(100)); vm.assume(amount < 1_000_000_000_000_000_000 ether); @@ -115,10 +109,9 @@ contract TestTransferHelper is Test { assert(IERC20(weth).balanceOf(griefingReceiver) - b4 == amount); } - function testSafeTransferERC20(address recipient, uint256 amount) public { - + function test_SafeTransferERC20(address recipient, uint256 amount) public { vm.assume(recipient != address(0) && recipient != address(th) && amount > 0); - + // fund contract vm.prank(ben); erc20.transfer(address(th), amount); @@ -135,12 +128,12 @@ contract TestTransferHelper is Test { // test amount with token tax ERC20 uint256 b2 = erc20fee.balanceOf(recipient); - th.safeTransferERC20(recipient, address(erc20fee), amount-1); - assert(erc20fee.balanceOf(recipient) - b2 == amount-2); + th.safeTransferERC20(recipient, address(erc20fee), amount - 1); + assert(erc20fee.balanceOf(recipient) - b2 == amount - 2); } } - function testSafeTransferFromERC20(address recipient, uint256 amount) public { + function test_SafeTransferFromERC20(address recipient, uint256 amount) public { vm.assume(recipient != address(0) && recipient != address(th) && amount > 0); // fund chris @@ -158,7 +151,11 @@ contract TestTransferHelper is Test { // test amount with regular ERC20 uint256 b1 = erc20.balanceOf(recipient); th.safeTransferFromERC20(chris, recipient, address(erc20), amount); - assert(erc20.balanceOf(recipient) - b1 == amount); + if (recipient != chris) { + assert(erc20.balanceOf(recipient) - b1 == amount); + } else { + assert(erc20.balanceOf(recipient) == b1); + } if (amount > 1) { // fund chris @@ -167,16 +164,15 @@ contract TestTransferHelper is Test { // test failure for allowance vm.expectRevert(); - th.safeTransferFromERC20(chris, recipient, address(erc20fee), amount-1); + th.safeTransferFromERC20(chris, recipient, address(erc20fee), amount - 1); // give allowance vm.prank(chris); - erc20fee.approve(address(th), amount-1); + erc20fee.approve(address(th), amount - 1); // test amount with token tax ERC20 - vm.expectRevert(InsufficentERC20Transfer.selector); - th.safeTransferFromERC20(chris, recipient, address(erc20fee), amount-1); + vm.expectRevert(TransferHelper.InsufficentERC20Transfer.selector); + th.safeTransferFromERC20(chris, recipient, address(erc20fee), amount - 1); } } - -} \ No newline at end of file +} diff --git a/test/royalties/EIP2981TL.t.sol b/test/royalties/EIP2981TL.t.sol index 4db9afd..edecc05 100644 --- a/test/royalties/EIP2981TL.t.sol +++ b/test/royalties/EIP2981TL.t.sol @@ -1,30 +1,34 @@ // SPDX-License-Identifier: MIT - -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import {MockEIP2981TL} from "../utils/MockEIP2981TL.sol"; -import {ZeroAddressError, MaxRoyaltyError} from "tl-sol-tools/royalties/EIP2981TL.sol"; +import {MockEIP2981TL} from "test/utils/MockEIP2981TL.sol"; +import {EIP2981TL} from "src/royalties/EIP2981TL.sol"; contract TestEIP2981TL is Test { MockEIP2981TL public mockContract; - ///////////////////// GENERAL TESTS ///////////////////// - function testDefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage) public { + function test_DefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) + public + { if (recipient == address(0)) { - vm.expectRevert(ZeroAddressError.selector); + vm.expectRevert(EIP2981TL.ZeroAddressError.selector); } else if (percentage > 10_000) { - vm.expectRevert(MaxRoyaltyError.selector); + vm.expectRevert(EIP2981TL.MaxRoyaltyError.selector); } mockContract = new MockEIP2981TL(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { - (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, 10000); + if (saleAmount > 3_000_000 ether) { + saleAmount = saleAmount % 3_000_000 ether; + } + uint256 expectedAmount = saleAmount * percentage / 10_000; + (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, saleAmount); assertEq(recipient, returnedRecipient); - assertEq(amount, percentage); + assertEq(amount, expectedAmount); } } - function testERC165Support(address recipient, uint16 percentage) public { + function test_ERC165Support(address recipient, uint16 percentage) public { if (recipient != address(0) && percentage <= 10_000) { mockContract = new MockEIP2981TL(recipient, uint256(percentage)); assertTrue(mockContract.supportsInterface(0x01ffc9a7)); // ERC165 interface id @@ -32,37 +36,47 @@ contract TestEIP2981TL is Test { } } - ///////////////////// DEFAULT OVERRIDE TEST ///////////////////// - function testOverrideDefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage) public { + function test_OverrideDefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) + public + { address defaultRecipient = makeAddr("account"); mockContract = new MockEIP2981TL(defaultRecipient, 10_000); if (recipient == address(0)) { - vm.expectRevert(ZeroAddressError.selector); + vm.expectRevert(EIP2981TL.ZeroAddressError.selector); } else if (percentage > 10_000) { - vm.expectRevert(MaxRoyaltyError.selector); + vm.expectRevert(EIP2981TL.MaxRoyaltyError.selector); } mockContract.setDefaultRoyalty(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { - (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, 10000); + if (saleAmount > 3_000_000 ether) { + saleAmount = saleAmount % 3_000_000 ether; + } + uint256 expectedAmount = saleAmount * percentage / 10_000; + (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, saleAmount); assertEq(recipient, returnedRecipient); - assertEq(amount, percentage); + assertEq(amount, expectedAmount); } } - ///////////////////// TOKEN OVERRIDE TEST ///////////////////// - function testOverrideTokenRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage) public { + function test_OverrideTokenRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) + public + { address defaultRecipient = makeAddr("account"); mockContract = new MockEIP2981TL(defaultRecipient, 10_000); if (recipient == address(0)) { - vm.expectRevert(ZeroAddressError.selector); + vm.expectRevert(EIP2981TL.ZeroAddressError.selector); } else if (percentage > 10_000) { - vm.expectRevert(MaxRoyaltyError.selector); + vm.expectRevert(EIP2981TL.MaxRoyaltyError.selector); } mockContract.setTokenRoyalty(tokenId, recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { - (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, 10000); + if (saleAmount > 3_000_000 ether) { + saleAmount = saleAmount % 3_000_000 ether; + } + uint256 expectedAmount = saleAmount * percentage / 10_000; + (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, saleAmount); assertEq(recipient, returnedRecipient); - assertEq(amount, percentage); + assertEq(amount, expectedAmount); } } } diff --git a/test/upgradeable/access/OwnableAccessControlUpgradeable.t.sol b/test/upgradeable/access/OwnableAccessControlUpgradeable.t.sol index 5154dc7..291fa3b 100644 --- a/test/upgradeable/access/OwnableAccessControlUpgradeable.t.sol +++ b/test/upgradeable/access/OwnableAccessControlUpgradeable.t.sol @@ -1,22 +1,22 @@ // SPDX-License-Identifier: MIT - -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import {MockOwnableAccessControlUpgradeable} from "../../utils/MockOwnableAccessControlUpgradeable.sol"; +import {MockOwnableAccessControlUpgradeable} from "test/utils/MockOwnableAccessControlUpgradeable.sol"; import { OwnableAccessControlUpgradeable, - NotRoleOrOwner, - NotSpecifiedRole -} from "tl-sol-tools/upgradeable/access/OwnableAccessControlUpgradeable.sol"; + OwnableUpgradeable +} from "src/upgradeable/access/OwnableAccessControlUpgradeable.sol"; -contract TestOwnableAccessControl is Test { +contract OwnableAccessControlTest is Test { MockOwnableAccessControlUpgradeable public mockContract; event RoleChange(address indexed from, address indexed user, bool indexed approved, bytes32 role); event AllRolesRevoked(address indexed from); - function testInitialization(address owner) public { + function test_Initialization(address owner) public { + vm.assume(owner != address(0)); + mockContract = new MockOwnableAccessControlUpgradeable(); mockContract.initialize(address(this)); mockContract = new MockOwnableAccessControlUpgradeable(); @@ -24,7 +24,7 @@ contract TestOwnableAccessControl is Test { assertEq(mockContract.owner(), owner); } - function testInitialValues() public { + function test_InitialValues() public { mockContract = new MockOwnableAccessControlUpgradeable(); mockContract.initialize(address(this)); // expect default owner and number @@ -32,7 +32,7 @@ contract TestOwnableAccessControl is Test { assertEq(mockContract.number(), 0); } - function testOwnerRole() public { + function test_OwnerRole() public { mockContract = new MockOwnableAccessControlUpgradeable(); mockContract.initialize(address(this)); // expect owner can change the number @@ -72,14 +72,20 @@ contract TestOwnableAccessControl is Test { mockContract.revokeAllRoles(); // expect reverts on other access controlled functions - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(3); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector( + OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.MINTER_ROLE() + ) + ); mockContract.onlyMinterFunction(3); } - function testAdminRole(address admin, address minter, uint256 newNumberOne, uint256 newNumberTwo) public { + function test_AdminRole(address admin, address minter, uint256 newNumberOne, uint256 newNumberTwo) public { mockContract = new MockOwnableAccessControlUpgradeable(); mockContract.initialize(address(this)); address[] memory admins = new address[](1); @@ -105,18 +111,22 @@ contract TestOwnableAccessControl is Test { // expect reverts on other role locked functions if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, admin)); mockContract.onlyOwnerFunction(newNumberOne); } if (admin != minter) { - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector( + OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.MINTER_ROLE() + ) + ); mockContract.onlyMinterFunction(newNumberOne); } // expect can't revoke all roles if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, admin)); mockContract.revokeAllRoles(); } @@ -129,28 +139,38 @@ contract TestOwnableAccessControl is Test { // check reverts happen for all functions vm.startPrank(admin, admin); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE()) + ); mockContract.setMinterRole(minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(newNumberOne); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminOrOwnerFunction(newNumberTwo); if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, admin)); mockContract.onlyOwnerFunction(newNumberOne); } if (admin != minter) { - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector( + OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.MINTER_ROLE() + ) + ); mockContract.onlyMinterFunction(newNumberOne); } // expect can't revoke all roles if (admin != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, admin)); mockContract.revokeAllRoles(); } @@ -166,7 +186,7 @@ contract TestOwnableAccessControl is Test { vm.stopPrank(); } - function testMinterRole(address minter, uint256 newNumber) public { + function test_MinterRole(address minter, uint256 newNumber) public { mockContract = new MockOwnableAccessControlUpgradeable(); mockContract.initialize(address(this)); // grant minter role and expect proper event log @@ -184,23 +204,29 @@ contract TestOwnableAccessControl is Test { assertEq(mockContract.number(), newNumber); // expect reverts on all other functions - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE()) + ); mockContract.setMinterRole(minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(newNumber); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminOrOwnerFunction(newNumber); if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, minter)); mockContract.onlyOwnerFunction(newNumber); } // expect can't revoke all roles if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, minter)); mockContract.revokeAllRoles(); } @@ -215,26 +241,36 @@ contract TestOwnableAccessControl is Test { // expect reverts on all functions vm.startPrank(minter, minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.MINTER_ROLE())); + vm.expectRevert( + abi.encodeWithSelector( + OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.MINTER_ROLE() + ) + ); mockContract.onlyMinterFunction(newNumber); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE()) + ); mockContract.setMinterRole(minter); - vm.expectRevert(abi.encodeWithSelector(NotSpecifiedRole.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotSpecifiedRole.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminFunction(newNumber); - vm.expectRevert(abi.encodeWithSelector(NotRoleOrOwner.selector, mockContract.ADMIN_ROLE())); + vm.expectRevert( + abi.encodeWithSelector(OwnableAccessControlUpgradeable.NotRoleOrOwner.selector, mockContract.ADMIN_ROLE()) + ); mockContract.onlyAdminOrOwnerFunction(newNumber); if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, minter)); mockContract.onlyOwnerFunction(newNumber); } // expect can't revoke all roles if (minter != address(this)) { - vm.expectRevert(bytes("Ownable: caller is not the owner")); + vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, minter)); mockContract.revokeAllRoles(); } diff --git a/test/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.t.sol b/test/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.t.sol index 0db819d..ab2d782 100644 --- a/test/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.t.sol +++ b/test/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.t.sol @@ -1,18 +1,22 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import {Receiver, RevertingReceiver} from "../../utils/Receivers.sol"; -import {WETH9} from "../../utils/WETH9.sol"; -import {MockERC20, MockERC20WithFee} from "../../utils/MockERC20.sol"; -import {RoyaltyPayoutHelperUpgradeable, IRoyaltyEngineV1} from "tl-sol-tools/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.sol"; -import {ChainalysisSanctionsOracle} from "tl-sol-tools/payments/SanctionsCompliance.sol"; import {Strings} from "openzeppelin/utils/Strings.sol"; -import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol"; - -contract ExternalRoyaltyPayoutHelper is Initializable, RoyaltyPayoutHelperUpgradeable { - - function initialize(address sanctionsOracle, address wethAddress, address royaltyEngineAddress) external initializer { +import {Receiver, RevertingReceiver} from "test/utils/Receivers.sol"; +import {WETH9} from "test/utils/WETH9.sol"; +import {MockERC20, MockERC20WithFee} from "test/utils/MockERC20.sol"; +import { + RoyaltyPayoutHelperUpgradeable, + IRoyaltyEngineV1 +} from "src/upgradeable/payments/RoyaltyPayoutHelperUpgradeable.sol"; +import {IChainalysisSanctionsOracle} from "src/payments/IChainalysisSanctionsOracle.sol"; + +contract ExternalRoyaltyPayoutHelper is RoyaltyPayoutHelperUpgradeable { + function initialize(address sanctionsOracle, address wethAddress, address royaltyEngineAddress) + external + initializer + { __RoyaltyPayoutHelper_init(sanctionsOracle, wethAddress, royaltyEngineAddress); } @@ -24,7 +28,10 @@ contract ExternalRoyaltyPayoutHelper is Initializable, RoyaltyPayoutHelperUpgrad _setRoyaltyEngineAddress(royaltyEngineAddress); } - function payoutRoyalties(address token, uint256 tokenId, address currency, uint256 salePrice) external returns(uint256) { + function payoutRoyalties(address token, uint256 tokenId, address currency, uint256 salePrice) + external + returns (uint256) + { return _payoutRoyalties(token, tokenId, currency, salePrice); } @@ -34,7 +41,6 @@ contract ExternalRoyaltyPayoutHelper is Initializable, RoyaltyPayoutHelperUpgrad } contract TestRoyaltyPayoutHelperUpgradeable is Test { - using Strings for uint256; ExternalRoyaltyPayoutHelper rph; @@ -61,37 +67,33 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { rph.initialize(address(0), weth, royaltyEngine); } - function testInit() public view { + function test_Init() public view { assert(rph.weth() == weth); assert(address(rph.royaltyEngine()) == royaltyEngine); } - - function testInitAgain() public { + + function test_InitAgain() public { vm.expectRevert(); rph.initialize(address(0), weth, royaltyEngine); } - function testUpdateWethAddress(address newWeth) public { + function test_UpdateWethAddress(address newWeth) public { rph.setWethAddress(newWeth); assert(rph.weth() == newWeth); } - function testUpdateRoyaltyEngine(address newRoyaltyEngine) public { + function test_UpdateRoyaltyEngine(address newRoyaltyEngine) public { rph.setRoyaltyEngineAddress(newRoyaltyEngine); assert(address(rph.royaltyEngine()) == newRoyaltyEngine); } - function testPayoutRoyaltiesEOA(uint256 salePrice) public { + function test_PayoutRoyaltiesEOA(uint256 salePrice) public { uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); assert(remainingSale == salePrice); } - function testPayoutRoyaltiesRevertingQuery(uint256 salePrice) public { - vm.mockCallRevert( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - "fail fail" - ); + function test_PayoutRoyaltiesRevertingQuery(uint256 salePrice) public { + vm.mockCallRevert(royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), "fail fail"); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); assert(remainingSale == salePrice); @@ -99,15 +101,12 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesUnequalLengthArrays(uint256 salePrice) public { - + function test_PayoutRoyaltiesUnequalLengthArrays(uint256 salePrice) public { address[] memory recipients = new address[](1); recipients[0] = address(1); uint256[] memory amounts = new uint256[](0); vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); @@ -116,14 +115,11 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesZeroLengthArrays(uint256 salePrice) public { - + function test_PayoutRoyaltiesZeroLengthArrays(uint256 salePrice) public { address[] memory recipients = new address[](0); uint256[] memory amounts = new uint256[](0); vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), salePrice); @@ -132,7 +128,7 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesMoreThanSalePrice() public { + function test_PayoutRoyaltiesMoreThanSalePrice() public { uint256 price = 1 ether; address[] memory recipients = new address[](2); recipients[0] = address(100); @@ -144,9 +140,7 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { vm.deal(address(rph), price); vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(0), price); @@ -156,7 +150,7 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesETH(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { + function test_PayoutRoyaltiesETH(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { vm.assume(salePrice > 4); vm.assume(numRecipients > 0); vm.assume(salePrice >= numRecipients); @@ -171,18 +165,14 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { } vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); if (sanctionsCompliance) { address newOracle = makeAddr("Sanctions compliance is soooooo fun"); rph.updateSanctionsOracle(newOracle); vm.mockCall( - newOracle, - abi.encodeWithSelector(ChainalysisSanctionsOracle.isSanctioned.selector), - abi.encode(true) + newOracle, abi.encodeWithSelector(IChainalysisSanctionsOracle.isSanctioned.selector), abi.encode(true) ); } @@ -205,7 +195,7 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesERC20(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { + function test_PayoutRoyaltiesERC20(uint8 numRecipients, uint256 salePrice, bool sanctionsCompliance) public { vm.assume(salePrice > 4); vm.assume(numRecipients > 0); vm.assume(salePrice >= numRecipients); @@ -220,18 +210,14 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { } vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); if (sanctionsCompliance) { address newOracle = makeAddr("Sanctions compliance is soooooo fun"); rph.updateSanctionsOracle(newOracle); vm.mockCall( - newOracle, - abi.encodeWithSelector(ChainalysisSanctionsOracle.isSanctioned.selector), - abi.encode(true) + newOracle, abi.encodeWithSelector(IChainalysisSanctionsOracle.isSanctioned.selector), abi.encode(true) ); } @@ -255,7 +241,7 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { vm.clearMockedCalls(); } - function testPayoutRoyaltiesERC20WithFee(uint8 numRecipients, uint128 salePrice) public { + function test_PayoutRoyaltiesERC20WithFee(uint8 numRecipients, uint128 salePrice) public { vm.assume(salePrice > 4); vm.assume(numRecipients > 0); vm.assume(salePrice >= numRecipients); @@ -270,21 +256,18 @@ contract TestRoyaltyPayoutHelperUpgradeable is Test { } vm.mockCall( - royaltyEngine, - abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), - abi.encode(recipients, amounts) + royaltyEngine, abi.encodeWithSelector(IRoyaltyEngineV1.getRoyalty.selector), abi.encode(recipients, amounts) ); vm.prank(ben); - erc20fee.transfer(address(rph), uint256(salePrice)+1); + erc20fee.transfer(address(rph), uint256(salePrice) + 1); uint256 remainingSale = rph.payoutRoyalties(address(1), 1, address(erc20fee), uint256(salePrice)); assert(remainingAmount == remainingSale); for (uint256 i = 0; i < numRecipients; i++) { - assert(erc20fee.balanceOf(recipients[i]) == price-1); + assert(erc20fee.balanceOf(recipients[i]) == price - 1); } vm.clearMockedCalls(); } - -} \ No newline at end of file +} diff --git a/test/upgradeable/payments/SanctionsComplianceUpgradeable.t.sol b/test/upgradeable/payments/SanctionsComplianceUpgradeable.t.sol index 8eb15ec..99fb97a 100644 --- a/test/upgradeable/payments/SanctionsComplianceUpgradeable.t.sol +++ b/test/upgradeable/payments/SanctionsComplianceUpgradeable.t.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {SanctionsComplianceUpgradeable, ChainalysisSanctionsOracle, SanctionedAddress} from "tl-sol-tools/upgradeable/payments/SanctionsComplianceUpgradeable.sol"; +import {SanctionsComplianceUpgradeable} from "src/upgradeable/payments/SanctionsComplianceUpgradeable.sol"; +import {IChainalysisSanctionsOracle} from "src/payments/IChainalysisSanctionsOracle.sol"; contract SanctionsComplianceUpgradeableTest is Test, SanctionsComplianceUpgradeable { - constructor() { initialize(); } @@ -15,7 +15,7 @@ contract SanctionsComplianceUpgradeableTest is Test, SanctionsComplianceUpgradea } function test_init(address sender) public view { - assert(address(oracle) == address(0)); + assert(address(oracle()) == address(0)); assert(_isSanctioned(sender, false) == false); } @@ -24,10 +24,10 @@ contract SanctionsComplianceUpgradeableTest is Test, SanctionsComplianceUpgradea emit SanctionsOracleUpdated(address(0), newOracle); _updateSanctionsOracle(newOracle); - assert(address(oracle) == newOracle); + assert(address(oracle()) == newOracle); } - function isSanctioned(address sender, bool shouldRevert) external view returns(bool) { + function isSanctioned(address sender, bool shouldRevert) external view returns (bool) { return _isSanctioned(sender, shouldRevert); } @@ -37,7 +37,11 @@ contract SanctionsComplianceUpgradeableTest is Test, SanctionsComplianceUpgradea vm.assume(newOracle != address(0)); _updateSanctionsOracle(newOracle); - vm.mockCall(newOracle, abi.encodeWithSelector(ChainalysisSanctionsOracle.isSanctioned.selector), abi.encode(isSanctioned_)); + vm.mockCall( + newOracle, + abi.encodeWithSelector(IChainalysisSanctionsOracle.isSanctioned.selector), + abi.encode(isSanctioned_) + ); if (isSanctioned_ && shouldRevert) { vm.expectRevert(SanctionedAddress.selector); @@ -48,4 +52,4 @@ contract SanctionsComplianceUpgradeableTest is Test, SanctionsComplianceUpgradea vm.clearMockedCalls(); } -} \ No newline at end of file +} diff --git a/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol b/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol index fdb507b..41fae17 100644 --- a/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol +++ b/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol @@ -1,31 +1,35 @@ // SPDX-License-Identifier: MIT - -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import {MockEIP2981TLUpgradeable} from "../../utils/MockEIP2981TLUpgradeable.sol"; -import {ZeroAddressError, MaxRoyaltyError} from "tl-sol-tools/upgradeable/royalties/EIP2981TLUpgradeable.sol"; +import {MockEIP2981TLUpgradeable} from "test/utils/MockEIP2981TLUpgradeable.sol"; +import {EIP2981TLUpgradeable} from "src/upgradeable/royalties/EIP2981TLUpgradeable.sol"; contract TestEIP2981TLUpgradeable is Test { MockEIP2981TLUpgradeable public mockContract; - ///////////////////// GENERAL TESTS ///////////////////// - function testDefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage) public { + function test_DefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) + public + { mockContract = new MockEIP2981TLUpgradeable(); if (recipient == address(0)) { - vm.expectRevert(ZeroAddressError.selector); + vm.expectRevert(EIP2981TLUpgradeable.ZeroAddressError.selector); } else if (percentage > 10_000) { - vm.expectRevert(MaxRoyaltyError.selector); + vm.expectRevert(EIP2981TLUpgradeable.MaxRoyaltyError.selector); } mockContract.initialize(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { - (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, 10000); + if (saleAmount > 3_000_000 ether) { + saleAmount = saleAmount % 3_000_000 ether; + } + uint256 expectedAmount = saleAmount * percentage / 10_000; + (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, saleAmount); assertEq(recipient, returnedRecipient); - assertEq(amount, percentage); + assertEq(amount, expectedAmount); } } - function testERC165Support(address recipient, uint16 percentage) public { + function test_ERC165Support(address recipient, uint16 percentage) public { if (recipient != address(0) && percentage <= 10_000) { mockContract = new MockEIP2981TLUpgradeable(); mockContract.initialize(recipient, uint256(percentage)); @@ -34,39 +38,49 @@ contract TestEIP2981TLUpgradeable is Test { } } - ///////////////////// DEFAULT OVERRIDE TEST ///////////////////// - function testOverrideDefaultRoyalty(uint256 tokenId, address recipient, uint16 percentage) public { + function test_OverrideDefaultRoyalty(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) + public + { address defaultRecipient = makeAddr("account"); mockContract = new MockEIP2981TLUpgradeable(); mockContract.initialize(defaultRecipient, 10_000); if (recipient == address(0)) { - vm.expectRevert(ZeroAddressError.selector); + vm.expectRevert(EIP2981TLUpgradeable.ZeroAddressError.selector); } else if (percentage > 10_000) { - vm.expectRevert(MaxRoyaltyError.selector); + vm.expectRevert(EIP2981TLUpgradeable.MaxRoyaltyError.selector); } mockContract.setDefaultRoyalty(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { - (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, 10000); + if (saleAmount > 3_000_000 ether) { + saleAmount = saleAmount % 3_000_000 ether; + } + uint256 expectedAmount = saleAmount * percentage / 10_000; + (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, saleAmount); assertEq(recipient, returnedRecipient); - assertEq(amount, percentage); + assertEq(amount, expectedAmount); } } - ///////////////////// TOKEN OVERRIDE TEST ///////////////////// - function testOverrideTokenRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage) public { + function test_OverrideTokenRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) + public + { address defaultRecipient = makeAddr("account"); mockContract = new MockEIP2981TLUpgradeable(); mockContract.initialize(defaultRecipient, 10_000); if (recipient == address(0)) { - vm.expectRevert(ZeroAddressError.selector); + vm.expectRevert(EIP2981TLUpgradeable.ZeroAddressError.selector); } else if (percentage > 10_000) { - vm.expectRevert(MaxRoyaltyError.selector); + vm.expectRevert(EIP2981TLUpgradeable.MaxRoyaltyError.selector); } mockContract.setTokenRoyalty(tokenId, recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { - (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, 10000); + if (saleAmount > 3_000_000 ether) { + saleAmount = saleAmount % 3_000_000 ether; + } + uint256 expectedAmount = saleAmount * percentage / 10_000; + (address returnedRecipient, uint256 amount) = mockContract.royaltyInfo(tokenId, saleAmount); assertEq(recipient, returnedRecipient); - assertEq(amount, percentage); + assertEq(amount, expectedAmount); } } } diff --git a/test/utils/MockEIP2981TL.sol b/test/utils/MockEIP2981TL.sol index 2ab8ae1..d0a4904 100644 --- a/test/utils/MockEIP2981TL.sol +++ b/test/utils/MockEIP2981TL.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; -/// @dev this contract does not have proper access control but is only for testing - -pragma solidity ^0.8.17; +import {EIP2981TL} from "src/royalties/EIP2981TL.sol"; -import {EIP2981TL} from "tl-sol-tools/royalties/EIP2981TL.sol"; +/// @dev this contract does not have proper access control but is only for testing contract MockEIP2981TL is EIP2981TL { constructor(address recipient, uint256 percentage) EIP2981TL(recipient, percentage) {} diff --git a/test/utils/MockEIP2981TLUpgradeable.sol b/test/utils/MockEIP2981TLUpgradeable.sol index d056f61..71fa7df 100644 --- a/test/utils/MockEIP2981TLUpgradeable.sol +++ b/test/utils/MockEIP2981TLUpgradeable.sol @@ -1,13 +1,11 @@ // SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; -/// @dev this contract does not have proper access control but is only for testing - -pragma solidity ^0.8.17; +import {EIP2981TLUpgradeable} from "src/upgradeable/royalties/EIP2981TLUpgradeable.sol"; -import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol"; -import {EIP2981TLUpgradeable} from "tl-sol-tools/upgradeable/royalties/EIP2981TLUpgradeable.sol"; +/// @dev this contract does not have proper access control but is only for testing -contract MockEIP2981TLUpgradeable is Initializable, EIP2981TLUpgradeable { +contract MockEIP2981TLUpgradeable is EIP2981TLUpgradeable { function initialize(address recipient, uint256 percentage) external initializer { __EIP2981TL_init(recipient, percentage); } diff --git a/test/utils/MockERC20.sol b/test/utils/MockERC20.sol index ef02184..e8ba83d 100644 --- a/test/utils/MockERC20.sol +++ b/test/utils/MockERC20.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; import {ERC20} from "openzeppelin/token/ERC20/ERC20.sol"; @@ -17,7 +17,7 @@ contract MockERC20WithFee is ERC20 { function transfer(address to, uint256 value) public override returns (bool) { address owner = _msgSender(); _burn(owner, 1); - _transfer(owner, to, value-1); + _transfer(owner, to, value - 1); return true; } @@ -25,7 +25,7 @@ contract MockERC20WithFee is ERC20 { address spender = _msgSender(); _spendAllowance(from, spender, value); _burn(from, 1); - _transfer(from, to, value-1); + _transfer(from, to, value - 1); return true; } -} \ No newline at end of file +} diff --git a/test/utils/MockOwnableAccessControl.sol b/test/utils/MockOwnableAccessControl.sol index db8ee02..76804bf 100644 --- a/test/utils/MockOwnableAccessControl.sol +++ b/test/utils/MockOwnableAccessControl.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; -pragma solidity ^0.8.17; - -import {OwnableAccessControl} from "tl-sol-tools/access/OwnableAccessControl.sol"; +import {OwnableAccessControl} from "src/access/OwnableAccessControl.sol"; contract MockOwnableAccessControl is OwnableAccessControl { uint256 public number; diff --git a/test/utils/MockOwnableAccessControlUpgradeable.sol b/test/utils/MockOwnableAccessControlUpgradeable.sol index cf50760..918cb1f 100644 --- a/test/utils/MockOwnableAccessControlUpgradeable.sol +++ b/test/utils/MockOwnableAccessControlUpgradeable.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; -pragma solidity ^0.8.17; +import {OwnableAccessControlUpgradeable} from "src/upgradeable/access/OwnableAccessControlUpgradeable.sol"; -import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol"; -import {OwnableAccessControlUpgradeable} from "tl-sol-tools/upgradeable/access/OwnableAccessControlUpgradeable.sol"; - -contract MockOwnableAccessControlUpgradeable is Initializable, OwnableAccessControlUpgradeable { +contract MockOwnableAccessControlUpgradeable is OwnableAccessControlUpgradeable { uint256 public number; bytes32 public ADMIN_ROLE = keccak256("ADMIN"); bytes32 public MINTER_ROLE = keccak256("MINTER"); diff --git a/test/utils/Receivers.sol b/test/utils/Receivers.sol index b42394d..e44f954 100644 --- a/test/utils/Receivers.sol +++ b/test/utils/Receivers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; contract Receiver { event EthReceived(uint256 indexed amount); @@ -17,9 +17,10 @@ contract RevertingReceiver { contract GriefingReceiver { event Grief(); + receive() external payable { for (uint256 i = 0; i < type(uint256).max; i++) { emit Grief(); } } -} \ No newline at end of file +} diff --git a/test/utils/WETH9.sol b/test/utils/WETH9.sol index 0a993b9..f3f554e 100644 --- a/test/utils/WETH9.sol +++ b/test/utils/WETH9.sol @@ -1,27 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; +pragma solidity ^0.8.20; contract WETH9 { string public name = "Wrapped Ether"; string public symbol = "WETH"; uint8 public decimals = 18; - event Approval( - address indexed src, - address indexed guy, - uint256 wad - ); - event Transfer( - address indexed src, - address indexed dst, - uint256 wad - ); + event Approval(address indexed src, address indexed guy, uint256 wad); + event Transfer(address indexed src, address indexed dst, uint256 wad); event Deposit(address indexed dst, uint256 wad); event Withdrawal(address indexed src, uint256 wad); mapping(address => uint256) public balanceOf; - mapping(address => - mapping(address => uint256)) public allowance; + mapping(address => mapping(address => uint256)) public allowance; receive() external payable { deposit(); @@ -43,33 +34,20 @@ contract WETH9 { return address(this).balance; } - function approve( - address guy, - uint256 wad - ) public returns (bool) { + function approve(address guy, uint256 wad) public returns (bool) { allowance[msg.sender][guy] = wad; emit Approval(msg.sender, guy, wad); return true; } - function transfer( - address dst, - uint256 wad - ) public returns (bool) { + function transfer(address dst, uint256 wad) public returns (bool) { return transferFrom(msg.sender, dst, wad); } - function transferFrom( - address src, - address dst, - uint256 wad - ) public returns (bool) { + function transferFrom(address src, address dst, uint256 wad) public returns (bool) { require(balanceOf[src] >= wad); - if ( - src != msg.sender && - allowance[src][msg.sender] != type(uint256).max - ) { + if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { require(allowance[src][msg.sender] >= wad); allowance[src][msg.sender] -= wad; } @@ -81,4 +59,4 @@ contract WETH9 { return true; } -} \ No newline at end of file +}