《Solidity 简易速速上手小册》第3章:Solidity 语法基础(2024 最新版)

在这里插入图片描述

文章目录

  • 3.1 变量和类型
    • 3.1.1 基础知识解析
      • 详细解析变量类型
      • 深入数据类型
      • 理解变量可见性
    • 3.1.2 重点案例:创建一个简单的存储合约
      • 案例 Demo:编写一个简单的数字存储合约
      • 案例代码:SimpleStorage.sol
      • 在 Remix 中进行交互:
      • 拓展操作:
    • 3.1.3 拓展案例 1:使用不同类型的变量
      • 案例 Demo:创建一个管理用户余额的合约
      • 案例代码:BalanceManager.sol
      • 在 Remix 或 Truffle 中进行交互
      • 拓展操作
    • 3.1.4 拓展案例 2:结构体和数组的使用
      • 案例 Demo:创建一个管理用户信息的合约
      • 案例代码:UserManagement.sol
      • 在 Remix 或 Truffle 中进行测试
      • 拓展操作
  • 3.2 函数和控制结构
    • 3.2.1 基础知识解析
      • 深入理解函数
      • 掌握控制结构
      • 使用函数修饰符
    • 3.2.2 重点案例:创建有条件逻辑的合约
      • 案例 Demo:编写一个基于条件逻辑的积分管理合约
      • 案例代码:PointsManager.sol
      • 在 Remix 或 Truffle 中进行测试
      • 拓展实践
    • 3.2.3 拓展案例 1:循环遍历用户
      • 案例 Demo:使用循环来更新用户信息
      • 案例代码:UserUpdater.sol
      • 在 Remix 或 Truffle 中进行测试
      • 拓展实践
    • 3.2.4 拓展案例 2:使用函数修饰符
      • 案例 Demo:使用函数修饰符限制访问
      • 案例代码:OwnerContract.sol
      • 在 Remix 或 Truffle 中进行测试
      • 拓展实践
  • 3.3 事件和继承
    • 3.3.1 基础知识解析
      • 深入理解事件
      • 继承的概念和应用
      • 使用场景和最佳实践
    • 3.3.2 重点案例:创建一个发出事件的合约
      • 案例 Demo:编写一个记录用户活动的合约
      • 案例代码:ActivityLogger.sol
      • 在 Remix 或 Truffle 中进行测试
      • 拓展实践
    • 3.3.3 拓展案例 1:使用继承来组织合约
      • 案例 Demo:构建基于继承的多层次用户管理系统
      • 案例代码
        • BaseContract.sol
        • AdminContract.sol
        • UserContract.sol
      • 测试和部署
      • 拓展实践
    • 3.3.4 拓展案例 2:多重继承和接口
      • 案例 Demo:创建一个集成多个功能的智能合约
      • 案例代码
        • Logger.sol
        • Authenticator.sol
        • ComprehensiveContract.sol
      • 测试和部署
      • 拓展实践

3.1 变量和类型

在 Solidity 的世界里,变量和类型是构建智能合约的基石,就像是为你的区块链冒险准备的基础装备一样。

3.1.1 基础知识解析

进入 Solidity 的世界,变量和类型就像是你的基本工具箱。理解它们就像是学会了如何在区块链上搭建房屋的基本技能。

详细解析变量类型

  1. 状态变量(State Variables):

    • 存储在区块链上,是合约的永久部分。
    • 类似于传统编程中的全局变量,但它们的生命周期与合约相同。
  2. 局部变量(Local Variables):

    • 在函数内声明和使用。
    • 生命周期仅限于函数的执行期间。
  3. 全局变量(Global Variables):

    • Solidity 提供的特殊变量,提供区块链的信息,如 msg.sender(函数调用者的地址)。

深入数据类型

  1. 值类型(Value Types):

    • 直接持有数据,包括:
      • 整数类型(如 uintint)。
      • 布尔型(bool)。
      • 地址类型(address)。
    • 这些类型在赋值或传递时被复制。
  2. 引用类型(Reference Types):

    • 指向数据存储位置的类型,包括:
      • 数组(固定大小和动态大小)。
      • 结构体(struct)。
      • 映射(mapping)。
    • 它们不是复制值,而是引用存储位置。
  3. 特殊数据类型:

    • bytes:固定大小或动态大小的字节序列。
    • enum:用户定义的类型,用于创建具名常量。

理解变量可见性

  1. 公共(public):

    • 对所有人可见,自动生成 getter 函数。
  2. 私有(private):

    • 仅在定义它们的合约内可见。
  3. 内部(internal):

    • 类似于私有,但对继承合约也可见。
  4. 外部(external):

    • 只能通过外部调用访问。

