diff --git a/contracts/mocks/MockFactory.sol b/contracts/mocks/MockFactory.sol index 67858771..8acfdb43 100644 --- a/contracts/mocks/MockFactory.sol +++ b/contracts/mocks/MockFactory.sol @@ -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); } @@ -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; + } } diff --git a/contracts/mocks/MockWallet.sol b/contracts/mocks/MockWallet.sol index f152f2d6..d266f3fd 100644 --- a/contracts/mocks/MockWallet.sol +++ b/contracts/mocks/MockWallet.sol @@ -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, diff --git a/test/allowlist/AllowlistERC721TransferApprovals.t.sol b/test/allowlist/AllowlistERC721TransferApprovals.t.sol new file mode 100644 index 00000000..a06d50d8 --- /dev/null +++ b/test/allowlist/AllowlistERC721TransferApprovals.t.sol @@ -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(); + } + + + +} \ No newline at end of file diff --git a/test/allowlist/AllowlistERC721TransfersApprovals.test.ts b/test/allowlist/AllowlistERC721TransfersApprovals.test.ts deleted file mode 100644 index 74f84603..00000000 --- a/test/allowlist/AllowlistERC721TransfersApprovals.test.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { expect } from "chai"; -import { ethers } from "hardhat"; -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { - ImmutableERC721MintByID, - MockMarketplace, - MockFactory, - OperatorAllowlist, - MockOnReceive, - MockOnReceive__factory, - MockWalletFactory, -} from "../../typechain-types"; -import { RegularAllowlistFixture, walletSCFixture, disguidedEOAFixture } from "../utils/DeployRegularFixtures"; - -describe("Allowlisted ERC721 Transfers", function () { - this.timeout(300_000); // 5 min - - let erc721: ImmutableERC721MintByID; - let walletFactory: MockWalletFactory; - let factory: MockFactory; - let operatorAllowlist: OperatorAllowlist; - let marketPlace: MockMarketplace; - let deployedAddr: string; // deployed SC wallet address - let moduleAddress: string; - let owner: SignerWithAddress; - let minter: SignerWithAddress; - let registrar: SignerWithAddress; - let scWallet: SignerWithAddress; - let accs: SignerWithAddress[]; - - beforeEach(async function () { - [owner, minter, registrar, scWallet, ...accs] = await ethers.getSigners(); - - // Get all required contracts - ({ erc721, walletFactory, factory, operatorAllowlist, marketPlace } = await RegularAllowlistFixture(owner)); - // Deploy the wallet fixture - ({ deployedAddr, moduleAddress } = await walletSCFixture(scWallet, walletFactory)); - - // Set up roles - await erc721.connect(owner).grantMinterRole(minter.address); - await operatorAllowlist.connect(owner).grantRegistrarRole(registrar.address); - }); - - describe("Operator Allowlist Registry setting", function () { - it("Should have operatorAllowlist set upon deployment", async function () { - expect(await erc721.operatorAllowlist()).to.equal(operatorAllowlist.address); - }); - }); - - describe("Approvals", function () { - it("Should not allow a non-Allowlisted operator to be approved", async function () { - await erc721.connect(minter).mint(minter.address, 1); - // Approve for all - await expect(erc721.connect(minter).setApprovalForAll(marketPlace.address, true)) - .to.be.revertedWith("ApproveTargetNotInAllowlist") - .withArgs(marketPlace.address); - // Approve - await expect(erc721.connect(minter).approve(marketPlace.address, 1)) - .to.be.revertedWith("ApproveTargetNotInAllowlist") - .withArgs(marketPlace.address); - }); - - it("Not allowlisted contracts should not be able to approve", async function () { - await erc721.connect(minter).mint(marketPlace.address, 2); - await expect(marketPlace.connect(minter).executeApproveForAll(minter.address, true)) - .to.be.revertedWith("ApproverNotInAllowlist") - .withArgs(marketPlace.address); - }); - - it("Should allow EOAs to be approved", async function () { - await erc721.connect(minter).mint(minter.address, 3); - await erc721.connect(minter).mint(minter.address, 1); - // Approve EOA addr - await erc721.connect(minter).approve(accs[0].address, 3); - await erc721.connect(minter).setApprovalForAll(accs[0].address, true); - expect(await erc721.getApproved(3)).to.be.equal(accs[0].address); - expect(await erc721.isApprovedForAll(minter.address, accs[0].address)).to.be.equal(true); - }); - - it("Should allow Allowlisted addresses to be approved", async function () { - // Add the mock marketplace to registry - await operatorAllowlist.connect(registrar).addAddressToAllowlist([marketPlace.address]); - // Approve marketplace on erc721 contract - await erc721.connect(minter).mint(minter.address, 2); - await erc721.connect(minter).approve(marketPlace.address, 2); - await erc721.connect(minter).setApprovalForAll(marketPlace.address, true); - expect(await erc721.getApproved(2)).to.be.equal(marketPlace.address); - expect(await erc721.isApprovedForAll(minter.address, marketPlace.address)).to.be.equal(true); - }); - - it("Should allow Allowlisted smart contract wallets to be approved", async function () { - // Allowlist the bytecode - await operatorAllowlist.connect(registrar).addWalletToAllowlist(deployedAddr); - await erc721.connect(minter).mint(minter.address, 3); - await erc721.connect(minter).approve(deployedAddr, 3); - // Approve the smart contract wallet - await erc721.connect(minter).setApprovalForAll(deployedAddr, true); - expect(await erc721.getApproved(3)).to.be.equal(deployedAddr); - expect(await erc721.isApprovedForAll(minter.address, deployedAddr)).to.be.equal(true); - }); - }); - - describe("Transfers", function () { - it("Should freely allow transfers between EOAs", async function () { - await erc721.connect(owner).grantMinterRole(accs[0].address); - await erc721.connect(owner).grantMinterRole(accs[1].address); - await erc721.connect(accs[0]).mint(accs[0].address, 1); - await erc721.connect(accs[1]).mint(accs[1].address, 2); - // Transfer - await erc721.connect(accs[0]).transferFrom(accs[0].address, accs[2].address, 1); - await erc721.connect(accs[1]).transferFrom(accs[1].address, accs[2].address, 2); - // Check balance - expect(await erc721.balanceOf(accs[2].address)).to.be.equal(2); - // Transfer again - await erc721.connect(accs[2]).transferFrom(accs[2].address, accs[0].address, 1); - await erc721.connect(accs[2]).transferFrom(accs[2].address, accs[1].address, 2); - // Check final balance - expect(await erc721.balanceOf(accs[2].address)).to.be.equal(0); - - // Approved EOA account should be able to transfer - await erc721.connect(accs[0]).setApprovalForAll(accs[2].address, true); - await erc721.connect(accs[2]).transferFrom(accs[0].address, accs[2].address, 1); - expect(await erc721.balanceOf(accs[2].address)).to.be.equal(1); - }); - - it("Should block transfers from a not allow listed contracts", async function () { - await erc721.connect(minter).mint(marketPlace.address, 5); - await expect(marketPlace.connect(minter).executeTransferFrom(marketPlace.address, minter.address, 5)) - .to.be.revertedWith("CallerNotInAllowlist") - .withArgs(marketPlace.address); - }); - - it("Should block transfers to a not allow listed address", async function () { - await erc721.connect(minter).mint(minter.address, 1); - await expect(erc721.connect(minter).transferFrom(minter.address, marketPlace.address, 1)) - .to.be.revertedWith("TransferToNotInAllowlist") - .withArgs(marketPlace.address); - }); - - it("Should not block transfers from an allow listed contract", async function () { - await operatorAllowlist.connect(registrar).addAddressToAllowlist([marketPlace.address]); - await erc721.connect(minter).mint(minter.address, 4); - await erc721.connect(minter).setApprovalForAll(marketPlace.address, true); - expect(await erc721.balanceOf(accs[3].address)).to.be.equal(0); - await marketPlace.connect(minter).executeTransfer(accs[3].address, 4); - expect(await erc721.balanceOf(accs[3].address)).to.be.equal(1); - }); - - it("Should not block transfers between allow listed smart contract wallets", async function () { - // Deploy more SC wallets - const salt = ethers.utils.keccak256("0x4567"); - const saltTwo = ethers.utils.keccak256("0x5678"); - const saltThree = ethers.utils.keccak256("0x6789"); - await walletFactory.connect(scWallet).deploy(moduleAddress, salt); - await walletFactory.connect(scWallet).deploy(moduleAddress, saltTwo); - await walletFactory.connect(scWallet).deploy(moduleAddress, saltThree); - const deployedAddr = await walletFactory.getAddress(moduleAddress, salt); - - await operatorAllowlist.connect(registrar).addWalletToAllowlist(deployedAddr); - - const deployedAddrTwo = await walletFactory.getAddress(moduleAddress, saltTwo); - const deployedAddrThree = await walletFactory.getAddress(moduleAddress, saltThree); - // Mint NFTs to the wallets - await erc721.connect(minter).mint(deployedAddr, 10); - await erc721.connect(minter).mint(deployedAddrTwo, 11); - - // Connect to wallets - const wallet = await ethers.getContractAt("MockWallet", deployedAddr); - const walletTwo = await ethers.getContractAt("MockWallet", deployedAddrTwo); - const walletThree = await ethers.getContractAt("MockWallet", deployedAddrThree); - - // Transfer between wallets - await wallet.transferNFT(erc721.address, deployedAddr, deployedAddrThree, 10); - await walletTwo.transferNFT(erc721.address, deployedAddrTwo, deployedAddrThree, 11); - expect(await erc721.balanceOf(deployedAddr)).to.be.equal(0); - expect(await erc721.balanceOf(deployedAddrTwo)).to.be.equal(0); - expect(await erc721.balanceOf(deployedAddrThree)).to.be.equal(2); - await walletThree.transferNFT(erc721.address, deployedAddrThree, deployedAddr, 10); - await walletThree.transferNFT(erc721.address, deployedAddrThree, deployedAddrTwo, 11); - expect(await erc721.balanceOf(deployedAddr)).to.be.equal(1); - expect(await erc721.balanceOf(deployedAddrTwo)).to.be.equal(1); - expect(await erc721.balanceOf(deployedAddrThree)).to.be.equal(0); - }); - }); - - describe("Malicious Contracts", function () { - // The EOA disguise attack vector is a where a pre-computed CREATE2 deterministic address is disguised as an EOA. - // By virtue of this, approvals and transfers to this address will pass. We need to catch actions from this address - // once it is deployed. - it("EOA disguise approve", async function () { - // This attack vector is where a CFA is approved prior to deployment. This passes as at the time of approval as - // the CFA is treated as an EOA, passing _validateApproval. - // This means post-deployment that the address is now an approved operator - // and is able to call transferFrom. - const { deployedAddr, salt, constructorByteCode } = await disguidedEOAFixture(erc721.address, factory, "0x1234"); - // Approve disguised EOA - await erc721.connect(minter).mint(minter.address, 1); - await erc721.connect(minter).setApprovalForAll(deployedAddr, true); - // Deploy disguised EOA - await factory.connect(accs[5]).deploy(salt, constructorByteCode); - expect(await erc721.isApprovedForAll(minter.address, deployedAddr)).to.be.equal(true); - // Attempt to execute a transferFrom, w/ msg.sender being the disguised EOA - const disguisedEOAFactory = ethers.getContractFactory("MockDisguisedEOA"); - const disguisedEOA = (await disguisedEOAFactory).attach(deployedAddr); - // Catch transfer as msg.sender != tx.origin - await expect(disguisedEOA.connect(minter).executeTransfer(minter.address, accs[5].address, 1)) - .to.be.revertedWith("CallerNotInAllowlist") - .withArgs(deployedAddr); - }); - - it("EOA disguise transferFrom", async function () { - // This vector is where the NFT is transferred to the CFA and executes a transferFrom inside its constructor. - // TODO: investigate why transferFrom calls fail within the constructor. This will be caught as msg.sender != tx.origin. - }); - - // 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. - it("onRecieve transferFrom", async function () { - // Deploy contract - const mockOnReceiveFactory = (await ethers.getContractFactory("MockOnReceive")) as MockOnReceive__factory; - const onRecieve: MockOnReceive = await mockOnReceiveFactory.deploy(erc721.address, accs[6].address); - // Mint and transfer to receiver contract - await erc721.connect(minter).mint(minter.address, 1); - // Fails as transfer 'to' is now allowlisted - await expect( - erc721.connect(minter)["safeTransferFrom(address,address,uint256)"](minter.address, onRecieve.address, 1) - ) - .to.be.revertedWith("TransferToNotInAllowlist") - .withArgs(onRecieve.address); - }); - }); -}); diff --git a/test/utils/DeployAllowlistProxy.sol b/test/utils/DeployAllowlistProxy.sol index 18552f23..322d2621 100644 --- a/test/utils/DeployAllowlistProxy.sol +++ b/test/utils/DeployAllowlistProxy.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.19; -import "forge-std/Test.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {OperatorAllowlistUpgradeable} from "../../contracts/allowlist/OperatorAllowlistUpgradeable.sol"; diff --git a/test/utils/DeployMockMarketPlace.sol b/test/utils/DeployMockMarketPlace.sol new file mode 100644 index 00000000..ec8764b7 --- /dev/null +++ b/test/utils/DeployMockMarketPlace.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache 2.0 +pragma solidity ^0.8.19; + +import {MockMarketplace} from "../../contracts/mocks/MockMarketplace.sol"; + + +/// Deploys the OperatorAllowlistUpgradeable contract behind an ERC1967 Proxy and returns the address of the proxy +contract DeployMockMarketPlace { + function run(address erc721Address) external returns (MockMarketplace) { + MockMarketplace marketplace = new MockMarketplace(erc721Address); + return marketplace; + } +} \ No newline at end of file diff --git a/test/utils/DeploySCW.sol b/test/utils/DeploySCW.sol index e5ccb736..97a7b222 100644 --- a/test/utils/DeploySCW.sol +++ b/test/utils/DeploySCW.sol @@ -6,7 +6,7 @@ import "forge-std/Test.sol"; import {MockWallet} from "../../contracts/mocks/MockWallet.sol"; import {MockWalletFactory} from "../../contracts/mocks/MockWalletFactory.sol"; -contract DeploySCWallet is Test { +contract DeploySCWallet { MockWallet public mockWalletModule; MockWallet public scw; MockWalletFactory public scmf;