实现效果

  1. 房主发送拍卖房屋信息,并且设置拍卖过期时间

2. 其它非房主可以登录进行拍卖,选择自己的出价


参考

https://dev.to/yongchanghe/build-a-simple-dapp-using-truffle-ganache-ethersjs-and-react1-52bl

工具集合

  1. 安装node,版本是v16.16.0
  2. npm install -g truffle, truffle可以让我们在本地开发合约,同时部署合约到链上。
  3. 安装ganache, ganache相当于是把以太网部署在本地,我们可以把合约发布到本地的以太网上。
  4. metamask,钱包可以添加账户,查看我们的账户余额,

初始化项目

  1. mkdir web3-ticket && truffle init

  1. 项目目录如下

  1. 修改truffle.config.js的development

4. 打开Ganache,新建workspace,选择项目的trffle.config.js


然后我们就可以看到很多账号了,而且里边还有余额


这样我们就可以通过truffle,连接到链上,同时部署合约到这里了

  1. 在web3-ticket/contracts下新建拍卖合约,Auction.sol,代码比较简单,关键地方都已经添加注释。
// SPDX-License-Identifier: MITpragma solidity ^0.8.17;

contract Auction { // Properties // 定义owner地址 address private owner; uint256 public startTime; uint256 public endTime; // 映射 mapping(address => uint256) public bids;

// 结构体 struct House {   string houseType;   string houseColor;   string houseLocation; }

// 最高出价,竞拍,最高价 struct HighestBid {   uint256 bidAmount;   address bidder; }

 House public newHouse; HighestBid public highestBid;

 // Insert modifiers here // Modifiers // 竞拍已经结束 modifier isOngoing() {   require(block.timestamp < endTime, 'This auction is closed.');   _; } // 竞拍还在进行 modifier notOngoing() {   require(block.timestamp >= endTime, 'This auction is still open.');   _; } // 是不是作者,如果不是没有权限 modifier isOwner() {   require(msg.sender == owner, 'Only owner can perform task.');   _; } // 不是作者 modifier notOwner() {   require(msg.sender != owner, 'Owner is not allowed to bid.');   _; } // Insert events here // Events,允许前端调用事件 event LogBid(address indexed _highestBidder, uint256 _highestBid); event LogWithdrawal(address indexed _withdrawer, uint256 amount); // Insert constructor and function here // Assign values to some properties during deployment constructor () {   owner = msg.sender;   startTime = block.timestamp;   endTime = block.timestamp + 12 hours;   newHouse.houseColor = '#FFFFFF';   newHouse.houseLocation = 'Sask, SK';   newHouse.houseType = 'Townhouse'; }

// makeBid 开始竞价,房子必须是在拍卖中,并且不能房主自己出价 function makeBid() public payable isOngoing() notOwner() returns (bool) {   uint256 bidAmount = bids[msg.sender] + msg.value;   // 当前出价要高于前面的出价,不然报错   require(bidAmount > highestBid.bidAmount, 'Bid error: Make a higher Bid.');

   highestBid.bidder = msg.sender;   highestBid.bidAmount = bidAmount;   bids[msg.sender] = bidAmount;   emit LogBid(msg.sender, bidAmount);   return true; }

// 付款 function withdraw() public notOngoing() isOwner() returns (bool) {   uint256 amount = highestBid.bidAmount;   bids[highestBid.bidder] = 0;   highestBid.bidder = address(0);   highestBid.bidAmount = 0;  // 向房主付款   (bool success, ) = payable(owner).call{ value: amount }("");   require(success, 'Withdrawal failed.');   emit LogWithdrawal(msg.sender, amount);   return true; } // 获取最高出价 function fetchHighestBid() public view returns (HighestBid memory) {   HighestBid memory _highestBid = highestBid;   return _highestBid; } // 获得当前房主 function getOwner() public view returns (address) {   return owner; }}
  1. 执行compile
truffle compile

7. 添加部署文件,在migrations下面添加2_initial_migrations.js

