2、定义猫的属性 以及 创建猫
//猫的基础数据
contract KittyBase is KittyAccessControl {
/*** EVENTS ***/
/// 出生事件
event Birth(address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes);
/// 转账事件
event Transfer(address from, address to, uint256 tokenId);
/*** DATA TYPES ***/
/// 猫的属性
struct Kitty {
// 猫的基因
uint256 genes;
// 出生日期
uint64 birthTime;
// 猫可以再次进行繁殖的最小区块,用于已经怀孕的猫
uint64 cooldownEndBlock;
// 猫 母亲 的ID
uint32 matronId;
// 猫 父亲的 ID
uint32 sireId;
// 怀孕期的猫的配偶 ID
// 通过这个参数可以知道怀孕的猫的配偶是谁
uint32 siringWithId;
// 繁殖冷却时间,与上面的cooldownEndBlock构成双重保障
//这两个参数之所以没放在一起,是因为一个是uint64,一个是uint16
// 这样可以节省gas
uint16 cooldownIndex;
// 猫的代数
uint16 generation;
}
/*** CONSTANTS ***/
// 数组,cooldownIndex 对应的冷却时间,
// 随着猫的代数和生育次数的增加,猫进行交配的冷却时间也会逐渐增加
// 最大冷却时间不会超过7天
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;
/*** STORAGE ***/
// 所有猫的ID
Kitty[] kitties;
//cat IDs to the address ;猫ID 对应 地址(用户)
mapping (uint256 => address) public kittyIndexToOwner;
// address to count of tokens that address owns
// 地址(用户) 对应 猫的数量
mapping (address => uint256) ownershipTokenCount;
// KittyIDs to an address that has been approved to call transferFrom()
// 授权 ,把uint256(猫id) 授权给address(用户) 交易 ,先授权,然后才能交易
mapping (uint256 => address) public kittyIndexToApproved;
// 授权,把uint256(猫id) 授权给address(用户)交配,先授权,然后才能交配
// 系统中所有待交配的猫
mapping (uint256 => address) public sireAllowedToAddress;
/// 买卖的交易所,实际上存储的是合约的地址
SaleClockAuction public saleAuction;
/// 交配的交易所,实际上存储的是合约的地址
SiringClockAuction public siringAuction;
// 交易
// 把 _from 拥有的_tokenId 猫,交易给 _to
function _transfer(address _from, address _to, uint256 _tokenId) internal {
// _to 用户拥有猫是数量 加 1
ownershipTokenCount[_to]++;
// _tokenId 猫的所有者 更改为 _to
kittyIndexToOwner[_tokenId] = _to;
// 判断 _from 地址是否为空
if (_from != address(0)) {
// _from 用户拥有猫是数量 减 1
ownershipTokenCount[_from]--;
// 删除猫的出售信息
delete sireAllowedToAddress[_tokenId];
// 删除猫的授权信息
delete kittyIndexToApproved[_tokenId];
}
// Emit the transfer event.
Transfer(_from, _to, _tokenId);
}
// 创建猫
/// @param _matronId The kitty ID of the matron of this cat (zero for gen0) 母亲ID
/// @param _sireId The kitty ID of the sire of this cat (zero for gen0) 父亲ID
/// @param _generation The generation number of this cat, must be computed by caller. 猫的代数
/// @param _genes The kitty's genetic code. 猫的基因
/// @param _owner The inital owner of this cat, must be non-zero (except for the unKitty, ID 0) 猫的拥有者
// 返回 猫的ID
function _createKitty(
uint256 _matronId,
uint256 _sireId,
uint256 _generation,
uint256 _genes,
address _owner
)
internal
returns (uint)
{
//是为了限制猫的数量 最多2^32次方只猫
require(_matronId == uint256(uint32(_matronId)));
require(_sireId == uint256(uint32(_sireId)));
// 是为了限制猫的代数 最多2^16
require(_generation == uint256(uint16(_generation)));
// 繁殖冷却时间最多是 7天 cooldowns[13] = 7天
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)
});
// 放入区块链(kitties)中,返回新生成的猫ID
uint256 newKittenId = kitties.push(_kitty) - 1;
// 判断,猫的ID不能大于2^32
require(newKittenId == uint256(uint32(newKittenId)));
// emit the birth event ,触发事件,发送给前端监听
Birth(
_owner,
newKittenId,
uint256(_kitty.matronId),
uint256(_kitty.sireId),
_kitty.genes
);
// 把猫 分配给 拥有者_owner
_transfer(0, _owner, newKittenId);
return newKittenId;
}
// 修改生成区块的秒数,只有 CEO CFO COO才能执行
// 权限控制见下面
function setSecondsPerBlock(uint256 secs) external onlyCLevel {
require(secs < cooldowns[0]);
secondsPerBlock = secs;
}
}
3、权限控制
//再以太猫这个项目中,有三个管理者
//CEO :
//CFO :
//COO :
contract KittyAccessControl {
event ContractUpgrade(address newContract);
address public ceoAddress;
address public cfoAddress;
address public cooAddress;
// 是否暂停
bool public paused = false;
/// 只有CEO才能调用
modifier onlyCEO() {
require(msg.sender == ceoAddress);
_;
}
/// 只有CFO才能调用
modifier onlyCFO() {
require(msg.sender == cfoAddress);
_;
}
/// 只有COO才能调用
modifier onlyCOO() {
require(msg.sender == cooAddress);
_;
}
// CEO COO onlyCFO 都可以调用
modifier onlyCLevel() {
require(
msg.sender == cooAddress ||
msg.sender == ceoAddress ||
msg.sender == cfoAddress
);
_;
}
// 更换CEO ,只有原来的CEO才能更换新的CEO
function setCEO(address _newCEO) external onlyCEO {
require(_newCEO != address(0));
ceoAddress = _newCEO;
}
// 更换CFO ,只有原来的CEO才能更换新的CFO
function setCFO(address _newCFO) external onlyCEO {
require(_newCFO != address(0));
cfoAddress = _newCFO;
}
// 更换COO ,只有原来的CEO才能更换新的COO
function setCOO(address _newCOO) external onlyCEO {
require(_newCOO != address(0));
cooAddress = _newCOO;
}
/*** 暂停 ***/
// 只有没有暂停时 才能执行
modifier whenNotPaused() {
require(!paused);
_;
}
/// 只有暂停时 才能执行
modifier whenPaused {
require(paused);
_;
}
/// 紧急暂停 ,只有三个管理者才有权限 执行
function pause() external onlyCLevel whenNotPaused {
paused = true;
}
/// 取消暂停,只有CEO才能执行
function unpause() public onlyCEO whenPaused {
// can't unpause if contract was upgraded
paused = false;
}
}
4、ERC721协议
ERC721代币 :核心是“Non-Fungible Tokens”,非同质代币。
简单的说,就是每一枚代币都是独一无二的,而ERC20 代币中,代币与代币是一样。
“以太猫”为例,每只以太猫拥有独一无二的基因,每只小猫和繁衍的后代也都是独一无二的。从原理上来看,每只以太猫在区块链平台上都是一条独一无二的代码,因此没有两只外表和特性完全相同的小猫。而且,ERC721每个代币都有一个独立唯一的tokenid,例如在cryptokitties里就是猫的id,独一无二。
/// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
/// @author Dieter Shirley <dete@axiomzen.co> (https://github.com/dete)
contract ERC721 {
// Required methods
function totalSupply() public view returns (uint256 total);
function balanceOf(address _owner) public view returns (uint256 balance);
function ownerOf(uint256 _tokenId) external view returns (address owner);
function approve(address _to, uint256 _tokenId) external;
function transfer(address _to, uint256 _tokenId) external;
function transferFrom(address _from, address _to, uint256 _tokenId) external;
// Events
event Transfer(address from, address to, uint256 tokenId);
event Approval(address owner, address approved, uint256 tokenId);
// 可选择的实现
// function name() public view returns (string name);
// function symbol() public view returns (string symbol);
// function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
// function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl);
// ERC165标准, 用于和外部合约交互,
function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}
5、以太猫(代币)元数据
为了和外部合约交互
contract ERC721Metadata {
/// @dev Given a token Id, returns a byte array that is supposed to be converted into string.
function getMetadata(uint256 _tokenId, string) public view returns (bytes32[4] buffer, uint256 count) {
if (_tokenId == 1) {
buffer[0] = "Hello World! :D";
count = 15;
} else if (_tokenId == 2) {
buffer[0] = "I would definitely choose a medi";
buffer[1] = "um length string.";
count = 49;
} else if (_tokenId == 3) {
buffer[0] = "Lorem ipsum dolor sit amet, mi e";
buffer[1] = "st accumsan dapibus augue lorem,";
buffer[2] = " tristique vestibulum id, libero";
buffer[3] = " suscipit varius sapien aliquam.";
count = 128;
}
}
}
6、ERC721实现
contract KittyOwnership is KittyBase, ERC721 {
/// 代币的名字
string public constant name = "CryptoKitties";
// 代币的缩写
string public constant symbol = "CK";
// ERC721元数据
ERC721Metadata public erc721Metadata;
//验证是否支持 ERC165协议
bytes4 constant InterfaceSignature_ERC165 =
bytes4(keccak256('supportsInterface(bytes4)'));
//验证是否支持 ERC721协议
bytes4 constant InterfaceSignature_ERC721 =
bytes4(keccak256('name()')) ^
bytes4(keccak256('symbol()')) ^
bytes4(keccak256('totalSupply()')) ^
bytes4(keccak256('balanceOf(address)')) ^
bytes4(keccak256('ownerOf(uint256)')) ^
bytes4(keccak256('approve(address,uint256)')) ^
bytes4(keccak256('transfer(address,uint256)')) ^
bytes4(keccak256('transferFrom(address,address,uint256)')) ^
bytes4(keccak256('tokensOfOwner(address)')) ^
bytes4(keccak256('tokenMetadata(uint256,string)'));
/// 实现ERC165 协议,作用是外部账户验证我们的合约是否实现我们想要的函数接口
function supportsInterface(bytes4 _interfaceID) external view returns (bool)
{
// DEBUG ONLY
//require((InterfaceSignature_ERC165 == 0x01ffc9a7) && (InterfaceSignature_ERC721 == 0x9a20483d));
return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
}
/// 设置代币元数据,只有CEO才能执行
function setMetadataAddress(address _contractAddress) public onlyCEO {
erc721Metadata = ERC721Metadata(_contractAddress);
}
// 判断猫的主人是否为传递进来的地址 _claimant
function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
return kittyIndexToOwner[_tokenId] == _claimant;
}
/// 判断 _claimant 地址是否已经有了 猫_tokenId 的授权
function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
return kittyIndexToApproved[_tokenId] == _claimant;
}
/// 把_tokenId授权_approved地址
// 地址可以是买卖交易所合约地址 , 即把 _tokenId猫授权给买卖交易所进行拍卖
function _approve(uint256 _tokenId, address _approved) internal {
kittyIndexToApproved[_tokenId] = _approved;
}
/// 返回地址(主人) 有多少只猫(代币)
function balanceOf(address _owner) public view returns (uint256 count) {
return ownershipTokenCount[_owner];
}
/// 转移代币 ,只有当程序 notPaused 没有暂停时才能使用
function transfer(
address _to,
uint256 _tokenId
)
external
whenNotPaused
{
// 安全检查,_to 不能为0
require(_to != address(0));
// 转账的地址不能为当前账户
require(_to != address(this));
//
require(_to != address(saleAuction));
require(_to != address(siringAuction));
// 当前的合约的调用者 是否 拥有这只猫
require(_owns(msg.sender, _tokenId));
// 转账
_transfer(msg.sender, _to, _tokenId);
}
/// 授权代币,同样的,只有当程序 notPaused 没有暂停时才能使用
function approve(
address _to,
uint256 _tokenId
)
external
whenNotPaused
{
// 只有拥有者才能授权
require(_owns(msg.sender, _tokenId));
// 函数调用,把_tokenId猫授权给_to
_approve(_tokenId, _to);
// 触发事件
Approval(msg.sender, _to, _tokenId);
}
/// 将 _from用户 的_tokenId猫 转移给 _to用户
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
external
whenNotPaused
{
// 安全检查,判断_to 地址是否合法
require(_to != address(0));
// 不能转移给本合约地址
require(_to != address(this));
// 检查权限,检查msg.sender是否获得了授权转移_tokenId猫
require(_approvedFor(msg.sender, _tokenId));
//检查_from 是否拥有 _tokenId猫
require(_owns(_from, _tokenId));
// 调用函数_transfer 进行转移
_transfer(_from, _to, _tokenId);
}
/// 返回当前猫的总数
function totalSupply() public view returns (uint) {
return kitties.length - 1;
}
/// 返回_tokenId猫的所有者
function ownerOf(uint256 _tokenId)
external
view
returns (address owner)
{
owner = kittyIndexToOwner[_tokenId];
require(owner != address(0));
}
/// 返回_owner所有者拥有的所有猫的id
function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
// 查询_owner 有几只猫
uint256 tokenCount = balanceOf(_owner);
if (tokenCount == 0) {
// Return an empty array
return new uint256[](0);
} else {
// 初始化一个长度为tokenCount的返回值result
uint256[] memory result = new uint256[](tokenCount);
uint256 totalCats = totalSupply();
uint256 resultIndex = 0;
// 猫的id 从 1 递增到 totalCats
uint256 catId;
// 从1开始循环遍历所有的totalCats
for (catId = 1; catId <= totalCats; catId++) {
//判断当前catId 的拥有者 是不是 _owner
if (kittyIndexToOwner[catId] == _owner) {
result[resultIndex] = catId;
resultIndex++;
}
}
return result;
}
}
/// 拷贝方法
/// Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol
function _memcpy(uint _dest, uint _src, uint _len) private view {
for(; _len >= 32; _len -= 32) {
// 汇编
assembly {
mstore(_dest, mload(_src))
}
_dest += 32;
_src += 32;
}
// Copy remaining bytes
uint256 mask = 256 ** (32 - _len) - 1;
assembly {
let srcpart := and(mload(_src), not(mask))
let destpart := and(mload(_dest), mask)
mstore(_dest, or(destpart, srcpart))
}
}
/// 将_rawBytes中长度为_stringLength 转成string 并返回
/// Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol
function _toString(bytes32[4] _rawBytes, uint256 _stringLength) private view returns (string) {
var outputString = new string(_stringLength);
uint256 outputPtr;
uint256 bytesPtr;
assembly {
outputPtr := add(outputString, 32)
bytesPtr := _rawBytes
}
_memcpy(outputPtr, bytesPtr, _stringLength);
return outputString;
}
/// 返回指向该元数据包的元数据的URI
function tokenMetadata(uint256 _tokenId, string _preferredTransport) external view returns (string infoUrl) {
require(erc721Metadata != address(0));
bytes32[4] memory buffer;
uint256 count;
(buffer, count) = erc721Metadata.getMetadata(_tokenId, _preferredTransport);
return _toString(buffer, count);
}
}
网友评论