掌握 Solidity 中的变量和类型就像是在区块链的建筑工地上熟悉每一件工具。了解它们的不同特性和用途,可以帮助你更有效地构建和维护你的智能合约。就像是一个优秀的工匠,知道什么时候该用锤子,什么时候该用螺丝刀。准备好了吗?让我们拿起工具,开始构建吧!

3.1.2 重点案例:创建一个简单的存储合约

假设你是一名热心的 Solidity 开发者,希望创建一个基础但实用的合约,让用户能够存储和检索一个数字。这个案例就像是在学习建筑时制作的第一个模型房子一样,基础但至关重要。

案例 Demo:编写一个简单的数字存储合约

  1. 初始化环境:

    • 选择一个合适的开发环境,比如 Remix IDE 或本地配置的 Truffle 项目。
  2. 编写合约:

    • 创建一个新的 Solidity 文件,命名为 SimpleStorage.sol
    • 在文件中定义一个合约 SimpleStorage,包含一个可以设置和获取值的状态变量。
  3. 定义状态变量和函数:

    • 使用 uint 类型定义一个状态变量来存储一个数字。
    • 编写一个函数来设置这个数字。
    • 编写另一个函数来检索这个数字。
  4. 编译和部署合约:

    • 在 Remix 或 Truffle 中编译合约。
    • 部署合约到 Ethereum 测试网络或本地开发环境。
  5. 测试合约功能:

    • 通过调用设置和获取函数,测试合约的存储和检索功能。

案例代码:SimpleStorage.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SimpleStorage {
    uint private storedNumber;

    function setNumber(uint _number) public {
        storedNumber = _number;
    }

    function getNumber() public view returns (uint) {
        return storedNumber;
    }
}

在这个 SimpleStorage 合约中,我们定义了一个私有状态变量 storedNumber 来存储一个无符号整数。我们提供了 setNumber 函数来更改这个数值,以及一个 getNumber 函数来检索它。

在 Remix 中进行交互:

  • 编译并部署 SimpleStorage 合约。
  • 使用 Remix 提供的界面调用 setNumber 函数存储一个值。
  • 然后调用 getNumber 函数检查存储的值是否正确。

拓展操作:

  • 添加事件: 你可以在合约中添加事件来记录每次数字更改的操作。
  • 权限控制: 为了提高合约的安全性,你可以添加只允许合约的所有者更改数字的逻辑。

通过这个简单的存储合约案例,你不仅学会了如何创建和部署基本的智能合约,还理解了如何通过合约交互修改和读取状态变量的值。这是学习 Solidity 的重要一步,就像是学会了如何建造和使用工具箱中的基本工具一样。

3.1.3 拓展案例 1:使用不同类型的变量

设想你正在构建一个稍微复杂的智能合约,需要处理不同类型的数据。这就像是你在建造一个多功能的太空站,它不仅能存储基本的供应品,还能处理复杂的数据和交易。

案例 Demo:创建一个管理用户余额的合约

  1. 初始化环境:
    • 选择你的开发环境,比如 Remix IDE 或一个本地 Truffle 项目。
  2. 编写合约:
    • 创建一个新的 Solidity 文件 BalanceManager.sol
    • 定义一个合约 BalanceManager
  3. 定义变量和结构:
    • 使用 address 类型来存储用户地址。
    • 使用 mapping 类型来关联地址和余额。
  4. 编写函数来管理余额:
    • 编写一个函数来更新用户的余额。
    • 编写另一个函数来获取指定用户的余额。
  5. 编译和部署合约:
    • 在你选择的环境中编译合约。
    • 将合约部署到以太坊测试网络或你的本地开发环境。
  6. 测试合约功能:
    • 测试更新和获取余额的功能。

案例代码:BalanceManager.sol

solidityCopy code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract BalanceManager {
    mapping(address => uint) public balances;

    function updateBalance(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }

    function getBalance(address user) public view returns (uint) {
        return balances[user];
    }
}

BalanceManager 合约中,我们使用了 mapping 类型来关联用户地址和其余额。updateBalance 函数允许用户设置自己的余额,而 getBalance 函数允许任何人查询指定用户的余额。

在 Remix 或 Truffle 中进行交互

  • 部署合约后,调用 updateBalance 函数来设置一个地址的余额。
  • 使用 getBalance 函数来验证余额是否正确更新。

拓展操作

  • 添加事件: 可以添加事件来记录每当用户余额更新时的情况。
  • 增加安全性: 实现一些安全措施,比如确保只有用户本人才能更新自己的余额。

