Truffle Suite教程。如何开发Ethereum智能合约

864 阅读12分钟

想过如何创建自己的区块链应用吗?当涉及到以太坊时,它从智能合约开始。

在本教程中,我们将重点讨论用Solidity语言编写的智能合约。我们将使用Truffle套件来部署本地版本的以太坊区块链,并使用以太坊虚拟机(EVM)编译智能合约。

如果你不熟悉上述技术,我建议首先阅读我们的使用Web3.js的Ethereum区块链开发指南

什么是Truffle套件?

以太坊是一个区块链,允许应用程序在其上运行。代码是以智能合约的形式用Solidity语言编写的。为了编译这些合约,我们需要一个Ethereum编译器,它可以将智能合约转换为机器可读代码。

Truffle套件是一个专门为以太坊上的区块链开发而制作的工具集合。该套件包括三件软件。

  1. Truffle,一个用于智能合约开发的框架
  2. Ganache,它使你能够在本地网络上设置一个个人的以太坊区块链,用于测试和开发
  3. Drizzle,用于创建DApp的用户界面,包括一个现成的组件集合

设置库和软件

在本教程中,我们需要一些节点包和软件,包括以太坊区块链、智能合约编译器和一个用于通信的JavaScript库。

安装Ganache

智能合约在以太坊区块链上运行,所以我们需要一个智能合约来部署和测试。我们也可以在活链上进行部署,但这将花费我们的以太币作为汽油费。所以让我们建立一个本地链,在那里做测试。当你对代码有把握并准备发布你的应用程序时,你就可以在实时链上部署了。

Ganache是本地链,安装在我们的电脑上,并在本地主机上运行。从Truffle Suite网站下载并安装Ganache。

Truffle Suite Website

你可以看到,Ganache提供了10个账户,每个账户有100个ETH。这些是假的Ethers,所以不要激动。另外,该链运行在127.0.0.1的7545端口。我们将使用这些账户在这条链上部署我们的智能合约。Ethers将帮助我们支付汽油费。

安装Node.js

我们需要Node.js来完成这个项目,所以要确保它已经安装在你的系统上。你可以从Node.js的官方网站上下载。

安装Web3.js

Web3.js是一个JavaScript库,能够通过JavaScript与Ethereum区块链进行通信。它有很多内置的功能,在开发中很有用。

你可以使用npmyarn 来安装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或基于浏览器的用户界面。相反,我们将创建智能合约,只用终端来处理它们。

我们项目的目录结构将看起来像这样。

Directory Structure

这里,client 是一个React项目文件夹,我们可以在这里创建应用程序的用户界面。里面有一个文件夹(client/src/contracts),存放着JSON格式的编译后的智能合约。这些文件是在我们编译智能合约的时候产生的。它们包含ABI、字节码和其他信息。

Two JSON Files

从上面的图片中,你可以看到,我们有两个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操作并管理一个水果列表。

基本上,我们的应用程序将显示一个不同水果的列表。你可以添加、更新和删除这些水果。

如果你以前开发过应用程序,我们将遵循一个你肯定熟悉的程序。

  1. 创建一个数组来保存水果的名称
  2. 创建一个函数,向数组推送一个新值
  3. 创建一个函数来改变给定索引上的值
  4. 创建一个函数来删除一个值
  5. 创建一个函数来返回数组

现在让我们在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 接受两个参数,fruitIndexnewFruitName ,并返回一个布尔值。它的工作原理是这样的:如果索引超出了数组的范围,它返回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文件。

Javascript Files

它们各自以一个数字开头,所以我们的第三个文件将以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 Console

这个命令将启动truffle控制台。它还会显示一些信息,如链网、账户、Mnemonic等。

Ganache默认提供10个账户。它们对你来说将是不同的。请不要在活链上使用任何这些私钥,因为它们对本文的所有访问者都是可见的,也就是说任何人都可以访问这些账户。

现在用这个命令编译合约。

> compile

Compile

你可以在client/src/contracts ,检查是否创建了Fruits.json 文件。

我们可以用这个命令将编译后的文件迁移到链上。

> migrate

这将把所有三个智能合约迁移到链上。

Starting Migration
Deploy Contracts
Fruits Contracts

Summary

最后,我们的应用程序在以太坊链上了。你可以看到,我们花了0.00153个Ethers的汽油费,交易从第一个账户发生。默认情况下,它总是采取第一个账户。我们现在可以执行各种操作。

获取水果列表

我们可以使用Web3.js来反应和编写各种数值。首先让我们把我们的合同实例存储在一个变量中。

let instance = await Fruits.deployed()

我们使用await ,因为区块链中的一切都是异步的,并返回一个承诺。

现在使用这个实例来获取数组。

> let fruits = instance.getFruits()
undefined
> fruits
[]

它将返回一个空数组,因为目前,我们的水果数组没有值。

Getfruits

添加一个水果到列表中

让我们添加一些水果。

> let result = await instance.addFruit("Apple")
undefined

result 将持有该事务。请记住,所有的读取操作都是免费的,但任何导致区块链变化的操作都要收取一定的气体费用。这个操作是给数组增加一个值,从而改变数据。因此,它被记录为一个交易。

Let Result

现在我们可以再次读取数组以检查其内容。

Await Instance

让我们再向列表中添加一些水果。

> 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")

记住,所有这些操作都将花费你的以太币。你可以在你的合约中创建一个函数,用于一次接受多个水果值,从而节省费用。

现在就读数组。

Truffle Develop Fruits

更新一个水果的名字

你可以在上图中看到,我把 "Guava "错拼成了 "Guavva"。让我们用updateFruit 函数来纠正它。它将接受索引和新值。索引是4。

> await instance.updateFruit(4, "Guava")

现在让我们读一下数组。

Spelling Fixed

拼写已经成功修正。

删除一个水果名称

最后一个操作是删除一个值。

> await instance.deleteFruit(4)

读取数值。

Guava Item Gone

正如你所看到的,"Guava "这一项已经从列表中消失了。

结论

创建智能合约并在区块链上部署是有趣而强大的。它给了传统编程一个新的视角。你可以使用这些技术创建各种应用程序,如在线投票、数字银行、钱包、拍卖等。

在本教程中,我们演示了如何创建一个智能合约并将其部署在本地链上。现在你可以尝试在以太坊的测试网络上进行部署。

你可以通过引入更多的功能来改进当前的应用程序,例如一次添加多个水果以节省交易成本。你也可以放一个检查,只允许唯一的水果。不使用数组,而尝试使用映射。

编码愉快!

The postTruffle Suite tutorial:如何开发以太坊智能合约首次出现在LogRocket博客上。