Web3.0-DApp开发
- 讲师: 千峰- kerwin
- b站地址: https://www.bilibili.com/video/BV1Fd4y1x7jR/?spm_id_from=333.337.search-card.all.click
- 源码地址: https://github.com/hufanglei/web3-DApp.git
001 课程简介
002 初始web3.0
003 认识区块链
004 以太坊介绍
005 DApp简介
006 MetaMask
007 Ganache
008 Web3.js
中文文档: https://learnblockchain.cn/docs/web3.js
Web3.js是一个库,它有很多函数,使用它可以在以太坊生态系统中通过HTTP或IPC与本地或者以太坊远程节点交互,如查看链上信息等
各种高级语言编写的程序可以使用web3 interface来与EVM交互,在此过程中使用是的JSON-RPC(一个无状态且轻量级的远程过程调用(RPC)传送协议,其传递内容透过JSON为主)
您可以使用web3.is来读取和写入以太坊区块链,而不是使用ajax从Web服务器读取和写入数据。

所以web3.js依赖BigNumber Library1,且会自动进行引入。 核心代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/web3@4.16.0/dist/web3.min.js"></script>
</head>
<body>
<h1 id="title"></h1>
<input type="text" id="toAccount">
<button id="send">发送</button>
<script>
// 连接到以太坊网络
// const web3 = new Web3('https://mainnet.infura.io/v3/your-infura-project-id');
var web3 = new Web3(Web3.givenProvider || 'http://localhost:7545');
// var web3 = new Web3(Web3.givenProvider || "ws://localhost:7545");
web3.eth.getBlockNumber().then(res=>{
console.log(res);
});
web3.eth.getChainId().then(res=>{
console.log(res);
});
web3.eth.getBalance("0xACaA8d29f7B3d5ECCd65b75xxxxxxxxxx").then(res=>{
console.log(res);
// 转换单位
console.log(web3.utils.fromWei(res, "ether"));
});
// 使用 promise
// web3.eth.sendTransaction({
// from: '0x270871c6dc7bA8e8Dxxxxxxxxxxxx',
// to: '0xACaA8d29f7B3d5ECCd65b7xxxxxxxxxx',
// value: web3.utils.toWei("1", "ether")
// })
// .then(function(receipt){
// console.log("转完了!")
// });
// 先授权
// web3.eth.requestAccounts().then(res=>{
// console.log("授权", res);
// title.innerHTML = res[0];
// });
// 获取账号
web3.eth.getAccounts().then(res=>{
console.log('-----',res,'---===');
title.innerHTML = res[0];
});
send.onclick = function(){
// 获取输入框的值
var toAccount = document.getElementById("toAccount").value;
console.log(toAccount);
// 转账
web3.eth.sendTransaction({
from: title.innerHTML,
to: toAccount,
value: web3.utils.toWei("1", "ether")
})
.then(function(receipt){
console.log("转完了!")
});
}
</script>
</body>
</html>009 编写智能合约 - 入门
智能合约
- Remix IDE
RemixIDE是开发以太坊智能合约的在线IDE工具,部署简单的智能合约非常方便Remixt址:https://remix.ethereum.org/
- 2.Truffle
Truffle是一个世界级的智能合约开发框架,专门为智能合约而生, ·管理智能合约的生命周期 ·自动化合约测试 。可编程,可部署,可发布合约 ·不用过多的关注网络管理 。强大的交互式控制台
目录结构:
- contracts/:存放solidity智能合约文件
- migrations/:truffle使用migration system 来控制合约的部署。
- test/:测试文件存放文字(javascript or solidity)
- truffle-config.js:配置文件
solidity语言
3-1 数据位置
solidity数据存储位置有三类:storage,memory和calldata。不同存储位置的gas成本不同。storage类型的数据存在链上,类似计算机的硬盘,消耗gas多;memory和caldata类型的临时存在内存里,消耗gas少。大致用法:
- 1,storage:合约里的状态变量默认都是storage,存储在链上。
- 2,memory:函数里的参数和临时变量一般用memory,存储在内存中,不上链。
- 3,caldata:和memory类似,存储在内存中,不上链。与memory的不同点在于calldata变量不能修改(immutable),一般用于函数的参数。
3-2 作用域
变量的作用域:Solidity中变量按作用域划分有三种,分别是状态变量(state variable),局部变量(local variable)和全局变量(global variable)。
- 1,状态变量:状态变量是数据存储在链上的变量,所有合约内函数都可以访问,gas消耗高。状态变量在合约 内、函数外声明。可以在函数里更改状态变量的值:
- 2,局部变量:局部变量是仅在函数执行过程中有效的变量,函数退出后,变量无效。局部变量的数据存储在内存里,不上链,gas低。局部变量在函数内声明。
- 3,全局变量:全局变量是全局范围工作的变量,都是solidity预留关键字。他们可以在函数内不声明直接使用(类似于msg.sender,block.number)
contract/StudentStorage.sol
pragma solidity >= 0.4.16 < 0.9.0;
contract StudentStorage {
// 创建 2个变量,username,age
string public name;
uint public age;
function setData(string memory _name, uint _age) public {
// string memory a; // 局部变量,存储在内存中
name = _name;
age = _age;
}
// function test(uint x, uint y) public pure returns (uint) {
// return x + y;
// }
// view (视图函数,只访问不修改状态), pure(纯函数,不访问,也不修改)
function getData() public view returns (string memory, uint) {
return (name, age);
}
}执行编译
(base) PS D:\project\idea_work\own\ganache-demo\code> truffle compile
Compiling your contracts...
===========================
✓ Fetching solc version list from solc-bin. Attempt #1
✓ Downloading compiler. Attempt #1.
> Compiling .\contracts\StudentStorage.sol
> Compilation warnings encountered:
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> project:/contracts/StudentStorage.sol
> Artifacts written to D:\project\idea_work\own\ganache-demo\code\build\contracts
> Compiled successfully using:
- solc: 0.8.21+commit.d9974bed.Emscripten.clang
(base) PS D:\project\idea_work\own\ganache-demo\code>部署
执行部署命令(直接执行部署也会执行编译):
truffle migrate
测试,进入控制台
3-3 脚本测试
- mocha测试
- truffle脚本
010 编写智能合约 - 进阶
contracts/StudentListStorage.sol
pragma solidity >= 0.4.16 < 0.9.0;
contract StudentListStorage {
// 结构体
struct Student {
uint id;
string name;
uint age;
}
// 动态数组
Student[] public StudentList; // 自动gettter()
// 创建 2个变量,username,age
string public name;
uint public age;
// struct ,动态数组,映射,string
function addList(string memory _name, uint _age) public returns (uint) {
uint count = StudentList.length;
uint index = count + 1;
// string memory a; // 局部变量,存储在内存中
StudentList.push(Student(index, _name, _age));
return StudentList.length;
}
// function test(uint x, uint y) public pure returns (uint) {
// return x + y;
// }
// view (视图函数,只访问不修改状态), pure(纯函数,不访问,也不修改)
function getList() public view returns (Student[] memory) {
Student[] memory list = StudentList;
return list;
}
}部署脚本: migrations/2_deploy.js
const Contracts = artifacts.require("./StudentListStorage.sol");
module.exports = async function (callback) {
// console.log("111111");
// console.log("---------");
const studentStorage = await Contracts.deployed();
await studentStorage.addList("hufl", 20);
let res = await studentStorage.getList();
console.log(res);
console.log(await studentStorage.StudentList(2));
callback();
}测试脚本 scripts/test2.js
const Contracts = artifacts.require("./StudentListStorage.sol");
module.exports = async function (callback) {
// console.log("111111");
// console.log("---------");
const studentStorage = await Contracts.deployed();
await studentStorage.addList("hufl", 20);
let res = await studentStorage.getList();
console.log(res);
console.log(await studentStorage.StudentList(2));
callback();
}执行测试脚本:
truffle exec scripts/test2.js

