このプロジェクトのソース https://cryptozombies.io/
0. 序文
前節ではゾンビの生成のみを実現し、ゾンビごとに特定の所有者を指定しませんでしたが、今回は主に複数ユーザーに対応するためのユーザー認証機能をプロジェクトに追加しました。同時に、攻撃融合機能が追加され、2つのゾンビを融合して新しいゾンビを形成し、同時にCryptoKittiesのチェーンコードを呼び出してCryptoKittiesとゾンビの融合を完了できます。
1. 重要な知識
- マッピングとアドレス。アドレスはSolidity 言語の一意のデータ型であり、イーサリアム ユーザー (通常のユーザーまたは契約ユーザーの場合があります) をマークします。マップは基本的に、データを保存および検索するためのキーと値のペアであり、次のように宣言されます。
//声明了一个地址到uint类型的映射。
mapping (address => uint) public accountBalance;
//声明了一个uint类型到字符串类型的映射。
mapping (uint => string) userIdToName;
- msg.sender : Solidity には、すべての関数から呼び出すことができるグローバル変数がいくつかあります。そのうちの 1 つは msg.sender で、現在の呼び出し元 (またはスマート コントラクト) のアドレスを参照します。以下は、msg.sender を使用してマッピングを更新する例です。
//声明一个映射。
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
// 更新我们的 `favoriteNumber` 映射来将 `_myNumber`存储在 `msg.sender`名下
favoriteNumber[msg.sender] = _myNumber;
// 存储数据至映射的方法和将数据存储在数组相似
}
function whatIsMyNumber() public view returns (uint) {
// 拿到存储在调用者地址名下的值
// 若调用者还没调用 setMyNumber, 则值为 `0`
return favoriteNumber[msg.sender];
}
- require :以下に示すように、実行中に特定の条件が満たされない場合に関数がエラーをスローし、実行を停止します。
function sayHiToVitalik(string _name) public returns (string) {
// 比较 _name 是否等于 "Vitalik". 如果不成立,抛出异常并终止程序
// (敲黑板: Solidity 并不支持原生的字符串比较, 我们只能通过比较
// 两字符串的 keccak256 哈希值来进行判断)
require(keccak256(_name) == keccak256("Vitalik"));
// 如果返回 true, 运行如下语句
return "Hi!";
}
- 継承と導入をサポートします。親クラスを継承すると、親クラスの関数が使用できます。同様に、引用後に内部のコントラクト関数も使用できます。継承と導入を合理的に使用すると、コードがより簡潔になります。
- ストレージ変数の 2 つの形式: storage または Memory。ストレージ変数とは、ブロックチェーンに永続的に保存される変数を指します。Memory 変数は一時的なものであり、外部関数がコントラクトを呼び出すと、Memory 変数は削除されます。
- external 修飾子と external修飾子: external と private は似ていますが、コントラクトが親コントラクトから継承する場合、コントラクトは親コントラクトで定義されている「内部」関数にアクセスできます。。
external は public と似ていますが、これらの関数はコントラクトの外部からのみ呼び出すことができ、コントラクト内の他の関数からは呼び出すことができない点が異なります。外部とパブリックをいつ使用するかについては後で説明します。 - 他のコントラクトと対話する:最初にインターフェイスを定義する必要があります。ブロックチェーン上にこのコントラクトがある場合:
contract LuckyNumber {
mapping(address => uint) numbers;
function setNum(uint _num) public {
numbers[msg.sender] = _num;
}
function getNum(address _myAddress) public view returns (uint) {
return numbers[_myAddress];
}
}
インターフェースを宣言できます。
contract NumberInterface {
function getNum(address _myAddress) public view returns (uint);
}
このように使用すると:
contract MyContract {
address NumberInterfaceAddress = 0xab38...;
// ^ 这是FavoriteNumber合约在以太坊上的地址
NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
// 现在变量 `numberContract` 指向另一个合约对象
function someFunction() public {
// 现在我们可以调用在那个合约中声明的 `getNum`函数:
uint num = numberContract.getNum(msg.sender);
// ...在这儿使用 `num`变量做些什么
}
}
2. コード
コードに関係するプログラミングの知識は上記です。
//zombiefeeding.sol
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
contract KittyInterface {
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 {
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
if (keccak256(_species) == keccak256("kitty")) {
newDna = newDna - newDna % 100 + 99;
}
_createZombie("NoName", newDna);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
feedAndMultiply(_zombieId, kittyDna, "kitty");
}
}