Initial commit

This commit is contained in:
Ting-Jun Wang 2024-03-21 12:00:51 +08:00 committed by GitHub
commit c0fb01c7d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1093 additions and 0 deletions

39
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Foundry Testing
run-name: Homework 1 Running by ${{ github.actor }}
on:
push:
branches:
- "**"
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
env:
FOUNDRY_PROFILE: ci
WORKFLOW_NAME: Foundry Test on ${{ github.ref_name }}
jobs:
check:
strategy:
fail-fast: true
name: Foundry Testing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Run Forge tests
run: |
cd hw1
forge test --mt test_check -vvv
id: test

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "hw1/lib/forge-std"]
path = hw1/lib/forge-std
url = https://github.com/foundry-rs/forge-std

1
README.md Normal file
View File

@ -0,0 +1 @@
# 2024-Spring-HW1

14
hw1/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
# Compiler files
cache/
out/
# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/
# Docs
docs/
# Dotenv file
.env

66
hw1/README.md Normal file
View File

@ -0,0 +1,66 @@
## Foundry
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
Foundry consists of:
- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
## Documentation
https://book.getfoundry.sh/
## Usage
### Build
```shell
$ forge build
```
### Test
```shell
$ forge test
```
### Format
```shell
$ forge fmt
```
### Gas Snapshots
```shell
$ forge snapshot
```
### Anvil
```shell
$ anvil
```
### Deploy
```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```
### Cast
```shell
$ cast <subcommand>
```
### Help
```shell
$ forge --help
$ anvil --help
$ cast --help
```

6
hw1/foundry.toml Normal file
View File

@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

1
hw1/lib/forge-std Submodule

@ -0,0 +1 @@
Subproject commit ae570fec082bfe1c1f45b0acca4a2b4f84d345ce

12
hw1/script/Counter.s.sol Normal file
View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
contract CounterScript is Script {
function setUp() public {}
function run() public {
vm.broadcast();
}
}

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/* Problem 1 Interface & Contract */
contract StudentV1 {
// Note: You can declare some state variable
function register() external returns (uint256) {
// TODO: please add your implementaiton here
}
}
/* Problem 2 Interface & Contract */
interface IClassroomV2 {
function isEnrolled() external view returns (bool);
}
contract StudentV2 {
function register() external view returns (uint256) {
// TODO: please add your implementaiton here
}
}
/* Problem 3 Interface & Contract */
contract StudentV3 {
function register() external view returns (uint256) {
// TODO: please add your implementaiton here
}
}