011 打通Web3.js到智能合约
ABI非常类似于 API(应用程序接口),一种人类可读的代码接口表示。ABI定义了用于与二进制合约交互的方法和结构,就像 API所做的那样,只是在较低的级别上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/web3@4.16.0/dist/web3.min.js"></script>
</head>
<body>
<input type="text" id="myname">
<input type="number" id="myage">
<button id="add">add</button>
<ul id="list">
</ul>
<script type="module">
// 连接到以太坊网络
var web3 = new Web3(Web3.givenProvider || 'http://localhost:7545');
// 先授权
// let account = await web3.eth.requestAccounts();
// console.log(account);
let accounts = await web3.eth.getAccounts();
console.log(accounts[0]);
let account = accounts[0];
// 连接智能合约程序
var studentListStorage = await new web3.eth.Contract([
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "StudentList",
"outputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
{
"inputs": [],
"name": "age",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
{
"inputs": [
{
"internalType": "string",
"name": "_name",
"type": "string"
},
{
"internalType": "uint256",
"name": "_age",
"type": "uint256"
}
],
"name": "addList",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getList",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "uint256",
"name": "age",
"type": "uint256"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"internalType": "struct StudentListStorage.Student[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
}
], "0xb18398aBd2Cd3280175C4227c177B1c4b8790e84"); // 合约地址
console.log(studentListStorage)
add.onclick = async function(){
console.log(myname.value, myage.value)
await studentListStorage
.methods
.addList(myname.value, myage.value)
.send({
from: account,
gas: 300000
})
// 添加完后,获取列表
getList()
async function getList(){
let res = await studentListStorage.methods.getList().call();
console.log(res)
list.innerHTML = res.map(item => `
<li>${item.id}---${item.name} ----${item.age}</li>
`).join('')
}
}
</script>
</body>
</html>012 加密货币
什么叫做代币? 代币可以在以太坊中表示任何东西
- 在线平台中的信誉积分
- 金融资产类似于公司股份的资产
- 像美元一样的法定货币
- 盎司黄金
- 及更多..
ERC-20就是一套基于以太坊网络的标准代币发行协议。有了ERC-20,开发者们得以高效、可靠、低成本地创造专属自己项目的代币;我们甚至可以将ERC-20视为以太坊网络为早期区块链世界做出的最重要贡献.
https://github.com/ethereum/ElPs/blob/master/ElPs/eip-20.md
https://github.com/ethereum/ercs/blob/master/ERCS/erc-20.md
ERC-20 的功能示例包括:
- 将代币从一个帐户转到另一个帐户
- 获取帐户的当前代币余额
- 获取网络上可用代币的总供应量
- 批准一个帐户中一定的代币金额由第三方帐户使用
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);pragma solidity >= 0.4.16 < 0.9.0;
contract HflToken {
string public name = "HFLToken";
}
013 转账
- 编写自己的智能合约
pragma solidity >= 0.4.16 < 0.9.0;
contract HflToken {
string public name = "HFLToken";
string public symbol = "HFL";
uint256 public decimals = 18;
uint256 public totalSupply;
// mapping(address => uint256) public balanceOf;
mapping(address => uint256) public balanceOf;
constructor() {
// totalSupply = _initialSupply * 10 ** decimals;
totalSupply = 1000000 * (10 ** decimals);
// 部署账号
balanceOf[msg.sender] = totalSupply;
}
// 触发 区块链日志 事件监听
event Transfer(address indexed _from, address indexed _to, uint256 _value);
function transfer(address _to, uint256 _value) public returns (bool success) {
require(_to!=address(0));
// require(balanceOf[msg.sender] >= _value);
// balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value); // 调用者余额减少
// balanceOf[_to] = balanceOf[msg.sender].add(_value); // 接收者余额增加
_transfer(msg.sender, _to, _value);
return true;
}
function _transfer(address _from, address _to, uint256 _value) internal {
require(_to!=address(0));
require(balanceOf[_from] >= _value);
// 从哪个账号发起的调用者
balanceOf[_from] = balanceOf[_from] - _value; // 调用者余额减少
balanceOf[_to] = balanceOf[_to] + _value; // 接收者余额增加
// 触发事件
emit Transfer(_from, _to, _value);
}
}部署文件
const Contracts = artifacts.require("HflToken.sol");
module.exports = function (deployer) {
deployer.deploy(Contracts);
}测试脚本
const Contracts = artifacts.require("./HflToken.sol");
const fromWei = (bn) => web3.utils.fromWei(bn, 'ether');
const toWei = (num) => web3.utils.toWei(num.toString(), 'ether');
module.exports = async function (callback) {
const HflToken = await Contracts.deployed();
// 测试第一个账号的余额
let res1 = await HflToken.balanceOf("0x5bD9E71C4fe650e33F470CBdC4E0ecD76eE8Fb8B");
console.log("第一个账号:", fromWei(res1));
let res11 = await HflToken.balanceOf("0x74d2F3d35F4981ffd219725E601dF893D554057e");
console.log("第二个账号:", fromWei(res11));
console.log("---------转账开始---------");
await HflToken.transfer("0x74d2F3d35F4981ffd219725E601dF893D554057e", toWei(10000), {
from: "0x5bD9E71C4fe650e33F470CBdC4E0ecD76eE8Fb8B"
});
console.log("---------转账结束---------");
let res2 = await HflToken.balanceOf("0x5bD9E71C4fe650e33F470CBdC4E0ecD76eE8Fb8B");
console.log("第一个账号:", fromWei(res2));
let res3 = await HflToken.balanceOf("0x74d2F3d35F4981ffd219725E601dF893D554057e");
console.log("第二个账号:", fromWei(res3));
// 测试第二个账号的余额
callback();
}命令行部署
truffle migrate --reset
命令行执行测试脚本
truffle exec ./scripts/transfer.js
查看效果: 
014 委托转账
核心方法: transferFrom方法
pragma solidity >= 0.4.16 < 0.9.0;
contract HflToken {
string public name = "HFLToken";
string public symbol = "HFL";
uint256 public decimals = 18;
uint256 public totalSupply;
// mapping(address => uint256) public balanceOf;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance; //定量定额
constructor() {
// totalSupply = _initialSupply * 10 ** decimals;
totalSupply = 1000000 * (10 ** decimals);
// 部署账号
balanceOf[msg.sender] = totalSupply;
}
// 触发 区块链日志 事件监听
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
function transfer(address _to, uint256 _value) public returns (bool success) {
require(_to!=address(0));
// require(balanceOf[msg.sender] >= _value);
// balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value); // 调用者余额减少
// balanceOf[_to] = balanceOf[msg.sender].add(_value); // 接收者余额增加
_transfer(msg.sender, _to, _value);
return true;
}
function _transfer(address _from, address _to, uint256 _value) internal {
require(_to!=address(0));
require(balanceOf[_from] >= _value);
// 从哪个账号发起的调用者
balanceOf[_from] = balanceOf[_from] - _value; // 调用者余额减少
balanceOf[_to] = balanceOf[_to] + _value; // 接收者余额增加
// 触发事件
emit Transfer(_from, _to, _value);
}
function approve(address _spender, uint256 _value) public returns (bool success) {
// msg.sender 当前网页登陆的账号
// _spender 授权的账号 第三方的交易所的账号地址
// _value 授权的金额
require(_spender != address(0));
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
// // return true;
// _approve(msg.sender, _spender, _value);
return true;
}
function _approve(address _owner, address _spender, uint256 _value) internal {
allowance[_owner][_spender] = _value;
}
// 被授权的交易所调用
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
// _from 某个放款账号
// _to 接收账号
// msg.sender 交易所账户 地址
require(balanceOf[_from] >= _value);
require(allowance[_from][msg.sender]>=_value);
// allowance[_from][msg.sender] = allowance[_from][msg.sender] - _value;
_approve(_from, msg.sender, allowance[_from][msg.sender] - _value);
_transfer(_from, _to, _value);
return true;
}
}015 交易所-存款
migrations/3_deploy.js
const Contracts = artifacts.require("HflToken.sol");
const Exchange = artifacts.require("Exchange.sol");
module.exports = async function (deployer) {
const accounts = await web3.eth.getAccounts();
const deployerAddress = accounts[0];
await deployer.deploy(Contracts);
await deployer.deploy(Exchange, deployerAddress, 10);
}scripts/test-deposit.js
const HflToken = artifacts.require("./HflToken.sol");
const Exchange = artifacts.require("./Exchange.sol");
const ETHER_ADDRESS = '0x0000000000000000000000000000000000000000';
const fromWei = (bn) => web3.utils.fromWei(bn, 'ether');
const toWei = (num) => web3.utils.toWei(num.toString(), 'ether');
module.exports = async function (callback) {
const hflToken = await HflToken.deployed();
const exchange = await Exchange.deployed();
const accounts = await web3.eth.getAccounts();
// 往合约上存以太币
// await exchange.depositEther({
// from: accounts[0],
// value: toWei(10)}
// );
// let res = await exchange.tokens(ETHER_ADDRESS, accounts[0]);
// console.log(fromWei(res));
// 往合约上存代币
// 授权
// await hflToken.approve(exchange.address, toWei(100000), {
// from: accounts[0]
// });
// await exchange.depositToken(hflToken.address, toWei(100000), {
// from: accounts[0]
// });
let res = await exchange.tokens(hflToken.address, accounts[0]);
console.log(fromWei(res));
// 1. 查询交易所合约的ETH余额
const ethBalanceWei = await web3.eth.getBalance(exchange.address);
console.log("\n1. 交易所合约的ETH余额:", web3.utils.fromWei(ethBalanceWei, 'ether'), "ETH");
// 2. 查询交易所合约持有的HFLToken总额 (通过查询代币合约)
const exchangeHflBalanceWei = await hflToken.balanceOf(exchange.address);
console.log("2. 交易所合约持有的HFL总额:", web3.utils.fromWei(exchangeHflBalanceWei, 'ether'), "HFL");
callback();
}016 交易所-取款
contracts/Exchange.sol 新增: withdrawEther(),withdrawToken()方法,event WithDraw()事件
pragma solidity >=0.4.16 <0.9.0;
import "./HflToken.sol";
contract Exchange {
// 收费账户地址
address public feeAccount;
// 费率百分比
uint256 public feePercent;
// 存储以太坊地址
address constant ETHER = address(0);
// 存储用户地址和代币地址
mapping(address => mapping(address => uint256)) public tokens;
// 构造函数
constructor(address _feeAccount, uint256 _feePercent) {
feeAccount = _feeAccount;
feePercent = _feePercent;
}
// 存以太币
function depositEther() public payable {
// msg.sender
// msg.value
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] + msg.value;
emit Deposit(ETHER, msg.sender, msg.value, tokens[ETHER][msg.sender]);
}
event Deposit(address token, address user, uint256 amount, uint256 balance);
event WithDraw(address token, address user, uint256 amount, uint256 balance);
// 存其他货币
function depositToken(address _token, uint256 _amount) public {
require(_token!=ETHER);
// 调用某个方法强行从你账户往当前交易所账户转钱
require(HflToken(_token).transferFrom(msg.sender, address(this), _amount));
tokens[_token][msg.sender] = tokens[_token][msg.sender] + _amount;
emit Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 提取以太币
function withdrawEther(uint256 _amount) public {
require(tokens[ETHER][msg.sender] >= _amount);
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
payable(msg.sender).transfer(_amount);
emit WithDraw(ETHER, msg.sender, _amount, tokens[ETHER][msg.sender]);
}
// 提取其他货币
function withdrawToken(address _token, uint256 _amount) public {
require(_token != ETHER);
require(tokens[_token][msg.sender] >= _amount);
tokens[_token][msg.sender] = tokens[_token][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
require(HflToken(_token).transfer(msg.sender, _amount));
emit WithDraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
function balanceOf(
address tokenAddress,
address user
) public view returns (uint256) {
// TODO
}
}scripts/test-withdraw.js
const HflToken = artifacts.require("./HflToken.sol");
const Exchange = artifacts.require("./Exchange.sol");
const ETHER_ADDRESS = '0x0000000000000000000000000000000000000000';
const fromWei = (bn) => web3.utils.fromWei(bn, 'ether');
const toWei = (num) => web3.utils.toWei(num.toString(), 'ether');
module.exports = async function (callback) {
const hflToken = await HflToken.deployed();
const exchange = await Exchange.deployed();
const accounts = await web3.eth.getAccounts();
// 测试取款以太币
// await exchange.withdrawEther(toWei(5), {
// from: accounts[0]
// });
// let res1 = await exchange.tokens(ETHER_ADDRESS, accounts[0]);
// console.log(fromWei(res1));
// 测试取其他代币
await exchange.withdrawToken(hflToken.address,toWei(50000), {
from: accounts[0]
});
let res2 = await exchange.tokens(hflToken.address, accounts[0]);
console.log(fromWei(res2));
// // 1. 查询交易所合约的ETH余额
// const ethBalanceWei = await web3.eth.getBalance(exchange.address);
// console.log("\n1. 交易所合约的ETH余额:", web3.utils.fromWei(ethBalanceWei, 'ether'), "ETH");
// // 2. 查询交易所合约持有的HFLToken总额 (通过查询代币合约)
// const exchangeHflBalanceWei = await hflToken.balanceOf(exchange.address);
// console.log("2. 交易所合约持有的HFL总额:", web3.utils.fromWei(exchangeHflBalanceWei, 'ether'), "HFL");
callback();
}017 交易所-流动池
https://github.com/hufanglei/web3-DApp/commit/fe584c72604ccca929377d07995cc01d298feee9
pragma solidity >=0.4.16 <0.9.0;
import "./HflToken.sol";
contract Exchange {
// 收费账户地址
address public feeAccount;
// 费率百分比
uint256 public feePercent;
// 存储以太坊地址
address constant ETHER = address(0);
// 存储用户地址和代币地址
mapping(address => mapping(address => uint256)) public tokens;
// 订单结构体
struct _Order {
uint256 id;
address user;
address tokenGet;
uint256 amountGet;
address tokenGive;
uint256 amountGive;
uint256 timestamp;
}
// _Order[] public orderlist;
mapping(uint256 => _Order) public orders;
uint256 public orderCount;
// 构造函数
constructor(address _feeAccount, uint256 _feePercent) {
feeAccount = _feeAccount;
feePercent = _feePercent;
}
// 存以太币
function depositEther() public payable {
// msg.sender
// msg.value
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] + msg.value;
emit Deposit(ETHER, msg.sender, msg.value, tokens[ETHER][msg.sender]);
}
event Deposit(address token, address user, uint256 amount, uint256 balance);
event WithDraw(
address token,
address user,
uint256 amount,
uint256 balance
);
event Order(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 存其他货币
function depositToken(address _token, uint256 _amount) public {
require(_token != ETHER);
// 调用某个方法强行从你账户往当前交易所账户转钱
require(
HflToken(_token).transferFrom(msg.sender, address(this), _amount)
);
tokens[_token][msg.sender] = tokens[_token][msg.sender] + _amount;
emit Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 提取以太币
function withdrawEther(uint256 _amount) public {
require(tokens[ETHER][msg.sender] >= _amount);
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
payable(msg.sender).transfer(_amount);
emit WithDraw(ETHER, msg.sender, _amount, tokens[ETHER][msg.sender]);
}
// 提取其他货币
function withdrawToken(address _token, uint256 _amount) public {
require(_token != ETHER);
require(tokens[_token][msg.sender] >= _amount);
tokens[_token][msg.sender] = tokens[_token][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
require(HflToken(_token).transfer(msg.sender, _amount));
emit WithDraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 查余额
function balanceOf(
address _token,
address _user
) public view returns (uint256) {
return tokens[_token][_user];
}
// makeOrder
function makeOrder(address _tokenGet,
uint256 _amountGet,
address _tokenGive,
uint256 _amountGive) public {
orderCount = orderCount + 1;
orders[orderCount] = _Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
// 发出订单
emit Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
}
// cancelOrder
// fillOrder
}018 交易所-取消订单
https://github.com/hufanglei/web3-DApp/commit/e10f379649da7e80a220d8236051fd71a60076f3 Exchange.sol
pragma solidity >=0.4.16 <0.9.0;
import "./HflToken.sol";
contract Exchange {
// 收费账户地址
address public feeAccount;
// 费率百分比
uint256 public feePercent;
// 存储以太坊地址
address constant ETHER = address(0);
// 存储用户地址和代币地址
mapping(address => mapping(address => uint256)) public tokens;
// 订单结构体
struct _Order {
uint256 id;
address user;
address tokenGet;
uint256 amountGet;
address tokenGive;
uint256 amountGive;
uint256 timestamp;
}
// _Order[] public orderlist;
mapping(uint256 => _Order) public orders;
mapping(uint256 => bool) public orderCancel;
uint256 public orderCount;
// 构造函数
constructor(address _feeAccount, uint256 _feePercent) {
feeAccount = _feeAccount;
feePercent = _feePercent;
}
// 存以太币
function depositEther() public payable {
// msg.sender
// msg.value
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] + msg.value;
emit Deposit(ETHER, msg.sender, msg.value, tokens[ETHER][msg.sender]);
}
event Deposit(address token, address user, uint256 amount, uint256 balance);
event WithDraw(
address token,
address user,
uint256 amount,
uint256 balance
);
// 创建订单事件
event Order(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 取消订单事件
event Cancel(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 存其他货币
function depositToken(address _token, uint256 _amount) public {
require(_token != ETHER);
// 调用某个方法强行从你账户往当前交易所账户转钱
require(
HflToken(_token).transferFrom(msg.sender, address(this), _amount)
);
tokens[_token][msg.sender] = tokens[_token][msg.sender] + _amount;
emit Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 提取以太币
function withdrawEther(uint256 _amount) public {
require(tokens[ETHER][msg.sender] >= _amount);
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
payable(msg.sender).transfer(_amount);
emit WithDraw(ETHER, msg.sender, _amount, tokens[ETHER][msg.sender]);
}
// 提取其他货币
function withdrawToken(address _token, uint256 _amount) public {
require(_token != ETHER);
require(tokens[_token][msg.sender] >= _amount);
tokens[_token][msg.sender] = tokens[_token][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
require(HflToken(_token).transfer(msg.sender, _amount));
emit WithDraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 查余额
function balanceOf(
address _token,
address _user
) public view returns (uint256) {
return tokens[_token][_user];
}
// makeOrder
function makeOrder(address _tokenGet,
uint256 _amountGet,
address _tokenGive,
uint256 _amountGive) public {
orderCount = orderCount + 1;
orders[orderCount] = _Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
// 发出订单
emit Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
}
// cancelOrder
function cancelOrder(uint256 _id) public {
_Order memory myorder = orders[_id];
require(myorder.id == _id);
orderCancel[_id] = true;
emit Cancel(myorder.id, msg.sender, myorder.tokenGet, myorder.amountGet, myorder.tokenGive, myorder.amountGive, block.timestamp);
}
// fillOrder
}019 交易所-完成交易
https://github.com/hufanglei/web3-DApp/commit/75bcf3a18513bffe45eb2a513bc230ae80a3117a Exchange.sol
pragma solidity >=0.4.16 <0.9.0;
import "./HflToken.sol";
contract Exchange {
// 收费账户地址
address public feeAccount;
// 费率百分比
uint256 public feePercent;
// 存储以太坊地址
address constant ETHER = address(0);
// 存储用户地址和代币地址
mapping(address => mapping(address => uint256)) public tokens;
// 订单结构体
struct _Order {
uint256 id;
address user;
address tokenGet;
uint256 amountGet;
address tokenGive;
uint256 amountGive;
uint256 timestamp;
}
// _Order[] public orderlist;
mapping(uint256 => _Order) public orders;
mapping(uint256 => bool) public orderCancel;
mapping(uint256 => bool) public orderFill;
uint256 public orderCount;
// 构造函数
constructor(address _feeAccount, uint256 _feePercent) {
feeAccount = _feeAccount;
feePercent = _feePercent;
}
// 存以太币
function depositEther() public payable {
// msg.sender
// msg.value
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] + msg.value;
emit Deposit(ETHER, msg.sender, msg.value, tokens[ETHER][msg.sender]);
}
event Deposit(address token, address user, uint256 amount, uint256 balance);
event WithDraw(
address token,
address user,
uint256 amount,
uint256 balance
);
// 创建订单事件
event Order(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 取消订单事件
event Cancel(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 填充订单事件
event Trade(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 存其他货币
function depositToken(address _token, uint256 _amount) public {
require(_token != ETHER);
// 调用某个方法强行从你账户往当前交易所账户转钱
require(
HflToken(_token).transferFrom(msg.sender, address(this), _amount)
);
tokens[_token][msg.sender] = tokens[_token][msg.sender] + _amount;
emit Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 提取以太币
function withdrawEther(uint256 _amount) public {
require(tokens[ETHER][msg.sender] >= _amount);
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
payable(msg.sender).transfer(_amount);
emit WithDraw(ETHER, msg.sender, _amount, tokens[ETHER][msg.sender]);
}
// 提取其他货币
function withdrawToken(address _token, uint256 _amount) public {
require(_token != ETHER);
require(tokens[_token][msg.sender] >= _amount);
tokens[_token][msg.sender] = tokens[_token][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
require(HflToken(_token).transfer(msg.sender, _amount));
emit WithDraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 查余额
function balanceOf(
address _token,
address _user
) public view returns (uint256) {
return tokens[_token][_user];
}
// makeOrder
function makeOrder(address _tokenGet,
uint256 _amountGet,
address _tokenGive,
uint256 _amountGive) public {
orderCount = orderCount + 1;
orders[orderCount] = _Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
// 发出订单
emit Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
}
// cancelOrder
function cancelOrder(uint256 _id) public {
_Order memory myorder = orders[_id];
require(myorder.id == _id);
orderCancel[_id] = true;
emit Cancel(myorder.id, msg.sender, myorder.tokenGet, myorder.amountGet, myorder.tokenGive, myorder.amountGive, block.timestamp);
}
// fillOrder
function fillOrder(uint256 _id) public {
_Order memory myorder = orders[_id];
require(myorder.id == _id);
orderFill[_id] = true;
// 账户余额 互换 && 小费收取
/**
xiaoming ,makeorder,
100 KWT ==> 1 ether
xiaoming ,少了1 ether
xiaoming 多了100KWT
-------------------------------
msg.sender, fillorder
msg.sender,多了1 ether
msg.sender 少了 100KWT
*/
// require(_id > 0 && _id <= orderCount);
// require(!orderCancel[_id]);
// _Order memory myorder = orders[_id];
// uint256 _feeAmount = ((myorder.amountGet * feePercent) / 100);
uint256 feeAmount = myorder.amountGet * feePercent / 100;
// tokens
tokens[myorder.tokenGet][msg.sender] = tokens[myorder.tokenGet][msg.sender] - myorder.amountGet + feeAmount;
tokens[myorder.tokenGet][feeAccount] = tokens[myorder.tokenGet][feeAccount] + feeAmount;
tokens[myorder.tokenGet][myorder.user] = tokens[myorder.tokenGet][myorder.user] + myorder.amountGet;
// 以太币
tokens[myorder.tokenGive][msg.sender] = tokens[myorder.tokenGive][msg.sender] + myorder.amountGive;
tokens[myorder.tokenGive][myorder.user] = tokens[myorder.tokenGive][myorder.user] - myorder.amountGive;
emit Trade(myorder.id, myorder.user, myorder.tokenGet, myorder.amountGet, myorder.tokenGive, myorder.amountGive, block.timestamp);
}
}020 测试订单
https://github.com/hufanglei/web3-DApp/commit/3358fd98daa300656270db04f1901dea4b906a57
Exchange.sol
pragma solidity >=0.4.16 <0.9.0;
import "./HflToken.sol";
contract Exchange {
// 收费账户地址
address public feeAccount;
// 费率百分比
uint256 public feePercent;
// 存储以太坊地址
address constant ETHER = address(0);
// 存储用户地址和代币地址
mapping(address => mapping(address => uint256)) public tokens;
// 订单结构体
struct _Order {
uint256 id;
address user;
address tokenGet;
uint256 amountGet;
address tokenGive;
uint256 amountGive;
uint256 timestamp;
}
// _Order[] public orderlist;
mapping(uint256 => _Order) public orders;
mapping(uint256 => bool) public orderCancel;
mapping(uint256 => bool) public orderFill;
uint256 public orderCount;
// 构造函数
constructor(address _feeAccount, uint256 _feePercent) {
feeAccount = _feeAccount;
feePercent = _feePercent;
}
// 存以太币
function depositEther() public payable {
// msg.sender
// msg.value
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] + msg.value;
emit Deposit(ETHER, msg.sender, msg.value, tokens[ETHER][msg.sender]);
}
event Deposit(address token, address user, uint256 amount, uint256 balance);
event WithDraw(
address token,
address user,
uint256 amount,
uint256 balance
);
// 创建订单事件
event Order(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 取消订单事件
event Cancel(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 填充订单事件
event Trade(
uint256 id,
address user,
address tokenGet,
uint256 amountGet,
address tokenGive,
uint256 amountGive,
uint256 timestamp
);
// 存其他货币
function depositToken(address _token, uint256 _amount) public {
require(_token != ETHER);
// 调用某个方法强行从你账户往当前交易所账户转钱
require(
HflToken(_token).transferFrom(msg.sender, address(this), _amount)
);
tokens[_token][msg.sender] = tokens[_token][msg.sender] + _amount;
emit Deposit(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 提取以太币
function withdrawEther(uint256 _amount) public {
require(tokens[ETHER][msg.sender] >= _amount);
tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
payable(msg.sender).transfer(_amount);
emit WithDraw(ETHER, msg.sender, _amount, tokens[ETHER][msg.sender]);
}
// 提取其他货币
function withdrawToken(address _token, uint256 _amount) public {
require(_token != ETHER);
require(tokens[_token][msg.sender] >= _amount);
tokens[_token][msg.sender] = tokens[_token][msg.sender] - _amount;
// 从当前合约地址往用户地址转账
require(HflToken(_token).transfer(msg.sender, _amount));
emit WithDraw(_token, msg.sender, _amount, tokens[_token][msg.sender]);
}
// 查余额
function balanceOf(
address _token,
address _user
) public view returns (uint256) {
return tokens[_token][_user];
}
// makeOrder
function makeOrder(address _tokenGet,
uint256 _amountGet,
address _tokenGive,
uint256 _amountGive) public {
require(balanceOf(_tokenGive, msg.sender) >= _amountGive, unicode"创建订单时余额不足");
orderCount = orderCount + 1;
orders[orderCount] = _Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
// 发出订单
emit Order(orderCount, msg.sender, _tokenGet, _amountGet, _tokenGive, _amountGive, block.timestamp);
}
// cancelOrder
function cancelOrder(uint256 _id) public {
_Order memory myorder = orders[_id];
require(myorder.id == _id);
orderCancel[_id] = true;
emit Cancel(myorder.id, msg.sender, myorder.tokenGet, myorder.amountGet, myorder.tokenGive, myorder.amountGive, block.timestamp);
}
// fillOrder
function fillOrder(uint256 _id) public {
_Order memory myorder = orders[_id];
require(myorder.id == _id);
// 账户余额 互换 && 小费收取
/**
xiaoming ,makeorder,
100 KWT ==> 1 ether
xiaoming ,少了1 ether
xiaoming 多了100KWT
-------------------------------
msg.sender, fillorder
msg.sender,多了1 ether
msg.sender 少了 100KWT
*/
// require(_id > 0 && _id <= orderCount);
// require(!orderCancel[_id]);
// _Order memory myorder = orders[_id];
// uint256 _feeAmount = ((myorder.amountGet * feePercent) / 100);
uint256 feeAmount = myorder.amountGet * feePercent / 100;
require(balanceOf(myorder.tokenGive, myorder.user) >= myorder.amountGive, unicode"创建订单的用户的余额不足");
require(balanceOf(myorder.tokenGet, myorder.user) >= myorder.amountGive + feeAmount, unicode"填充订单的用户的余额不足");
// tokens
tokens[myorder.tokenGet][msg.sender] = tokens[myorder.tokenGet][msg.sender] - myorder.amountGet - feeAmount;
tokens[myorder.tokenGet][feeAccount] = tokens[myorder.tokenGet][feeAccount] + feeAmount;
tokens[myorder.tokenGet][myorder.user] = tokens[myorder.tokenGet][myorder.user] + myorder.amountGet;
// 以太币
tokens[myorder.tokenGive][msg.sender] = tokens[myorder.tokenGive][msg.sender] + myorder.amountGive;
tokens[myorder.tokenGive][myorder.user] = tokens[myorder.tokenGive][myorder.user] - myorder.amountGive;
orderFill[_id] = true;
emit Trade(myorder.id, myorder.user, myorder.tokenGet, myorder.amountGet, myorder.tokenGive, myorder.amountGive, block.timestamp);
}
}scripts/test-order.js
const HflToken = artifacts.require("./HflToken.sol");
const Exchange = artifacts.require("./Exchange.sol");
const ETHER_ADDRESS = '0x0000000000000000000000000000000000000000';
const fromWei = (bn) => web3.utils.fromWei(bn, 'ether');
const toWei = (num) => web3.utils.toWei(num.toString(), 'ether');
const wait = (seconds) => {
const milliseconds = seconds * 1000;
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
module.exports = async function (callback) {
try {
const hflToken = await HflToken.deployed();
const exchange = await Exchange.deployed();
const accounts = await web3.eth.getAccounts();
// 第一步 account0 ---> account1 10W
await hflToken.transfer(accounts[1],toWei(100000), {
from: accounts[0]
});
// 第二步 account0 ---> 交易所存入 10以太币
await exchange.depositEther({
from: accounts[0],
value: toWei(10)
});
let res1 = await exchange.tokens(ETHER_ADDRESS, accounts[0]);
// console.log("account[0]-在交易所的以太币", fromWei(res1));
// 第二步 account0 ---> 交易所存入 10000 HFL
await hflToken.approve(exchange.address, toWei(100000), {
from: accounts[0]
});
await exchange.depositToken(hflToken.address, toWei(100000), {
from: accounts[0]
});
let res2 = await exchange.tokens(hflToken.address, accounts[0]);
// console.log("account[0]-在交易所的HFL币", fromWei(res2));
// 第三步 account1 ---> 交易所存入 5以太币
await exchange.depositEther({
from: accounts[1],
value: toWei(5)
});
let res3 = await exchange.tokens(ETHER_ADDRESS, accounts[1]);
// console.log("account[1]-在交易所的以太币", fromWei(res3));
// 第三步 account1 ---> 交易所存入 5000 HFL
await hflToken.approve(exchange.address, toWei(50000), {
from: accounts[1]
});
await exchange.depositToken(hflToken.address, toWei(50000), {
from: accounts[1]
});
let res4 = await exchange.tokens(hflToken.address, accounts[1]);
console.log("account[1]-在交易所的HFL币", fromWei(res4));
let orderId = 0;
let res;
// 创建订单
res = await exchange.makeOrder(hflToken.address, toWei(1000), ETHER_ADDRESS, toWei(0.1), {
from: accounts[0]
});
// console.log("res",res);
orderId = res.logs[0].args.id;
console.log("创建一个订单,订单id为"+orderId);
await wait(1);
// 取消订单
res = await exchange.makeOrder(hflToken.address, toWei(2000), ETHER_ADDRESS, toWei(0.2), {
from: accounts[0]
});
// console.log("res",res);
orderId = res.logs[0].args.id;
await exchange.cancelOrder(orderId, {
from: accounts[0]
});
console.log("取消一个订单,订单id为"+orderId);
await wait(1);
// 完成订单
res = await exchange.makeOrder(hflToken.address, toWei(3000), ETHER_ADDRESS, toWei(0.3), {
from: accounts[0]
});
orderId = res.logs[0].args.id;
await exchange.fillOrder(orderId, {
from: accounts[1]
});
console.log("完成一个订单,订单id为"+orderId);
console.log("account[0]-在交易所的HFL币", fromWei(
await exchange.tokens(hflToken.address, accounts[0])
));
console.log("account[0]-在交易所的以太币", fromWei(
await exchange.tokens(ETHER_ADDRESS, accounts[0])
));
console.log("account[1]-在交易所的HFL币", fromWei(
await exchange.tokens(hflToken.address, accounts[1])
));
console.log("account[1]-在交易所的以太币", fromWei(
await exchange.tokens(ETHER_ADDRESS, accounts[1])
));
console.log("account[2]-在交易所的HFL币", fromWei(
await exchange.tokens(hflToken.address, accounts[2])
));
}catch(error){
console.log(error)
}
callback();
}运行命令
- 部署和编译
truffle migrate --reset - 测试
truffle exec .\scripts\test-order.js
运行结果: 
021 架构整合
https://github.com/hufanglei/hflContracts/commit/b83ef5a675eaf75428a8c823bb07244da13d74a7
022 React合约连接
https://github.com/hufanglei/hflContracts/commit/518d10ceb492552207dcba3b43d946e535f76a42https://github.com/hufanglei/hflContracts/commit/7067881169671f8d60683c17ce0a6463e9725940
023 资产信息
npm i --save redux react-redux
npm i --save @reduxjs/toolkit
024 React - Balance组件
025 React - antd引入
npm i antd --save
https://github.com/hufanglei/hflContracts/commit/929427f1a8073d7f82ad54b16f7e3719ada1ff94
026 React - Order组件
027 React - Order组件
https://github.com/hufanglei/hflContracts/commit/4d551e8e4899709bf1ab68d1df63989f8f77dacb
028 React - Order组件渲染
https://github.com/hufanglei/hflContracts/commit/302b894b1cf3bcb258a6ff255853f95dbc78f133
操作步骤:
- 运行
ganache -d - 将账号1,2,3加入到metaMask钱包中
- 运行
truffle migrate --reset - metaMask钱包切换到第一个账户
- 运行
truffle exec .\scripts\test-order.js - 运行
npm start - 点击 取消按钮,执行 取消订单
- 点击 完成按钮,执行 完成订单
028 React - 事件订阅
- 运行
truffle exec .\scripts\test-order2.js
