智能合约开发

· 2685 字 · 6 分钟 · 黄国政

Dapp #

  • 去中心化应用。
  • 运行在区块链或分布式网络上,而非传统服务器。
  • 去中心化技术栈 + 智能合约编程 + 前端与区块链的交互方式。

Dapp 与传统应用的比较:

对传统应用来说,相当于去银行办业务,到前台(前端)填写表单,由柜员(后端服务器)处理我们的请求,这个过程是我们与柜员直接沟通,然后完成操作。但在 Dapp 中主要是通过代理人办理区块链业务,到前台(前端网页)填写表单,然后私人代理(钱包,如 MetaMask)会验证我们的身份,帮我们签字。邮递员(RPC 节点)负责传递信息,最后由公证处(区块链)来进行最终的执行和记录。

Dapp 架构 #

  • 前端

    • Dapp 与用户交互的界面(由 HTML、CSS、Javascript、React、Rust 构建)。
    • Dapp 前端不直接连接区块链网络,通过「钱包注入的 provider」或「第三方 RPC 节点」与区块链交互。
      • 需要区块链钱包(如 MetaMask)进行身份验证和签署交易,确保隐私和安全。
      • 通过 RPC 节点对智能合约发起只读调用,获取合约状态、事件日志等链上数据(通俗来说,「只读」可以被理解为想查看账户余额,前端通过 RPC 节点,然后直接读取区块链数据,最后现实结果)。
      • 对需要修改状态的操作,由前端构造对智能合约的交易调用,交由钱包完成签名后,再通过 RPC 节点广播到区块链网络并最终上链执行(这里可以简单理解为想转账,然后前端构造交易,钱包随之弹出确认窗口,RPC 节点再广播到区块链,矿工进行打包执行,状态发生改变)。
补充
RPC:一种通信协议,允许应用程序通过网络调用远程服务器上的函数或方法。可以被理解为区块链网络的「入口」,帮助发送请求和接收数据。
  • 智能合约

    • Dapp 的核心,定义应用的业务逻辑。
    • 部署在区块链上。
    • 由 Solidity 编写,通过 EVM 执行。
  • 数据检索器

    • 智能合约以 Event 的形式释放日志事件,数据检索器检索这些数据并写入 PostgreSOL 等传统数据库中(这部分还是感到困惑)。
  • 区块链和去中心化存储

    • 区块链存储智能合约的状态数据及交易记录。
    • 去中心化存储包括 IPFS 或 Arweave,存储大规模非结构化数据,如图片、视频、文档等。
      • 去中心化存储使得所有数据在多个节点上备份,确保数据持久性和去中心化特性。

Dapp 开发流程 #

(1)需求分析

  • 功能需求:转账?查询余额?创建投票?
  • 区块链平台选择:在哪个平台构建 Dapp?以太坊?Solana?Polygon?
  • 用户体验设计:Dapp 界面设计和交互流程。

(2)智能合约开发

  • 编写智能合约:使用 Solidity 或其他相关语言编写合约,确保满足(1)需求分析。
  • 编写测试用例:为智能合约编写单元测试,确保合约逻辑正确。
  • 审计和优化:对合约进行安全审计,确保合约安全性,避免常见漏洞——重入攻击、整数溢出等。

(3)检索器开发

  • 确定功能需要的数据内容:前端使用的数据大部分来自检索器。
  • 编写检索器程序:主流检索器框架(如 ponder 和 subgraph)都用了 TypeScript 语言作为检索器的程序编写语言。
  • 部署和运维:编写程序完成后用 Docker 部署到云服务器中。

(4)前端开发

  • 选择前端框架:可用现代前端框架如 React、Vue 来构建 UI,前端会通过 JavaScript 与智能合约进行交互。
  • 连接钱包:通过 MetaMask 等 Web3 钱包,用户可以连接到 Dapp,并授权其与智能合约交互。
  • 显示区块链数据:前端需要从区块链和检索器内获取数据——如数据余额、交易记录,通过用户界面展示。
  • 处理交易签名与确认:用户发起交易时,前端与钱包交互,获取用户签名并将交易发送到区块链。

