diff --git a/src/access/OwnableAccessControl.sol b/src/access/OwnableAccessControl.sol index fc715f3..06179b7 100644 --- a/src/access/OwnableAccessControl.sol +++ b/src/access/OwnableAccessControl.sol @@ -32,7 +32,7 @@ abstract contract OwnableAccessControl is Ownable { /// @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); /*////////////////////////////////////////////////////////////////////////// diff --git a/src/payments/TransferHelper.sol b/src/payments/TransferHelper.sol index 7d3a520..9a48f16 100644 --- a/src/payments/TransferHelper.sol +++ b/src/payments/TransferHelper.sol @@ -43,11 +43,13 @@ abstract contract TransferHelper { /// @notice Function to force transfer ETH, with a gas limit /// @dev On failure to send the ETH, the ETH is converted to WETH and sent /// @dev Care should be taken to always pass the proper WETH address that adheres to IWETH + /// @dev If the `amount` is zero, the function returns in order to save gas /// @param recipient The recipient of the ETH /// @param amount The amount of ETH to send /// @param weth The WETH token address /// @param gasLimit The gas to forward function _safeTransferETH(address recipient, uint256 amount, address weth, uint256 gasLimit) internal { + if (amount == 0) return; (bool success,) = recipient.call{value: amount, gas: gasLimit}(""); if (!success) { IWETH token = IWETH(weth); @@ -64,10 +66,12 @@ abstract contract TransferHelper { /// @dev Does not check if the sender has enough balance as that is handled by the token contract /// @dev Does not check for token tax as that could lock up funds in the contract /// @dev Reverts on failure to transfer + /// @dev If the `amount` is zero, the function returns in order to save gas /// @param recipient The recipient of the ERC-20 token /// @param currency The address of the ERC-20 token /// @param amount The amount of ERC-20 to send function _safeTransferERC20(address recipient, address currency, uint256 amount) internal { + if (amount == 0) return; IERC20(currency).safeTransfer(recipient, amount); } @@ -75,11 +79,15 @@ abstract contract TransferHelper { /// @dev Does not check if the sender has enough balance or allowance for this contract as that is handled by the token contract /// @dev Reverts on failure to transfer /// @dev Reverts if there is a token tax taken out + /// @dev Returns and doesn't do anything if the sender and recipient are the same address + /// @dev If the `amount` is zero, the function returns in order to save gas /// @param sender The sender of the tokens /// @param recipient The recipient of the ERC-20 token /// @param currency The address of the ERC-20 token /// @param amount The amount of ERC-20 to send function _safeTransferFromERC20(address sender, address recipient, address currency, uint256 amount) internal { + if (amount == 0) return; + if (sender == recipient) return; IERC20 token = IERC20(currency); uint256 intialBalance = token.balanceOf(recipient); token.safeTransferFrom(sender, recipient, amount); diff --git a/src/royalties/EIP2981TL.sol b/src/royalties/EIP2981TL.sol index bb056f8..6272197 100644 --- a/src/royalties/EIP2981TL.sol +++ b/src/royalties/EIP2981TL.sol @@ -9,7 +9,7 @@ import {IEIP2981} from "src/royalties/IEIP2981.sol"; /// while allowing for specific token overrides /// @dev Follows EIP-2981 (https://eips.ethereum.org/EIPS/eip-2981) /// @author transientlabs.xyz -/// @custom:version 3.0.0 +/// @custom:version 3.1.0 abstract contract EIP2981TL is IEIP2981, ERC165 { /*////////////////////////////////////////////////////////////////////////// Types @@ -29,6 +29,16 @@ abstract contract EIP2981TL is IEIP2981, ERC165 { uint256 private _defaultPercentage; mapping(uint256 => RoyaltySpec) private _tokenOverrides; + /*////////////////////////////////////////////////////////////////////////// + Events + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Event to emit when the default roylaty is updated + event DefaultRoyaltyUpdate(address indexed sender, address newRecipient, uint256 newPercentage); + + /// @dev Event to emit when a token royalty is overriden + event TokenRoyaltyOverride(address indexed sender, uint256 indexed tokenId, address newRecipient, uint256 newPercentage); + /*////////////////////////////////////////////////////////////////////////// Errors //////////////////////////////////////////////////////////////////////////*/ @@ -61,6 +71,7 @@ abstract contract EIP2981TL is IEIP2981, ERC165 { if (newPercentage > 10_000) revert MaxRoyaltyError(); _defaultRecipient = newRecipient; _defaultPercentage = newPercentage; + emit DefaultRoyaltyUpdate(msg.sender, newRecipient, newPercentage); } /// @notice Function to override royalty spec on a specific token @@ -72,6 +83,7 @@ abstract contract EIP2981TL is IEIP2981, ERC165 { if (newPercentage > 10_000) revert MaxRoyaltyError(); _tokenOverrides[tokenId].recipient = newRecipient; _tokenOverrides[tokenId].percentage = newPercentage; + emit TokenRoyaltyOverride(msg.sender, tokenId, newRecipient, newPercentage); } /*////////////////////////////////////////////////////////////////////////// diff --git a/src/upgradeable/royalties/EIP2981TLUpgradeable.sol b/src/upgradeable/royalties/EIP2981TLUpgradeable.sol index b8d4a6a..47df0a6 100644 --- a/src/upgradeable/royalties/EIP2981TLUpgradeable.sol +++ b/src/upgradeable/royalties/EIP2981TLUpgradeable.sol @@ -9,7 +9,7 @@ import {IEIP2981} from "src/royalties/IEIP2981.sol"; /// while allowing for specific token overrides /// @dev Follows EIP-2981 (https://eips.ethereum.org/EIPS/eip-2981) /// @author transientlabs.xyz -/// @custom:version 3.0.0 +/// @custom:version 3.1.0 abstract contract EIP2981TLUpgradeable is IEIP2981, ERC165Upgradeable { /*////////////////////////////////////////////////////////////////////////// Types @@ -47,6 +47,16 @@ abstract contract EIP2981TLUpgradeable is IEIP2981, ERC165Upgradeable { uint256 public constant BASIS = 10_000; + /*////////////////////////////////////////////////////////////////////////// + Events + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Event to emit when the default roylaty is updated + event DefaultRoyaltyUpdate(address indexed sender, address newRecipient, uint256 newPercentage); + + /// @dev Event to emit when a token royalty is overriden + event TokenRoyaltyOverride(address indexed sender, uint256 indexed tokenId, address newRecipient, uint256 newPercentage); + /*////////////////////////////////////////////////////////////////////////// Errors //////////////////////////////////////////////////////////////////////////*/ @@ -91,6 +101,7 @@ abstract contract EIP2981TLUpgradeable is IEIP2981, ERC165Upgradeable { if (newPercentage > 10_000) revert MaxRoyaltyError(); $.defaultRecipient = newRecipient; $.defaultPercentage = newPercentage; + emit DefaultRoyaltyUpdate(msg.sender, newRecipient, newPercentage); } /// @notice Function to override royalty spec on a specific token @@ -103,6 +114,7 @@ abstract contract EIP2981TLUpgradeable is IEIP2981, ERC165Upgradeable { if (newPercentage > 10_000) revert MaxRoyaltyError(); $.tokenOverrides[tokenId].recipient = newRecipient; $.tokenOverrides[tokenId].percentage = newPercentage; + emit TokenRoyaltyOverride(msg.sender, tokenId, newRecipient, newPercentage); } /*////////////////////////////////////////////////////////////////////////// diff --git a/test/royalties/EIP2981TL.t.sol b/test/royalties/EIP2981TL.t.sol index edecc05..271ca93 100644 --- a/test/royalties/EIP2981TL.t.sol +++ b/test/royalties/EIP2981TL.t.sol @@ -8,6 +8,12 @@ import {EIP2981TL} from "src/royalties/EIP2981TL.sol"; contract TestEIP2981TL is Test { MockEIP2981TL public mockContract; + /// @dev Event to emit when the default roylaty is updated + event DefaultRoyaltyUpdate(address indexed sender, address newRecipient, uint256 newPercentage); + + /// @dev Event to emit when a token royalty is overriden + event TokenRoyaltyOverride(address indexed sender, uint256 indexed tokenId, address newRecipient, uint256 newPercentage); + function test_DefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) public { @@ -15,6 +21,9 @@ contract TestEIP2981TL is Test { vm.expectRevert(EIP2981TL.ZeroAddressError.selector); } else if (percentage > 10_000) { vm.expectRevert(EIP2981TL.MaxRoyaltyError.selector); + } else { + vm.expectEmit(true, true, true, true); + emit DefaultRoyaltyUpdate(address(this), recipient, percentage); } mockContract = new MockEIP2981TL(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { @@ -45,6 +54,9 @@ contract TestEIP2981TL is Test { vm.expectRevert(EIP2981TL.ZeroAddressError.selector); } else if (percentage > 10_000) { vm.expectRevert(EIP2981TL.MaxRoyaltyError.selector); + } else { + vm.expectEmit(true, true, true, true); + emit DefaultRoyaltyUpdate(address(this), recipient, percentage); } mockContract.setDefaultRoyalty(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { @@ -67,6 +79,9 @@ contract TestEIP2981TL is Test { vm.expectRevert(EIP2981TL.ZeroAddressError.selector); } else if (percentage > 10_000) { vm.expectRevert(EIP2981TL.MaxRoyaltyError.selector); + } else { + vm.expectEmit(true, true, true, true); + emit TokenRoyaltyOverride(address(this), tokenId, recipient, percentage); } mockContract.setTokenRoyalty(tokenId, recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { diff --git a/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol b/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol index 41fae17..0b70747 100644 --- a/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol +++ b/test/upgradeable/royalties/EIP2981TLUpgradeable.t.sol @@ -8,6 +8,12 @@ import {EIP2981TLUpgradeable} from "src/upgradeable/royalties/EIP2981TLUpgradeab contract TestEIP2981TLUpgradeable is Test { MockEIP2981TLUpgradeable public mockContract; + /// @dev Event to emit when the default roylaty is updated + event DefaultRoyaltyUpdate(address indexed sender, address newRecipient, uint256 newPercentage); + + /// @dev Event to emit when a token royalty is overriden + event TokenRoyaltyOverride(address indexed sender, uint256 indexed tokenId, address newRecipient, uint256 newPercentage); + function test_DefaultRoyaltyInfo(uint256 tokenId, address recipient, uint16 percentage, uint256 saleAmount) public { @@ -16,6 +22,9 @@ contract TestEIP2981TLUpgradeable is Test { vm.expectRevert(EIP2981TLUpgradeable.ZeroAddressError.selector); } else if (percentage > 10_000) { vm.expectRevert(EIP2981TLUpgradeable.MaxRoyaltyError.selector); + } else { + vm.expectEmit(true, true, true, true); + emit DefaultRoyaltyUpdate(address(this), recipient, percentage); } mockContract.initialize(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { @@ -48,6 +57,9 @@ contract TestEIP2981TLUpgradeable is Test { vm.expectRevert(EIP2981TLUpgradeable.ZeroAddressError.selector); } else if (percentage > 10_000) { vm.expectRevert(EIP2981TLUpgradeable.MaxRoyaltyError.selector); + } else { + vm.expectEmit(true, true, true, true); + emit DefaultRoyaltyUpdate(address(this), recipient, percentage); } mockContract.setDefaultRoyalty(recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) { @@ -71,6 +83,9 @@ contract TestEIP2981TLUpgradeable is Test { vm.expectRevert(EIP2981TLUpgradeable.ZeroAddressError.selector); } else if (percentage > 10_000) { vm.expectRevert(EIP2981TLUpgradeable.MaxRoyaltyError.selector); + } else { + vm.expectEmit(true, true, true, true); + emit TokenRoyaltyOverride(address(this), tokenId, recipient, percentage); } mockContract.setTokenRoyalty(tokenId, recipient, uint256(percentage)); if (recipient != address(0) && percentage <= 10_000) {