通过这个案例,你不仅学会了如何在智能合约中处理不同类型的变量,还理解了如何创建更为动态和交互式的合约功能。

3.1.4 拓展案例 2:结构体和数组的使用

假设你是一位热衷于探索更高级 Solidity 特性的开发者,现在你想要创建一个合约,用于管理多个用户的信息。在这种情况下,结构体(Structs)和数组(Arrays)是完美的工具。

案例 Demo:创建一个管理用户信息的合约

  1. 定义用户结构体:

    • 创建一个结构体 User,包含用户的各种信息,比如姓名、ID 和余额。
  2. 使用数组存储结构体:

    • 定义一个 User 结构体的动态数组,用于存储多个用户信息。
  3. 编写函数来管理用户信息:

    • 实现添加新用户、获取用户信息和更新用户信息的函数。
  4. 测试合约功能:

    • 部署合约到测试网络或本地开发环境,并测试添加和获取用户信息的功能。

案例代码:UserManagement.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract UserManagement {
    struct User {
        string name;
        uint id;
        uint balance;
    }

    User[] private users;

    function addUser(string memory _name, uint _id, uint _balance) public {
        users.push(User(_name, _id, _balance));
    }

    function getUser(uint _index) public view returns (string memory, uint, uint) {
        require(_index < users.length, "User does not exist.");
        User memory user = users[_index];
        return (user.name, user.id, user.balance);
    }

    function updateUserBalance(uint _index, uint _newBalance) public {
        require(_index < users.length, "User does not exist.");
        User storage user = users[_index];
        user.balance = _newBalance;
    }
}

在这个 UserManagement 合约中,我们定义了一个 User 结构体来存储用户信息,并使用一个动态数组 users 来管理多个用户。合约提供了添加新用户、获取特定用户信息和更新用户余额的功能。

在 Remix 或 Truffle 中进行测试

  • 编译并部署 UserManagement 合约。
  • 使用合约提供的函数测试添加用户、获取用户信息和更新用户余额的功能。
  • 观察合约如何处理多个用户的信息。

拓展操作

  • 增加安全性: 可以添加权限控制,确保只有合约所有者或特定用户能够添加或更新用户信息。
  • 优化存储: 考虑使用映射(Mapping)来更高效地索引和检索用户信息。

通过这个案例,你可以看到结构体和数组在管理复杂数据方面的强大能力。它们让你能够在智能合约中以有组织和高效的方式处理大量信息,就像是为你的区块链应用构建了一个健壮而灵活的数据框架。

通过掌握 Solidity 中的变量和类型,你就能开始构建智能合约的基础结构了。记住,合适的变量和类型选择对合约的性能和安全性至关重要。就像在太空探险中选择正确的装备一样,正确的变量和类型能让你的合约在区块链的星际旅行中更加顺利。

3.2 函数和控制结构

跳进 Solidity 的世界,函数和控制结构就像是你的魔法书,里面充满了神奇的咒语和符号,帮助你操控你的智能合约。

3.2.1 基础知识解析

在 Solidity 的世界中,函数和控制结构是编织智能合约魔法的基础。它们就像是一套复杂的舞步,需要精确和逻辑来指导合约的行为。

深入理解函数

  1. 函数的本质:

    • 函数是一系列指令的集合,用于执行特定的任务。
    • 在 Solidity 中,函数可以读取和修改合约的状态。
  2. 函数类型:

    • 纯函数(pure): 不读取也不修改合约状态的函数。
    • 视图函数(view): 只读取不修改合约状态的函数。
    • 支付函数(payable): 接收以太币(ETH)的函数。
  3. 函数参数和返回值:

    • 函数可以有参数和返回值,参数用于传递数据,返回值用于输出数据。

掌握控制结构

  1. 条件语句(if, else):

    • 允许根据条件执行不同的代码路径。
    • 在 Solidity 中,这是处理决策逻辑的关键。
  2. 循环(for, while, do while):

    • 使你能够重复执行某段代码,直到满足特定条件。
    • 在处理数组或映射时特别有用。

使用函数修饰符

  1. 修饰符概念:

    • 函数修饰符是可重用的代码块,用于修改函数的行为。
    • 它们常用于访问控制和检查前置条件。
  2. 常见修饰符:

    • public:任何人都可以调用的函数。
    • private:只能在合约内部调用的函数。
    • internal:只能在合约内部及其派生合约中调用的函数。
    • external:只能从合约外部调用的函数。

理解了函数和控制结构,你的智能合约就像是被精心编排的舞蹈,每个步骤都清晰、有序。这些基本元素不仅是构建合约的基石,还能确保你的合约逻辑严密、有效。