const Auction = artifacts.require("Auction");

module.exports = function (deployer) {  deployer.deploy(Auction);};
  1. 在test下新建测试文件Auctioin.test.js
const Auction = artifacts.require("Auction");

contract("Auction", async (accounts) => {  let auction;  const ownerAccount = accounts[0];  const userAccountOne = accounts[1];  const userAccountTwo = accounts[2];  const amount = 5000000000000000000; // 5 ETH  const smallAmount = 3000000000000000000; // 3 ETH

  beforeEach(async () => {    auction = await Auction.new({ from: ownerAccount });  });

  it("should make bid.", async () => {    await auction.makeBid({ value: amount, from: userAccountOne });    const bidAmount = await auction.bids(userAccountOne);    assert.equal(bidAmount, amount);  });

  it("should reject owner's bid.", async () => {    try {      await auction.makeBid({ value: amount, from: ownerAccount });    } catch (e) {      assert.include(e.message, "Owner is not allowed to bid.");    }  });

  it("should require higher bid amount.", async () => {    try {      await auction.makeBid({ value: amount, from: userAccountOne });      await auction.makeBid({ value: smallAmount, from: userAccountTwo });    } catch (e) {      assert.include(e.message, "Bid error: Make a higher Bid.");    }  });

  it("should fetch highest bid.", async () => {    await auction.makeBid({ value: amount, from: userAccountOne });    const highestBid = await auction.fetchHighestBid();    assert.equal(highestBid.bidAmount, amount);    assert.equal(highestBid.bidder, userAccountOne);  });

  it("should fetch owner.", async () => {    const owner = await auction.getOwner();    assert.equal(owner, ownerAccount);  });});

然后执行

truffle developtest

如果出现下图,说明测试成功


完成后目录文件如下


  1. 使用creact-react-app,在web3-ticket目录下

安装依赖

cd clientnpm install ethers @ethersproject/units
  1. 修改truuffle.config.js的配置,配置合约的输出目录

  1. 发布合约到本地的链上,也就是Ganache模式的链上,因为前面已经启动过了,这里直接发布就可以。

出现下图,说明已经成功部署。


可以看到,部署成功,因为每次需要消耗ETH,所以这里从100ETH到99.98 ETH


同时可以发现在前端的目录client下,增加了contracts目录,如下图


  1. 修改client/src/app.js
import "./App.css";import { useEffect, useState } from "react";import { ethers } from "ethers";import { parseEther, formatEther } from "@ethersproject/units";import Auction from "./contracts/Auction.json";// 这里的地址是前面合约部署成功之后的合约地址const AuctionContractAddress = "0xD50acf2Aaa9183b2aC00cd4D27bC3145c919013d";const emptyAddress = "0x0000000000000000000000000000000000000000";

