DApp设计与开发 课程笔记(四):NFT交易市场合约

news/2024/7/4 17:00:25/文章来源:https://www.cnblogs.com/ranxi169/p/18279014

笔记对应课程内容为成都信息工程大学区块链产业学院老师梁培利DApp 设计与开发 10-13 课

笔记中提到的名词不做过多解释 不懂就搜!

开发准备:

  • 编辑IDE环境:Vscode+Hardhat
  • 调试:Remix
  • ERC标准智能合约生成巫师:https://docs.openzeppelin.com/contracts/5.x/wizard

动手学

Windows 需要使用 Powershell,不能使用git bash

mkdir nft-market
cd nft-market/
npx hardhat init

npm install @openzeppelin/contracts
npm i @remix-project/remixd
remixd -s C:\Users\Administrator\Desktop\nft-market --remix-ide https://remix.ethereum.org

remix共享本地文件夹教程 - 走走停停走走 - 博客园 (cnblogs.com)

solcjs 和 hardhat在管理solidity编译版本上有什么区别?

solcjshardhat 都是与 Solidity 相关的工具,但在管理 Solidity 编译版本上有一些区别:

  1. solcjs:

    • 独立的 Solidity 编译器solcjs 是 Solidity 的官方编译器的 JavaScript 包装器。它允许你在 JavaScript 或 Node.js 环境中调用 Solidity 编译器,以便编译和部署智能合约。
    • 版本管理solcjs 可以通过 npm(Node.js 的包管理器)来安装不同版本的 Solidity 编译器。你可以选择安装并使用特定的 Solidity 版本来编译你的合约。
  2. Hardhat:

    • 开发框架Hardhat 是一个以太坊智能合约开发的工具套件和框架,它整合了多种功能,包括编译、测试、部署等。
    • 集成 solc:Hardhat 默认集成了 Solidity 编译器(通常使用的是 solc,不是 solcjs)。它通过自身的配置文件来管理 Solidity 版本和编译选项。
    • 版本管理:Hardhat 允许在项目配置中指定 Solidity 的版本,它会根据配置文件来选择合适的 Solidity 编译器版本进行编译。Hardhat 会自动下载和管理所需的 Solidity 版本,使得开发者可以在项目中轻松切换和管理不同的 Solidity 版本。

区别总结

  • solcjs 是一个单独的 JavaScript 包,主要用于直接调用 Solidity 编译器进行合约编译,需要手动安装和管理 Solidity 版本。
  • Hardhat 是一个完整的以太坊智能合约开发框架,集成了 Solidity 编译器,可以通过配置文件轻松管理 Solidity 版本,同时提供了测试、部署等功能,更适合大型和复杂的以太坊项目开发。

复制第三方模板合约库的智能合约代码:https://docs.openzeppelin.com/contracts/5.x/wizard

在你已经理解了NFT原理之后,就不用完全自己构建一个智能合约了,直接复制一个拿来就能用了

然后在remix部署合约

即可运行合约函数

但是交易市场的智能合约是没有拿来就能用的。

附上述代码:

erc20-usdt.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract cUSDT is ERC20 {constructor() ERC20("fake usdt in oktc", "cUSDT") {_mint(msg.sender, 1*10*8*10**18);}
}

erc-721-nft.sol

// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";contract NFTM is ERC721, ERC721Enumerable, Ownable {constructor(address initialOwner)ERC721("NFTM", "NFTM")Ownable(initialOwner){}function _baseURI() internal pure override returns (string memory) {return "https://sample.onefly.top/";}function safeMint(address to, uint256 tokenId) public onlyOwner {_safeMint(to, tokenId);}// The following functions are overrides required by Solidity.function _update(address to, uint256 tokenId, address auth)internaloverride(ERC721, ERC721Enumerable)returns (address){return super._update(to, tokenId, auth);}function _increaseBalance(address account, uint128 value)internaloverride(ERC721, ERC721Enumerable){super._increaseBalance(account, value);}function supportsInterface(bytes4 interfaceId)publicviewoverride(ERC721, ERC721Enumerable)returns (bool){return super.supportsInterface(interfaceId);}
}