3.2.2 重点案例:创建有条件逻辑的合约

想象你是一个热心的开发者,准备创造一个智能合约来管理用户积分。这个合约将使用条件逻辑来处理不同的情景,就像是一个游戏中的关卡选择器。

案例 Demo:编写一个基于条件逻辑的积分管理合约

  1. 初始化开发环境:

    • 选择合适的开发工具,例如 Remix IDE 或本地的 Truffle 环境。
  2. 创建智能合约:

    • 新建一个 Solidity 文件,比如命名为 PointsManager.sol
    • 定义合约框架和所需的状态变量。
  3. 编写条件逻辑函数:

    • 创建函数来增加用户积分。
    • 使用 if 语句来判断用户是否达到某个积分阈值,并据此执行不同的操作。
  4. 部署和测试合约:

    • 在选定的环境中部署合约。
    • 测试不同积分阈值下的合约逻辑。

案例代码:PointsManager.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract PointsManager {
    mapping(address => uint256) public userPoints;

    function addPoints(address user, uint256 points) public {
        userPoints[user] += points;

        // 检查用户是否达到积分阈值
        if (userPoints[user] >= 100) {
            // 用户达到积分阈值的特殊逻辑
            // 例如:升级用户等级、发放奖励等
        }
    }
}

PointsManager 合约中,我们使用了一个映射来跟踪用户的积分,并创建了一个 addPoints 函数来增加用户积分。通过在函数中添加 if 语句,我们为达到特定积分阈值的用户实现了特殊的逻辑。

在 Remix 或 Truffle 中进行测试

  • 部署合约到你选择的开发环境。
  • 通过调用 addPoints 函数测试不同用户的积分情况。
  • 观察当用户达到特定积分时,合约是否按预期执行特殊逻辑。

拓展实践

  • 引入事件: 在用户达到积分阈值时触发事件,以便于跟踪和记录。
  • 更复杂的条件逻辑: 实现更多的条件判断,比如不同积分阶段提供不同的奖励。

通过这个案例,你不仅实践了 Solidity 中的条件逻辑,也学会了如何根据实际情况调整合约的行为。这就像是在编程的世界中设置了一个智能的路标,引导你的合约在不同情景下采取不同的行动。

3.2.3 拓展案例 1:循环遍历用户

设想你正在开发一个更高级的智能合约,需要处理和更新多个用户的信息。在这种情况下,使用循环来遍历用户数组将是一种高效的方法。

案例 Demo:使用循环来更新用户信息

  1. 设置开发环境:

    • 选择一个开发工具,比如 Remix IDE 或本地的 Truffle 环境。
  2. 编写智能合约:

    • 创建一个新的 Solidity 文件,命名为 UserUpdater.sol
    • 定义一个包含用户信息的结构体和一个存储多个用户的数组。
  3. 编写循环逻辑:

    • 创建一个函数,使用 for 循环遍历用户数组,并对每个用户执行特定操作。
  4. 部署和测试合约:

    • 在所选环境中部署合约。
    • 测试循环逻辑是否正确处理每个用户的信息。

案例代码:UserUpdater.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract UserUpdater {
    struct User {
        address addr;
        uint256 score;
    }

    User[] public users;

    function addUser(address _addr, uint256 _score) public {
        users.push(User(_addr, _score));
    }

    function updateAllScores(uint256 _scoreIncrement) public {
        for (uint i = 0; i < users.length; i++) {
            users[i].score += _scoreIncrement;
        }
    }
}

UserUpdater 合约中,我们定义了一个 User 结构体和一个 users 数组来存储用户信息。addUser 函数用于添加新用户,而 updateAllScores 函数使用 for 循环来更新所有用户的分数。

在 Remix 或 Truffle 中进行测试

  • 编译并部署 UserUpdater 合约。
  • 使用 addUser 函数添加几个用户。
  • 调用 updateAllScores 函数并检查是否所有用户的分数都正确更新。

拓展实践

  • 引入条件检查: 在循环中添加条件语句,以实现更复杂的业务逻辑。
  • 优化性能: 考虑合约的 Gas 成本,特别是当处理大量数据时。

通过这个案例,你不仅学会了如何在 Solidity 中使用循环来有效处理多个元素,还掌握了结构体和数组的使用。这些技能将帮助你建立更复杂和动态的智能合约,就像是在区块链的世界中搭建了一座多功能的建筑。

3.2.4 拓展案例 2:使用函数修饰符