function App() {  // Use hooks to manage component state  const [account, setAccount] = useState("");  const [amount, setAmount] = useState(0);  const [myBid, setMyBid] = useState(0);  const [isOwner, setIsOwner] = useState(false);  const [highestBid, setHighestBid] = useState(0);  const [highestBidder, setHighestBidder] = useState("");

  // Sets up a new Ethereum provider and returns an interface for interacting with the smart contract  // 使用ethers获取合约  async function initializeProvider() {    const provider = new ethers.providers.Web3Provider(window.ethereum);    const signer = provider.getSigner();    return new ethers.Contract(AuctionContractAddress, Auction.abi, signer);  }

  // Displays a prompt for the user to select which accounts to connect  // 弹出钱包,让我们选择账户  async function requestAccount() {    const account = await window.ethereum.request({      method: "eth_requestAccounts",    });    setAccount(account[0]);  }  // 获取最高出价  async function fetchHighestBid() {    if (typeof window.ethereum !== "undefined") {      const contract = await initializeProvider();      try {        const highestBid = await contract.fetchHighestBid();        const { bidAmount, bidder } = highestBid;

        // Convert bidAmount from Wei to Ether and round value to 4 decimal places        setHighestBid(          parseFloat(formatEther(bidAmount.toString())).toPrecision(4)        );        setHighestBidder(bidder.toLowerCase());      } catch (e) {        console.log("error fetching highest bid: ", e);      }    }  }

  async function fetchMyBid() {    if (typeof window.ethereum !== "undefined") {      const contract = await initializeProvider();      try {        const myBid = await contract.bids(account);        setMyBid(parseFloat(formatEther(myBid.toString())).toPrecision(4));      } catch (e) {        console.log("error fetching my bid: ", e);      }    }  }

  async function fetchOwner() {    if (typeof window.ethereum !== "undefined") {      const contract = await initializeProvider();      try {        const owner = await contract.getOwner();        setIsOwner(owner.toLowerCase() === account);      } catch (e) {        console.log("error fetching owner: ", e);      }    }  }  // 提价出价  async function submitBid(event) {    event.preventDefault();    if (typeof window.ethereum !== "undefined") {      const contract = await initializeProvider();      try {        // User inputs amount in terms of Ether, convert to Wei before sending to the contract.        const wei = parseEther(amount);        await contract.makeBid({ value: wei });        // Wait for the smart contract to emit the LogBid event then update component state        contract.on("LogBid", (_, __) => {          fetchMyBid();          fetchHighestBid();        });      } catch (e) {        console.log("error making bid: ", e);      }    }  }  // 房屋主人获取转账  async function withdraw() {    if (typeof window.ethereum !== "undefined") {      const contract = await initializeProvider();      // Wait for the smart contract to emit the LogWithdrawal event and update component state      contract.on("LogWithdrawal", (_) => {        fetchMyBid();        fetchHighestBid();      });      try {        await contract.withdraw();      } catch (e) {        console.log("error withdrawing fund: ", e);      }    }  }

  useEffect(() => {    requestAccount();  }, []);

  useEffect(() => {    if (account) {      fetchOwner();      fetchMyBid();      fetchHighestBid();    }  }, [account]);

  return (    <div      style={{        textAlign: "center",        width: "50%",        margin: "0 auto",        marginTop: "100px",      }}    >      {isOwner ? (        <button type="button" onClick={withdraw}>          Withdraw        </button>      ) : (        ""      )}      <div        style={{          textAlign: "center",          marginTop: "20px",          paddingBottom: "10px",          border: "1px solid black",        }}      >        <p>连接的账户是: {account}</p>        <p>我的出价: {myBid}</p>        <p>拍卖出价最高是: {highestBid}</p>        <p>          拍卖出价最高账户是:{" "}          {highestBidder === emptyAddress            ? "null"            : highestBidder === account            ? "Me"            : highestBidder}        </p>        {!isOwner ? (          <form onSubmit={submitBid}>            <input              value={amount}              onChange={(event) => setAmount(event.target.value)}              name="Bid Amount"              type="number"              placeholder="Enter Bid Amount"            />            <button type="submit">提交</button>          </form>        ) : (          ""        )}      </div>    </div>  );}

export default App;
  1. chrome安装metamask

安装过程比较简单,最主要的是要把钱包的助记词记好