老师和我都推荐使用 github copilot来编程,学生免费申请可以看我之前的文章:2023.3申请github copilot x 学生认证以及Jetbrain专业版学生教育免费教程 - 知乎 (zhihu.com)

nft-market.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";contract Market {IERC20 public erc20;IERC721 public erc721;bytes4 private constant Magic_On_Erc721_Received = 0x150b7a02;struct Order {address seller;uint256 tokenId;uint256 price;}mapping(uint256 => Order) public orderOfId; // token id => orderOrder[] public orders;mapping(uint256 => uint256) public idToOrderIndex; // token id => order idevent Deal(address seller, address buyer, uint256 tokenId, uint256 price); //事件是合约与外部世界通信的唯一方式event NewOrder(uint256 tokenId, uint256 price);event PriceChanged(address seller, uint256 tokenId, uint256 previousPrice, uint256 price);event OrderCancled(address seller, uint256 tokenId);constructor(address _erc20, address _erc721) {require(_erc20 != address(0) && _erc721 != address(0), "invalid zero address");erc20 = IERC20(_erc20);erc721 = IERC721(_erc721);}function buy(uint256 _tokenId) external {address seller = orderOfId[_tokenId].seller;address buyer = msg.sender;uint256 price = orderOfId[_tokenId].price;require(erc20.transferFrom(buyer, seller, price), "transfer failed");//ierc20包装的erc20 transferFrom方法erc721.safeTransferFrom(address(this), buyer, _tokenId); //address(this)是合约本身地址//清除订单emit Deal(seller, buyer, _tokenId, price);//emit关键字用于触发事件}   function cancelOrder(uint256 _tokenId) external {address seller = orderOfId[_tokenId].seller;require(msg.sender == seller, "only seller can cancel order");erc721.safeTransferFrom(address(this), seller, _tokenId);uint256 orderId = idToOrderIndex[_tokenId];//清除订单emit OrderCancled(seller, _tokenId);}function changePrice(uint256 _tokenId, uint256 _price) external {address seller = orderOfId[_tokenId].seller;require(msg.sender == seller, "only seller can change price");uint256 previousPrice = orderOfId[_tokenId].price;orderOfId[_tokenId].price = _price;Order storage order = orders[idToOrderIndex[_tokenId]]; //修改链上订单数据order.price = _price;emit PriceChanged(seller, _tokenId, previousPrice, _price);}function OnERC721Received( //ERC721回调函数 难点address operator,  address from,uint256 tokenId,bytes calldata data) external returns (bytes4) {uint256 price = toUint256(data,0);require(price >0, "price must be greater than 0");orders.push(Order(from, tokenId, price));orderOfId[tokenId] = Order(from, tokenId, price);idToOrderIndex[tokenId] = orders.length - 1;emit NewOrder(tokenId, price);return Magic_On_Erc721_Received;}function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {require(_bytes.length >= (_start + 32), "toUint256 out of bounds");uint256 tempUint;assembly {tempUint := mload(add(add(_bytes, 0x20), _start))}return tempUint;}function removeOrder(uint256 _tokenId) internal {uint256 orderId = idToOrderIndex[_tokenId];uint256 lastOrderId = orders.length - 1;if (orderId != lastOrderId) {Order storage lastOrder = orders[lastOrderId];orders[orderId] = lastOrder;idToOrderIndex[lastOrder.tokenId] = orderId;}orders.pop();delete orderOfId[_tokenId];delete idToOrderIndex[_tokenId];}}

测试

在remix提供的手动测试按钮测试基本功能后,我们可以进一步利用hardhat使用js代码进行测试。