View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ID31eg4t3 {
function proxyCall(bytes calldata data) external returns (address);
function changeResult() external;
}
contract Attack {
address internal immutable victim;
// TODO: Declare some variable here
// Note: Checkout the storage layout in victim contract
constructor(address addr) payable {
victim = addr;
}
// NOTE: You might need some malicious function here
function exploit() external {
// TODO: Add your implementation here
// Note: Make sure you know how delegatecall works
// bytes memory data = ...
}
}

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract LiaoToken is IERC20 {
// TODO: you might need to declare several state variable here
mapping(address account => uint256) private _balances;
mapping(address account => bool) isClaim;
uint256 private _totalSupply;
string private _name;
string private _symbol;
event Claim(address indexed user, uint256 indexed amount);
constructor(string memory name_, string memory symbol_) payable {
_name = name_;
_symbol = symbol_;
}
function decimals() public pure returns (uint8) {
return 18;
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function claim() external returns (bool) {
if (isClaim[msg.sender]) revert();
_balances[msg.sender] += 1 ether;
_totalSupply += 1 ether;
emit Claim(msg.sender, 1 ether);
return true;
}
function transfer(address to, uint256 amount) external returns (bool) {
// TODO: please add your implementaiton here
}
function transferFrom(address from, address to, uint256 value) external returns (bool) {
// TODO: please add your implementaiton here
}
function approve(address spender, uint256 amount) external returns (bool) {
// TODO: please add your implementaiton here
}
function allowance(address owner, address spender) public view returns (uint256) {
// TODO: please add your implementaiton here
}
}

View File

@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC721 {
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
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);
}
interface IERC721TokenReceiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external
returns (bytes4);
}
contract NFinTech is IERC721 {
// Note: I have declared all variables you need to complete this challenge
string private _name;
string private _symbol;
uint256 private _tokenId;
mapping(uint256 => address) private _owner;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApproval;
mapping(address => bool) private isClaim;
mapping(address => mapping(address => bool)) _operatorApproval;
error ZeroAddress();
constructor(string memory name_, string memory symbol_) payable {
_name = name_;
_symbol = symbol_;
}
function claim() public {
if (isClaim[msg.sender] == false) {
uint256 id = _tokenId;
_owner[id] = msg.sender;
_balances[msg.sender] += 1;
isClaim[msg.sender] = true;
_tokenId += 1;
}
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function balanceOf(address owner) public view returns (uint256) {
if (owner == address(0)) revert ZeroAddress();
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owner[tokenId];
if (owner == address(0)) revert ZeroAddress();
return owner;
}
function setApprovalForAll(address operator, bool approved) external {
// TODO: please add your implementaiton here
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
// TODO: please add your implementaiton here
}
function approve(address to, uint256 tokenId) external {
// TODO: please add your implementaiton here
}
function getApproved(uint256 tokenId) public view returns (address operator) {
// TODO: please add your implementaiton here
}
function transferFrom(address from, address to, uint256 tokenId) public {
// TODO: please add your implementaiton here
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) public {
// TODO: please add your implementaiton here
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
// TODO: please add your implementaiton here
}
}

View File

@ -0,0 +1,110 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {Test, console} from "forge-std/Test.sol";
import {StudentV1, StudentV2, StudentV3} from "../../src/Classroom/Classroom.sol";
/* Problem 1 Interface & Contract */
interface IStudentV1 {
function register() external returns (uint256);
}
contract ClassroomV1 {
uint256 public code = 1000;
bool public isEnrolled;
function enroll(address student) public {
if (IStudentV1(student).register() >= code && !isEnrolled) {
isEnrolled = true;
code = IStudentV1(student).register();
}
}
}
/* Problem 2 Interface & Contract */
interface IStudentV2 {
function register() external view returns (uint256);
}
contract ClassroomV2 {
uint256 public code = 1000;
bool public isEnrolled;
function enroll(address student) public {
if (IStudentV2(student).register() >= code && !isEnrolled) {
isEnrolled = true;
code = IStudentV2(student).register();
}
}
}
/* Problem 3 Interface & Contract */
interface IStudentV3 {
function register() external view returns (uint256);
}
contract ClassroomV3 {
uint256 public code = 1000;
bool public isEnrolled;
function enroll(address student) public {
if (IStudentV3(student).register() >= code) {
code = IStudentV3(student).register();
}
}
}
/* The testing contract starts here */
contract ClassroomTest is Test {
ClassroomV1 internal class1;
ClassroomV2 internal class2;
ClassroomV3 internal class3;
address internal user;
function setUp() public {
class1 = new ClassroomV1();
class2 = new ClassroomV2();
class3 = new ClassroomV3();
user = makeAddr("user");
vm.deal(user, 1 ether);
}
/* Problem 1 Test */
function test_check_student_v1() public {
vm.startPrank(user);
StudentV1 student = new StudentV1();
class1.enroll(address(student));
vm.stopPrank();
assertEq(class1.code(), 123);
console.log("Get 10 points");
}
/* Problem 2 Test */
function test_check_student_v2() public {
vm.startPrank(user);
StudentV2 student = new StudentV2();
class2.enroll(address(student));
vm.stopPrank();
assertEq(class2.code(), 123);
console.log("Get 10 points");
}
/* Problem 3 Test */
function test_check_student_v3() public {
vm.startPrank(user);
StudentV3 student = new StudentV3();
class3.enroll{gas: 10000 wei}(address(student));
vm.stopPrank();
assertEq(class3.code(), 123);
console.log("Get 10 points");
}
}

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Test, console2} from "forge-std/Test.sol";
import {Attack} from "../../src/Delegation/Delegation.sol";
contract D31eg4t3 {
uint256 var0 = 12345;
uint8 var1 = 32;
string private var2;
address private var3;
uint8 private var4;
address public owner;
mapping(address => bool) public result;
modifier onlyOwner() {
require(msg.sender == owner, "Not a Owner");
_;
}
constructor() {
owner = msg.sender;
}
function proxyCall(bytes calldata data) public returns (address) {
(bool success,) = address(msg.sender).delegatecall(data);
require(success, "Delegate Failed");
return owner;
}
}
contract D31eg4t3Test is Test {
D31eg4t3 internal delegate;
Attack internal attack;
address internal hacker;
function setUp() public {
delegate = new D31eg4t3();
attack = new Attack(address(delegate));
hacker = makeAddr("hacker");
}
function test_check_exploit() public {
vm.prank(hacker, hacker);
attack.exploit();
bool result = delegate.result(hacker);
assertTrue(result);
address owner = delegate.owner();
assertEq(owner, hacker);
console2.log("Get 10 points");
}
}

View File

