practice collation (1) Basic knowledge of
practice finishing (2) Geth client
practice finishing (3) Remix development and deployment smart contract
practice finishing (4) Truffle smart contract development framework
The practice of Ethereum (5) DApp development process record (part 1)
practice finishing (5) DApp development process record (part 2)
practice sorting (6) file decentralized storage
Note: The DAPP crowdfunding requirements and contract implementation involved in this article come from the chain community
Through the previous content, we have roughly understood how to use Ganache + Truffle to develop smart contracts. A complete application also includes a user interface. We refer to applications built on smart contracts as DAPPs, that is, decentralized applications.
DAPP development
Conventional Internet applications are front-end requests to a centralized server, and the server responds to data synchronously. DAPP is a front-end request for any node in the decentralized network. After the node receives the transaction request, it is broadcast to the entire network, and a consensus is reached in the network to complete the transaction.
In DAPP applications, a request sent to a node is called a "transaction", and it needs to be signed by an associated wallet before it can be sent to the node; in addition, because transactions need to wait for network consensus, most of them are asynchronous, and the results are generally obtained through event callbacks.
front-end and smart contract
The two most important parts of developing DAPP applications are front-end applications and smart contracts. The smart contract runs on the Ethereum Virtual Machine (EVM), and the front end calls the smart contract by initiating a request to the node. The front-end part is the same as the Internet front-end application, you can use any front-end framework you are good at, such as Vue or React to develop, and then call the smart contract through the web3.js function library. Ethereum provides a JSON-RPC interface for interacting with nodes. The Web3 function library is a JSON-RPC encapsulation. The mainstream languages have Web3 implementations. JavaScript is web3.js and Java is web3j.
Crowdfunding project demand analysis
Suppose I am going to collaborate on a book, but I am not sure how many people are willing to buy it. Therefore, I initiated a crowdfunding. If within a month, I can raise 10 ETH, I will write, and each user who participates in the crowdfunding will give away one copy. If they fail to raise enough funds, the user can get back the investment. Of funds. At the same time, in order to allow users to actively participate, a tiered price is set. Initially, the price for participating in the crowdfunding is very low (0.02 ETH). Every time 1 ETH is raised, the price will increase by 0.002 ETH.
Three external actions (functions) of the contract can be summarized from the demand:
- The user remits money into the contract, which is realized by implementing the contract's return function;
- When the user redeems the remittance, this function needs to be called by the user to take effect after the crowdfunding fails to meet the standard;
- The initiator withdraws funds. This function needs to be called by the initiator after the crowdfunding reaches the target.
In addition, I further sorted out the logic and found that it is necessary to save some state variables and add corresponding logic:
- Record the amount of user crowdfunding, you can use a mapping type to save;
- Record the current crowdfunding price, the price can be saved using a mapping type;
- Record the expiration time of the contract crowdfunding, use the uint type to save the expiration time, you can use the current time plus 30 days as the expiration time in the constructor;
- Record the beneficiaries of crowdfunding, use the address type to record, and record the contract creator in the constructor;
- Record the current crowdfunding status (whether it has been closed). If the crowdfunding meets the standard (the creator should close the status in time when withdrawing funds), users need to be prevented from participating.
Create a front-end application
For the front end, we use Vue to develop. If you don’t know Vue.js, it is recommended to read the official Vue.js tutorial first.
Install first if Vue CLI is not installed:
npm install -g @vue/cli
Create a crowdfunding front-end project:
vue create crowdfunding
Implement crowdfunding contracts
Writing smart contracts
truffle initialization
cd crowdfunding
truffle init
Create Crowdfunding.sol under contracts:
pragma solidity >=0.6.0 <0.7.0;
contract Crowdfunding {
// 创作者
address public author;
// 参与金额
mapping(address => uint) public joined;
// 众筹目标
uint constant Target = 10 ether;
// 众筹截止时间
uint public endTime;
// 记录当前众筹价格
uint public price = 0.02 ether;
// 作者提取资金之后,关闭众筹
bool public closed = false;
// 部署合约时调用,初始化作者及众筹结束时间
constructor() public {
author = msg.sender;
endTime = now + 30 days;
}
// 更新价格,这是一个内部函数
function updatePrice() internal {
uint rise = address(this).balance / 1 ether * 0.002 ether;
price = 0.02 ether + rise;
}
// 用户向合约转账时,触发的回调函数
receive() external payable {
require(now < endTime && !closed , "众筹已结束");
require(joined[msg.sender] == 0, "你已经参与过众筹");
require(msg.value >= price, "出价太低了");
joined[msg.sender] = msg.value;
updatePrice();
}
// 作者提取资金
function withdrawFund() external {
require(msg.sender == author, "你不是作者");
require(address(this).balance >= Target, "未达到众筹目标");
closed = true;
msg.sender.transfer(address(this).balance);
}
// 读者赎回资金
function withdraw() external {
require(now > endTime, "还未到众筹结束时间");
require(!closed, "众筹达标, 众筹资金已提取");
require(Target > address(this).balance, "众筹达标,你没法提取资金");
msg.sender.transfer(joined[msg.sender]);
}
}
Compile smart contract
truffle compile
Deploy smart contracts
Create a deployment script under migrations, 2_crowfunding.js:
const crowd = artifacts.require("Crowdfunding");
module.exports = function (deployer) {
deployer.deploy(crowd);
};
Configure the network to be deployed in truffle-config.js, and at the same time ensure that Ganache is running, perform smart contract deployment:
truffle migrate
Crowdfunding front-end implementation
The scaffolding project created by Vue has a HelloWorld.vue component by default. We write our own CrowdFund.vue component and replace the HelloWorld.vue in App.vue.
App.vue is modified to:
<template>
<div id="app">
<CrowdFund/>
</div>
</template>
<script>
import CrowdFund from './components/CrowdFund.vue'
export default {
name: 'App',
components: {
CrowdFund
}
}
</script>
Then complete the crowdfunding interface and corresponding logic in CrowdFund.vue. The interface needs to display the following parts:
- The current amount of crowdfunding;
- The deadline for crowdfunding;
- The current crowdfunding price, participate in crowdfunding button;
- If it has already participated, the price of its participation and the redemption button will be displayed;
- If it is a creator, a button to withdraw funds is displayed.
CrowdFund.vue is modified as follows:
<template>
<div class="content">
<h3> 新书众筹</h3>
<span>以最低的价格获取我的新书 </span>
<!-- 众筹的总体状态 -->
<div class="status">
<div v-if="!closed">已众筹资金:<b>{{ total }} ETH </b></div>
<div v-if="closed"> 众筹已完成 </div>
<div>众筹截止时间:{{ endDate }}</div>
</div>
<!-- 当读者参与过,显示如下div -->
<div v-if="joined" class="card-bkg">
<div class="award-des">
<span> 参与价格 </span>
<b> {{ joinPrice }} ETH </b>
</div>
<button :disabled="closed" @click="withdraw">赎回</button>
</div>
<!-- 当读者还未参与时,显示如下div -->
<div v-if="!joined" class="card-bkg">
<div class="award-des">
<span> 当前众筹价格 </span>
<b> {{ price }} ETH </b>
</div>
<button :disabled="closed" @click="join">参与众筹</button>
</div>
<!-- 如果是创作者,显示 -->
<div class="box" v-if="isAuthor">
<button :disabled="closed" @click="withdrawFund"> 提取资金</button>
</div>
</div>
</template>
Continue to write the logic part of JavaScript, truffle-contract and web3 are required to interact with the contract, first install:
npm install --save truffle-contract web3
CrowdFund.vue is modified as follows:
<script>
import Web3 from "web3";
import contract from "truffle-contract";
import crowd from '../../build/contracts/Crowdfunding.json';
export default {
name: 'CrowdFund',
data() {
return {
price: null,
total: 0,
closed: true,
joinPrice: null,
joined: false,
endDate: "null",
isAuthor: true,
};
},
// 当前Vue组件被创建时回调的hook 函数
async created() {
// 初始化web3及账号
await this.initWeb3Account()
// 初始化合约实例
await this.initContract()
// 获取合约的状态信息
await this.getCrowdInfo()
},
methods: {
// 初始化 web3及账号
async initWeb3Account() {
if (window.ethereum) {
this.provider = window.ethereum;
try {
await window.ethereum.enable();
} catch (error) {
// console.log("User denied account access");
}
} else if (window.web3) {
this.provider = window.web3.currentProvider;
} else {
this.provider = new Web3.providers.HttpProvider("http://127.0.0.1:7545");
}
this.web3 = new Web3(this.provider);
this.web3.eth.getAccounts().then(accs => {
this.account = accs[0]
});
},
// 初始化合约实例
async initContract() {
const crowdContract = contract(crowd);
crowdContract.setProvider(this.provider);
this.crowdFund = await crowdContract.deployed();
},
// 获取合约的状态信息
async getCrowdInfo() {
// 获取合约的余额
this.web3.eth.getBalance(this.crowdFund.address).then(
r => {
this.total = this.web3.utils.fromWei(r)
}
);
// 获取读者的参与金额, joined 在合约中是public 的状态变量,自动生成相应的访问器函数
this.crowdFund.joined(this.account).then(
r => {
if (r > 0) {
this.joined = true
this.joinPrice = this.web3.utils.fromWei(r)
}
}
);
// 获取合约的关闭状态
this.crowdFund.closed().then(
r => this.closed = r
);
// 获取当前的众筹价格
this.crowdFund.price().then(
r => this.price = this.web3.utils.fromWei(r)
);
// 获取众筹截止时间
this.crowdFund.endTime().then(r => {
var endTime = new Date(r * 1000)
// 把时间戳转化为本地时间
this.endDate = endTime.toLocaleDateString().replace(/\//g, "-") + " " + endTime.toTimeString().substr(0, 8);
});
// 获取众筹创作者地址
this.crowdFund.author().then(r => {
if (this.account == r) {
this.isAuthor = true
} else {
this.isAuthor = false
}
});
},
// 读者点击参与众筹时调用
join() {
this.web3.eth.sendTransaction({
from: this.account,
to: this.crowdFund.address,
value: this.web3.utils.toWei(this.price)
}).then(() =>
this.getCrowdInfo()
);
},
// 赎回
withdraw() {
this.crowdFund.withdraw(
this.crowdFund.withdraw({
from: this.account
}).then(() => {
this.getCrowdInfo()
})
);
},
// 提取资金
withdrawFund() {
this.crowdFund.withdrawFund({
from: this.account
}).then(() => {
this.getCrowdInfo()
})
},
}
}
</script>
After that, all the crowdfunding cases are completed.
DAPP operation
Run the application in the project directory:
npm run serve
The browser address can visit http://localhost:8080 to experience the DAPP:
Pay attention to ensure that the network connected to MetaMask is the same as the network deployed by the contract, so that the sub-DAPP can obtain contract data through web3
DAPP release
npm run build
In the dist directory, build the complete front-end code released by the user. Copy it to the public network server for external services.
practice collation (1) Basic knowledge of
practice finishing (2) Geth client
practice finishing (3) Remix development and deployment of smart contracts
practice finishing (4) Truffle smart contract development framework
The practice of Ethereum (5) DApp development process record (part 1)
practice finishing (5) DApp development process record (part 2)
practice sorting (6) file decentralized storage
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。