解构我们的首个智能合约 — ZombieFactory

本文详细解析了 CryptoZombies 教程中第一个智能合约 ZombieFactory 的代码,逐行解释了合约的版本声明、合约定义、事件、状态变量、结构体、数组和映射,以及核心函数(创建僵尸、生成随机 DNA 和创建随机僵尸)的实现原理和作用,展示了智能合约的基本结构和 Solidity 语言的关键特性。

解构我们的第一个智能合约 — ZombieFactory

欢迎回到我们的博客系列,我们将通过精彩的、基于游戏的教程 CryptoZombies! 学习 Web3。在我们的上一篇文章中,我们概述了该平台。今天,我们将卷起袖子,一头扎进代码中。

我们已经完成了第一课“制作僵尸工厂”,并且已经完成了“Solidity:从初学者到中级智能合约”课程的 22%!让我们逐行分解为它提供支持的智能合约。

完整代码:ZombieFactory.sol

首先,让我们看看我们将要解释的完整代码。

pragma solidity >=0.5.0 <0.6.0;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string memory _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        emit NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));
        return rand % dnaModulus;
    }

    function createRandomZombie(string memory _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }
}

逐行解释

1. 版本 Pragma:

pragma solidity >=0.5.0 <0.6.0;
  • 它是什么:这是一个 pragma 指令,是编译器的指令。
  • 它的作用:它指定此源代码是为大于或等于 0.5.0 但小于 0.6.0 的 Solidity 编译器版本编写的。这至关重要,因为 Solidity 语言在主要版本 (0.x.x) 之间可能会发生显着变化,并且代码在不同版本上的行为可能不同,或者根本无法编译。它可以确保我们的代码可预测地运行。

2. 合约声明

contract ZombieFactory {
    ...
}
  • 它是什么:这声明了一个名为 ZombieFactory 的合约。
  • 它的作用:在 Solidity 中,contract 是以太坊应用程序的基本构建块。它类似于面向对象编程中的“类”。它包含将存在于以太坊区块链上特定地址的所有变量和函数。可以把它想象成工厂大楼本身。

3. 事件:

event NewZombie(uint zombieId, string name, uint dna);
  • 它是什么:事件的声明。
  • 它的作用:事件是你的智能合约将区块链上发生的某些事情传达给你的应用程序前端的一种方式(它可以“监听”这些事件)。每当创建一个新僵尸时,将触发此特定事件 NewZombie。它记录了僵尸的 idnamedna,因此我们的 Web 应用程序可以轻松捕获并显示此信息。

4. 状态变量 & DNA 数学

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
  • 它是什么:这些是状态变量。dnaDigits 是一个设置为 16 的无符号整数 ( uint)。dnaModulus 是另一个设置为 10^16 的 uint
  • 它的作用:在函数外部声明的变量是状态变量,并永久存储在区块链的合约存储中。
  • dnaDigits = 16:这定义了我们的僵尸 DNA 将是一个 16 位数字。
  • dnaModulus = 10 ** 16:这是一个数学运算符,将 dnaModulus 设置为 10,000,000,000,000,000。稍后我们将使用模运算符 % 来确保我们生成的任何随机数都在我们期望的 16 位数字范围内。

5. 结构体:

struct Zombie {
    string name;
    uint dna;
}
  • 它是什么:struct(结构体)是一种自定义类型,用于对多个变量进行分组。
  • 它的作用:在这里,我们定义了 Zombie 是什么。在我们的游戏中,每个僵尸都有两个属性:一个 name(字符串)和一个 dna(一个 uint,它是其唯一的 16 位数字标识符)。此结构体是我们工厂将创建的每个僵尸的蓝图。

6. 数组和映射:

Zombie[] public zombies;
  • 它是什么:声明为 public 的动态数组。
  • 它的作用:此数组将保存我们工厂创建的所有僵尸。它是 Zombie 结构体的集合。public 关键字会自动创建一个 getter 函数,因此任何人都可以调用 zombies(id) 来获取特定索引处的僵尸。
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
  • 它是什么:这些是映射,从本质上讲,它们是键值存储。
  • 它的作用:

