ZombieFactory 升级僵尸 - 继承、接口和合约间通信

本文介绍了如何在以太坊上实现合约间的通信,通过继承和接口,使得 ZombieFeeding 合约能够与 CryptoKitties 合约交互,实现喂养僵尸猫的功能。文章还讨论了合约优化的重要性,包括访问控制和合约地址管理,以及以太坊的可组合性。

升级我们的僵尸——继承、接口和合约间通信

合约通信

欢迎回来,僵尸指挥官们!在我们的第一篇博文中,我们解构了 ZombieFactory 并学习了 Solidity 的基础知识。我们现在已经完成了 CryptoZombies 课程的 22%,并准备好投入到下一个激动人心的阶段:让我们的僵尸进食!

本课介绍了 Web3 开发中一些最强大的概念:合约继承、接口以及与区块链上其他合约的交互。我们还将了解对我们原始工厂所做的关键优化。

进化的代码:ZombieFeeding.sol

这是我们 ZombieFeeding 合约的新代码,它直接建立在我们的工厂之上。

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol"; // 导入我们之前的合约

contract KittyInterface { // 1. 定义一个接口
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory { // 2. 继承的应用

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);

  function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) public {
    require(msg.sender == zombieToOwner[_zombieId]); // 3. 访问控制
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    // 4. 特殊的 Kitty 逻辑
    if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId); // 5. 调用一个外部合约
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

优化的基础:ZombieFactory.sol

在我们深入研究之前,让我们看一下对我们原始合约所做的关键安全和逻辑优化:

在你的收件箱中获取 Zrhmz 的故事

变更 1:private 改为 internal

// Before: function _createZombie(string memory _name, uint _dna) private {
// After:
function _createZombie(string memory _name, uint _dna) internal {

变更 2:添加一个 Requirement:

function createRandomZombie(string memory _name) public {
    require(ownerZombieCount[msg.sender] == 0); // 新的优化
    uint randDna = _generateRandomDna(_name);
    randDna = randDna - randDna % 100; // 新的逻辑
    _createZombie(_name, randDna);
}
  • require(ownerZombieCount[msg.sender] == 0):这是一个关键的访问控制和安全优化。它可以防止用户通过免费创建无限的僵尸军队来垃圾邮件网络。现在,一个玩家只能创建一个起始僵尸。
  • randDna = randDna - randDna % 100:此行确保起始僵尸的 DNA 的最后两位数字为 00。这对于以后创建特殊的僵尸“类”非常重要,并且不会干扰我们接下来将看到的 kitty 逻辑。

ZombieFeeding 的逐行分解

1. 合约继承:建立在我们的基础上:

import "./zombiefactory.sol";
contract ZombieFeeding is ZombieFactory {
  • import "./zombiefactory.sol";: 这个指令将我们第一个合约文件中的所有代码拉入这个文件中。它就像 C++ 中的 #include
  • is ZombieFactory: 这是继承。我们的 ZombieFeeding 合约是 ZombieFactory 的子合约。这意味着它可以访问其所有 public 和 internal 函数和变量(zombies 数组,dnaModulus_createZombie() 等)。这促进了代码重用和逻辑组织。

2. 定义一个接口:与其他合约对话:

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (...);
}
  • 它是什么:接口就像合约 ABI(应用程序二进制接口)。它定义了函数的名称、参数、返回类型和可见性,没有任何实际逻辑。
  • 为什么使用它:我们希望我们的合约与 Ethereum 主网上的真实 CryptoKitties 合约进行交互。我们不需要整个合约;我们只需要知道如何调用 getKitty 函数。该接口充当承诺或蓝图,告诉我们的合约:“如果你调用一个看起来像这样的函数,外部合约将知道如何响应。”

3. 初始化接口和安全性考虑

address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
  • 此代码创建对外部合约的引用。它将 kittyContract 变量指向官方 CryptoKitties 合约地址。
  • SEO 和开发人员提示:像这样硬编码地址对于学习来说是可以的,但这不是生产的最佳实践。如果 CryptoKitties 合约被升级并移动,我们的合约将永远失效!更好的模式是创建一个函数(例如,setKittyContractAddress),允许合约所有者稍后更新此地址。这是在专业智能合约中常见的优化。

4. 核心喂养逻辑

function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    ...
    if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }
  • require(msg.sender == zombieToOwner[_zombieId]):另一个关键的访问控制检查。它确保只有僵尸的所有者才能喂养它。你不能喂养别人的僵尸!
  • Kitty 逻辑:此 if 语句检查 _species 字符串是否为“kitty”。因为我们无法在 Solidity 中直接比较字符串,所以我们通过比较它们的加密哈希来做到这一点。如果是 kitty,它会修改新的 DNA 以 99 结尾。这创建了一个特殊的“kitty-zombie”,这是一个有趣的功能,它使用了我们优化的工厂中建立的 DNA 规则。

5. 进行外部调用

function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }
  • 这是奇迹发生的地方。此函数采用 kitty ID。
  • (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);: 此行调用外部 CryptoKitties 合约。充满逗号的语法允许我们忽略我们不关心的所有返回值(例如 isGestatingcooldownIndex 等),只捕获最后一个:kitty 的 genes,我们将其存储为 kittyDna

为什么本课是一次量子飞跃

本课教授了 Ethereum 的真正超能力:可组合性。你的去中心化应用程序(DApp)不是一个孤立的岛屿。它可以与区块链上的每个其他公共合约进行交互并利用其功能。这种将合约“管道”连接在一起的能力是 Ethereum 经常被称为“世界计算机”的原因。

我们的僵尸不再只是单个合约中的数据;它们是包括 CryptoKitties 以及可能无数其他项目的更大生态系统的一部分。

在我们的下一篇文章中,我们将看到如何赋予我们的僵尸所有权和攻击能力!准备好迎接更多的继承和战斗机制。

你认为区块链可组合性最令人着迷的部分是什么?是安全挑战还是无限创新的潜力?让我们在下面的评论中讨论!

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

0 条评论

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