Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gms-1404] migrate OAL related erc721 tests to forge #176

Merged
merged 2 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions contracts/mocks/MockFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
pragma solidity 0.8.19;

import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

contract MockFactory {
bytes private constant MOCK_DISGUISED_EOA_BYTECODE =
hex"608060405234801561001057600080fd5b5060405161021338038061021383398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b610180806100936000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80639d76ea581461003b578063e58ef8a81461006a575b600080fd5b60005461004e906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b61007d61007836600461010e565b61007f565b005b6000546040516323b872dd60e01b81526001600160a01b038581166004830152848116602483015260448201849052909116906323b872dd90606401600060405180830381600087803b1580156100d557600080fd5b505af11580156100e9573d6000803e3d6000fd5b50505050505050565b80356001600160a01b038116811461010957600080fd5b919050565b60008060006060848603121561012357600080fd5b61012c846100f2565b925061013a602085016100f2565b915060408401359050925092509256fea2646970667358221220cc26e879b9dbccdd8ff34bda1c5675a4b1a8497cba91bea35b6b744a41374a9a64736f6c63430008130033";

function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) {
return Create2.computeAddress(salt, codeHash);
}
Expand All @@ -12,4 +16,20 @@ contract MockFactory {
// slither-disable-next-line unused-return
Create2.deploy(0, salt, code);
}

function deployMockEOAWithERC721Address(IERC721 tokenAddress, bytes32 salt) external returns (address) {
bytes memory encodedParams = abi.encode(address(tokenAddress));
bytes memory constructorBytecode = abi.encodePacked(bytes(MOCK_DISGUISED_EOA_BYTECODE), encodedParams);
address mockDisguisedEOAAddress = Create2.deploy(0, salt, constructorBytecode);

return mockDisguisedEOAAddress;
}

function computeMockDisguisedEOAAddress(IERC721 tokenAddress, bytes32 salt) external view returns (address) {
bytes memory encodedParams = abi.encode(address(tokenAddress));
bytes memory constructorBytecode = abi.encodePacked(bytes(MOCK_DISGUISED_EOA_BYTECODE), encodedParams);
address computedAddress = Create2.computeAddress(salt, keccak256(constructorBytecode));

return computedAddress;
}
}
10 changes: 10 additions & 0 deletions contracts/mocks/MockWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ contract MockWallet {
IERC1155(token).safeTransferFrom(from, to, tokenId, amount, "");
}

function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4) {
emit Received(operator, from, tokenId, 1, data);
return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
}