假设你正在开发一个智能合约,需要确保某些关键功能只能由合约的所有者调用。在这种情况下,使用函数修饰符来添加权限控制是非常有效的方法。

案例 Demo:使用函数修饰符限制访问

  1. 设置开发环境:

    • 选择合适的开发环境,比如 Remix IDE 或 Truffle。
  2. 编写智能合约:

    • 创建一个新的 Solidity 文件,例如 OwnerContract.sol
    • 定义一个合约,其中包括所有者地址和需要受限制的功能。
  3. 定义函数修饰符:

    • 创建一个修饰符,用于检查调用者是否是合约的所有者。
    • 将这个修饰符应用于需要限制访问的函数。
  4. 部署和测试合约:

    • 在所选环境中部署合约。
    • 测试受限函数,确保只有所有者可以调用它们。

案例代码:OwnerContract.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract OwnerContract {
    address public owner;

    constructor() {
        owner = msg.sender; // 设置部署合约的人为所有者
    }

    // 修饰符:只有所有者可以调用
    modifier onlyOwner() {
        require(msg.sender == owner, "Only the owner can call this function.");
        _;
    }

    function changeOwner(address _newOwner) public onlyOwner {
        owner = _newOwner;
    }

    // 其他受限制的功能...
}

在这个 OwnerContract 合约中,我们定义了一个 onlyOwner 修饰符来确保只有合约的所有者可以执行某些操作。例如,changeOwner 函数允许当前所有者将所有权转移给另一个地址,但是由于 onlyOwner 修饰符的限制,只有当前所有者才能调用此函数。

在 Remix 或 Truffle 中进行测试

  • 部署 OwnerContract 合约。
  • 作为所有者尝试调用 changeOwner 函数,并观察操作是否成功。
  • 从非所有者账户尝试相同操作,并确保操作失败。

拓展实践

  • 增加多个修饰符: 对于更复杂的访问控制,可以结合多个修饰符使用。
  • 实现权限日志: 使用事件记录所有权变更或特定函数调用。

通过这个案例,你学会了如何使用函数修饰符来增加智能合约的安全性。就像是在你的数字城堡中增加了一道安全门,确保只有授权人员才能进入。

3.3 事件和继承

欢迎来到 Solidity 的另一个激动人心的章节:事件和继承。这就像学习如何给你的智能合约装上广播系统和搭建多层建筑的技术。

3.3.1 基础知识解析

在 Solidity 的世界里,事件和继承是构建复杂、高效和模块化智能合约的关键工具。就像是在一个复杂的电子设备中添加指示灯和模块化组件。

深入理解事件

  1. 事件(Events)的作用:

    • 用于在区块链上记录日志和通知订阅者(如前端应用)合约状态的改变。
    • 提供了一种低成本的数据存储方式。
  2. 事件的特性:

    • 可索引参数(indexed): 最多三个参数可以被标记为 indexed,使它们在日志中可搜索。
    • 数据存储: 事件数据存储在交易日志中,与区块链的状态分开。

继承的概念和应用

  1. 继承的基本原理:

    • 允许一个合约继承另一个合约的方法和变量,减少代码重复,提高复用性。
    • 类似于面向对象编程中的继承概念。
  2. 多重继承和接口:

    • Solidity 支持多重继承,意味着一个合约可以继承多个父合约。
    • 接口可以用于定义合约之间的标准交互方式。

使用场景和最佳实践

  1. 事件的使用场景:

    • 当合约状态改变时(如转账、状态更新)发出事件。
    • 为了节省成本,将非必要数据存储在事件而非区块链状态中。
  2. 继承的最佳实践:

    • 识别共通功能,将其放在基础合约中。
    • 通过继承扩展和定制基础合约的行为。
    • 注意合约大小限制,过多的继承可能导致部署问题。

通过深入理解事件和继承,你的智能合约开发就像是获得了额外的维度。事件让合约与外部世界沟通,而继承带来了结构清晰、易于管理的代码架构。

3.3.2 重点案例:创建一个发出事件的合约

设想你正在开发一个智能合约,旨在记录并通知用户的活动或状态变化。在这个合约中,我们将使用事件来实现这一功能,就像安装了一个广播系统,向外界发送重要通知。

案例 Demo:编写一个记录用户活动的合约

  1. 初始化开发环境:

    • 在 Remix IDE 或本地 Truffle 环境中开始你的项目。
  2. 编写智能合约:

    • 创建一个新的 Solidity 文件,例如命名为 ActivityLogger.sol
    • 在合约中定义一个事件来记录用户活动。
  3. 定义事件:

    • 创建一个事件,比如 UserActivity,用于记录用户的行为和相关数据。
  4. 触发事件:

    • 在合约的函数中,根据逻辑触发定义好的事件。
  5. 部署和测试合约:

    • 将合约部署到以太坊测试网络或本地开发环境。
    • 通过调用触发事件的函数来测试事件是否正常工作。

