本文详细解析了 CryptoZombies 教程中第一个智能合约 ZombieFactory 的代码,逐行解释了合约的版本声明、合约定义、事件、状态变量、结构体、数组和映射,以及核心函数(创建僵尸、生成随机 DNA 和创建随机僵尸)的实现原理和作用,展示了智能合约的基本结构和 Solidity 语言的关键特性。
欢迎回到我们的博客系列,我们将通过精彩的、基于游戏的教程 CryptoZombies! 学习 Web3。在我们的上一篇文章中,我们概述了该平台。今天,我们将卷起袖子,一头扎进代码中。
我们已经完成了第一课“制作僵尸工厂”,并且已经完成了“Solidity:从初学者到中级智能合约”课程的 22%!让我们逐行分解为它提供支持的智能合约。
首先,让我们看看我们将要解释的完整代码。
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);
}
}
pragma solidity >=0.5.0 <0.6.0;
0.5.0
但小于 0.6.0
的 Solidity 编译器版本编写的。这至关重要,因为 Solidity 语言在主要版本 (0.x.x) 之间可能会发生显着变化,并且代码在不同版本上的行为可能不同,或者根本无法编译。它可以确保我们的代码可预测地运行。contract ZombieFactory {
...
}
ZombieFactory
的合约。contract
是以太坊应用程序的基本构建块。它类似于面向对象编程中的“类”。它包含将存在于以太坊区块链上特定地址的所有变量和函数。可以把它想象成工厂大楼本身。event NewZombie(uint zombieId, string name, uint dna);
NewZombie
。它记录了僵尸的 id
、name
和 dna
,因此我们的 Web 应用程序可以轻松捕获并显示此信息。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 位数字范围内。struct Zombie {
string name;
uint dna;
}
struct
(结构体)是一种自定义类型,用于对多个变量进行分组。Zombie
是什么。在我们的游戏中,每个僵尸都有两个属性:一个 name
(字符串)和一个 dna
(一个 uint,它是其唯一的 16 位数字标识符)。此结构体是我们工厂将创建的每个僵尸的蓝图。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
(一个计数)。这对于执行规则至关重要,例如限制新玩家可以创建多少个僵尸。
函数 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
。这意味着它只能由此合约内部的其他函数调用,而不能从外部调用。zombies.push(...) - 1
:它使用给定的 _name
和 _dna
创建一个新的 Zombie
结构体,并将其推送到 zombies
数组中。push
函数返回数组的新长度,因此我们减 1 以获得最后一个索引,即我们新僵尸的 id
。zombieToOwner[id] = msg.sender
:它更新我们的映射以记录交易的发送者 ( msg.sender
) 现在拥有此僵尸。ownerZombieCount[msg.sender]++
:它增加所有者拥有的僵尸的数量。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
。这意味着它仅从区块链读取;它不会修改任何状态。keccak256(...)
:这是一个加密哈希函数,它接受一个输入并返回一个随机的 256 位十六进制数。它是以太坊安全性的支柱。abi.encodePacked(_str)
:在对其进行哈希处理之前,它将输入字符串打包成紧凑的二进制格式。uint(...)
:我们将哈希结果从字节强制转换为 uint
(一个大整数)。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
。这是外部用户和我们应用程序的前端将调用的函数。_name
。_generateRandomDna
函数,传递名称以生成唯一的 DNA。_createZombie
函数。CryptoZombies 教你以清晰的关注点分离来构建你的代码。公共函数简单易懂,而复杂的逻辑则隐藏在私有辅助函数中。这使得代码更具可读性、可测试性和安全性 — 这是智能合约开发中的一项基本最佳实践。
在我们的下一篇博文中,我们将看到如何扩展此工厂以允许僵尸进食和升级!冒险才刚刚开始。
在第一课中,你觉得最有趣的 Solidity 概念是什么?是映射、伪随机数生成还是其他什么?请在下面的评论中告诉我们!
- 原文链接: blog.blockmagnates.com/d...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!