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

task3: Georgiafab #564

Merged
merged 4 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions members/Georgiafab/task3/.env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID="2c01535b38b05886544b0c8c2b544d28"
NEXT_PUBLIC_APP_NAME="nft collection"
NEXT_PUBLIC_EVN="test"
3 changes: 3 additions & 0 deletions members/Georgiafab/task3/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
51 changes: 51 additions & 0 deletions members/Georgiafab/task3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

node_modules
.env

# Hardhat files
/cache
/artifacts

# TypeChain files
/typechain
/typechain-types

# solidity-coverage files
/coverage
/coverage.json
52 changes: 52 additions & 0 deletions members/Georgiafab/task3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## task3 介绍

1. 部署在 Sepolia 测试网上
![bind-wallet](./readme/deploy.png "deploy")
2. 提交全部合约文件(ERC20、ERC721、NFTMarket)
在 contract 文件夹中,其他文件用来测试的,忽略掉
3. 提交上架 NFT、购买 NFT 的交易哈希
提交上架 NFT: 0x754e73598b6b0059510532a5b53dd6068dcd81ac3a7cdf9d2ed5d06b1662562d
购买 NFT: 0xf937159ae007f8de3cd163752d66b33a8ad8897926f8e619c9b436c689d864e0

## 环境搭建:

- Hardhat
- react

## 钱包与合约访问

- wagmi
- metamask
- Ethersjs

---

- 编译合约:
`npx hardhat compile`
- 开启本地网络:
`npx hardhat node`
- 在本地网络上测试合约:
`npx hardhat test --network localhost`
- 在本地网络上部署合约:
`npx hardhat ignition deploy ./ignition/modules/ERC20Token.ts --network localhost`

---

开启前端服务:
`npx run dev`

## 合约

# Sample Hardhat Project

This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract.

Try running some of the following tasks:

```shell
npx hardhat help
npx hardhat test
REPORT_GAS=true npx hardhat test
npx hardhat node
npx hardhat ignition deploy ./ignition/modules/NFTMarket.ts
```
67 changes: 67 additions & 0 deletions members/Georgiafab/task3/contracts/ERC20Token.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

contract ERC20Token {
string public name;
string public symbol;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);

constructor(
string memory _name,
string memory _symbol,
uint256 _totalSupply
) {
name = _name;
symbol = _symbol;
totalSupply = _totalSupply;
balanceOf[msg.sender] = _totalSupply;
}

function transfer(
address _to,
uint256 _value
) external returns (bool success) {
require(balanceOf[msg.sender] >= _value, "Insufficient balance");
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}

function approval(
address _spender,
uint256 _value
) external returns (bool success) {
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}

function transferFrom(
address _from,
address _to,
uint256 _value
) external returns (bool success) {
require(_value <= balanceOf[_from], "Insufficient balance");
require(_value <= allowance[_from][msg.sender], "Allowance exceeded");
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
allowance[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
}
}

// 0x2AF374aA4DE0559be70b254a49F1A8B7edc23689

// localhost: 0x5FbDB2315678afecb367f032d93F642f64180aa3
73 changes: 73 additions & 0 deletions members/Georgiafab/task3/contracts/ERC721Token.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;

contract ERC721Token {
string public name;
string public symbol;
uint256 public totalSupply;

mapping(uint256 => address) public ownerOf;
mapping(address => uint256[]) public ownedTokens;
mapping(uint256 => address) public approved;
mapping(address => mapping(address => bool)) public operatorApprovals;

event Transfer(address indexed from, address indexed to, uint256 tokenId);
event Approval(
address indexed owner,
address indexed approved,
uint256 tokenId
);

constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}

function mint(address _to, uint256 _tokenId) external {
require(ownerOf[_tokenId] == address(0), "Token already exists");
ownerOf[_tokenId] = _to;
ownedTokens[_to].push(_tokenId);
totalSupply++;
emit Transfer(address(0), _to, _tokenId);
}

