IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 区块链 -> 从0开发一个Dapp -> 正文阅读

[区块链]从0开发一个Dapp

实现效果

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

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

alt

参考

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
alt
  1. 项目目录如下
alt
  1. 修改truffle.config.js的development

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

alt

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

alt

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

  1. 在web3-ticket/contracts下新建拍卖合约,Auction.sol,代码比较简单,关键地方都已经添加注释。
//?SPDX-License-Identifier:?MIT
pragma?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

alt 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?develop
test

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

alt

完成后目录文件如下

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

安装依赖

cd?client
npm?install?ethers?@ethersproject/units
  1. 修改truuffle.config.js的配置,配置合约的输出目录
alt
  1. 发布合约到本地的链上,也就是Ganache模式的链上,因为前面已经启动过了,这里直接发布就可以。

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

alt

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

alt

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

alt
  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

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

这里主要是需要把钱包连接到Ganache启动的链上,点击增加Add Network

alt

把这里的地址复制进去

alt

效果如下图

alt

然后选择连接上Ganache

alt

在Ganache中查看账号,通过私钥导入账号,这里我们导入两个账号 alt

效果如下

alt
  1. 启动前端
npm?start

启动服务,会弹出metamask,首先选择Account3,因为Account2是房主自己,Account1是默认添加的。

alt

这里因为是需要输入拍卖假,我们选择10个ETH,查看效果

alt

可以看到这里就弹出metamask,让我们确认我们是否转入10ETH,

alt

我们点击confirm,可以看到我们账号已经少了10ETH

alt

我们再在metamask里边切换会房屋主人的查看页面,选择Account2,看看效果

alt
alt

我们就可以看到最高出价的账户是哪一个。

当我们点击withDraw的时候,发现如下错误 alt

关键的错误是指这里 alt

我们去合约代码Auction.sol里边查看,发现是函数装饰器抛出的错误,因为我们在endTime里边设置的拍卖时间是12个小时,所以这里还在拍卖中,所以房主不能结束拍卖。

?//?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';
?}

?//?竞拍还在进行
?modifier?notOngoing()?{
???require(block.timestamp?>=?endTime,?'This?auction?is?still?open.');
???_;
?}

这里我们可以等待12小时之后,房主再上来点击witdhDraw或者重新编译部署合约, 这里我们选择重新编译部署合约 这里我们把Auction.sol里边的结束时间,设置为5 minutes

?constructor?()?{
???owner?=?msg.sender;
???startTime?=?block.timestamp;
???endTime?=?block.timestamp?+?5?minutes;
???newHouse.houseColor?=?'#FFFFFF';
???newHouse.houseLocation?=?'Sask,?SK';
???newHouse.houseType?=?'Townhouse';
?}

重新测试

alt 重新comile

alt

重新部署

alt

这里我们看到部署的地址发生了改变

alt

我们需要把contract address,复制到前端代码的这里替换 在client/src/app.js

//?这里是前面部署合约的时候的contract?address
//?最新部署地址
const?AuctionContractAddress?=?"0xAf0C3Db2cC621254E5394B538abdbC50bB860b04";
//?以前部署的地址
//?const?AuctionContractAddress?=?"0xe83CfD83BFcb732f207C1Ba94eD4341170667A07";

然后还是先选择Account3 出价竞拍,然后再切换回Account2,查看

alt

等等超过5分钟再看

alt

然后我们去我们账户中查看,可以看到

alt

本文由 mdnice 多平台发布

  区块链 最新文章
盘点具备盈利潜力的几大加密板块,以及潜在
阅读笔记|让区块空间成为商品,打造Web3云
区块链1.0-比特币的数据结构
Team Finance被黑分析|黑客自建Token“瞒天
区块链≠绿色?波卡或成 Web3“生态环保”标
期货从入门到高深之手动交易系列D1课
以太坊基础---区块验证
进入以太坊合并的五个数字
经典同态加密算法Paillier解读 - 原理、实现
IPFS/Filecoin学习知识科普(四)
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:33:53  更:2022-09-21 00:34:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:40:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码