想过如何创建自己的区块链应用吗?当涉及到以太坊时,它从智能合约开始。
在本教程中,我们将重点讨论用Solidity语言编写的智能合约。我们将使用Truffle套件来部署本地版本的以太坊区块链,并使用以太坊虚拟机(EVM)编译智能合约。
如果你不熟悉上述技术,我建议首先阅读我们的使用Web3.js的Ethereum区块链开发指南。
什么是Truffle套件?
以太坊是一个区块链,允许应用程序在其上运行。代码是以智能合约的形式用Solidity语言编写的。为了编译这些合约,我们需要一个Ethereum编译器,它可以将智能合约转换为机器可读代码。
Truffle套件是一个专门为以太坊上的区块链开发而制作的工具集合。该套件包括三件软件。
设置库和软件
在本教程中,我们需要一些节点包和软件,包括以太坊区块链、智能合约编译器和一个用于通信的JavaScript库。
安装Ganache
智能合约在以太坊区块链上运行,所以我们需要一个智能合约来部署和测试。我们也可以在活链上进行部署,但这将花费我们的以太币作为汽油费。所以让我们建立一个本地链,在那里做测试。当你对代码有把握并准备发布你的应用程序时,你就可以在实时链上部署了。
Ganache是本地链,安装在我们的电脑上,并在本地主机上运行。从Truffle Suite网站下载并安装Ganache。
你可以看到,Ganache提供了10个账户,每个账户有100个ETH。这些是假的Ethers,所以不要激动。另外,该链运行在127.0.0.1的7545端口。我们将使用这些账户在这条链上部署我们的智能合约。Ethers将帮助我们支付汽油费。
安装Node.js
我们需要Node.js来完成这个项目,所以要确保它已经安装在你的系统上。你可以从Node.js的官方网站上下载。
安装Web3.js
Web3.js是一个JavaScript库,能够通过JavaScript与Ethereum区块链进行通信。它有很多内置的功能,在开发中很有用。
你可以使用npm 或yarn 来安装Web3.js。
npm install web3 -g
安装Truffle
Truffle提供了智能合约的编译器。我们需要它将Solidity代码转换为机器可读的代码,从而可以部署在Ganache区块链上。
使用以下命令安装Truffle。
npm install truffle -g
创建智能合约
为了创建智能合约,我们首先需要创建一个项目目录,在那里我们将保存所有的Solidity文件。让我们创建一个名称为solidity ,并在终端使用cd solidity ,移动到该目录。
现在,我们的项目是空的。为了使用它,我们需要一些模板代码。例如,如果你想用React创建用户界面,你需要安装React。
Truffle已经提供了一些包,这些包被称为box。这些包是不同框架的捆绑,如Truffle、Ganache、React、Web3和Redux,还有一个是针对Vue.js开发者的。它们一起完成了端到端的应用开发,从客户端UI到区块链智能合约。
在这篇文章中,我们将使用Truffle提供的React盒子。
安装React盒子
要安装React盒子,请运行以下命令。
truffle unbox react
这将安装Web3.js、React、Ganache CLI、Truffle和Ethereum。从某种意义上说,我们并不真的需要在第一步就安装所有这些工具。
在本教程中,我们不会关注React或基于浏览器的用户界面。相反,我们将创建智能合约,只用终端来处理它们。
我们项目的目录结构将看起来像这样。
这里,client 是一个React项目文件夹,我们可以在这里创建应用程序的用户界面。里面有一个文件夹(client/src/contracts),存放着JSON格式的编译后的智能合约。这些文件是在我们编译智能合约的时候产生的。它们包含ABI、字节码和其他信息。
从上面的图片中,你可以看到,我们有两个JSON文件。这是因为Truffle在我们拆开React捆绑包时已经提供了两个智能合约。如果你打开其中任何一个,你会发现像这样的代码。
{
"contractName": "SimpleStorage",
"abi": [
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "x",
"type": "uint256"
}
],
"name": "set",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "get",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
"metadata": "{\"compiler\":{\"version\":\"0.5.16+commit.9c3226ce\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"methods\":{}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"project:/contracts/SimpleStorage.sol\":\"SimpleStorage\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"project:/contracts/SimpleStorage.sol\":{\"keccak256\":\"0x512df1603c5f878921707d236bc53d974afe05b4d9de4b6094249bac5ab60efe\",\"urls\":[\"bzz-raw://0d6de97971b1c387f984fa7ea1d9ec10f8a63d68cc63bf8bd00d8c3a7c9e3ee1\",\"dweb:/ipfs/Qmbt92T34sHzedfJjDsvbisvLhRtghNwS6VW8tqrGkrqTD\"]}},\"version\":1}",
"bytecode": "0x608060405234801561001057600080fd5b5060c68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea265627a7a7231582044ccb2c2d46346d523107088f3e26a4c8a2ec3ec8b2e3a6edb1bc8574d5c5f5264736f6c63430005100032",
"deployedBytecode": "0x6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea265627a7a7231582044ccb2c2d46346d523107088f3e26a4c8a2ec3ec8b2e3a6edb1bc8574d5c5f5264736f6c63430005100032",
"sourceMap": "66:176:1:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;66:176:1;;;;;;;",
"deployedSourceMap": "66:176:1:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;66:176:1;;;;;;;;;;;;;;;;;;;;;;;;113:53;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;113:53:1;;;;;;;;;;;;;;;;;:::i;:::-;;170:70;;;:::i;:::-;;;;;;;;;;;;;;;;;;;113:53;160:1;147:10;:14;;;;113:53;:::o;170:70::-;206:4;225:10;;218:17;;170:70;:::o",
"source": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.21 <0.7.0;\n\ncontract SimpleStorage {\n uint storedData;\n\n function set(uint x) public {\n storedData = x;\n }\n\n function get() public view returns (uint) {\n return storedData;\n }\n}\n",
"sourcePath": "C:\\Users\\akash\\Desktop\\solidity\\contracts\\SimpleStorage.sol",
"ast": {
"absolutePath": "project:/contracts/SimpleStorage.sol",
"exportedSymbols": {
"SimpleStorage": [
59
]
},
"id": 60,
"nodeType": "SourceUnit",
"nodes": [
{
"id": 38,
"literals": [
"solidity",
">=",
"0.4",
".21",
"<",
"0.7",
".0"
],
"nodeType": "PragmaDirective",
"src": "32:32:1"
},
{
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"documentation": null,
"fullyImplemented": true,
"id": 59,
"linearizedBaseContracts": [
59
],
"name": "SimpleStorage",
"nodeType": "ContractDefinition",
"nodes": [
{
"constant": false,
"id": 40,
"name": "storedData",
"nodeType": "VariableDeclaration",
"scope": 59,
"src": "93:15:1",
"stateVariable": true,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 39,
"name": "uint",
"nodeType": "ElementaryTypeName",
"src": "93:4:1",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"value": null,
"visibility": "internal"
},
{
"body": {
"id": 49,
"nodeType": "Block",
"src": "141:25:1",
"statements": [
{
"expression": {
"argumentTypes": null,
"id": 47,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"leftHandSide": {
"argumentTypes": null,
"id": 45,
"name": "storedData",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 40,
"src": "147:10:1",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"nodeType": "Assignment",
"operator": "=",
"rightHandSide": {
"argumentTypes": null,
"id": 46,
"name": "x",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 42,
"src": "160:1:1",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"src": "147:14:1",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"id": 48,
"nodeType": "ExpressionStatement",
"src": "147:14:1"
}
]
},
"documentation": null,
"id": 50,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "set",
"nodeType": "FunctionDefinition",
"parameters": {
"id": 43,
"nodeType": "ParameterList",
"parameters": [
{
"constant": false,
"id": 42,
"name": "x",
"nodeType": "VariableDeclaration",
"scope": 50,
"src": "126:6:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 41,
"name": "uint",
"nodeType": "ElementaryTypeName",
"src": "126:4:1",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"value": null,
"visibility": "internal"
}
],
"src": "125:8:1"
},
"returnParameters": {
"id": 44,
"nodeType": "ParameterList",
"parameters": [],
"src": "141:0:1"
},
"scope": 59,
"src": "113:53:1",
"stateMutability": "nonpayable",
"superFunction": null,
"visibility": "public"
},
{
"body": {
"id": 57,
"nodeType": "Block",
"src": "212:28:1",
"statements": [
{
"expression": {
"argumentTypes": null,
"id": 55,
"name": "storedData",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 40,
"src": "225:10:1",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"functionReturnParameters": 54,
"id": 56,
"nodeType": "Return",
"src": "218:17:1"
}
]
},
"documentation": null,
"id": 58,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "get",
"nodeType": "FunctionDefinition",
"parameters": {
"id": 51,
"nodeType": "ParameterList",
"parameters": [],
"src": "182:2:1"
},
"returnParameters": {
"id": 54,
"nodeType": "ParameterList",
"parameters": [
{
"constant": false,
"id": 53,
"name": "",
"nodeType": "VariableDeclaration",
"scope": 58,
"src": "206:4:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName": {
"id": 52,
"name": "uint",
"nodeType": "ElementaryTypeName",
"src": "206:4:1",
"typeDescriptions": {
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"value": null,
"visibility": "internal"
}
],
"src": "205:6:1"
},
"scope": 59,
"src": "170:70:1",
"stateMutability": "view",
"superFunction": null,
"visibility": "public"
}
],
"scope": 60,
"src": "66:176:1"
}
],
"src": "32:211:1"
},
"legacyAST": {
"attributes": {
"absolutePath": "project:/contracts/SimpleStorage.sol",
"exportedSymbols": {
"SimpleStorage": [
59
]
}
},
"children": [
{
"attributes": {
"literals": [
"solidity",
">=",
"0.4",
".21",
"<",
"0.7",
".0"
]
},
"id": 38,
"name": "PragmaDirective",
"src": "32:32:1"
},
{
"attributes": {
"baseContracts": [
null
],
"contractDependencies": [
null
],
"contractKind": "contract",
"documentation": null,
"fullyImplemented": true,
"linearizedBaseContracts": [
59
],
"name": "SimpleStorage",
"scope": 60
},
"children": [
{
"attributes": {
"constant": false,
"name": "storedData",
"scope": 59,
"stateVariable": true,
"storageLocation": "default",
"type": "uint256",
"value": null,
"visibility": "internal"
},
"children": [
{
"attributes": {
"name": "uint",
"type": "uint256"
},
"id": 39,
"name": "ElementaryTypeName",
"src": "93:4:1"
}
],
"id": 40,
"name": "VariableDeclaration",
"src": "93:15:1"
},
{
"attributes": {
"documentation": null,
"implemented": true,
"isConstructor": false,
"kind": "function",
"modifiers": [
null
],
"name": "set",
"scope": 59,
"stateMutability": "nonpayable",
"superFunction": null,
"visibility": "public"
},
"children": [
{
"children": [
{
"attributes": {
"constant": false,
"name": "x",
"scope": 50,
"stateVariable": false,
"storageLocation": "default",
"type": "uint256",
"value": null,
"visibility": "internal"
},
"children": [
{
"attributes": {
"name": "uint",
"type": "uint256"
},
"id": 41,
"name": "ElementaryTypeName",
"src": "126:4:1"
}
],
"id": 42,
"name": "VariableDeclaration",
"src": "126:6:1"
}
],
"id": 43,
"name": "ParameterList",
"src": "125:8:1"
},
{
"attributes": {
"parameters": [
null
]
},
"children": [],
"id": 44,
"name": "ParameterList",
"src": "141:0:1"
},
{
"children": [
{
"children": [
{
"attributes": {
"argumentTypes": null,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"operator": "=",
"type": "uint256"
},
"children": [
{
"attributes": {
"argumentTypes": null,
"overloadedDeclarations": [
null
],
"referencedDeclaration": 40,
"type": "uint256",
"value": "storedData"
},
"id": 45,
"name": "Identifier",
"src": "147:10:1"
},
{
"attributes": {
"argumentTypes": null,
"overloadedDeclarations": [
null
],
"referencedDeclaration": 42,
"type": "uint256",
"value": "x"
},
"id": 46,
"name": "Identifier",
"src": "160:1:1"
}
],
"id": 47,
"name": "Assignment",
"src": "147:14:1"
}
],
"id": 48,
"name": "ExpressionStatement",
"src": "147:14:1"
}
],
"id": 49,
"name": "Block",
"src": "141:25:1"
}
],
"id": 50,
"name": "FunctionDefinition",
"src": "113:53:1"
},
{
"attributes": {
"documentation": null,
"implemented": true,
"isConstructor": false,
"kind": "function",
"modifiers": [
null
],
"name": "get",
"scope": 59,
"stateMutability": "view",
"superFunction": null,
"visibility": "public"
},
"children": [
{
"attributes": {
"parameters": [
null
]
},
"children": [],
"id": 51,
"name": "ParameterList",
"src": "182:2:1"
},
{
"children": [
{
"attributes": {
"constant": false,
"name": "",
"scope": 58,
"stateVariable": false,
"storageLocation": "default",
"type": "uint256",
"value": null,
"visibility": "internal"
},
"children": [
{
"attributes": {
"name": "uint",
"type": "uint256"
},
"id": 52,
"name": "ElementaryTypeName",
"src": "206:4:1"
}
],
"id": 53,
"name": "VariableDeclaration",
"src": "206:4:1"
}
],
"id": 54,
"name": "ParameterList",
"src": "205:6:1"
},
{
"children": [
{
"attributes": {
"functionReturnParameters": 54
},
"children": [
{
"attributes": {
"argumentTypes": null,
"overloadedDeclarations": [
null
],
"referencedDeclaration": 40,
"type": "uint256",
"value": "storedData"
},
"id": 55,
"name": "Identifier",
"src": "225:10:1"
}
],
"id": 56,
"name": "Return",
"src": "218:17:1"
}
],
"id": 57,
"name": "Block",
"src": "212:28:1"
}
],
"id": 58,
"name": "FunctionDefinition",
"src": "170:70:1"
}
],
"id": 59,
"name": "ContractDefinition",
"src": "66:176:1"
}
],
"id": 60,
"name": "SourceUnit",
"src": "32:211:1"
},
"compiler": {
"name": "solc",
"version": "0.5.16+commit.9c3226ce.Emscripten.clang"
},
"networks": {
"5777": {
"events": {},
"links": {},
"address": "0xc21DB75B9B17Cb43Cd983A16BaA6c73b314B1E8f",
"transactionHash": "0xada7b561df968c3d23485d25cbd22a66d1d4b76a86137dbf1bef858e816ff0c2"
}
},
"schemaVersion": "3.4.3",
"updatedAt": "2021-10-29T10:14:41.665Z",
"networkType": "ethereum",
"devdoc": {
"methods": {}
},
"userdoc": {
"methods": {}
}
}
上面的大量代码块包含ABI、字节码、源代码和大量的其他信息。从ABI中我们可以得到可调用函数的列表。这个文件包含了所有的信息,如网络ID、合同地址、交易哈希、编译器细节、链的细节等等。
你可能想知道为什么这个文件会在UI目录下?那是因为Web3.js使用它来进行操作。这个文件使Web3.js能够识别链上的智能合约。同时,ABI也有助于获得函数列表。
contracts是存放 Solidity 文件的目录。您将把所有的源代码放在这里migrations是通知Truffle哪些Solidity文件需要被迁移到链上的文件。test是用于创建测试文件truffle-config.js包含 Truffle 的一些配置设置。
这里是truffle-config.js 的内容。
const path = require("path");
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
contracts_build_directory: path.join(__dirname, "client/src/contracts"),
networks: {
develop: {
port: 8545
}
}
};
正如你所看到的,client/src/contracts 被设置为持有编译的代码。开发网络被设置为8545端口。这是这个盒子运行Ganache的端口。
如果你看一下我们安装Ganache的顶部,你可以看到它是在7545端口运行的,但这个是在8545运行的,因为7545已经被我们安装的Ganache使用了。如果你愿意,你可以把这个端口改为7545,这样Truffle就会使用我们安装的Ganache和账户,而不是box提供的。我将它保持在8545。
在Solidity中编写智能合约
现在是时候写一些代码了。我们将进行CRUD操作并管理一个水果列表。
基本上,我们的应用程序将显示一个不同水果的列表。你可以添加、更新和删除这些水果。
如果你以前开发过应用程序,我们将遵循一个你肯定熟悉的程序。
- 创建一个数组来保存水果的名称
- 创建一个函数,向数组推送一个新值
- 创建一个函数来改变给定索引上的值
- 创建一个函数来删除一个值
- 创建一个函数来返回数组
现在让我们在Solidity中编写代码。
在contracts 目录中创建一个新文件,并将其称为Fruits.sol 。我们将在文件的开头指出我们所支持的许可证和 solidity 版本。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
接下来,声明contract 范围,我们将在这里写下所有的代码。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
contract Fruits {
}
创建一个数组来存放水果。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
contract Fruits {
string[] myFruits;
}
这是一个带有私有修饰符的字符串数组,这意味着它不能在合同之外被访问,因此我们不能直接改变其值。
接下来,创建一个函数来添加一个新的值。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
contract Fruits {
string[] myFruits;
function addFruit(string memory fruitName) public {
myFruits.push(fruitName);
}
}
我们创建了一个名为addFruit 的函数,它接受一个字符串作为参数,fruitName 。这被声明为公共的,所以它可以被用户界面或终端调用。在函数主体中,我们只是将值推送到数组中。
更新该值。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
contract Fruits {
string[] myFruits;
function addFruit(string memory fruitName) public {
myFruits.push(fruitName);
}
function updateFruit(uint fruitIndex, string memory newFruitName) public returns (bool) {
if(myFruits.length > fruitIndex){
myFruits[fruitIndex] = newFruitName;
return true;
}
return false;
}
}
updateFruit 接受两个参数,fruitIndex 和newFruitName ,并返回一个布尔值。它的工作原理是这样的:如果索引超出了数组的范围,它返回false 。否则,它在提供的索引处用一个新的提供的果实名称改变数组的值,并返回true 。
接下来的步骤是创建删除函数。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
contract Fruits {
string[] myFruits;
function addFruit(string memory fruitName) public {
myFruits.push(fruitName);
}
function updateFruit(uint fruitIndex, string memory newFruitName) public returns (bool) {
if(myFruits.length > fruitIndex){
myFruits[fruitIndex] = newFruitName;
return true;
}
return false;
}
function deleteFruit(uint fruitIndex) public returns (bool) {
if(myFruits.length > fruitIndex){
for(uint i=fruitIndex; i < myFruits.length-1; i++){
myFruits[i] = myFruits[i+1];
}
myFruits.pop();
return true;
}
return false;
}
}
在这里,我们要检查索引是否出界,然后通过用所提供的索引的下一个值来替换数组的值来更新数组。这样一来,所提供的索引处的值就会丢失。最后,我们跳出最后一个值并返回true 。
最后一步是返回数组。要读取数组的所有值。
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;
contract Fruits {
string[] myFruits;
function addFruit(string memory fruitName) public {
myFruits.push(fruitName);
}
function updateFruit(uint fruitIndex, string memory newFruitName) public returns (bool) {
if(myFruits.length > fruitIndex){
myFruits[fruitIndex] = newFruitName;
return true;
}
return false;
}
function deleteFruit(uint fruitIndex) public returns (bool) {
if(myFruits.length > fruitIndex){
for(uint i=fruitIndex; i < myFruits.length-1; i++){
myFruits[i] = myFruits[i+1];
}
myFruits.pop();
return true;
}
return false;
}
function getFruits() public view returns (string[] memory) {
return myFruits;
}
}
使用Truffle编译智能合约
现在我们已经完成了智能合约的编码,是时候使用Truffle编译它了。但首先我们需要创建一个迁移文件,向Truffle表明我们想把这个迁移到链上。
如果你在migration 文件夹中检查,你会发现两个JavaScript文件。
它们各自以一个数字开头,所以我们的第三个文件将以3开头,以此类推。这些代码几乎是标准的。对于2_deploy_contracts ,它是。
var SimpleStorage = artifacts.require("./SimpleStorage.sol");
module.exports = function(deployer) {
deployer.deploy(SimpleStorage);
};
让我们添加我们的迁移文件,3_fruits_contracts 。
var FruitsList = artifacts.require("./Fruits.sol");
module.exports = function(deployer) {
deployer.deploy(FruitsList);
};
我们已经准备好编译和迁移我们的水果合同了。移动到终端,运行以下命令。
> truffle develop
这个命令将启动truffle控制台。它还会显示一些信息,如链网、账户、Mnemonic等。
Ganache默认提供10个账户。它们对你来说将是不同的。请不要在活链上使用任何这些私钥,因为它们对本文的所有访问者都是可见的,也就是说任何人都可以访问这些账户。
现在用这个命令编译合约。
> compile
你可以在client/src/contracts ,检查是否创建了Fruits.json 文件。
我们可以用这个命令将编译后的文件迁移到链上。
> migrate
这将把所有三个智能合约迁移到链上。
最后,我们的应用程序在以太坊链上了。你可以看到,我们花了0.00153个Ethers的汽油费,交易从第一个账户发生。默认情况下,它总是采取第一个账户。我们现在可以执行各种操作。
获取水果列表
我们可以使用Web3.js来反应和编写各种数值。首先让我们把我们的合同实例存储在一个变量中。
let instance = await Fruits.deployed()
我们使用await ,因为区块链中的一切都是异步的,并返回一个承诺。
现在使用这个实例来获取数组。
> let fruits = instance.getFruits()
undefined
> fruits
[]
它将返回一个空数组,因为目前,我们的水果数组没有值。
添加一个水果到列表中
让我们添加一些水果。
> let result = await instance.addFruit("Apple")
undefined
result 将持有该事务。请记住,所有的读取操作都是免费的,但任何导致区块链变化的操作都要收取一定的气体费用。这个操作是给数组增加一个值,从而改变数据。因此,它被记录为一个交易。
现在我们可以再次读取数组以检查其内容。
让我们再向列表中添加一些水果。
> await instance.addFruit("Mango")
> await instance.addFruit("Banana")
> await instance.addFruit("Orange")
> await instance.addFruit("Guava")
> await instance.addFruit("Pineapple")
> await instance.addFruit("Water Melon")
> await instance.addFruit("Papaya")
> await instance.addFruit("Strawberry")
记住,所有这些操作都将花费你的以太币。你可以在你的合约中创建一个函数,用于一次接受多个水果值,从而节省费用。
现在就读数组。
更新一个水果的名字
你可以在上图中看到,我把 "Guava "错拼成了 "Guavva"。让我们用updateFruit 函数来纠正它。它将接受索引和新值。索引是4。
> await instance.updateFruit(4, "Guava")
现在让我们读一下数组。
拼写已经成功修正。
删除一个水果名称
最后一个操作是删除一个值。
> await instance.deleteFruit(4)
读取数值。
正如你所看到的,"Guava "这一项已经从列表中消失了。
结论
创建智能合约并在区块链上部署是有趣而强大的。它给了传统编程一个新的视角。你可以使用这些技术创建各种应用程序,如在线投票、数字银行、钱包、拍卖等。
在本教程中,我们演示了如何创建一个智能合约并将其部署在本地链上。现在你可以尝试在以太坊的测试网络上进行部署。
你可以通过引入更多的功能来改进当前的应用程序,例如一次添加多个水果以节省交易成本。你也可以放一个检查,只允许唯一的水果。不使用数组,而尝试使用映射。
编码愉快!
The postTruffle Suite tutorial:如何开发以太坊智能合约首次出现在LogRocket博客上。