From f4e2856a7166415d8436d7f474c142cda62c8a71 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Wed, 12 Jun 2024 10:18:50 +0200 Subject: [PATCH 1/4] Refactor --- script/Deploy.s.sol | 5 ++- src/PermissionedERC20Wrapper.sol | 56 +++--------------------- test/unit/PermissionedERC20Wrapper.t.sol | 5 ++- 3 files changed, 11 insertions(+), 55 deletions(-) diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index c05f384..725e3b7 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -3,7 +3,8 @@ pragma solidity ^0.8.13; import {Script} from "forge-std/Script.sol"; import {Memberlist} from "src/Memberlist.sol"; -import {PermissionedERC20Wrapper, IERC20Metadata} from "src/PermissionedERC20Wrapper.sol"; +import {PermissionedERC20Wrapper} from "src/PermissionedERC20Wrapper.sol"; +import {ERC20PermissionedBase, IERC20} from "lib/erc20-permissioned/src/ERC20PermissionedBase.sol"; contract DeployScript is Script { address USDC = vm.envAddress("UNDERLYING_TOKEN"); @@ -21,7 +22,7 @@ contract DeployScript is Script { PermissionedERC20Wrapper wrappedUSDC = new PermissionedERC20Wrapper( "Attested USDC", "aUSDC", - IERC20Metadata(USDC), + IERC20(USDC), morpho, bundler, attestationService, diff --git a/src/PermissionedERC20Wrapper.sol b/src/PermissionedERC20Wrapper.sol index 46398fc..8c9157a 100644 --- a/src/PermissionedERC20Wrapper.sol +++ b/src/PermissionedERC20Wrapper.sol @@ -3,10 +3,7 @@ pragma solidity ^0.8.20; import {Memberlist} from "src/Memberlist.sol"; import {Auth} from "lib/liquidity-pools/src/Auth.sol"; -import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; -import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; -import {ERC20Wrapper} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Wrapper.sol"; -import {ERC20Permit} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import {ERC20PermissionedBase, IERC20} from "lib/erc20-permissioned/src/ERC20PermissionedBase.sol"; import {IERC20Metadata} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IAttestationService { @@ -37,21 +34,12 @@ struct Attestation { /// VERIFIED_COUNTRY attestation of anything other than "US". Attestations are provided by Coinbase /// through the Ethereum Attestation Service. /// @author Modified from OpenZeppelin Contracts v5.0.0 (token/ERC20/extensions/ERC20Wrapper.sol) -contract PermissionedERC20Wrapper is Auth, ERC20, ERC20Wrapper, ERC20Permit { +contract PermissionedERC20Wrapper is Auth, ERC20PermissionedBase { bytes32 public constant verifiedCountrySchemaUid = 0x1801901fabd0e6189356b4fb52bb0ab855276d84f7ec140839fbd1f6801ca065; bytes32 public constant verifiedAccountSchemaUid = 0xf8b05c79f090979bf4a80270aba232dff11a10d9ca55c4f88de95317970f0de9; - /// @notice The address of the Morpho contract. - address public immutable MORPHO; - - /// @notice The address of the Bundler contract. - address public immutable BUNDLER; - - /// @notice The underlying token. - IERC20Metadata public immutable _underlying; - Memberlist public memberlist; IAttestationService public attestationService; IAttestationIndexer public attestationIndexer; @@ -59,20 +47,16 @@ contract PermissionedERC20Wrapper is Auth, ERC20, ERC20Wrapper, ERC20Permit { constructor( string memory name_, string memory symbol_, - IERC20Metadata underlyingToken_, + IERC20 underlyingToken_, address morpho_, address bundler_, address attestationService_, address attestationIndexer_, address memberlist_ - ) ERC20Wrapper(underlyingToken_) ERC20Permit(name_) ERC20(name_, symbol_) { - MORPHO = morpho_; - BUNDLER = bundler_; + ) ERC20PermissionedBase(name_, symbol_, underlyingToken_, morpho_, bundler_) { attestationService = IAttestationService(attestationService_); attestationIndexer = IAttestationIndexer(attestationIndexer_); memberlist = Memberlist(memberlist_); - if (address(underlyingToken_) == address(this)) revert ERC20InvalidUnderlying(address(this)); - _underlying = underlyingToken_; wards[msg.sender] = 1; emit Rely(msg.sender); @@ -87,43 +71,13 @@ contract PermissionedERC20Wrapper is Auth, ERC20, ERC20Wrapper, ERC20Permit { } // --- ERC20 wrapping --- - /** - * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. - */ - function depositFor(address account, uint256 value) public override returns (bool) { - address sender = _msgSender(); - if (sender == address(this)) revert ERC20InvalidSender(address(this)); - if (account == address(this)) revert ERC20InvalidReceiver(account); - SafeERC20.safeTransferFrom(_underlying, sender, address(this), value); - _mint(account, value); - return true; - } - - /** - * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. - */ - function withdrawTo(address account, uint256 value) public override returns (bool) { - if (account == address(this)) revert ERC20InvalidReceiver(account); - _burn(_msgSender(), value); - SafeERC20.safeTransfer(_underlying, account, value); - return true; - } - function _update(address from, address to, uint256 value) internal virtual override { require(hasPermission(to), "PermissionedERC20Wrapper/no-permission"); super._update(from, to, value); } - function decimals() public view virtual override(ERC20, ERC20Wrapper) returns (uint8) { - try IERC20Metadata(address(_underlying)).decimals() returns (uint8 value) { - return value; - } catch { - return super.decimals(); - } - } - // --- Permission checks --- - function hasPermission(address account) public view returns (bool attested) { + function hasPermission(address account) public view override returns (bool attested) { if ( account == address(this) || account == address(0) || account == MORPHO || account == BUNDLER || memberlist.isMember(account) diff --git a/test/unit/PermissionedERC20Wrapper.t.sol b/test/unit/PermissionedERC20Wrapper.t.sol index 130f736..2f7686f 100644 --- a/test/unit/PermissionedERC20Wrapper.t.sol +++ b/test/unit/PermissionedERC20Wrapper.t.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.13; import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import {Memberlist} from "src/Memberlist.sol"; -import {PermissionedERC20Wrapper, IERC20Metadata} from "src/PermissionedERC20Wrapper.sol"; +import {PermissionedERC20Wrapper} from "src/PermissionedERC20Wrapper.sol"; import {Test} from "forge-std/Test.sol"; +import {IERC20} from "lib/erc20-permissioned/src/ERC20PermissionedBase.sol"; contract SimpleERC20 is ERC20 { constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {} @@ -28,7 +29,7 @@ contract PermissionedERC20WrapperTest is Test { wrappedUSDC = new PermissionedERC20Wrapper( "attested USDC", "aUSDC", - IERC20Metadata(address(usdc)), + IERC20(address(usdc)), address(0), address(0), address(0x4200000000000000000000000000000000000021), From a7237a936aee645755152fa9fa0649b49726db7a Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Wed, 12 Jun 2024 10:27:53 +0200 Subject: [PATCH 2/4] Remove invalid tests --- src/PermissionedERC20Wrapper.sol | 3 +-- test/unit/PermissionedERC20Wrapper.t.sol | 29 ------------------------ 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/PermissionedERC20Wrapper.sol b/src/PermissionedERC20Wrapper.sol index 8c9157a..61794ac 100644 --- a/src/PermissionedERC20Wrapper.sol +++ b/src/PermissionedERC20Wrapper.sol @@ -70,13 +70,12 @@ contract PermissionedERC20Wrapper is Auth, ERC20PermissionedBase { else revert("PermissionedERC20Wrapper/file-unrecognized-param"); } - // --- ERC20 wrapping --- + // --- Permission checks --- function _update(address from, address to, uint256 value) internal virtual override { require(hasPermission(to), "PermissionedERC20Wrapper/no-permission"); super._update(from, to, value); } - // --- Permission checks --- function hasPermission(address account) public view override returns (bool attested) { if ( account == address(this) || account == address(0) || account == MORPHO || account == BUNDLER diff --git a/test/unit/PermissionedERC20Wrapper.t.sol b/test/unit/PermissionedERC20Wrapper.t.sol index 2f7686f..001192b 100644 --- a/test/unit/PermissionedERC20Wrapper.t.sol +++ b/test/unit/PermissionedERC20Wrapper.t.sol @@ -143,28 +143,6 @@ contract PermissionedERC20WrapperTest is Test { assertEq(usdc.balanceOf(userNonUS1), 100); } - function test_WithdrawTo_WithNonPermissioned_Works() public { - deal(address(wrappedUSDC), userUS, 100); - deal(address(usdc), address(wrappedUSDC), 100); - vm.startPrank(userUS); - wrappedUSDC.approve(address(wrappedUSDC), 100); - wrappedUSDC.withdrawTo(userUS, 100); - vm.stopPrank(); - assertEq(wrappedUSDC.balanceOf(userUS), 0); - assertEq(usdc.balanceOf(userUS), 100); - } - - function test_WithdrawTo_FromNonPermissionedToPermissioned_Works() public { - deal(address(wrappedUSDC), userUS, 100); - deal(address(usdc), address(wrappedUSDC), 100); - vm.startPrank(userUS); - wrappedUSDC.approve(address(wrappedUSDC), 100); - wrappedUSDC.withdrawTo(userNonUS1, 100); - vm.stopPrank(); - assertEq(wrappedUSDC.balanceOf(userNonUS1), 0); - assertEq(usdc.balanceOf(userNonUS1), 100); - } - function test_transfer_FromPermissionedToPermissioned_Works() public { deal(address(wrappedUSDC), userNonUS1, 100); vm.prank(userNonUS1); @@ -187,11 +165,4 @@ contract PermissionedERC20WrapperTest is Test { vm.prank(userNonUS1); wrappedUSDC.transfer(userUS, 100); } - - function test_transfer_FromNonPermissionedToPermissioned_Works() public { - deal(address(wrappedUSDC), userUS, 100); - vm.prank(userUS); - wrappedUSDC.transfer(userNonUS1, 100); - assertEq(wrappedUSDC.balanceOf(userNonUS1), 100); - } } From 269cc8c56d418e6361f8fe61a182fd7dc748edce Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Wed, 12 Jun 2024 10:30:19 +0200 Subject: [PATCH 3/4] Remove _update --- src/PermissionedERC20Wrapper.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/PermissionedERC20Wrapper.sol b/src/PermissionedERC20Wrapper.sol index 61794ac..dfd4f9c 100644 --- a/src/PermissionedERC20Wrapper.sol +++ b/src/PermissionedERC20Wrapper.sol @@ -71,11 +71,6 @@ contract PermissionedERC20Wrapper is Auth, ERC20PermissionedBase { } // --- Permission checks --- - function _update(address from, address to, uint256 value) internal virtual override { - require(hasPermission(to), "PermissionedERC20Wrapper/no-permission"); - super._update(from, to, value); - } - function hasPermission(address account) public view override returns (bool attested) { if ( account == address(this) || account == address(0) || account == MORPHO || account == BUNDLER From 9eb319e1d322576721b68c25b759c9a8bf81348d Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Wed, 12 Jun 2024 10:34:42 +0200 Subject: [PATCH 4/4] Cleanup --- src/PermissionedERC20Wrapper.sol | 5 +---- test/unit/PermissionedERC20Wrapper.t.sol | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/PermissionedERC20Wrapper.sol b/src/PermissionedERC20Wrapper.sol index dfd4f9c..bbc8ddb 100644 --- a/src/PermissionedERC20Wrapper.sol +++ b/src/PermissionedERC20Wrapper.sol @@ -72,10 +72,7 @@ contract PermissionedERC20Wrapper is Auth, ERC20PermissionedBase { // --- Permission checks --- function hasPermission(address account) public view override returns (bool attested) { - if ( - account == address(this) || account == address(0) || account == MORPHO || account == BUNDLER - || memberlist.isMember(account) - ) { + if (super.hasPermission(account) || memberlist.isMember(account)) { return true; } diff --git a/test/unit/PermissionedERC20Wrapper.t.sol b/test/unit/PermissionedERC20Wrapper.t.sol index 001192b..0be515b 100644 --- a/test/unit/PermissionedERC20Wrapper.t.sol +++ b/test/unit/PermissionedERC20Wrapper.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.13; import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import {Memberlist} from "src/Memberlist.sol"; import {PermissionedERC20Wrapper} from "src/PermissionedERC20Wrapper.sol"; +import {ERC20PermissionedBase, IERC20} from "lib/erc20-permissioned/src/ERC20PermissionedBase.sol"; import {Test} from "forge-std/Test.sol"; import {IERC20} from "lib/erc20-permissioned/src/ERC20PermissionedBase.sol"; @@ -117,7 +118,7 @@ contract PermissionedERC20WrapperTest is Test { deal(address(usdc), userUS, 100); vm.startPrank(userUS); usdc.approve(address(wrappedUSDC), 100); - vm.expectRevert("PermissionedERC20Wrapper/no-permission"); + vm.expectRevert(abi.encodeWithSelector(ERC20PermissionedBase.NoPermission.selector, userUS)); wrappedUSDC.depositFor(userUS, 100); vm.stopPrank(); } @@ -161,7 +162,7 @@ contract PermissionedERC20WrapperTest is Test { function test_transfer_FromPermissionedToNonPermissioned_Fails() public { deal(address(wrappedUSDC), userNonUS1, 100); - vm.expectRevert("PermissionedERC20Wrapper/no-permission"); + vm.expectRevert(abi.encodeWithSelector(ERC20PermissionedBase.NoPermission.selector, userUS)); vm.prank(userNonUS1); wrappedUSDC.transfer(userUS, 100); }