案例代码:ActivityLogger.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ActivityLogger {
    event UserActivity(address indexed user, string activity, uint timestamp);

    function logActivity(string memory _activity) public {
        emit UserActivity(msg.sender, _activity, block.timestamp);
    }
}

ActivityLogger 合约中,我们定义了一个 UserActivity 事件,当用户执行某个行为时,这个事件将被触发,记录下用户地址、活动内容和时间戳。

在 Remix 或 Truffle 中进行测试

  • 在 Remix 或 Truffle 中编译并部署 ActivityLogger 合约。
  • 调用 logActivity 函数并传入一个活动字符串,如 "joined a game"
  • 观察交易日志,确认 UserActivity 事件是否被正确记录和触发。

拓展实践

  • 引入更多事件: 可以为不同类型的用户活动定义不同的事件,以提供更详细的信息。
  • 前端集成: 开发一个前端应用,使用 Web3.js 或 ethers.js 监听并响应这些事件。

通过这个案例,你就学会了如何在智能合约中使用事件来记录和通知关键信息。这就像是在你的合约中安装了一个高效的通信系统,它不仅使合约与外部世界连接起来,还为数据的跟踪和反应提供了强大的支持。

3.3.3 拓展案例 1:使用继承来组织合约

假设你正在开发一个复杂的智能合约系统,需要管理不同层次的用户和权限。在这种情况下,使用继承来组织你的合约不仅可以提高代码的可重用性,还能提高整体架构的清晰度。

案例 Demo:构建基于继承的多层次用户管理系统

  1. 创建基础合约:

    • 开发一个基础合约 BaseContract,包含所有合约共有的逻辑和状态变量。
  2. 构建继承合约:

    • 创建特定功能的合约,如 AdminContractUserContract,它们从 BaseContract 继承。
  3. 实现特定功能:

    • 在继承的合约中实现特定于管理员和普通用户的功能。
  4. 部署和测试合约:

    • 在以太坊测试网络或本地开发环境部署各个合约。
    • 测试继承合约中的特定功能以确保它们按预期工作。

案例代码

BaseContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract BaseContract {
    address owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }

    // 共有逻辑...
}
AdminContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./BaseContract.sol";

contract AdminContract is BaseContract {
    // 管理员特有的逻辑...

    function adminFunction() public onlyOwner {
        // 特定于管理员的功能
    }
}
UserContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./BaseContract.sol";

contract UserContract is BaseContract {
    // 用户特有的逻辑...

    function userFunction() public {
        // 特定于用户的功能
    }
}

在这个案例中,BaseContract 作为基础合约,包含所有类型合约共有的逻辑和状态变量,比如所有权管理。AdminContractUserContract 分别继承 BaseContract,添加了特定于管理员和用户的功能。

测试和部署

  • 在 Remix 或 Truffle 中部署这些合约。
  • 验证 AdminContractUserContract 是否正确继承了 BaseContract 的特性。
  • 测试 adminFunctionuserFunction 确保它们的访问权限和功能按预期工作。

拓展实践

  • 添加更多层次的合约: 你可以继续扩展这个体系,添加更多具有特定功能的合约。
  • 实现接口: 为了更好的模块化设计,可以定义接口并让这些合约实现特定的接口。

通过使用继承,你的智能合约架构就像是精心设计的建筑,每层都有其特定的功能和目的。这样的设计不仅使代码更加整洁,还大大提高了开发效率和可维护性。

3.3.4 拓展案例 2:多重继承和接口

假设你正在开发一个综合性智能合约,需要集成多个独立功能,例如日志记录和权限管理。在这种情况下,利用多重继承和接口可以极大地提高代码的模块化和可重用性。

案例 Demo:创建一个集成多个功能的智能合约

  1. 定义接口和基础合约:

    • 创建几个基础合约,每个合约实现特定的功能,如 LoggerAuthenticator
    • 定义接口,为合约提供标准化的功能框架。
  2. 构建多重继承合约:

    • 创建一个新合约,比如叫做 ComprehensiveContract,它继承自上述所有基础合约。
  3. 实现合约功能:

    • 在继承的合约中整合和扩展基础合约的功能。
  4. 部署和测试合约:

    • 将整合的合约部署到以太坊测试网络或本地开发环境。
    • 测试合约的集成功能以确保它们正常工作。

