Skip to content

Commit

Permalink
Merge pull request #10 from centrifuge/morpho-inherit
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
hieronx authored Jun 12, 2024
2 parents 12be13f + 9eb319e commit 196a786
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 96 deletions.
5 changes: 3 additions & 2 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -21,7 +22,7 @@ contract DeployScript is Script {
PermissionedERC20Wrapper wrappedUSDC = new PermissionedERC20Wrapper(
"Attested USDC",
"aUSDC",
IERC20Metadata(USDC),
IERC20(USDC),
morpho,
bundler,
attestationService,
Expand Down
67 changes: 6 additions & 61 deletions src/PermissionedERC20Wrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -37,42 +34,29 @@ 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;

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);
Expand All @@ -86,48 +70,9 @@ contract PermissionedERC20Wrapper is Auth, ERC20, ERC20Wrapper, ERC20Permit {
else revert("PermissionedERC20Wrapper/file-unrecognized-param");
}

// --- 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) {
if (
account == address(this) || account == address(0) || account == MORPHO || account == BUNDLER
|| memberlist.isMember(account)
) {
function hasPermission(address account) public view override returns (bool attested) {
if (super.hasPermission(account) || memberlist.isMember(account)) {
return true;
}

Expand Down
39 changes: 6 additions & 33 deletions test/unit/PermissionedERC20Wrapper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ 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 {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";

contract SimpleERC20 is ERC20 {
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}
Expand All @@ -28,7 +30,7 @@ contract PermissionedERC20WrapperTest is Test {
wrappedUSDC = new PermissionedERC20Wrapper(
"attested USDC",
"aUSDC",
IERC20Metadata(address(usdc)),
IERC20(address(usdc)),
address(0),
address(0),
address(0x4200000000000000000000000000000000000021),
Expand Down Expand Up @@ -116,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();
}
Expand All @@ -142,28 +144,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);
Expand All @@ -182,15 +162,8 @@ 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);
}

function test_transfer_FromNonPermissionedToPermissioned_Works() public {
deal(address(wrappedUSDC), userUS, 100);
vm.prank(userUS);
wrappedUSDC.transfer(userNonUS1, 100);
assertEq(wrappedUSDC.balanceOf(userNonUS1), 100);
}
}

0 comments on commit 196a786

Please sign in to comment.