Today we are going to write a complete decentralized (blockchain) application (Dapps). This article can be combined with writing smart contracts .
write in front
Before reading this article, you should have some understanding of Ethereum and smart contracts. If you don’t know it yet, I suggest you read what Ethereum is.
In addition, you’d better have some knowledge of HTML and JavaScript.
This article teaches you to develop decentralized applications through examples. The application effect is as follows:
From this article, you can learn:
- Build a smart contract development environment
- Create a Truffle project
- Write smart contracts
- Compile and deploy smart contracts to the blockchain
- How to interact with smart contracts via Web3
- Use of MetaMask
Small column users can download the complete Dapp code at the end of the tutorial.
Background of the project
Pete has a pet store with 16 pets, and he wants to develop a decentralized application for people to adopt pets.
In the truffle box, the code of the website part of the pet-shop has been provided, we only need to write the contract and interaction part.
Environment construction
Ganache (or Ganache CLI) has replaced testrpc.
Create project
1. Create a project directory and enter
> mkdir pet-shop-tutorial > cd pet-shop-tutorial
2. Use truffle unbox to create a project
> truffle unbox pet-shop Downloading... Unpacking... Setting up... Unbox successful. Sweet! Commands: Compile: truffle compile Migrate: truffle migrate Test contracts: truffle test Run dev server: npm run dev
You can also use truffle init to create a completely new project.
Project directory structure
contracts/
The smart contract folder, where all smart contract files are placed, contains an important contract, Migrations.sol (more on this later)
migrations/
that handles deploying (migrating) smart contracts, migrations are an extra special contract for Save contract changes.
test/
Smart Contract Test Case Folder
truffle.js/
Configuration File
Other codes can be ignored temporarily
Write smart contracts
Smart contracts are responsible for the background logic and storage of distributed applications. Smart contracts are written in solidity, you can read
the solidity series of articles
In the contracts directory, add the contract file Adoption.sol
pragma solidity ^ 0.4.17; contract Adoption { address[16] public adopters; // Save the address of adopters // Adopt a pet function adopt(uint petId) public returns (uint) { require(petId >= 0 && petId <= 15); // make sure the id is within the array length adopters[petId] = msg.sender; // save the calling address return petId; } // return the adopter function getAdopters() public view returns (address[16]) { return adopters; } }
Compile and deploy smart contracts
Truffle integrates a developer console that can be used to generate a development chain for testing and deploying smart contracts.
compile
Solidity is a compiled language, which requires readable Solidity code to be compiled into EVM bytecode to run.
Under the root directory pet-shop-tutorial of dapp,
> truffle compile
output
Compiling ./contracts/Adoption.sol... Writing artifacts to ./build/contracts
deploy
Once compiled, it can be deployed to the blockchain.
There is already a 1_initial_migration.js deployment script in the migrations folder to deploy the Migrations.sol contract.
Migrations.sol is used to ensure that the same contracts are not deployed.
Now let's create a deployment script of our own2_deploy_contracts.js
var Adoption = artifacts.require("Adoption"); module.exports = function(deployer) { deployer.deploy(Adoption); };
Before executing the deployment, you need to ensure that there is a blockchain running. You can use
Ganache to open a private chain for development and testing. By default, a development chain will run on port 7545.
This is what Ganache looks like after it starts:
Next execute the deploy command:
truffle migrate
After execution, there is a similar output,
Using network 'develop'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x3076b7dac65afc44ec51508bf6f2b6894f833f0f9560ecad2d6d41ed98a4679f Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0 Saving successful migration to network... ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying Adoption... ... 0x2c6ab4471c225b5473f2079ee42ca1356007e51d5bb57eb80bfeb406acc35cd4 Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10 Saving successful migration to network... ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0 Saving artifacts...
In the open Ganache, you can see the changes in the state of the blockchain, and now 4 blocks have been generated. At this point, the smart contract has been deployed.
test
Now let's test the smart contract, the test cases can be written in JavaScript or Solidity, here we use Solidity.
Create a new one in the test
directory TestAdoption.sol
and write a test contract
pragma solidity ^ 0.4.17; import "truffle/Assert.sol"; // Assertions introduced import "truffle/DeployedAddresses.sol"; // used to get the address of the tested contract import "../contracts/Adoption.sol"; // contract under test contract TestAdoption { Adoption adoption = Adoption(DeployedAddresses.Adoption()); // Adoption test case function testUserCanAdoptPet() public { uint returnedId = adoption.adopt(8); uint expected = 8; Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded."); } // pet owner test case function testGetAdopterAddressByPetId() public { // It is expected that the address of the adopter is the address of this contract, because the transaction is initiated by the test contract, address expected = this; address adopter = adoption.adopters(8); Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded."); } // Test all adopters function testGetAdopterAddressByPetIdInArray() public { // The address of the adopter is the address of this contract address expected = this; address[16] memory adopters = adoption.getAdopters(); Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded."); } }
Assert.sol and DeployedAddresses.sol are provided by the Truffle framework, and the truffle directory is not provided in the test directory.
Add the test case for adopt in the TestAdoption contract
run test cases
In a terminal, execute
truffle test
If the test passes, the terminal outputs:
Using network 'develop'. Compiling ./contracts/Adoption.sol... Compiling ./test/TestAdoption.sol... Compiling truffle/Assert.sol... Compiling truffle/DeployedAddresses.sol... TestAdoption ✓ testUserCanAdoptPet (62ms) ✓ testGetAdopterAddressByPetId (53ms) ✓ testGetAdopterAddressByPetIdInArray (73ms) 3 passing (554ms)
Create user interface and smart contract interaction
Now that we have written, deployed and tested our contract, let's write the UI for the contract so that the contract can actually be used.
In the Truffle Box pet-shop
, the front-end code of the application has been included, and the code is under the src/
folder.
Open in the editor and you src/js/app.js
can see the App object used to manage the entire application. The init function loads the pet information and initializes web3 .
Web3 is a library that implements communication with Ethereum nodes. We use web3 to interact with contracts.
initialize web3
Next, let's edit app.js and modify initWeb3():
delete the comment and modify it to:
initWeb3: function() { // Is there an injected web3 instance? if (typeof web3 !== 'undefined') { App.web3Provider = web3.currentProvider; } else { // If no injected web3 instance is detected, fall back to Ganache App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545'); } web3 = new Web3(App.web3Provider); return App.initContract(); }
The code prefers to use the web3 instance provided by Mist or MetaMask , if not, create one from the local environment.
instantiate the contract
Using truffle-contract will help us save the contract deployment information, so we don't need to manually modify the contract address. Modify the initContract() code as follows:
initContract: function() { // Load Adoption.json, which saves Adoption's ABI (interface description) information and deployed network (address) information. It generates ABI when compiling the contract, and appends network information when deploying $.getJSON('Adoption.json', function(data) { // Create an interactive TruffleContract instance with Adoption.json data. var AdoptionArtifact = data; App.contracts.Adoption = TruffleContract(AdoptionArtifact); // Set the provider for our contract App.contracts.Adoption.setProvider(App.web3Provider); // Use our contract to retrieve and mark the adopted pets return App.markAdopted(); }); return App.bindEvents(); }
Handling adoption
Modify the markAdopted() code:
markAdopted: function(adopters, account) { was adoptionInstance; App.contracts.Adoption.deployed().then(function(instance) { adoptionInstance = instance; // Call getAdopters() of the contract, use call to read information without consuming gas return adoptionInstance.getAdopters.call(); }).then(function(adopters) { for (i = 0; i < adopters.length; i++) { if (adopters[i] !== '0x0000000000000000000000000000000000000000') { $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true); } } }).catch(function(err) { console.log(err.message); }); }
Modify handleAdopt() code:
handleAdopt: function(event) { event.preventDefault(); var petId = parseInt($(event.target).data('id')); was adoptionInstance; // get user account web3.eth.getAccounts(function(error, accounts) { if (error) { console.log(error); } var account = accounts[0]; App.contracts.Adoption.deployed().then(function(instance) { adoptionInstance = instance; // Send transaction to adopt pet return adoptionInstance.adopt(petId, {from: account}); }).then(function(result) { return App.markAdopted(); }).catch(function(err) { console.log(err.message); }); }); }
run in browser
Install MetaMask
MetaMask is an Ethereum light client in the form of a plug-in. It is a good choice to use MetaMask to interact with our dapp during the development process. Install it through this link . After the installation is complete, a small fox icon will be displayed on the browser toolbar.
Configure wallet
After accepting the privacy statement, the following page will appear:
Here we restore a wallet created for us by Ganache as our development and test wallet. Click Import Existing DEN on the page and enter the mnemonic displayed by Ganache.
spoon chief pass thunder any eagle force rally body annual roof trip
Then the desired password, click OK.
As shown in the figure:
Connect to develop a blockchain network
The default connection is the Ethereum main network (displayed in the upper left corner), select Custom RPC , add a network: http://127.0.0.1:7545 , after clicking back, the display is as follows: This is the upper left corner displayed as Private Network , and the account is Ganache The default first account in .
So far, the installation and configuration of MetaMask has been completed.
Install and configure lite-server
Next, a local web server is required to provide service access. A lite-server is provided in the Truffle Box pet-shop that can be used directly. Let's see how it works. bs-config.json indicates the working directory of lite-server.
{ "server": { "baseDir": ["./src", "./build/contracts"] } }
./src is the website file directory
./build/contracts is the contract output directory
At the same time, the dev command was added to the scripts in the package.json file:
"scripts": { "dev": "lite-server", "test": "echo \"Error: no test specified\" && exit 1" },
When running npm run dev, it will start lite-server
start the service
> npm run dev
The browser will automatically open to display our dapp, as shown in the first picture in this article.
Now we have adopted a pet and have a look. When we click on Adopt , MetaMask will prompt us to confirm the transaction, as shown in the figure:
After clicking Submit to confirm, you can see that the pet has been adopted successfully.
In MetaMask, the list of transactions can also be seen:
Well, congratulations, you have taken a solid step towards becoming a decentralized application developer.
If you encounter problems in learning, please come to my knowledge planet to exchange.