案例代码

Logger.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Logger {
    event Log(string message);

    function emitLog(string memory _message) internal {
        emit Log(_message);
    }
}
Authenticator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Authenticator {
    address owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Not authorized");
        _;
    }
}
ComprehensiveContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./Logger.sol";
import "./Authenticator.sol";

contract ComprehensiveContract is Logger, Authenticator {
    function secureAction() public onlyOwner {
        emitLog("Secure action performed");
        // 这里执行安全操作
    }
}

在这个案例中,LoggerAuthenticator 合约分别提供日志记录和权限验证的功能。ComprehensiveContract 通过继承这两个合约,集成了日志记录和权限管理的功能。

测试和部署

  • 在 Remix 或 Truffle 中部署 ComprehensiveContract 合约。
  • 作为合约所有者,尝试调用 secureAction 函数。
  • 验证是否正确记录了日志并且只有所有者可以执行该操作。

拓展实践

  • 引入更多的基础合约: 根据需要,你可以引入更多具有特定功能的基础合约。
  • 使用接口定义标准: 为了确保一致性,你可以为合约功能定义接口,并实现这些接口。

通过这个案例,你学会了如何利用多重继承和接口来构建一个功能丰富且高度模块化的智能合约。这种方式不仅提高了代码的复用性,还使得合约的维护和扩展变得更加简单。

通过掌握事件和继承,你可以让你的智能合约更加模块化和高效,就像是为它们装上了能够广播重要信息的天线,同时建立起一座座彼此连接的建筑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/396447.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

css3盒子

盒子模型 一.看透网页布局本质二.认识盒子三.盒子的边框&#xff08;border&#xff09;1.概念2.简写及分开写法3.合并问题&#xff08;会相加&#xff09;4.边框会影响盒子实际大小 四.盒子的内边距&#xff08;padding&#xff09;1.概念2.简写3.内边距会影响盒子实际大小4.特…

小迪安全2023最新版笔记集合--续更

小迪安全2023最新版笔记集合–续更 小迪安全2023最新笔记集合 章节一 ---- 基础入门&#xff1a; 知识点集合&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服务/负载均衡等 安全产品&#xff1a;CDN/WAF/IDS/IPS/蜜罐/防火墙/杀毒等 渗透命令&#xff1a;文件上传下…

代码随想录刷题笔记-Day20

1. 二叉树的最近公共祖先 236. 二叉树的最近公共祖先https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/ 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#x…

NS安装-CentOS服务器安装Nightscout CGM

NS CGM 安装必要条件 有自己的云服务器好像没有2&#xff0c;有云服务器就行了 安装顺序 先安装数据库&#xff0c;目前支持的是 MongoDB &#xff0c;官方推荐4&#xff0c;其实目前最新版本就行。可以用宝塔安装&#xff0c;比较简单克隆代码&#xff0c;我是放到 /opt/ns…

自动化上位机开发C#100例:如何用面向对象的方式封装雷赛运动控制卡EtherCAT总线卡(C#代码)

自动化上位机开发C#100例:雷赛运动控制卡EtherCAT总线卡C#封装类 文章目录 LTDMC.dll下载LTDMC.cs LTDMC.dll C#调用封装下载ICard.cs 运动控制卡接口Card.cs 运动控制卡抽象类CardLTDMC.cs 雷赛运动控制卡EtherCAT总线卡实现类CardList.cs 总线卡列表封装 LTDMC.dll下载 最新…

HarmonyOS4.0系列——08、整合UI常用组件

HarmonyOS4.0 系列——08、UI 组件 Blank Blank 组件在横竖屏占满空余空间效果 // xxx.ets Entry Component struct BlankExample {build() {Column() {Row() {Text(Button).fontSize(18)Blank()Toggle({type: ToggleType.Switch}).margin({top: 14,bottom: 14,left: 6,righ…

Cannot invoke “java.sql.Connection.prepareStatement(String)“ because “conn“

下载sqlite-jdbc&#xff0c;放在目录下&#xff0c;然后IDEA右键jar文件选择“加入库”即可解决 Central Repository: org/xerial/sqlite-jdbc/3.36.0.1

第一个 Angular 项目 - 动态页面

第一个 Angular 项目 - 动态页面 使用的所有技巧都在下面的笔记里&#xff1a; [Angular 基础] - 数据绑定(databinding) [Angular 基础] - 指令(directives) 以上为静态页面&#xff0c;即不涉及到跨组件交流的内容 以下涉及到组件内的沟通&#xff0c;从这开始数据就“活”…