从0开发一个Dapp相关推荐

  1. 技术员如何开发一个DAPP区块链应用(以宠物商店为例)

    1. 文章摘要 [本文目标] 通过逐步的指导和截图举证,一步步带领一个技术新手完成一个宠物商店DAPP应用的开发和部署. [环境前置条件] 参考<第一课 如何在WINDOWS环境下搭建以太坊开发 ...

  2. 第七课 技术小白如何开发一个DAPP区块链应用(以宠物商店为例)

    1. 文章摘要 [本文目标] 通过逐步的指导和截图举证,一步步带领一个技术小白完成一个宠物商店DAPP应用的开发和部署. [环境前置条件] 参考<第一课 如何在WINDOWS环境下搭建以太坊开发 ...

  3. 如何从0开发一个Atom组件

    最近用Atom写博客比较多,然后发现一个很严重的问题.. 没有一个我想要的上传图片的方式,比如某乎上边就可以直接copy/paste文件,然后进行上传. 然而在Atom上没有找到类似的插件,最接近的一 ...

  4. Yii 2.0开发一个仿京东商城平台(源码+数据库+模板)

    <仿京东购物商城>是一个电商网站的实战课程,主要讲解开发商城的必备要点,帮助你系统掌握Web商城类功能和开发流程.其中页面包括:首页.列表页.商品详情页.购物车.订单.登录.注册.支付(支 ...

  5. vc6开发一个抓包软件_开发一个软件多少钱?传统app开发与0代码app制作方法对比...

    开发一个软件多少钱?app开发难吗?app制作需要哪些流程? app开发很难:按照传统的开发方式需要最少5名以上的技术人员,团队配合花费3个月左右的时间才能搞定,成本20万以上. app开发也很简单: ...

  6. 稳扎稳打Silverlight(18) - 2.0视频之详解MediaElement, 开发一个简易版的全功能播放器...

    [索引页] [×××] 稳扎稳打Silverlight(18) - 2.0视频之详解MediaElement, 开发一个简易版的全功能播放器 作者:webabcd 介绍 Silverlight 2.0 ...

  7. 从 0 到 1 开发一个聊天通讯 服务 复盘总结

    前言 在上个月初,接到一个需求,要开发一个 聊天通讯 模块 并且 集成到 项目中的多个 入口,实现业务数据的记录追踪. 接到需求后,还挺开心,这是我第一次 搞 通讯 类的需求,之前一直是 B 端 的业 ...

  8. 可视化入门:从 0 到 1 开发一个图表库

    作者介绍 万木:蚂蚁体验技术部前端工程师,AntV G2 栈的核心维护者,多次在 IEEE Vast Challenge,ChinaVis Challenge 等数据分析挑战赛中获得优异成绩.喜欢写代 ...

  9. java学习笔记(二十八)——开发一个小项目(VMeeting3.0)

    上篇文章按照较规范的产品需求文档梳理了项目的逻辑,感觉开发起来明晰了很多:挂上一篇文章java学习笔记(二十七)--开发一个小项目(VMeeting2.0)_Biangbangbing的博客-CSDN ...

最新文章

  1. PC微信逆向:分析发送xml名片call
  2. C++和C语言的关系
  3. linux 下设置定时任务
  4. C/C++中善用大括号
  5. zabbix yum安装
  6. 【数据结构】30、hashmap=》hash 计算方式
  7. Linux查看文件内容的几种方式
  8. 操作系统—覆盖与交换
  9. 【Vue源码】Vue中DOM的异步更新策略以及nextTick机制
  10. sprintf函数的用法
  11. 第三代oid铺码软件_点读笔的原理
  12. 虚拟机共享服务器设置,虚拟机共享网络设置教程
  13. Rest-assured框架详解
  14. 怎么安装win10系统?Win10系统安装教程
  15. 计算机核心论文如何审稿,计算机核心期刊排名及投稿经验(范文).doc
  16. java_获得用户显示器大小
  17. 乐视X520(乐2 全网通)一键刷机教程
  18. 2022年“研究生科研素养提升”系列公益讲座在线测评题目
  19. pytorch程序调通
  20. 数据库备份:Xtrabackup实现完全备份及增量备份

热门文章

  1. Docker安装Jenkins
  2. win7计算机cmd查看设备编码,查看windows操作系统的默认编码(字符集)
  3. 美团数据指标体系搭建实战
  4. 微信小程序存储(数据缓存,存储和取值)
  5. java计算机毕业设计信息统计系统源程序+mysql+系统+lw文档+远程调试
  6. 12月17日第壹简报,星期六,农历十一月廿四
  7. 基础概念之电路板的概念
  8. ODOO15委外加工(外协)业财一体凭证生成方案
  9. xml布局html 实例,布局xml文件和模板phtml的对应关系
  10. SpringBoot访问本地图片