Skip to content
Draft
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
140 changes: 140 additions & 0 deletions test/trees/ERC721.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
BalanceOf
├── when the owner is the zero address
│ └── it should revert
└── when the owner is not the zero address
└── it should return the number of tokens owned by the owner

OwnerOf
├── when the token does not exist
│ └── it should revert
└── when the token exists
└── it should return the owner of the token

Approve
├── when the token does not exist
│ └── it should revert
└── when the token exists
├── when the caller is not the owner and not an approved operator
│ └── it should revert
└── when the caller is the owner or an approved operator
├── it should set the approved address for the token
└── it should emit an {Approval} event

SetApprovalForAll
├── when the operator is the zero address
│ └── it should revert
└── when the operator is not the zero address
├── it should set the operator approval status for the caller
└── it should emit an {ApprovalForAll} event

GetApproved
├── when the token does not exist
│ └── it should revert
└── when the token exists
└── it should return the approved address for the token

IsApprovedForAll
└── it should return the operator approval status

TransferFrom
├── when the token does not exist
│ └── it should revert
└── when the token exists
├── when the from address is not the owner
│ └── it should revert
└── when the from address is the owner
├── when the to address is the zero address
│ └── it should revert
└── when the to address is not the zero address
├── when the caller is not authorized
│ └── it should revert
└── when the caller is authorized
├── it should transfer ownership of the token to the recipient
├── it should decrement the sender's balance
├── it should increment the receiver's balance
├── it should clear the approval for the token
└── it should emit a {Transfer} event

SafeTransferFrom (without data)
├── when the token does not exist
│ └── it should revert
└── when the token exists
├── when the from address is not the owner
│ └── it should revert
└── when the from address is the owner
├── when the to address is the zero address
│ └── it should revert
└── when the to address is not the zero address
├── when the caller is not authorized
│ └── it should revert
└── when the caller is authorized
├── when the receiver is a contract that does not implement onERC721Received
│ └── it should revert
├── when the receiver is a contract that returns an incorrect value
│ └── it should revert
└── when the receiver is an EOA or returns the correct selector
├── it should transfer ownership of the token to the recipient
├── it should decrement the sender's balance
├── it should increment the receiver's balance
├── it should clear the approval for the token
└── it should emit a {Transfer} event

SafeTransferFrom (with data)
├── when the token does not exist
│ └── it should revert
└── when the token exists
├── when the from address is not the owner
│ └── it should revert
└── when the from address is the owner
├── when the to address is the zero address
│ └── it should revert
└── when the to address is not the zero address
├── when the caller is not authorized
│ └── it should revert
└── when the caller is authorized
├── when the receiver is a contract that does not implement onERC721Received
│ └── it should revert
├── when the receiver is a contract that returns an incorrect value
│ └── it should revert
└── when the receiver is an EOA or returns the correct selector
├── it should transfer ownership of the token to the recipient
├── it should decrement the sender's balance
├── it should increment the receiver's balance
├── it should clear the approval for the token
├── it should pass the data to onERC721Received
└── it should emit a {Transfer} event

Mint
├── when the recipient is the zero address
│ └── it should revert
└── when the recipient is not the zero address
├── when the token already exists
│ └── it should revert
└── when the token does not exist
├── it should assign the token to the recipient
├── it should increment the recipient's balance
└── it should emit a {Transfer} event from the zero address

Burn
├── when the token does not exist
│ └── it should revert
└── when the token exists
├── when the caller is not authorized
│ └── it should revert
└── when the caller is authorized
├── it should remove the token (set owner to zero address)
├── it should decrement the owner's balance
├── it should clear the approval for the token
└── it should emit a {Transfer} event to the zero address

Name
└── it should return the token collection name

Symbol
└── it should return the token collection symbol

TokenURI
├── when the token does not exist
│ └── it should revert
└── when the token exists
└── it should return the token URI
19 changes: 19 additions & 0 deletions test/unit/token/ERC721/Approve/facet/ERC721ApproveFacetBase.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30 <0.9.0;

/* Compose
* https://compose.diamonds
*/

import {Base_Test} from "test/Base.t.sol";
import {ERC721ApproveFacet} from "src/token/ERC721/Approve/ERC721ApproveFacet.sol";

contract ERC721ApproveFacet_Base_Test is Base_Test {
ERC721ApproveFacet internal facet;

function setUp() public virtual override {
Base_Test.setUp();
facet = new ERC721ApproveFacet();
vm.label(address(facet), "ERC721ApproveFacet");
}
}
63 changes: 63 additions & 0 deletions test/unit/token/ERC721/Approve/facet/fuzz/approve.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30;

/* Compose
* https://compose.diamonds
*/

