369 lines
9.6 KiB
Solidity
369 lines
9.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.0;
|
|
|
|
import {Test, console2} from "forge-std/Test.sol";
|
|
import "../../src/NFinTech/NFinTech.sol";
|
|
|
|
/// @title NFinTech Test
|
|
/// @author Louis Tsai
|
|
/// @notice Do NOT modify this contract or you might get 0 points for the assingment.
|
|
|
|
contract NFinTechTest is Test {
|
|
NFinTech internal nft;
|
|
address internal Bob;
|
|
address internal Alice;
|
|
address internal user;
|
|
|
|
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
|
|
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
|
|
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
|
|
|
|
function setUp() public {
|
|
nft = new NFinTech("NFinTech", "NFT");
|
|
|
|
user = makeAddr("user");
|
|
|
|
Bob = makeAddr("Bob");
|
|
vm.prank(Bob);
|
|
nft.claim();
|
|
|
|
Alice = makeAddr("Alice");
|
|
vm.prank(Alice);
|
|
nft.claim();
|
|
}
|
|
|
|
/* Default Tests */
|
|
function test_name() public {
|
|
string memory name = nft.name();
|
|
assertEq(name, "NFinTech");
|
|
}
|
|
|
|
function test_symbol() public {
|
|
string memory symbol = nft.symbol();
|
|
assertEq(symbol, "NFT");
|
|
}
|
|
|
|
function test_balanceOf() public {
|
|
uint256 balance;
|
|
|
|
vm.prank(Bob);
|
|
balance = nft.balanceOf(Bob);
|
|
assertEq(balance, 1);
|
|
|
|
vm.prank(Alice);
|
|
balance = nft.balanceOf(Alice);
|
|
assertEq(balance, 1);
|
|
|
|
vm.expectRevert();
|
|
nft.balanceOf(address(0));
|
|
}
|
|
|
|
function test_ownerOf() public {
|
|
address owner;
|
|
|
|
owner = nft.ownerOf(0);
|
|
assertEq(owner, Bob);
|
|
|
|
owner = nft.ownerOf(1);
|
|
assertEq(owner, Alice);
|
|
|
|
vm.expectRevert();
|
|
nft.ownerOf(3);
|
|
}
|
|
|
|
/* PART 1: Complete approve related function -> 10 points */
|
|
|
|
function test_approve() public returns (bool) {
|
|
vm.prank(Bob);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Approval(Bob, Alice, 0);
|
|
nft.approve(Alice, 0);
|
|
|
|
address operator = nft.getApproved(0);
|
|
assertEq(operator, Alice);
|
|
|
|
return true;
|
|
}
|
|
|
|
function test_setApprovalForAll() public returns (bool) {
|
|
bool approved;
|
|
vm.prank(Bob);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit ApprovalForAll(Bob, Alice, true);
|
|
nft.setApprovalForAll(Alice, true);
|
|
|
|
approved = nft.isApprovedForAll(Bob, Alice);
|
|
assertEq(approved, true);
|
|
|
|
vm.prank(Bob);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit ApprovalForAll(Bob, Alice, false);
|
|
nft.setApprovalForAll(Alice, false);
|
|
|
|
approved = nft.isApprovedForAll(Bob, Alice);
|
|
assertEq(approved, false);
|
|
|
|
vm.prank(Bob);
|
|
vm.expectRevert();
|
|
nft.setApprovalForAll(address(0), true);
|
|
|
|
return true;
|
|
}
|
|
|
|
function test_approve_not_token_owner() public returns (bool) {
|
|
vm.prank(Bob);
|
|
vm.expectRevert();
|
|
nft.approve(Alice, 1);
|
|
return true;
|
|
}
|
|
|
|
function test_approve_then_setApprovalForAll() public returns (bool) {
|
|
vm.prank(Bob);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit ApprovalForAll(Bob, Alice, true);
|
|
nft.setApprovalForAll(Alice, true);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Approval(Bob, user, 0);
|
|
nft.approve(user, 0);
|
|
|
|
address operator = nft.getApproved(0);
|
|
assertEq(operator, user);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* PART 2: Complete transferFrom function -> 10 points */
|
|
function test_transferFrom() public returns (bool) {
|
|
uint256 balance;
|
|
address owner;
|
|
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Transfer(Bob, Alice, 0);
|
|
nft.transferFrom(Bob, Alice, 0);
|
|
|
|
balance = nft.balanceOf(Bob);
|
|
assertEq(balance, 0);
|
|
balance = nft.balanceOf(Alice);
|
|
assertEq(balance, 2);
|
|
|
|
owner = nft.ownerOf(0);
|
|
assertEq(owner, Alice);
|
|
owner = nft.ownerOf(1);
|
|
assertEq(owner, Alice);
|
|
|
|
return true;
|
|
}
|
|
|
|
function test_transferFrom_zero_address() public returns (bool) {
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectRevert();
|
|
nft.transferFrom(Bob, address(0), 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
function test_transferFrom_not_owner() public returns (bool) {
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectRevert();
|
|
nft.transferFrom(Bob, address(0), 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* PART 3: Complete safeTransferFrom function -> 10 points */
|
|
function test_safeTransferFrom_eoa() public returns (bool) {
|
|
uint256 balance;
|
|
address owner;
|
|
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Transfer(Bob, Alice, 0);
|
|
nft.transferFrom(Bob, Alice, 0);
|
|
|
|
balance = nft.balanceOf(Bob);
|
|
assertEq(balance, 0);
|
|
balance = nft.balanceOf(Alice);
|
|
assertEq(balance, 2);
|
|
|
|
owner = nft.ownerOf(0);
|
|
assertEq(owner, Alice);
|
|
owner = nft.ownerOf(1);
|
|
assertEq(owner, Alice);
|
|
|
|
return true;
|
|
}
|
|
|
|
function test_safeTransferFrom_ca_success() public returns (bool) {
|
|
uint256 balance;
|
|
address owner;
|
|
|
|
MockSuccessReceiver receiver = new MockSuccessReceiver();
|
|
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Transfer(Bob, address(receiver), 0);
|
|
nft.safeTransferFrom(Bob, address(receiver), 0);
|
|
|
|
balance = nft.balanceOf(Bob);
|
|
assertEq(balance, 0);
|
|
balance = nft.balanceOf(address(receiver));
|
|
assertEq(balance, 1);
|
|
|
|
owner = nft.ownerOf(0);
|
|
assertEq(owner, address(receiver));
|
|
owner = nft.ownerOf(1);
|
|
assertEq(owner, Alice);
|
|
|
|
return true;
|
|
}
|
|
|
|
function test_safeTransferFrom_ca_failure() public returns (bool) {
|
|
MockBadReceiver receiver = new MockBadReceiver();
|
|
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectRevert();
|
|
nft.safeTransferFrom(Bob, address(receiver), 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* PART 4: Mixed operation test */
|
|
function test_approve_then_transferFrom() public {
|
|
uint256 balance;
|
|
address owner;
|
|
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Transfer(Bob, Alice, 0);
|
|
nft.transferFrom(Bob, Alice, 0);
|
|
|
|
balance = nft.balanceOf(Bob);
|
|
assertEq(balance, 0);
|
|
balance = nft.balanceOf(Alice);
|
|
assertEq(balance, 2);
|
|
|
|
owner = nft.ownerOf(0);
|
|
assertEq(owner, Alice);
|
|
owner = nft.ownerOf(1);
|
|
assertEq(owner, Alice);
|
|
}
|
|
|
|
function test_approve_user_then_transferFrom() public {
|
|
uint256 balance;
|
|
address owner;
|
|
|
|
vm.prank(Bob);
|
|
nft.approve(Alice, 0);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Transfer(Bob, user, 0);
|
|
nft.transferFrom(Bob, user, 0);
|
|
|
|
balance = nft.balanceOf(Bob);
|
|
assertEq(balance, 0);
|
|
balance = nft.balanceOf(user);
|
|
assertEq(balance, 1);
|
|
|
|
owner = nft.ownerOf(0);
|
|
assertEq(owner, user);
|
|
owner = nft.ownerOf(1);
|
|
assertEq(owner, Alice);
|
|
}
|
|
|
|
function test_setApprovalForAll_then_transferFrom() public {
|
|
uint256 balance;
|
|
address owner;
|
|
|
|
vm.prank(Bob);
|
|
nft.setApprovalForAll(Alice, true);
|
|
|
|
vm.prank(Alice);
|
|
vm.expectEmit(true, true, true, false);
|
|
emit Transfer(Bob, user, 0);
|
|
nft.transferFrom(Bob, user, 0);
|
|
|
|
balance = nft.balanceOf(Bob);
|
|
assertEq(balance, 0);
|
|
balance = nft.balanceOf(user);
|
|
assertEq(balance, 1);
|
|
|
|
owner = nft.ownerOf(0);
|
|
assertEq(owner, user);
|
|
owner = nft.ownerOf(1);
|
|
assertEq(owner, Alice);
|
|
}
|
|
/* We use the following parts to calculate your score */
|
|
|
|
function test_check_approve_related_points() public {
|
|
test_approve();
|
|
test_setApprovalForAll();
|
|
test_approve_not_token_owner();
|
|
test_approve_then_setApprovalForAll();
|
|
console2.log("Get 10 points");
|
|
}
|
|
|
|
function test_check_transferFrom_points() public {
|
|
test_transferFrom();
|
|
setUp();
|
|
test_transferFrom_zero_address();
|
|
setUp();
|
|
test_transferFrom_not_owner();
|
|
console2.log("Get 10 points");
|
|
}
|
|
|
|
function test_check_safeTransferFrom_points() public {
|
|
test_safeTransferFrom_eoa();
|
|
setUp();
|
|
test_safeTransferFrom_ca_failure();
|
|
setUp();
|
|
test_safeTransferFrom_ca_success();
|
|
console2.log("Get 10 points");
|
|
}
|
|
|
|
function test_check_mix_operation() public {
|
|
test_approve_then_transferFrom();
|
|
setUp();
|
|
test_approve_user_then_transferFrom();
|
|
setUp();
|
|
test_setApprovalForAll_then_transferFrom();
|
|
console2.log("Get 10 points");
|
|
}
|
|
}
|
|
|
|
contract MockSuccessReceiver is IERC721TokenReceiver {
|
|
function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4) {
|
|
return IERC721TokenReceiver.onERC721Received.selector;
|
|
}
|
|
}
|
|
|
|
contract MockBadReceiver is IERC721TokenReceiver {
|
|
function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4) {
|
|
return bytes4(keccak256("approve(address,uint256)"));
|
|
}
|
|
}
|