板块一 Servlet编程:第四节 HttpServletResponse对象全解与重定向 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程&#xff1a;第四节 HttpServletResponse对象全解与重定向 一、什么是HttpServletResponse二、响应数据的常用方法三、响应乱码问题字符流乱码字节流乱码 四、重定向&#xff1a;sendRedirect请求转发和重定向的区别 在上一节中&#xff0c;我们系统的学习了…

react封装通用Modal弹窗组件

目录 1、【src/component/modal/hoc.js】 2、【src/component/modal/componentModal.js】 3、【src/page/projectView.js】 【说明】&#xff1a;后台管理的项目中会经常遇到弹窗&#xff0c;于是封装了一个简单的公共弹窗组件 这个公共组件不适用复杂的功能&#xff0c;简单的…

业务型 编辑器组件的封装(复制即可使用)

使用需要安装 wangeditor npm i --save wangeditor import React from react; import E from wangeditor; import ./index.lessclass EditorElem extends React.Component {constructor(props) {super(props);this.isChange false;this.state {}}componentDidMount() {con…

并发List、Set、ConcurrentHashMap底层原理

并发List、Set、ConcurrentHashMap底层原理 ArrayList: List特点&#xff1a;元素有放入顺序&#xff0c;元素可重复 存储结构&#xff1a;底层采用数组来实现 public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Clon…

后端开发怎么学?

后端开发怎么学&#xff1f; 后端开发可以简单地理解为与前端开发相对应的开发方向。前端开发主要负责构建用户界面、维护用户体验等方面的工作&#xff0c;而后端开发则主要负责处理数据、逻辑和算法等方面的工作。后端开发旨在为前端应用程序提供支持&#xff0c;以帮助实现可…

stm32学习笔记-STLINK使用

stm32学习笔记-STLINK使用 使用ST-LINK调试程序进度表格 使用ST-LINK调试程序 说明 组成 总结 记录使用STLINK进行项目的烧写和调试&#xff0c;旨在高效的进行代码调试学习工具包括笔记本、keil5MDK、stm32f030c8t6电表主机、STLINK V2、导线、电表代码总的来说&#xff0…

vue3项目配置按需自动引入自定义组件unplugin-vue-components

我们通常在项目中&#xff0c;需要手动引入自定义的各种组件&#xff0c;如果涉及的页面功能比较多的话&#xff0c;光是import的长度都能赶上春联了。 如果&#xff0c;能有一个插件帮我们实现自动引入&#xff0c;是不是要谢天谢地了呢&#xff1f; 接下来就进入我们的主角u…

Uniapp-开发小程序

文章目录 前言一、npm run xxx —— cross-env: Permission denied解决方法&#xff08;亲测有效&#xff09;其他解决方法&#xff1a; 二、macOS 微信开发者工具选择uniapp 用 vscode 开发 总结 前言 macOS下 uniapp 开发小程序。 一、npm run xxx —— cross-env: Permissi…

数据结构:动态内存分配+内存分区+宏+结构体

一、作业 1.定义一个学生结构体&#xff0c;包含结构体成员&#xff1a;身高&#xff0c;姓名&#xff0c;成绩&#xff1b;定义一个结构体数组有7个成员&#xff0c;要求终端输入结构体成员的值&#xff0c;根据学生成绩&#xff0c;进行冒泡排序。 #include <stdio.h>…

大数据技术之 Kafka

大数据技术之 Kafka 文章目录 大数据技术之 Kafka第 1 章 Kafka 概述1.1 定义1.2 消息队列1.2.1 传统消息队列的应用场景1.2.2 消息队列的两种模式 1.3 Kafka 基础架构 第 2 章 Kafka 快速入门2.1 安装部署2.1.1 集群规划2.1.2 集群部署2.1.3 集群启停脚本 2.2 Kafka 命令行操作…

【Go语言】Go语言中的变量和常量

Go语言中的变量和常量 1 变量 变量相当于是对一块数据存储空间的命名&#xff0c;程序可以通过定义一个变量来申请一块数据存储空间&#xff0c;之后可以通过引用变量名来使用这块存储空间。 Go 语言是强类型静态语言&#xff0c;所以变量的声明与赋值方式与 PHP/Python 等动…

基于java的眼镜店仓库管理系统

源码获取&#xff0c;加V&#xff1a;qq2056908377 摘要&#xff1a; 随着电子商务的兴起&#xff0c;越来越多的商家选择在线销售他们的产品。眼镜店作为零售业的一种&#xff0c;也不例外。随着市场需求的不断增加&#xff0c;眼镜店需要更加高效的管理他们的仓库和库存&…