import {ERC721ApproveFacet_Base_Test} from "../ERC721ApproveFacetBase.t.sol";
import {ERC721StorageUtils} from "test/utils/storage/ERC721StorageUtils.sol";

import {ERC721ApproveFacet} from "src/token/ERC721/Approve/ERC721ApproveFacet.sol";

/**
* @dev BTT spec: test/trees/ERC721.tree
*/
contract Approve_ERC721ApproveFacet_Fuzz_Unit_Test is ERC721ApproveFacet_Base_Test {
using ERC721StorageUtils for address;

function testFuzz_ShouldRevert_TokenDoesNotExist(address to, uint256 tokenId) external {
vm.expectRevert(abi.encodeWithSelector(ERC721ApproveFacet.ERC721NonexistentToken.selector, tokenId));
facet.approve(to, tokenId);
}

function testFuzz_ShouldRevert_CallerNotOwnerAndNotOperator(address owner, address to, uint256 tokenId)
external
whenTokenExists
{
vm.assume(owner != ADDRESS_ZERO);
vm.assume(owner != users.alice);

address(facet).mint(owner, tokenId);

vm.expectRevert(abi.encodeWithSelector(ERC721ApproveFacet.ERC721InvalidApprover.selector, users.alice));
facet.approve(to, tokenId);
}

function testFuzz_Approve_CallerIsOwner(address to, uint256 tokenId) external whenTokenExists {
address(facet).mint(users.alice, tokenId);

vm.expectEmit(address(facet));
emit ERC721ApproveFacet.Approval(users.alice, to, tokenId);
facet.approve(to, tokenId);

assertEq(address(facet).getApproved(tokenId), to, "getApproved(tokenId)");
}

function testFuzz_Approve_CallerIsApprovedOperator(address owner, address to, uint256 tokenId)
external
whenTokenExists
{
vm.assume(owner != ADDRESS_ZERO);
vm.assume(owner != users.alice);

address(facet).mint(owner, tokenId);
address(facet).setApprovalForAll(owner, users.alice, true);

vm.expectEmit(address(facet));
emit ERC721ApproveFacet.Approval(owner, to, tokenId);
facet.approve(to, tokenId);

assertEq(address(facet).getApproved(tokenId), to, "getApproved(tokenId)");
}
}
46 changes: 46 additions & 0 deletions test/unit/token/ERC721/Approve/facet/fuzz/setApprovalForAll.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30;

/* Compose
* https://compose.diamonds
*/

import {ERC721ApproveFacet_Base_Test} from "../ERC721ApproveFacetBase.t.sol";
import {ERC721StorageUtils} from "test/utils/storage/ERC721StorageUtils.sol";

import {ERC721ApproveFacet} from "src/token/ERC721/Approve/ERC721ApproveFacet.sol";

/**
* @dev BTT spec: test/trees/ERC721.tree
*/
contract SetApprovalForAll_ERC721ApproveFacet_Fuzz_Unit_Test is ERC721ApproveFacet_Base_Test {
using ERC721StorageUtils for address;

function testFuzz_ShouldRevert_OperatorIsZeroAddress(bool approved) external {
vm.expectRevert(abi.encodeWithSelector(ERC721ApproveFacet.ERC721InvalidOperator.selector, ADDRESS_ZERO));
facet.setApprovalForAll(ADDRESS_ZERO, approved);
}

function testFuzz_SetApprovalForAll_ApproveTrue(address operator) external whenOperatorNotZeroAddress {
vm.assume(operator != ADDRESS_ZERO);

vm.expectEmit(address(facet));
emit ERC721ApproveFacet.ApprovalForAll(users.alice, operator, true);
facet.setApprovalForAll(operator, true);

assertEq(address(facet).isApprovedForAll(users.alice, operator), true, "isApprovedForAll");
}

function testFuzz_SetApprovalForAll_ApproveFalse(address operator) external whenOperatorNotZeroAddress {
vm.assume(operator != ADDRESS_ZERO);

// First set to true
address(facet).setApprovalForAll(users.alice, operator, true);

vm.expectEmit(address(facet));
emit ERC721ApproveFacet.ApprovalForAll(users.alice, operator, false);
facet.setApprovalForAll(operator, false);

assertEq(address(facet).isApprovedForAll(users.alice, operator), false, "isApprovedForAll");
}
}
107 changes: 107 additions & 0 deletions test/unit/token/ERC721/Burn/facet/fuzz/burnERC721.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30;

/* Compose
* https://compose.diamonds
*/

import {Base_Test} from "test/Base.t.sol";
import {ERC721StorageUtils} from "test/utils/storage/ERC721StorageUtils.sol";