const { expect } = require("chai");
const { ethers } = require("hardhat");describe("Market", function () {let usdt, market, myNft, accountA, accountB;beforeEach(async () => {[accountA, accountB] = await ethers.getSigners();const USDT = await ethers.getContractFactory("cUSDT");usdt = await USDT.deploy();const MyNFT = await ethers.getContractFactory("MyNFT");myNft = await MyNFT.deploy(accountA.address);        const Market = await ethers.getContractFactory("Market");market = await Market.deploy(usdt.target, myNft.target);await myNft.safeMint(accountA.address);await myNft.safeMint(accountB.address);await usdt.approve(market.target, 10**18);});it("its erc20 address should be usdt", async() => {expect(await market.erc20()).to.equal(usdt.target);});it("its nft address should be myNft", async() => {expect(await market.erc721()).to.equal(myNft.target);});it("accountB shuold have 2 nfts", async() => {expect(await myNft.balanceOf(accountB.address)).to.equal(2);});it("accountA should have usdt", async() => {expect(await usdt.balanceOf(accountA.address)).to.equal(10**18);});// expect(await myNft['safeTransferFrom(address,address,uint256,bytes)'](accountB.address,market.target,0,price)).to.emit (market,"Neworder");// expect(await myNft['safeTransferFrom(address,address,uint256,bytes)'](accountB.address,market.target,1,price)).to.emit (market,"Neworder");// expect(await myNft.balanceOf(accountB.address)).to.equal(0);// expect(await myNft.balanceOf(market.target)).to.equal(2);// expect(await market.orders(0)).to.equal(true);// expect(await market.orders(1)).to.equal(true);// expect(await market.getorderLength()).to.equal(2);// expect((await market.connect(accountB).getMyNFTs())[0][0]).to.equal(accountB.address);// expect((await market.connect(accountB).getMyNFTs())[0][1]).to.equal(0)// expect((await market.connect(accountB).getMyNFTs())[0][2]).to.equal(price);});    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/736051.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

杂论前端之unipaa_2024-7

创建一个带有 "register-btn" 类的按钮,具有 "getUserInfo" 的开放类型以及一个点击事件处理程序 "register()" --><button class="register-btn" open-type="getUserInfo" bindtap="register"></bu…

算法金 | Transformer,一个神奇的算法模型!!

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」抱个拳,送个礼 在现代自然语言处理(NLP)领域,Transformer 模型的出现带来了革命性的变化。它极大地提升了语言模型的性能和效率,而自注意力机制是其中的核心组件。…

SPI驱动-基于ICM20608六轴MEMS传感器

1 IMX6ULL的SPI控制器简介 SPI是全双工同步串行通讯总线,是一个四线结构的总线协议,其使用比IIC简洁许多,具体关于SPI总线协议的内容可以自行查阅资料。 1.1 SPI控制器介绍 imx6ull中有四个ECSPI控制器,也即四个增强型SPI控制器,也可以当作普通的SPI控制器使用。这里又要和…

基于dspic33ck64mp105的电机控制器开发①

原理图是基于 microchip官方的 MCP1722_Power_Tools 参考设计而来,修改部分如下:https://www.microchip.com/en-us/tools-resources/reference-designs/portable-power-tool-reference-design1,修改了电源模块2,修改了栅极驱动3,增加了蓝牙通信模块4,修改了原版AUX的端口…

7、 Django-路由-router-页面跳转

概念: 在实际开发过程中、一个Django项目会包含很多的app、这时候如果我们只在主路由里进行配置就会显得杂乱无章、 所以通常在每个app中创建各自的urls.py路由模块、然后从根路由出发、将app所属的url请求、全部转发到相应的urls.py 模块 而这个从主路由转发到各个应用的路由…

10、 Django-模板-templates

模板语法 #模板中的变量语法:{{ var }}如果变量不存在、则插入空字符串#方法不能有参数{{ int }}{{ str }}{{ list }}{{ list.0 }}{{ dict }}{{ dict.a }} #dict[a]{{ func }} #传递函数{{ class_obj.func }} #传递类.方法#列表、使用索引、不允许负索引it…

Simple WPF: WPF 透明窗体和鼠标事件穿透

一个自定义WPF窗体的解决方案,借鉴了吕毅老师的WPF制作高性能的透明背景的异形窗口一文,并在此基础上增加了鼠标穿透的功能。可以使得透明窗体的鼠标事件穿透到下层,在下层窗体中响应。一个自定义WPF窗体的解决方案,借鉴了吕毅老师的WPF制作高性能的透明背景的异形窗口一文…

VMware安装Win11环境

准备 Win11的iso镜像 下载链接:https://www.microsoft.com/zh-cn/software-download/windows11/?open_in_browser=true 配置步骤 步骤一——创建虚拟机 1、点击创建新虚拟机2、使用典型模式3、选择镜像位置4、选择虚拟机存放位置5、输入密码,此密码可以随便写6、选择单个文件…

2、爬虫-安装anaconda工具

1、官网:https://www.anaconda.com/download-success2、一直下一步安装即可 3、打开4、输入:jupyter notebook 打开有一个浏览器的界面 5、右上角点击new新建python3(ipykernel)本文来自博客园,作者:little小新,转载请注明原文链接:https://www.cnblogs.com/littlecc/…

50、k8s-DashBoard(k8s的web)-部署

1、下载yaml文件:https://github.com/kubernetes/dashboard/blob/v2.0.0/aio/deploy/recommended.yaml 2、修改yaml文件的service 配置: --------------------------------------------- ---kind: Service apiVersion: v1 metadata:labels:k8s-app: kubernetes-dashboardname…

[JLU] 数据结构与算法上机题解思路分享-第二次上机

这是吉林等通知大学数据结构与算法上机题的题解思路,没有精妙的解法,只是一个记录罢前言 首先,请务必自己尽全力尝试实现题目,直接看成品代码,思维就被拘束了,也很容易被查重。 这里只是思路解析的博客,代码仓库在 JLU_Data_Structures_Record 希望你能在这里找到你想要…

docker 基础学习--尚硅谷教程

1、新建、启动容器docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 2、列出当前所有正在运行的容器docker ps [OPTIONS] 3、退出容器4、重启、退出、删除容器 5、启动守护式容器 docker 常见命令

Bruno安装使用

下载地址直接解压,右键Bruno.exe创建快捷方式创建集合右键集合,New Request指定名称和url指定参数保存并测试

《与光重聚》 —— 属于他的夏日花火

题目是歌曲名称,from 《夏日花火》 (国产galgame ; 视觉小说「夏日花火」原声音乐集 - dizzylab PV视频 因为被平衡树制裁了,所以心血来潮写这篇文章,姑且算是练笔吧(毕竟好久不写鲜花了; 游戏本体发行时间:2022年10月28日,目前无DLC(永远的遗憾; 游戏背景设计是D…

玄机-第一章 应急响应-Linux日志分析

玄机-第一章 应急响应-Linux日志分析 账号root密码linuxrz ssh root@IP 1.有多少IP在爆破主机ssh的root帐号,如果有多个使用","分割 2.ssh爆破成功登陆的IP是多少,如果有多个使用","分割 3.爆破用户名字典是什么?如果有多个使用","分割 4.登陆…

使用 .NET 构建 UI 界面的各种方式

微软搞出了很多构建 UI 程序的框架,如 WinForms WPF WinUI MAUI,他们之间的简单对比可以看如下这篇官方文档 Overview of framework options - Windows apps | Microsoft Learn 本文主要是记录一下在搜索相关问题时,了解到的内容,不一定准确,如果发现错误,请留言补充。 1…

Docker详细安装教程

安装Docker: # 1,, 卸载旧的版本# 2,需要的安装 yum install -y yum-utils# 3, 设置镜像的仓库 https://blog.csdn.net/qq_43168442/article/details/116770163 (访问这个博客网站进行配置)# 更新yum软件包索引 yum makecache fast# 4,安装docker docker-ce 社区 ee企业…

详细讲解 Keil Pack Installer,以及通过 Keil 官网获取 Pack

前言 大家好,我是梁国庆。 收到粉丝留言,说 Keil 安装 Pack 不太明白,可不可以详细演示一下?当然可以有,直接视频+文章全部安排,我就是宠粉。 PS:第一次录视频有些紧张,见谅哈。微信视频号:https://weixin.qq.com/sph/AXbpYwEaw b站:https://www.bilibili.com/video…

webdav协议及我的笔记方案(私有部署)

背景 用markdown用于文章写作,有几年时间了,不是很喜欢折腾,主要就是在电脑上写,用的笔记软件就是typora。由于里面有很多工作相关的,以及个人资料相关的(包含了各种账号、密码啥的),所以不敢往各种云服务上放,还是想着数据由自己来管着。 自己管数据的话,就是数据存…