function batchTransfer1155(
address token,
address from,
Expand Down
268 changes: 268 additions & 0 deletions test/allowlist/AllowlistERC721TransferApprovals.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
pragma solidity 0.8.19;

import "forge-std/Test.sol";

import {MockWallet} from "../../contracts/mocks/MockWallet.sol";
import {MockWalletFactory} from "../../contracts/mocks/MockWalletFactory.sol";
import {MockFactory} from "../../contracts/mocks/MockFactory.sol";
import {ImmutableERC721} from "../../contracts/token/erc721/preset/ImmutableERC721.sol";
import {IImmutableERC721Errors} from "../../contracts/errors/Errors.sol";
import {OperatorAllowlistEnforcementErrors} from "../../contracts/errors/Errors.sol";
import {OperatorAllowlistUpgradeable} from "../../contracts/allowlist/OperatorAllowlistUpgradeable.sol";
import {Sign} from "../utils/Sign.sol";
import {DeployOperatorAllowlist} from "../utils/DeployAllowlistProxy.sol";
import {DeploySCWallet} from "../utils/DeploySCW.sol";
import {DeployMockMarketPlace} from "../utils/DeployMockMarketPlace.sol";
import {MockMarketplace} from "../../contracts/mocks/MockMarketplace.sol";
import {MockDisguisedEOA} from "../../contracts/mocks/MockDisguisedEOA.sol";
import {MockOnReceive} from "../../contracts/mocks/MockOnReceive.sol";


contract AllowlistERC721TransferApprovals is Test {
OperatorAllowlistUpgradeable public allowlist;
ImmutableERC721 public immutableERC721;
DeploySCWallet public deploySCWScript;
DeployMockMarketPlace public deployMockMarketPlaceScript;
MockMarketplace public mockMarketPlace;
MockFactory mockEOAFactory;

uint256 feeReceiverKey = 1;

address public admin = makeAddr("roleAdmin");
address public upgrader = makeAddr("roleUpgrader");
address public registrar = makeAddr("roleRegisterar");
address public scwOwner = makeAddr("scwOwner");
address public minter = makeAddr("minter");

address feeReceiver = vm.addr(feeReceiverKey);
address proxyAddr;
address nonAuthorizedWallet;
address scwAddr;
address scwModuleAddr;

function setUp() public {
DeployOperatorAllowlist deployScript = new DeployOperatorAllowlist();
proxyAddr = deployScript.run(admin, upgrader, registrar);

allowlist = OperatorAllowlistUpgradeable(proxyAddr);

immutableERC721 = new ImmutableERC721(
admin,
"test",
"USDC",
"test-base-uri",
"test-contract-uri",
address(allowlist),
feeReceiver,
0
);

mockEOAFactory = new MockFactory();

nonAuthorizedWallet = address(0x2);

deploySCWScript = new DeploySCWallet();

deployMockMarketPlaceScript = new DeployMockMarketPlace();
mockMarketPlace = deployMockMarketPlaceScript.run(address(immutableERC721));

_giveMinterRole();
}

function testDeployment() public {
assertEq(address(immutableERC721.operatorAllowlist()), proxyAddr);
}

function _addSCWAddressAllowListAndApprove(address _address) private {
vm.startPrank(registrar);
allowlist.addWalletToAllowlist(_address);
immutableERC721.setApprovalForAll(_address, true);
vm.stopPrank();
}

function _giveMinterRole() public {
vm.prank(admin);
immutableERC721.grantMinterRole(minter);
bytes32 minterRole = immutableERC721.MINTER_ROLE();
assertTrue(immutableERC721.hasRole(minterRole, minter));
}

function testShouldNotApproveNoneOALSCW() public {
bytes32 salt = keccak256(abi.encodePacked("0x1234"));
(scwAddr, scwModuleAddr) = deploySCWScript.run(salt);

vm.prank(minter);
immutableERC721.safeMint(admin, 1);

vm.startPrank(admin);
vm.expectRevert(abi.encodeWithSignature("ApproveTargetNotInAllowlist(address)", scwAddr));
immutableERC721.setApprovalForAll(scwAddr, true);

vm.expectRevert(abi.encodeWithSignature("ApproveTargetNotInAllowlist(address)", scwAddr));
immutableERC721.approve(scwAddr, 1);
vm.stopPrank();
}

function testShouldNotAllowApproveFromNoneOALContract() public {
vm.startPrank(minter);
immutableERC721.mint(address(mockMarketPlace), 1);
vm.expectRevert(abi.encodeWithSignature("ApproverNotInAllowlist(address)", address(mockMarketPlace)));
mockMarketPlace.executeApproveForAll(minter, true);
vm.stopPrank();
}

function testShouldApproveEOA() public {
vm.startPrank(minter);
immutableERC721.safeMint(minter, 1);
immutableERC721.safeMint(minter, 2);

immutableERC721.approve(admin, 1);
assertEq(immutableERC721.getApproved(1), admin);
immutableERC721.setApprovalForAll(admin, true);
assertTrue(immutableERC721.isApprovedForAll(minter, admin));
vm.stopPrank();
}

function testShouldApproveWalletInOAL() public {
bytes32 salt = keccak256(abi.encodePacked("0x1234"));
(scwAddr, ) = deploySCWScript.run(salt);

vm.prank(registrar);
allowlist.addWalletToAllowlist(scwAddr);

vm.startPrank(minter);
immutableERC721.safeMint(minter, 1);
immutableERC721.safeMint(minter, 2);
immutableERC721.approve(scwAddr, 1);
assertEq(immutableERC721.getApproved(1), scwAddr);
immutableERC721.setApprovalForAll(scwAddr, true);
assertTrue(immutableERC721.isApprovedForAll(minter, scwAddr));
vm.stopPrank();
}

function testShouldApproveAddrInOAL() public {
address[] memory addressTargets = new address[](1);
addressTargets[0] = address(mockMarketPlace);

vm.prank(registrar);
allowlist.addAddressesToAllowlist(addressTargets);

vm.startPrank(minter);
immutableERC721.safeMint(minter, 1);
immutableERC721.safeMint(minter, 2);
immutableERC721.approve(address(mockMarketPlace), 1);
assertEq(immutableERC721.getApproved(1), address(mockMarketPlace));
immutableERC721.setApprovalForAll(address(mockMarketPlace), true);
assertTrue(immutableERC721.isApprovedForAll(minter, address(mockMarketPlace)));
vm.stopPrank();
}

function testTransferBetweenEOAs() public {
vm.startPrank(minter, minter);
immutableERC721.safeMint(minter, 1);
immutableERC721.approve(admin, 1);
immutableERC721.transferFrom(minter, admin, 1);
assertEq(immutableERC721.ownerOf(1), admin);
vm.stopPrank();
}

function testBlockTransferForNoneOALWallet() public {
bytes32 salt = keccak256(abi.encodePacked("0x1234"));
(scwAddr, ) = deploySCWScript.run(salt);

vm.startPrank(minter, minter);
immutableERC721.safeMint(minter, 1);
vm.expectRevert(abi.encodeWithSignature("TransferToNotInAllowlist(address)", scwAddr));
immutableERC721.transferFrom(minter, scwAddr, 1);
vm.stopPrank();
}

function testBlockTransferForNoneOALAddr() public {
vm.startPrank(minter, minter);
immutableERC721.safeMint(minter, 1);
vm.expectRevert(abi.encodeWithSignature("TransferToNotInAllowlist(address)", address(mockMarketPlace)));
immutableERC721.transferFrom(minter, address(mockMarketPlace), 1);
vm.stopPrank();
}

function testTransferToAddrInOAL() public {
address[] memory addressTargets = new address[](1);
addressTargets[0] = address(mockMarketPlace);

vm.prank(registrar);
allowlist.addAddressesToAllowlist(addressTargets);

vm.startPrank(minter, minter);
immutableERC721.safeMint(minter, 1);
immutableERC721.transferFrom(minter, address(mockMarketPlace), 1);
assertEq(immutableERC721.ownerOf(1), address(mockMarketPlace));
vm.stopPrank();
}

function testTransferToWalletInOAL() public {
bytes32 salt = keccak256(abi.encodePacked("0x1234"));
(scwAddr, ) = deploySCWScript.run(salt);

vm.prank(registrar);
allowlist.addWalletToAllowlist(scwAddr);

vm.startPrank(minter, minter);
immutableERC721.safeMint(minter, 1);
immutableERC721.transferFrom(minter, scwAddr, 1);
assertEq(immutableERC721.ownerOf(1), scwAddr);
vm.stopPrank();
}

function testTransferBetweenSCWInOAL() public {
bytes32 salt = keccak256(abi.encodePacked("0x1234"));
(address scwAddr1, ) = deploySCWScript.run(salt);
MockWallet scw1 = MockWallet(scwAddr1);

bytes32 salt2 = keccak256(abi.encodePacked("0x5678"));
(address scwAddr2, ) = deploySCWScript.run(salt2);

vm.startPrank(registrar);
allowlist.addWalletToAllowlist(scwAddr1);
allowlist.addWalletToAllowlist(scwAddr2);

vm.startPrank(minter, minter);
immutableERC721.safeMint(scwAddr1, 1);
scw1.transferNFT(address(immutableERC721), scwAddr1, scwAddr2, 1);
assertEq(immutableERC721.ownerOf(1), scwAddr2);
vm.stopPrank();
}

function testDisguisedEOAApprovalTransfer() public {
vm.startPrank(minter, minter);
immutableERC721.safeMint(minter, 1);
bytes32 salt = keccak256(abi.encodePacked("0x1234"));

address create2Addr = mockEOAFactory.computeMockDisguisedEOAAddress(immutableERC721, salt);

immutableERC721.setApprovalForAll(create2Addr, true);
mockEOAFactory.deployMockEOAWithERC721Address(immutableERC721, salt);

assertTrue(immutableERC721.isApprovedForAll(minter, create2Addr));

MockDisguisedEOA disguisedEOA = MockDisguisedEOA(create2Addr);

vm.expectRevert(abi.encodeWithSignature("CallerNotInAllowlist(address)", create2Addr));
disguisedEOA.executeTransfer(minter, admin, 1);
vm.stopPrank();
}

// Here the malicious contract attempts to transfer the token out of the contract by calling transferFrom in onERC721Received
// However, sending to the contract will fail as the contract is not in the allowlist.
function testOnReceiveTransferFrom() public {
MockOnReceive onReceive = new MockOnReceive(immutableERC721, admin);

vm.startPrank(minter, minter);
immutableERC721.safeMint(minter, 1);
vm.expectRevert(abi.encodeWithSignature("TransferToNotInAllowlist(address)", address(onReceive)));
immutableERC721.safeTransferFrom(minter, address(onReceive), 1, "");
vm.stopPrank();
}



}
Loading
Loading