zombieToOwner:此映射跟踪哪个僵尸属于谁。给定一个僵尸的 id(一个 uint),它返回一个 address。这就是我们知道特定僵尸属于特定玩家的方式。

ownerZombieCount:此映射跟踪玩家拥有多少个僵尸。给定一个玩家的 address,它返回一个 uint(一个计数)。这对于执行规则至关重要,例如限制新玩家可以创建多少个僵尸。

7. 核心函数

函数 1:_createZombie(私有):

function _createZombie(string memory _name, uint _dna) private {
    uint id = zombies.push(Zombie(_name, _dna)) - 1;
    zombieToOwner[id] = msg.sender;
    ownerZombieCount[msg.sender]++;
    emit NewZombie(id, _name, _dna);
}
  • 可见性:private。这意味着它只能由此合约内部的其他函数调用,而不能从外部调用。
  • 它的作用:这是执行创建僵尸的实际工作的内部函数。
  1. zombies.push(...) - 1:它使用给定的 _name_dna 创建一个新的 Zombie 结构体,并将其推送到 zombies 数组中。push 函数返回数组的新长度,因此我们减 1 以获得最后一个索引,即我们新僵尸的 id
  2. zombieToOwner[id] = msg.sender:它更新我们的映射以记录交易的发送者 ( msg.sender) 现在拥有此僵尸。
  3. ownerZombieCount[msg.sender]++:它增加所有者拥有的僵尸的数量。
  4. emit NewZombie(...):它触发 NewZombie 事件,以提醒外界新僵尸诞生了!

函数 2:_generateRandomDna(私有视图)

function _generateRandomDna(string memory _str) private view returns (uint) {
    uint rand = uint(keccak256(abi.encodePacked(_str)));
    return rand % dnaModulus;
}
  • 可见性:private。状态可变性:view。这意味着它仅从区块链读取;它不会修改任何状态。
  • 它的作用:此函数基于输入字符串生成一个“伪随机”数。
  1. keccak256(...):这是一个加密哈希函数,它接受一个输入并返回一个随机的 256 位十六进制数。它是以太坊安全性的支柱。
  2. abi.encodePacked(_str):在对其进行哈希处理之前,它将输入字符串打包成紧凑的二进制格式。
  3. uint(...):我们将哈希结果从字节强制转换为 uint(一个大整数)。
  4. return rand % dnaModulus:我们使用模运算符 % 来确保随机数在我们的 16 位数字限制内(0 到 9,999,999,999,999,999)。这将成为僵尸的 DNA。

函数 3:createRandomZombie(公共):

function createRandomZombie(string memory _name) public {
    uint randDna = _generateRandomDna(_name);
    _createZombie(_name, randDna);
}
  • 可见性:public。这是外部用户和我们应用程序的前端将调用的函数。
  • 它的作用:这是一个简单的、面向公众的函数,它将所有内容联系在一起。
  1. 它接受僵尸的所需 _name
  2. 它调用私有 _generateRandomDna 函数,传递名称以生成唯一的 DNA。
  3. 然后,它使用名称和新生成的 DNA 调用私有 _createZombie 函数。

为什么这种结构对于学习来说很棒

CryptoZombies 教你以清晰的关注点分离来构建你的代码。公共函数简单易懂,而复杂的逻辑则隐藏在私有辅助函数中。这使得代码更具可读性、可测试性和安全性 — 这是智能合约开发中的一项基本最佳实践。

在我们的下一篇博文中,我们将看到如何扩展此工厂以允许僵尸进食和升级!冒险才刚刚开始。

在第一课中,你觉得最有趣的 Solidity 概念是什么?是映射、伪随机数生成还是其他什么?请在下面的评论中告诉我们!

  • 原文链接: blog.blockmagnates.com/d...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

1 条评论

请先 登录 后评论
blockmagnates
blockmagnates
The New Crypto Publication on The Block