import {ERC721BurnFacet} from "src/token/ERC721/Burn/ERC721BurnFacet.sol";

/**
* @dev BTT spec: test/trees/ERC721.tree
*/
contract BurnERC721_ERC721BurnFacet_Fuzz_Unit_Test is Base_Test {
using ERC721StorageUtils for address;

ERC721BurnFacet internal facet;

function setUp() public virtual override {
Base_Test.setUp();
facet = new ERC721BurnFacet();
vm.label(address(facet), "ERC721BurnFacet");
}

function testFuzz_ShouldRevert_TokenDoesNotExist(uint256 tokenId) external {
vm.expectRevert(abi.encodeWithSelector(ERC721BurnFacet.ERC721NonexistentToken.selector, tokenId));
facet.burnERC721(tokenId);
}

function testFuzz_ShouldRevert_CallerNotAuthorized(address owner, uint256 tokenId) external whenTokenExists {
vm.assume(owner != ADDRESS_ZERO);
vm.assume(owner != users.alice);

address(facet).mint(owner, tokenId);

vm.expectRevert(
abi.encodeWithSelector(ERC721BurnFacet.ERC721InsufficientApproval.selector, users.alice, tokenId)
);
facet.burnERC721(tokenId);
}

function testFuzz_Burn_CallerIsOwner(uint256 tokenId, address approved)
external
whenTokenExists
whenCallerIsAuthorized
{
address(facet).mint(users.alice, tokenId);
address(facet).setApproved(tokenId, approved);

uint256 balanceBefore = address(facet).balanceOf(users.alice);

vm.expectEmit(address(facet));
emit ERC721BurnFacet.Transfer(users.alice, ADDRESS_ZERO, tokenId);
facet.burnERC721(tokenId);

assertEq(address(facet).ownerOf(tokenId), ADDRESS_ZERO, "ownerOf(tokenId)");
assertEq(address(facet).balanceOf(users.alice), balanceBefore - 1, "balanceOf(owner)");
assertEq(address(facet).getApproved(tokenId), ADDRESS_ZERO, "getApproved(tokenId)");
}

function testFuzz_Burn_CallerIsApprovedOperator(address owner, uint256 tokenId, address approved)
external
whenTokenExists
whenCallerIsAuthorized
{
vm.assume(owner != ADDRESS_ZERO);
vm.assume(owner != users.alice);

address(facet).mint(owner, tokenId);
address(facet).setApprovalForAll(owner, users.alice, true);
address(facet).setApproved(tokenId, approved);

uint256 balanceBefore = address(facet).balanceOf(owner);

vm.expectEmit(address(facet));
emit ERC721BurnFacet.Transfer(owner, ADDRESS_ZERO, tokenId);
facet.burnERC721(tokenId);

assertEq(address(facet).ownerOf(tokenId), ADDRESS_ZERO, "ownerOf(tokenId)");
assertEq(address(facet).balanceOf(owner), balanceBefore - 1, "balanceOf(owner)");
assertEq(address(facet).getApproved(tokenId), ADDRESS_ZERO, "getApproved(tokenId)");
}

function testFuzz_Burn_CallerIsTokenApproved(address owner, uint256 tokenId)
external
whenTokenExists
whenCallerIsAuthorized
{
vm.assume(owner != ADDRESS_ZERO);
vm.assume(owner != users.alice);

address(facet).mint(owner, tokenId);
address(facet).setApproved(tokenId, users.alice);

uint256 balanceBefore = address(facet).balanceOf(owner);

vm.expectEmit(address(facet));
emit ERC721BurnFacet.Transfer(owner, ADDRESS_ZERO, tokenId);
facet.burnERC721(tokenId);

assertEq(address(facet).ownerOf(tokenId), ADDRESS_ZERO, "ownerOf(tokenId)");
assertEq(address(facet).balanceOf(owner), balanceBefore - 1, "balanceOf(owner)");
assertEq(address(facet).getApproved(tokenId), ADDRESS_ZERO, "getApproved(tokenId)");
}
}
19 changes: 19 additions & 0 deletions test/unit/token/ERC721/Data/facet/ERC721DataFacetBase.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.30 <0.9.0;

/* Compose
* https://compose.diamonds
*/

import {Base_Test} from "test/Base.t.sol";
import {ERC721DataFacet} from "src/token/ERC721/Data/ERC721DataFacet.sol";

contract ERC721DataFacet_Base_Test is Base_Test {
ERC721DataFacet internal facet;

function setUp() public virtual override {
Base_Test.setUp();
facet = new ERC721DataFacet();
vm.label(address(facet), "ERC721DataFacet");
}
}
Loading
Loading