上一节讲解了Ownable
、ERC721
、GeneScienceInterface
、KittyAccessControl
四个合约,今天来看看接下来的KittyBase
这一合约。KittyBase
是CryptoKitties的基类/合约。保存所有常见的structs, events 和 base variables。
contract KittyBase is KittyAccessControl {
event Birth(address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes);
event Transfer(address from, address to, uint256 tokenId);
struct Kitty {
uint256 genes;
uint64 birthTime;
uint64 cooldownEndBlock;
uint32 matronId;
uint32 sireId;
uint32 siringWithId;
uint16 cooldownIndex;
uint16 generation;
}
uint32[14] public cooldowns = [
uint32(1 minutes),
uint32(2 minutes),
uint32(5 minutes),
uint32(10 minutes),
uint32(30 minutes),
uint32(1 hours),
uint32(2 hours),
uint32(4 hours),
uint32(8 hours),
uint32(16 hours),
uint32(1 days),
uint32(2 days),
uint32(4 days),
uint32(7 days)
];
uint256 public secondsPerBlock = 15;
Kitty[] kitties;
mapping (uint256 => address) public kittyIndexToOwner;
mapping (address => uint256) ownershipTokenCount;
mapping (uint256 => address) public kittyIndexToApproved;
mapping (uint256 => address) public sireAllowedToAddress;
SaleClockAuction public saleAuction;
SiringClockAuction public siringAuction;
function _transfer(address _from, address _to, uint256 _tokenId) internal {
ownershipTokenCount[_to]++;
kittyIndexToOwner[_tokenId] = _to;
if (_from != address(0)) {
ownershipTokenCount[_from]--;
delete sireAllowedToAddress[_tokenId];
delete kittyIndexToApproved[_tokenId];
}
Transfer(_from, _to, _tokenId);
}
function _createKitty(
uint256 _matronId,
uint256 _sireId,
uint256 _generation,
uint256 _genes,
address _owner
)
internal
returns (uint)
{
require(_matronId == uint256(uint32(_matronId)));
require(_sireId == uint256(uint32(_sireId)));
require(_generation == uint256(uint16(_generation)));
uint16 cooldownIndex = uint16(_generation / 2);
if (cooldownIndex > 13) {
cooldownIndex = 13;
}
Kitty memory _kitty = Kitty({
genes: _genes,
birthTime: uint64(now),
cooldownEndBlock: 0,
matronId: uint32(_matronId),
sireId: uint32(_sireId),
siringWithId: 0,
cooldownIndex: cooldownIndex,
generation: uint16(_generation)
});
uint256 newKittenId = kitties.push(_kitty) - 1;
require(newKittenId == uint256(uint32(newKittenId)));
Birth(
_owner,
newKittenId,
uint256(_kitty.matronId),
uint256(_kitty.sireId),
_kitty.genes
);
_transfer(0, _owner, newKittenId);
return newKittenId;
}
function setSecondsPerBlock(uint256 secs) external onlyCLevel {
require(secs < cooldowns[0]);
secondsPerBlock = secs;
}
}
事件:
event Birth(address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes)
:每当一只新的小猫出现时,就会发生Birth
事件。这显然包括通过giveBirth
方法创建小猫,但在创建新的Gen0猫时也被调用。event Transfer(address from, address to, uint256 tokenId)
:在ERC721的当前草案中定义的Transfer
事件。每次分配猫咪所有权,包括出生时触发。结构体:
/// 代表小猫的`struct`。**CryptoKitties**中的每只猫都用一个这种结构的拷贝来表示。
/// 注意`Kitty`中的成员的顺序,符合**紧密打包**规则。
struct Kitty {
// 小猫的DNA被打包成256位,猫的DNA永远不会改变。
uint256 genes;
// 当这只猫出现的时候,来自块的时间戳。
uint64 birthTime;
// 这只猫可以重新参与繁殖的最小时间戳。
// 同样用于怀孕计时器以及siring冷却。
uint64 cooldownEndBlock;
// 这只猫的母亲的ID,为GE0猫设定为0。
uint32 matronId;
// 这只猫的父亲的ID,为GE0猫设定为0。
uint32 sireId;
// 为怀孕的猫设置siring猫的id,未怀孕则为0
uint32 siringWithId;
// 作为冷却数组的索引,Gen0猫是从0开始的,其他代猫是(gen/2)
// 每发生一次繁殖行为则加1
uint16 cooldownIndex;
// 这只猫的“世代数”。CK合约出售的猫被称为“GE0”,代号为0。
// 所有其他猫的世代数是他们父母的两代数中最大的一个加1
// 即 max(matron.generation, sire.generation) + 1
uint16 generation;
}
成员变量:
uint32[14] public cooldowns
:一个查找表,指示在任何成功繁殖动作后的冷却时间,称为母体的“怀孕时间”和“siring冷却”。设计时,每次猫被繁殖时,冷却时间大致翻倍,鼓励主人不要再重复饲养同一只猫。在一周内(一只猫可以繁殖无数次,最大的冷却时间是7天)。uint256 public secondsPerBlock
:当前块之间有多少秒的近似值,即15秒。Kitty[] kitties
:一个包含所有存在的猫的Kitty结构的数组。每个猫的ID实际上是这个数组的索引。mapping (uint256 => address) public kittyIndexToOwner
:小猫的ID到拥有者的地址之间的映射mapping (address => uint256) ownershipTokenCount
:所有者地址到小猫数量的计数mapping (uint256 => address) public kittyIndexToApproved
:从KittyIDs映射到已被批准调用transferFrom()
的地址。每个猫咪在任何时候只能有一个批准的地址。零值意味着没有批准。mapping (uint256 => address) public sireAllowedToAddress
:一个从KittyIDs到一个地址的映射,它已经被批准使用这个Kitty通过breedWith()
来进行siring。每只猫咪在任何时候只能有一个被批准的地址。零值意味着没有批准。SaleClockAuction public saleAuction
:处理猫咪销售的ClockAuction合约的地址。SiringClockAuction public siringAuction
:一个定制ClockAuction子类合同的地址,处理siring拍卖。需要与saleAuction分开,因为在sales和siring拍卖之后采取的行动是完全不同的。函数:
/// 将特定小猫的所有权转移给一个地址。
function _transfer(address _from, address _to, uint256 _tokenId) internal {
// 由于小猫的数量上限为2 ^ 32,不会溢出。
ownershipTokenCount[_to]++;
// 转移所有权
kittyIndexToOwner[_tokenId] = _to;
// 当创建新的小猫,从0x0,但我们没法表示这个地址。
if (_from != address(0)) {
ownershipTokenCount[_from]--;
// 一旦猫咪被转移,也可以清除sire的补助。
delete sireAllowedToAddress[_tokenId];
//清除任何先前批准的所有权交易
delete kittyIndexToApproved[_tokenId];
}
// 触发transfer事件.
Transfer(_from, _to, _tokenId);
}
/// 创建新的Kitty并存储它.这个方法不进行任何检查,只应在
/// 输入数据已知是有效的。将同时生成Birth事件和Transfer事件。
/// @param _matronId 这只猫的母亲ID(GE0为零)
/// @param _sireId 这只猫的父亲ID(GE0为零)
/// @param _generation 生成该猫的生成数,必须由调用方计算
/// @param _genes 猫猫的DNA
/// @param _owner 这只猫的拥有者
function _createKitty(
uint256 _matronId,
uint256 _sireId,
uint256 _generation,
uint256 _genes,
address _owner
)
internal
returns (uint)
{
require(_matronId == uint256(uint32(_matronId)));
require(_sireId == uint256(uint32(_sireId)));
require(_generation == uint256(uint16(_generation)));
// 新的Kitty以与父母Gen/2相同的冷却开始。
uint16 cooldownIndex = uint16(_generation / 2);
if (cooldownIndex > 13) {
cooldownIndex = 13;
}
// 构建kitty
Kitty memory _kitty = Kitty({
genes: _genes,
birthTime: uint64(now),
cooldownEndBlock: 0,
matronId: uint32(_matronId),
sireId: uint32(_sireId),
siringWithId: 0,
cooldownIndex: cooldownIndex,
generation: uint16(_generation)
});
// 将kitty放进kitties中,返回得到kittyId
uint256 newKittenId = kitties.push(_kitty) - 1;
require(newKittenId == uint256(uint32(newKittenId)));
// 触发Birth事件
Birth(
_owner,
newKittenId,
uint256(_kitty.matronId),
uint256(_kitty.sireId),
_kitty.genes
);
_transfer(0, _owner, newKittenId);
return newKittenId;
}
function setSecondsPerBlock(uint256 secs) external onlyCLevel
:调整区块之间的秒数,只有CXO们可以设置。