在 Web3Bridge 学习区块链的第三周,我们学习了 Web3 架构、Solidity、工厂合约以及 Solidity 中的继承。

1. Web 3.0 应用架构
核心架构差异:
- Web 2.0:中心化服务器、数据库和后端逻辑
- Web 3.0:去中心化状态机(区块链),智能合约取代后端,没有中心化数据库
关键组件:
前端
- 与 Web 2.0 类似,但与智能合约通信,而不是传统的 API
- 使用 Web3.js 或 Ethers.js 库进行区块链交互
区块链层
- 以太坊区块链:由点对点节点维护的全局、可访问的状态机
- 智能合约:定义应用程序逻辑,用 Solidity/Vyper 编写,编译成字节码
- 以太坊虚拟机(EVM):执行智能合约逻辑
Web3 应用中的基础设施
- 节点提供商:诸如 Infura/Alchemy 之类的服务管理区块链节点基础设施
- 交易签名:MetaMask 处理私钥管理和交易签名
- 去中心化存储:IPFS/Swarm 用于链下数据存储,以降低成本
数据读取方法
- 智能合约事件:使用 Web3.js 监听区块链事件
- The Graph:使用 GraphQL 的链下索引解决方案,用于高效的数据查询
扩展解决方案
- Layer 2 解决方案:Polygon、Optimistic Rollups、zkRollups 用于更快、更便宜的交易
- 开发工具:Hardhat 框架用于构建、测试和部署智能合约
2. 智能合约应用程序二进制接口(ABI)
ABI 充当接口,使外部应用程序能够与区块链上的智能合约进行通信。它类似于 Web 开发中的 API,但可将高级语言调用转换为 EVM 可读的字节码。它通常是一个 JSON RPC 文件,其中包含方法签名和数据类型。
- 智能合约以字节码的形式存储在 EVM 上,没有 ABI,直接通信是不可能的
- ABI 编码将函数调用转换为 EVM 可以处理的字节
- 函数签名使用前 4 个字节来标识被调用的函数
- 诸如 Hardhat/Truffle 之类的工具会自动生成 ABI 文件
- 对于与智能合约交互的任何前端应用程序至关重要
3. Solidity
Solidity 是一种面向对象的编程语言,受 C++、JavaScript 和 Python 的影响。Solidity 通常将人类可读的代码编译为机器可读的代码,称为字节码,该字节码在以太坊虚拟机 (EVM) 上运行。此字节码将被部署并存储在 EVM 或其他与 EVM 兼容的机器上。Solidity 用于编写智能合约。
Solidity 的组成部分
- 第一条注释,它是一条机器可读的行(// SPDX-License-Identifier: MIT),用于指定涵盖该代码的许可。
- pragma 指令通常是任何 Solidity 文件中的第一行代码。Pragma 是一条指令,它告诉编译器应使用哪个编译器版本将人类可读的 Solidity 代码转换为机器可读的字节码。
- 分号在 Solidity 中必不可少。
- 合约
- 函数,即可执行代码。
智能合约中的变量作用域
智能合约中有三个变量作用域
- 状态变量:这些变量在智能合约中存储永久数据。它们位于合约内部但函数外部
- 局部变量:这些变量保存数据的时间非常短,通常在计算期间。它们位于函数内部,无法在函数外部访问
- 全局变量:这些变量和函数由 Solidity“注入”到你的代码中,无需从任何地方专门创建或导入它们即可使用。它们提供有关代码运行的区块链环境的信息,还包括用于程序中一般用途的实用程序函数。
可见性类型
- 公共函数和变量可以在合约内部、外部、其他智能合约和外部帐户中访问。
- 私有函数和变量只能在其声明的智能合约中访问。
- 内部可见性类似于私有可见性,因为内部函数和变量只能从声明它们的合约中访问。
- 外部可见性说明符不适用于变量——只有函数可以指定为外部。
构造函数
它是一种特殊类型的函数,仅在合约创建时执行一次。它不包含在最终部署的字节码中。
在你的收件箱中获取 Ifeoluwa Sanni 的故事
免费加入 Medium 以获取这位作者的更新。
存在抽象合约和接口合约
Solidity 数据类型
- 值类型:包括 uint、int、addresses、booleans、fixed size bytes
- 引用类型: 包括固定大小数组、动态大小数组、字节数组、复合结构和映射。
结构体 是具有一个或多个属性或属性的数据片段,用于指定每个属性的数据类型和名称。
函数修饰符
修饰符是一段代码,可以在运行主函数之前或之后自动运行。修饰符也可以从父合约继承。
- View:不修改状态的只读函数
- Pure:不读取或修改状态的函数
- Payable:可以接收 Ether 的函数
- Virtual/Override:用于继承功能
Solidity 中的错误处理
Solidity 中有三种处理错误的方法
- require() 函数 用于验证输入、验证返回值以及在继续执行代码逻辑之前检查其他条件。
- assert() 函数 与 require() 非常相似,只不过它抛出类型为 Panic(uint256) 的错误,而不是 Error(string)。
- revert() 调用 的使用情况与 require() 相同,但当条件逻辑更加复杂时。
4. 数据位置 — 存储、内存和堆栈
存储
它是用于状态变量的永久区块链存储。它具有一个键值存储,其中包含 256 位键和值。由于网络共识要求,它是最昂贵的。它是一个合约状态,必须在函数调用之间保持不变
内存
这是函数执行期间的临时存储。它在函数启动时创建,在函数结束时销毁。它比存储便宜。它用于临时变量和函数参数。
堆栈
它是 EVM 计算区域。它具有 1024 个项目的容量,每个项目 256 位。它是一种基于堆栈的计算(不是基于寄存器的计算)。它用于在计算期间存储中间值。
5. Solidity 中的工厂模式
工厂合约 是一种创建和管理其他合约的多个实例的合约。 它类似于传统编程中的工厂模式
优点
- 这用于集中跟踪和管理子合约
- 它提高了 gas 效率,因为合约工厂只部署一次,并创建子合约/实例。
- 它通过受控部署来提高合约安全性
工厂合约的实现
常规工厂模式
contract Factory { address[] public deployedContracts; function createContract() public { address newContract = address(new ChildContract()); deployedContracts.push(newContract); } }克隆工厂模式 (EIP-1167)
- 问题:普通工厂通过重新部署相同的代码来浪费 gas
- 解决方案:创建最小的代理合约,将调用委托给主实现
- Gas 节省:显著降低部署成本
- 实现:使用
delegatecall在主合约上下文中执行函数
6. Solidity 中的继承
继承是面向对象编程 (OOP) 中的一个强大概念。推理编程中继承的最佳方式是将其视为代码片段通过导入和嵌入从其他代码片段“继承”数据和函数的方式。
Solidity 中的继承还允许开发人员访问、使用和修改从继承的合约的属性(数据)和函数(行为)。
继承的优点
代码重用:继承有助于方便和广泛的代码重用。想象一下,一系列应用程序代码从其他代码继承,而这些代码又从其他代码继承,依此类推。
模块化:允许将复杂的合约分解为更小、更易于管理的组件
可升级性:为创建可升级的合约模式奠定了基础
DRY 原则:遵循“不要重复自己”的原则,允许将通用功能编写一次并继承
重要的开发原则
- 安全第一:智能合约一旦部署就无法更改
- Gas 优化:每个操作都需要花费 gas;优化效率
- 类型安全:Solidity 的严格类型可以防止许多运行时错误
- 状态管理:了解不同数据位置的成本影响
- 测试:由于不可变性,全面的测试至关重要
- 可升级性:考虑使用代理模式进行合约升级
- 原文链接: blog.blockmagnates.com/w...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~