@ -0,0 +1,182 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {LiaoToken} from "../../src/LiaoToken/LiaoToken.sol";
import {Test, console2} from "forge-std/Test.sol";
/// @title Liao Token Test
/// @author Louis Tsai
/// @notice Do NOT modify this contract or you might get 0 points for the assingment.
contract LiaoTokenTest is Test {
LiaoToken internal token;
address internal Bob;
address internal Alice;
address internal user;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Claim(address indexed user, uint256 indexed amount);
function setUp() public {
token = new LiaoToken("LiaoToken", "Liao");
Bob = makeAddr("Bob");
Alice = makeAddr("Alice");
user = makeAddr("user");
vm.prank(Bob);
vm.expectEmit(true, true, false, false);
emit Claim(Bob, 1 ether);
token.claim();
vm.prank(Alice);
vm.expectEmit(true, true, false, false);
emit Claim(Alice, 1 ether);
token.claim();
}
/* Default Tests */
function test_decimal() public {
uint8 decimals = token.decimals();
assertEq(decimals, 18);
}
function test_name() public {
string memory name = token.name();
assertEq(name, "LiaoToken");
}
function test_symbol() public {
string memory symbol = token.symbol();
assertEq(symbol, "Liao");
}
function test_totalSupply() public {
uint256 totalSupply = token.totalSupply();
assertEq(totalSupply, 2 ether);
}
function testBalanceOf() public {
uint256 balance;
balance = token.balanceOf(Bob);
assertEq(balance, 1 ether);
balance = token.balanceOf(Alice);
assertEq(balance, 1 ether);
}
/* PART 1: Complete transfer function -> 10 points */
function test_transfer() public returns (bool) {
vm.prank(Bob);
vm.expectEmit(true, true, false, false);
emit Transfer(Bob, Alice, 0.5 ether);
bool success = token.transfer(Alice, 0.5 ether);
assertTrue(success);
uint256 balance;
balance = token.balanceOf(Bob);
assertEq(balance, 0.5 ether);
balance = token.balanceOf(Alice);
assertEq(balance, 1.5 ether);
return true;
}
function test_transfer_balance_not_enough() public returns (bool) {
vm.prank(Bob);
vm.expectRevert();
token.transfer(Alice, 1.5 ether);
return true;
}
/* PART 2: Complete approve and allowance function -> 10 points */
function test_approve_function() public returns (bool) {
vm.prank(Bob);
vm.expectEmit(true, true, false, false);
emit Approval(Bob, Alice, 1 ether);
bool success = token.approve(Alice, 1 ether);
assertTrue(success);
uint256 allowance = token.allowance(Bob, Alice);
assertEq(allowance, 1 ether);
return true;
}
/* PART 3: Complete transferFrom function -> 10 points */
function test_transferFrom() public returns (bool) {
bool success;
uint256 balance;
vm.prank(Bob);
vm.expectEmit(true, true, false, false);
emit Approval(Bob, Alice, 1 ether);
success = token.approve(Alice, 1 ether);
assertTrue(success);
vm.prank(Alice);
vm.expectEmit(true, true, false, false);
emit Transfer(Bob, user, 1 ether);
success = token.transferFrom(Bob, user, 1 ether);
assertTrue(success);
balance = token.balanceOf(Bob);
assertEq(balance, 0);
balance = token.balanceOf(user);
assertEq(balance, 1 ether);
return true;
}
function test_transferFrom_allowance_not_enough() public returns (bool) {
bool success;
vm.prank(Bob);
vm.expectEmit(true, true, false, false);
emit Approval(Bob, Alice, 1 ether);
success = token.approve(Alice, 0.5 ether);
assertTrue(success);
vm.prank(Alice);
vm.expectRevert();
token.transferFrom(Alice, user, 1 ether);
return true;
}
function test_transferFrom_balance_not_enough() public returns (bool) {
bool success;
vm.prank(Bob);
vm.expectEmit(true, true, false, false);
emit Approval(Bob, Alice, 1 ether);
success = token.approve(Alice, 0.5 ether);
assertTrue(success);
vm.prank(Alice);
vm.expectRevert();
token.transferFrom(Alice, user, 2 ether);
return true;
}
/* We use the following parts to calculate your score */
function test_check_transfer_points() public {
bool success = test_transfer() && test_transfer_balance_not_enough();
if (success) console2.log("Get 10 points");
}
function test_check_approve_points() public {
bool success = test_approve_function();
if (success) console2.log("Get 10 points");
}
function test_check_transferFrom_points() public {
bool success =
test_transferFrom() && test_transferFrom_allowance_not_enough() && test_transferFrom_balance_not_enough();
if (success) console2.log("Get 10 points");
}
}

View File

@ -0,0 +1,368 @@
// 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)"));
}
}