(5)与区块链交互

  • 读取数据:前端通过智能合约的公开函数读取区块链上的状态数据——余额、合约信息。
  • 发送交易:当用户发起交易时,前端需要通过钱包签署交易并发送到区块链,执行合约中的功能(如转账)。

(6)部署与上线

  • 部署智能合约:使用 Hardhat 或 Foundry 等工具将智能合约部署到测试网(如 Sepolia)或主网。
  • 前端部署:将前端应用部署到去中心化平台(如 IPFS)或传统的 Web 服务(如 Vercel)。
  • 发布和维护:将 Dapp 上线,收集用户反馈并定期更新合约和前端。

RPC 简述 #

RPC(Remote Procedure Call),意为「远程过程调用」,是连接前端应用与区块链网络的关键桥梁。RPC 是理解 Dapp(去中心应用)和传统应用之间差别很重要的一个概念,我认为主要体现在两者的交互方式上。

Dapp 和传统应用都有前端。传统应用没有 RPC,数据和资产属于平台,前端通过调用后端 API 来获取数据和执行操作,「转账」时告诉银行服务器个人信息和密码,银行服务器验证过后完成操作;Dapp 中,数据和资产都属于私人,前端通过个人钱包和 RPC 与区块链进行交互,「转账」时需要个人的钱包签名,然后再由 RPC 广播到全网,全网的验证者验证信息无误后执行操作。

RPC 功能:

  • 读取链上数据。包括查询账户余额、交易历史,读取智能合约的状态变量,获取区块信息、Gas 价格等。
  • 发送交易。将用户签名的交易广播到区块网络,查询交易状态和确认数,估算交易所需的 Gas 费用。
  • 事件监听。监听智能合约事件(Events),实时获取链上状态变化。
  • 网络管理。切换不同的区块链网络(主网、测试网)等。

详见 RPC 节点服务详解

Solidity 智能合约编程 #

Solidity 是面向合约的高级编程语言,专门用于在以太坊虚拟机(EVM)上编写智能合约。

  • 每个 Solidity 文件都必须以版本声明开始:

    pragma solidity ^0.8.0;
    
  • // 是 solidity 文件中的单行注释符号。

  • pragma 关键字用于声明 solidity 源代码所需的编译器版本,确保合约在兼容的编译器环境中正确编译。

  • contract 关键字用于定义一个智能合约,其语法格式为 contract 合约名 { ... }

  • 函数是 Solidity 智能合约中执行具体逻辑操作的核心组成部分。

    • 通过函数,可以实现对状态变量的读取、修改,或执行特定业务逻辑。
  • 一个智能合约的基本结构通常由三部分组成:状态变量、构造函数、普通函数。

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    contract MyContract {
        // 状态变量
        uint256 public myNumber;
    
        // 构造函数
        constructor() {
            myNumber = 100;
        }
    
        // 函数
        function setNumber(uint256 _number) public {
            myNumber = _number;
        }
    }
    
  • Solidity 中函数的标准声明格式如下:

    function <函数名>(<参数列表>)
      <可见性>
      <状态可变性>
      <修饰符列表>
      <虚拟/重写关键字>
      returns (<返回值列表>)
    {
      // 函数体
    }
    
说明
  • <函数名>:函数的名称;
  • <参数列表>:传入函数的参数;
  • <可见性修饰符>:如 public、private、internal、external;
  • <状态可变性修饰符>:如 view、pure、payable;
  • <函数修饰符>:如 onlyOwner 等自定义逻辑控制;
  • virtual/override:用于支持继承与函数重写;
  • returns:定义返回值及其类型。
  • 状态变量是指在合约中定义的、其值永久存储在区块链上的变量。它们用于记录合约的持久化数据,构成合约的整体状态。当合约被部署后,这些变量将被写入区块链,并在合约的整个生命周期中保持可访问性和可追踪性。