From f9c2a1a9666df83e37dc88d87a592ef0f8300ce3 Mon Sep 17 00:00:00 2001
From: macho <541842961@qq.com>
Date: Sat, 26 Oct 2024 15:52:23 +0800
Subject: [PATCH] feat:task3
---
members/feligo887/task1/.gitignore | 24 ------
members/feligo887/task1/README.md | 50 -----------
members/feligo887/task1/eslint.config.js | 28 -------
members/feligo887/task1/index.html | 13 ---
members/feligo887/task1/package.json | 34 --------
members/feligo887/task1/public/vite.svg | 1 -
members/feligo887/task1/src/App.tsx | 18 ----
members/feligo887/task1/src/assets/react.svg | 1 -
.../task1/src/components/AddToDo/index.tsx | 22 -----
.../task1/src/components/Header/index.tsx | 9 --
.../task1/src/components/ToDoList/index.tsx | 40 ---------
.../src/components/ToDoList/style.module.css | 23 ------
members/feligo887/task1/src/index.css | 68 ---------------
members/feligo887/task1/src/main.tsx | 10 ---
members/feligo887/task1/src/store/index.ts | 8 --
.../task1/src/store/modules/index.ts | 1 -
.../task1/src/store/modules/toDoList.ts | 35 --------
members/feligo887/task1/src/style/reset.css | 48 -----------
members/feligo887/task1/src/vite-env.d.ts | 1 -
members/feligo887/task1/tsconfig.app.json | 24 ------
members/feligo887/task1/tsconfig.json | 7 --
members/feligo887/task1/tsconfig.node.json | 22 -----
members/feligo887/task1/vite.config.ts | 10 ---
members/feligo887/task3/CustomERC20.sol | 15 ++++
members/feligo887/task3/CustomNFT.sol | 20 +++++
members/feligo887/task3/NFTMarketplace.sol | 82 +++++++++++++++++++
26 files changed, 117 insertions(+), 497 deletions(-)
delete mode 100644 members/feligo887/task1/.gitignore
delete mode 100644 members/feligo887/task1/README.md
delete mode 100644 members/feligo887/task1/eslint.config.js
delete mode 100644 members/feligo887/task1/index.html
delete mode 100644 members/feligo887/task1/package.json
delete mode 100644 members/feligo887/task1/public/vite.svg
delete mode 100644 members/feligo887/task1/src/App.tsx
delete mode 100644 members/feligo887/task1/src/assets/react.svg
delete mode 100644 members/feligo887/task1/src/components/AddToDo/index.tsx
delete mode 100644 members/feligo887/task1/src/components/Header/index.tsx
delete mode 100644 members/feligo887/task1/src/components/ToDoList/index.tsx
delete mode 100644 members/feligo887/task1/src/components/ToDoList/style.module.css
delete mode 100644 members/feligo887/task1/src/index.css
delete mode 100644 members/feligo887/task1/src/main.tsx
delete mode 100644 members/feligo887/task1/src/store/index.ts
delete mode 100644 members/feligo887/task1/src/store/modules/index.ts
delete mode 100644 members/feligo887/task1/src/store/modules/toDoList.ts
delete mode 100644 members/feligo887/task1/src/style/reset.css
delete mode 100644 members/feligo887/task1/src/vite-env.d.ts
delete mode 100644 members/feligo887/task1/tsconfig.app.json
delete mode 100644 members/feligo887/task1/tsconfig.json
delete mode 100644 members/feligo887/task1/tsconfig.node.json
delete mode 100644 members/feligo887/task1/vite.config.ts
create mode 100644 members/feligo887/task3/CustomERC20.sol
create mode 100644 members/feligo887/task3/CustomNFT.sol
create mode 100644 members/feligo887/task3/NFTMarketplace.sol
diff --git a/members/feligo887/task1/.gitignore b/members/feligo887/task1/.gitignore
deleted file mode 100644
index a547bf36d..000000000
--- a/members/feligo887/task1/.gitignore
+++ /dev/null
@@ -1,24 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
diff --git a/members/feligo887/task1/README.md b/members/feligo887/task1/README.md
deleted file mode 100644
index 74872fd4a..000000000
--- a/members/feligo887/task1/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# React + TypeScript + Vite
-
-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
-
-Currently, two official plugins are available:
-
-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
-
-## Expanding the ESLint configuration
-
-If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
-
-- Configure the top-level `parserOptions` property like this:
-
-```js
-export default tseslint.config({
- languageOptions: {
- // other options...
- parserOptions: {
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
- tsconfigRootDir: import.meta.dirname,
- },
- },
-})
-```
-
-- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
-- Optionally add `...tseslint.configs.stylisticTypeChecked`
-- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
-
-```js
-// eslint.config.js
-import react from 'eslint-plugin-react'
-
-export default tseslint.config({
- // Set the react version
- settings: { react: { version: '18.3' } },
- plugins: {
- // Add the react plugin
- react,
- },
- rules: {
- // other rules...
- // Enable its recommended rules
- ...react.configs.recommended.rules,
- ...react.configs['jsx-runtime'].rules,
- },
-})
-```
diff --git a/members/feligo887/task1/eslint.config.js b/members/feligo887/task1/eslint.config.js
deleted file mode 100644
index 092408a9f..000000000
--- a/members/feligo887/task1/eslint.config.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import js from '@eslint/js'
-import globals from 'globals'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
-import tseslint from 'typescript-eslint'
-
-export default tseslint.config(
- { ignores: ['dist'] },
- {
- extends: [js.configs.recommended, ...tseslint.configs.recommended],
- files: ['**/*.{ts,tsx}'],
- languageOptions: {
- ecmaVersion: 2020,
- globals: globals.browser,
- },
- plugins: {
- 'react-hooks': reactHooks,
- 'react-refresh': reactRefresh,
- },
- rules: {
- ...reactHooks.configs.recommended.rules,
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
- ],
- },
- },
-)
diff --git a/members/feligo887/task1/index.html b/members/feligo887/task1/index.html
deleted file mode 100644
index e4b78eae1..000000000
--- a/members/feligo887/task1/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- Vite + React + TS
-
-
-
-
-
-
diff --git a/members/feligo887/task1/package.json b/members/feligo887/task1/package.json
deleted file mode 100644
index 12345696f..000000000
--- a/members/feligo887/task1/package.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "name": "vite-project",
- "private": true,
- "version": "0.0.0",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "tsc -b && vite build",
- "lint": "eslint .",
- "preview": "vite preview"
- },
- "dependencies": {
- "@emotion/react": "^11.13.3",
- "@emotion/styled": "^11.13.0",
- "@mui/icons-material": "^6.1.0",
- "@mui/material": "^6.1.0",
- "react": "^18.3.1",
- "react-dom": "^18.3.1",
- "valtio": "^2.0.0"
- },
- "devDependencies": {
- "@eslint/js": "^9.9.0",
- "@types/react": "^18.3.3",
- "@types/react-dom": "^18.3.0",
- "@vitejs/plugin-react": "^4.3.1",
- "eslint": "^9.9.0",
- "eslint-plugin-react-hooks": "^5.1.0-rc.0",
- "eslint-plugin-react-refresh": "^0.4.9",
- "globals": "^15.9.0",
- "typescript": "^5.5.3",
- "typescript-eslint": "^8.0.1",
- "vite": "^5.4.1"
- }
-}
diff --git a/members/feligo887/task1/public/vite.svg b/members/feligo887/task1/public/vite.svg
deleted file mode 100644
index e7b8dfb1b..000000000
--- a/members/feligo887/task1/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/members/feligo887/task1/src/App.tsx b/members/feligo887/task1/src/App.tsx
deleted file mode 100644
index 757ea107a..000000000
--- a/members/feligo887/task1/src/App.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import Header from './components/Header'
-import AddToDo from './components/AddToDo'
-import ToDoList from './components/ToDoList'
-
-function App() {
-
- return (
-
- )
-}
-
-export default App
diff --git a/members/feligo887/task1/src/assets/react.svg b/members/feligo887/task1/src/assets/react.svg
deleted file mode 100644
index 6c87de9bb..000000000
--- a/members/feligo887/task1/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/members/feligo887/task1/src/components/AddToDo/index.tsx b/members/feligo887/task1/src/components/AddToDo/index.tsx
deleted file mode 100644
index 2ce883b40..000000000
--- a/members/feligo887/task1/src/components/AddToDo/index.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { useState } from 'react'
-import { Input, Button } from '@mui/material'
-
-import { addToDoList } from '../../store/modules/toDoList.ts'
-
-const AddToDo = () => {
- const [ inpVal, setInpVal ] = useState( '' )
- const handleButton = () => {
- addToDoList( inpVal )
- setInpVal( '' )
- }
-
- return (
-
- ) => setInpVal( event.target.value ) }/>
-
-
- )
-}
-
-export default AddToDo
diff --git a/members/feligo887/task1/src/components/Header/index.tsx b/members/feligo887/task1/src/components/Header/index.tsx
deleted file mode 100644
index b72826511..000000000
--- a/members/feligo887/task1/src/components/Header/index.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-const Header = () => {
- return (
-
- To Do List
-
- )
-}
-
-export default Header
\ No newline at end of file
diff --git a/members/feligo887/task1/src/components/ToDoList/index.tsx b/members/feligo887/task1/src/components/ToDoList/index.tsx
deleted file mode 100644
index 893357cb8..000000000
--- a/members/feligo887/task1/src/components/ToDoList/index.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Checkbox } from '@mui/material'
-import { DeleteForever } from '@mui/icons-material'
-
-// store
-import { useSnapshot } from 'valtio'
-import store from '../../store'
-import { removeToDoList, changeToDoList } from '../../store/modules/toDoList.ts'
-// style
-import style from './style.module.css'
-// type
-import type { ToDoList } from '../../store/modules/toDoList.ts'
-
-const ToDoItem = ( props: { item: ToDoList['list'][0], index: number } ) => {
- const { item, index } = props
- return (
-
- changeToDoList( index ) }/>
- { item.text }
- removeToDoList( index ) }/>
-
- )
-}
-
-const ToDoList = () => {
- const snapshot = useSnapshot( store )
- const { toDoList } = snapshot
- return (
-
- {
- toDoList.list.map( ( item, index ) => {
- return (
-
- )
- } )
- }
-
- )
-}
-
-export default ToDoList
diff --git a/members/feligo887/task1/src/components/ToDoList/style.module.css b/members/feligo887/task1/src/components/ToDoList/style.module.css
deleted file mode 100644
index ce841bffb..000000000
--- a/members/feligo887/task1/src/components/ToDoList/style.module.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.to-do-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-top: 8px;
- padding-right: 8px;
- position: relative;
-}
-.to-do-item::after {
- content: '';
- display: block;
- width: 0;
- height: 1px;
- background-color: #666;
- position: absolute;
- top: 50%;
- left: 0;
- transform: translateY(-50%);
- transition: all 0.3s;
-}
-.to-do-item.check::after {
- width: 100%;
-}
diff --git a/members/feligo887/task1/src/index.css b/members/feligo887/task1/src/index.css
deleted file mode 100644
index 6119ad9a8..000000000
--- a/members/feligo887/task1/src/index.css
+++ /dev/null
@@ -1,68 +0,0 @@
-:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-a:hover {
- color: #535bf2;
-}
-
-body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
-}
-
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
-}
diff --git a/members/feligo887/task1/src/main.tsx b/members/feligo887/task1/src/main.tsx
deleted file mode 100644
index 79346cf8b..000000000
--- a/members/feligo887/task1/src/main.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import App from './App.tsx'
-import './style/reset.css'
-
-createRoot(document.getElementById('root')!).render(
-
-
- ,
-)
diff --git a/members/feligo887/task1/src/store/index.ts b/members/feligo887/task1/src/store/index.ts
deleted file mode 100644
index 6a4d5efe5..000000000
--- a/members/feligo887/task1/src/store/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { proxy } from 'valtio'
-import { toDoList } from './modules/toDoList.ts'
-
-const store = proxy({
- toDoList
-})
-
-export default store
diff --git a/members/feligo887/task1/src/store/modules/index.ts b/members/feligo887/task1/src/store/modules/index.ts
deleted file mode 100644
index 1c9ec27f6..000000000
--- a/members/feligo887/task1/src/store/modules/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { toDoList as default } from './toDoList'
diff --git a/members/feligo887/task1/src/store/modules/toDoList.ts b/members/feligo887/task1/src/store/modules/toDoList.ts
deleted file mode 100644
index e50299091..000000000
--- a/members/feligo887/task1/src/store/modules/toDoList.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { proxy, subscribe } from 'valtio'
-
-export interface ToDoList {
- list: Array<{ isChecked: boolean, text: string }>
-}
-
-const loadState = () => {
- const toDoList = localStorage.getItem( 'toDoList' )
- return toDoList ? JSON.parse( toDoList ) : { list: [] }
-}
-
-// store
-export const toDoList = proxy( loadState() )
-
-// 增加数据
-export const addToDoList = ( text: string ) => {
- toDoList.list.push( {
- isChecked: false,
- text,
- } )
-}
-// 修改数据选中状态数据
-export const changeToDoList = ( index: number ) => {
- const record = toDoList.list[ index ]
- record.isChecked = !record.isChecked
- toDoList.list[ index ] = record
-}
-// 删除数据
-export const removeToDoList = ( index: number ) => {
- toDoList.list.splice( index, 1 )
-}
-
-subscribe( toDoList, () => {
- localStorage.setItem( 'toDoList', JSON.stringify( toDoList ) )
-} )
diff --git a/members/feligo887/task1/src/style/reset.css b/members/feligo887/task1/src/style/reset.css
deleted file mode 100644
index 59a52051c..000000000
--- a/members/feligo887/task1/src/style/reset.css
+++ /dev/null
@@ -1,48 +0,0 @@
-/* http://meyerweb.com/eric/tools/css/reset/
- v2.0 | 20110126
- License: none (public domain)
-*/
-
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font-size: 100%;
- font: inherit;
- vertical-align: baseline;
-}
-/* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
- display: block;
-}
-body {
- line-height: 1;
-}
-ol, ul {
- list-style: none;
-}
-blockquote, q {
- quotes: none;
-}
-blockquote:before, blockquote:after,
-q:before, q:after {
- content: '';
- content: none;
-}
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
diff --git a/members/feligo887/task1/src/vite-env.d.ts b/members/feligo887/task1/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe2a..000000000
--- a/members/feligo887/task1/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/members/feligo887/task1/tsconfig.app.json b/members/feligo887/task1/tsconfig.app.json
deleted file mode 100644
index f0a235055..000000000
--- a/members/feligo887/task1/tsconfig.app.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "isolatedModules": true,
- "moduleDetection": "force",
- "noEmit": true,
- "jsx": "react-jsx",
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["src"]
-}
diff --git a/members/feligo887/task1/tsconfig.json b/members/feligo887/task1/tsconfig.json
deleted file mode 100644
index 1ffef600d..000000000
--- a/members/feligo887/task1/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "files": [],
- "references": [
- { "path": "./tsconfig.app.json" },
- { "path": "./tsconfig.node.json" }
- ]
-}
diff --git a/members/feligo887/task1/tsconfig.node.json b/members/feligo887/task1/tsconfig.node.json
deleted file mode 100644
index 0d3d71446..000000000
--- a/members/feligo887/task1/tsconfig.node.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2022",
- "lib": ["ES2023"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "isolatedModules": true,
- "moduleDetection": "force",
- "noEmit": true,
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/members/feligo887/task1/vite.config.ts b/members/feligo887/task1/vite.config.ts
deleted file mode 100644
index 8394b47c2..000000000
--- a/members/feligo887/task1/vite.config.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
-
-// https://vitejs.dev/config/
-export default defineConfig({
- plugins: [react()],
- server: {
- port: 1753
- }
-})
diff --git a/members/feligo887/task3/CustomERC20.sol b/members/feligo887/task3/CustomERC20.sol
new file mode 100644
index 000000000..a5681daca
--- /dev/null
+++ b/members/feligo887/task3/CustomERC20.sol
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+
+contract CustomERC20 is ERC20 {
+ constructor() ERC20("CustomToken", "CTK") {
+ _mint(msg.sender, 1000000 * 10 ** decimals()); // 初始铸造 100 万代币
+ }
+ // 铸造更多代币(仅供合约所有者使用)
+ function mint(address to,uint256 amount) external {
+ _mint(to,amount);
+ }
+
+}
\ No newline at end of file
diff --git a/members/feligo887/task3/CustomNFT.sol b/members/feligo887/task3/CustomNFT.sol
new file mode 100644
index 000000000..c3576b208
--- /dev/null
+++ b/members/feligo887/task3/CustomNFT.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
+
+contract CustomNFT is ERC721URIStorage {
+ uint256 public tokenCounter;
+ constructor() ERC721("CustomNFT", "CNFT") {
+ tokenCounter = 0;
+ }
+
+ function createNFT(string memory tokenURI) public payable returns (uint256) {
+ uint256 newTokenId = tokenCounter;
+ _safeMint(msg.sender,newTokenId);
+ _setTokenURI(newTokenId,tokenURI);
+ tokenCounter++;
+ return newTokenId;
+ }
+}
\ No newline at end of file
diff --git a/members/feligo887/task3/NFTMarketplace.sol b/members/feligo887/task3/NFTMarketplace.sol
new file mode 100644
index 000000000..428334711
--- /dev/null
+++ b/members/feligo887/task3/NFTMarketplace.sol
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; // 导入ERC721接口,用于NFT交互
+import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // 导入ERC20接口,用于自定义代币交互
+
+// 创建一个简单的NFT市场合约
+contract NFTMarketplace {
+
+ // 定义一个结构体,用于保存每个上架的NFT信息
+ struct Listing {
+ address seller; // 卖家地址
+ address nftContract; // NFT合约地址
+ uint256 tokenId; // NFT的Token ID
+ uint256 price; // NFT价格,使用自定义的ERC20代币
+ }
+
+ // 保存所有上架的NFT,每个上架项通过一个自增的ID来标识
+ mapping(uint256 => Listing) public listings;
+ uint256 public listingCounter; // 记录当前有多少个NFT上架,作为列表的ID
+
+ // 自定义ERC20代币,用于购买NFT
+ IERC20 public erc20Token;
+
+ // 定义两个事件:一个用于NFT上架,另一个用于NFT被购买
+ event NFTListed(address indexed seller, address indexed nftContract, uint256 indexed tokenId, uint256 price);
+ event NFTBought(address indexed buyer, address indexed nftContract, uint256 indexed tokenId, uint256 price);
+
+ // 构造函数:初始化时传入自定义的ERC20代币合约地址
+ constructor(address _erc20Token) {
+ erc20Token = IERC20(_erc20Token); // 初始化自定义ERC20代币
+ listingCounter = 0; // 初始化上架ID计数器,从0开始
+ }
+
+ // 上架NFT函数,允许用户将自己的NFT上架到市场
+ // 参数:_nftContract 是NFT合约地址,_tokenId 是NFT的Token ID,_price 是上架的价格
+ function listNFT(address _nftContract, uint256 _tokenId, uint256 _price) external {
+ IERC721 nft = IERC721(_nftContract); // 通过NFT合约地址构造ERC721接口实例
+
+ // 检查调用者是否是该NFT的所有者
+ require(nft.ownerOf(_tokenId) == msg.sender, "You are not the owner of this NFT");
+
+ // 检查市场合约是否被授权操作该NFT
+ require(nft.isApprovedForAll(msg.sender, address(this)) || nft.getApproved(_tokenId) == address(this), "Marketplace contract is not approved");
+
+ // 将NFT上架信息保存到映射中
+ listings[listingCounter] = Listing({
+ seller: msg.sender, // 卖家为当前调用者
+ nftContract: _nftContract, // NFT的合约地址
+ tokenId: _tokenId, // NFT的Token ID
+ price: _price // 上架价格(使用ERC20代币)
+ });
+
+ // 触发NFT上架事件,方便外部系统监听
+ emit NFTListed(msg.sender, _nftContract, _tokenId, _price);
+
+ // 增加上架ID计数器,为下一个NFT上架分配唯一ID
+ listingCounter++;
+ }
+
+ // 购买NFT函数,允许用户购买市场上架的NFT
+ // 参数:_listingId 是上架的NFT ID
+ function buyNFT(uint256 _listingId) external {
+ // 获取上架的NFT信息
+ Listing memory listing = listings[_listingId];
+
+ // 确保NFT已经上架,价格必须大于0
+ require(listing.price > 0, "This NFT is not listed");
+
+ // 使用自定义的ERC20代币支付,检查是否代币转移成功
+ require(erc20Token.transferFrom(msg.sender, listing.seller, listing.price), "ERC20 token transfer failed");
+
+ // 调用NFT合约,安全地将NFT从卖家转移给买家
+ IERC721(listing.nftContract).safeTransferFrom(listing.seller, msg.sender, listing.tokenId);
+
+ // 触发NFT购买事件
+ emit NFTBought(msg.sender, listing.nftContract, listing.tokenId, listing.price);
+
+ // 删除该NFT的上架信息,防止重复购买
+ delete listings[_listingId];
+ }
+}
\ No newline at end of file