function approve(address _to, uint256 _tokenId) external {
require(msg.sender == ownerOf[_tokenId], "Not token owner");
approved[_tokenId] = _to;
emit Approval(msg.sender, _to, _tokenId);
}

function transferFrom(
address _from,
address _to,
uint256 _tokenId
) external {
require(ownerOf[_tokenId] == _from, "Not token owner");
require(
msg.sender == _from ||
msg.sender == approved[_tokenId] ||
operatorApprovals[_from][msg.sender],
"Not approved"
);
ownerOf[_tokenId] = _to;
// Update owner's token list
uint256[] storage fromTokens = ownedTokens[_from];
for (uint256 i = 0; i < fromTokens.length; i++) {
if (fromTokens[i] == _tokenId) {
fromTokens[i] = fromTokens[fromTokens.length - 1];
fromTokens.pop();
break;
}
}
ownedTokens[_to].push(_tokenId);

emit Transfer(_from, _to, _tokenId);
}

function setApprovalForAll(address _operator, bool _approved) external {
operatorApprovals[msg.sender][_operator] = _approved;
}
}

// 0x6F817c5d3ccd451fd38B4cB77E78d85FD1F0810d
// localhost: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
113 changes: 113 additions & 0 deletions members/Georgiafab/task3/contracts/NFTMarket.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

import "./ERC20Token.sol";
import "./ERC721Token.sol";

contract NFTMarket {
ERC20Token public tokenContract;
ERC721Token public nftContract;

struct Listing {
address seller; // 上架者地址
address nftContract; // NFT 合约地址
uint256 tokenId; // NFT的Token ID
uint256 price; // 上架价格(使用ERC20代币)
bool active; // 上架状态
}

// 存储所有上架的NFT信息
Listing[] public listings;

// 上架NFT事件
event NFTListed(
uint256 indexed listingId,
address indexed seller,
address indexed nftContract,
uint256 tokenId,
uint256 price
);
// 购买NFT事件
event NFTSold(
uint256 indexed listingId,
address indexed buyer,
address indexed seller,
address nftContract,
uint256 tokenId,
uint256 price
);

// 合约构造函数,初始化ERC20代币合约和ERC721 NFT合约地址
constructor(address _tokenContractAddress, address _nftContractAddress) {
tokenContract = ERC20Token(_tokenContractAddress);
nftContract = ERC721Token(_nftContractAddress);
}
function getTokenContract() external view returns (ERC20Token) {
return tokenContract;
}

function getNftContract() external view returns (ERC721Token) {
return nftContract;
}

// 上架NFT
function listNFT(
address _nftContract,
uint256 _tokenId,
uint256 _price
) external {
// 确保上架者是NFT的所有者
require(nftContract.ownerOf(_tokenId) == msg.sender, "Not token owner");
// 确保上架价格大于零
require(_price > 0, "Price must be greater than zero");

// 将NFT信息添加到上架列表中
listings.push(
Listing(msg.sender, _nftContract, _tokenId, _price, true)
);

// 触发上架NFT事件
emit NFTListed(
listings.length - 1,
msg.sender,
_nftContract,
_tokenId,
_price
);
}

// 购买NFT
function buyNFT(uint256 _listingId) external {
// 获取上架信息
Listing storage listing = listings[_listingId];
// 确保上架状态为激活
require(listing.active, "Listing not active");

// 确保买家拥有足够的ERC20代币用于购买
require(
tokenContract.balanceOf(msg.sender) >= listing.price,
"Insufficient balance"
);

// 从买家转移ERC20代币给卖家
tokenContract.transferFrom(msg.sender, listing.seller, listing.price);
// 从卖家转移NFT给买家
nftContract.transferFrom(listing.seller, msg.sender, listing.tokenId);

// 更新上架状态为非激活
listing.active = false;
// 触发购买NFT事件
emit NFTSold(
_listingId,
msg.sender,
listing.seller,
listing.nftContract,
listing.tokenId,
listing.price
);
}
}

// 0x804D1F8eDcd0dC5F87D25F2B36D0655a7CABf50A
// localhost: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
Loading