ERC-20 和 ERC-721
以太坊有很多ERC标准,如ERC-20、ERC-223、ERC-721、ERC-998、ERC-1155等,其中最被大众熟知的是ERC-20 和 ERC-721
更多标准可以点击这里了解
在了解ERC-20 和 ERC-721之前,先理解一下FT和NFT
什么是FT和NFT
FT(Fungible Token),即同质化代币,是可以相互代替的、可随意拆分的token,如USDT、BNB等,1个USDT可以被拆分成若干份进行交易,每一个USDT本质上都是相同的。
NFT(Non-Fungible Token),即非同质化代币,是相对于FT产生的概念,是不可分割、不可代替的,具有唯一性,如CryptoKitties(加密猫),每一个token都是唯一的。
ERC-20
ERC-20标准是以太坊FT(同质化代币)的标准,如USDT、BNB、SHIB等都是基于此标准
ERC-20合约解读见之前写过的以太坊USDT合约详解
ERC-721
ERC-721标准是以太坊NFT(非同质化代币)的标准,CryptoKitties(加密猫) 可以说是ERC-721的代表作
CryptoKitties加密猫合约
/**
*Submitted for verification at Etherscan.io on 2017-11-28
*/
pragma solidity ^ 0.4 .11;
//Ownable合约
contract Ownable {
//定义一个合约所有者地址变量
address public owner;
//构造方法 只会在部署合约时执行 初始化合约所有者地址
function Ownable() {
owner = msg.sender;
}
//函数修改器
modifier onlyOwner() {
//只有所有者才回满足修改器的条件
require(msg.sender == owner);
_;
}
//一个转移合约所有者的函数 后面public onlyOwner 表示使用onlyOwner这个修改器
//满足修改器的条件,才可以执行函数内的代码
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
//https://eips.ethereum.org/EIPS/eip-721
//erc721标准必须要实现ERC721和ERC165
//erc721标准的基本核心函数
contract ERC721 {
//721标准 必要的函数
//查询合约上代币总数
function totalSupply() public view returns(uint256 total);
//查询指定地址代币余额
function balanceOf(address _owner) public view returns(uint256 balance);
//查询token所属的地址
function ownerOf(uint256 _tokenId) external view returns(address owner);
//授权 将token授权给_to地址
function approve(address _to, uint256 _tokenId) external;
//转账交易 将token发送给_to地址
function transfer(address _to, uint256 _tokenId) external;
//代理交易 将token从_from账户发送给_to账户
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);
// Optional 下面四个是721协议的可选函数
// 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);
// ERC-165 Compatibility (https://github.com/ethereum/EIPs/issues/165)
function supportsInterface(bytes4 _interfaceID) external view returns(bool);
}
//基因合约 外部合约
contract GeneScienceInterface {
//判断是否是基因合约
function isGeneScience() public pure returns(bool);
//给出小猫1和2的基因,返回一个基因组合-可能有随机因素
//genes1 妈妈的基因
//genes2 父亲的基因
//return 孩子的基因
function mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) public returns(uint256);
}
//维护&升级合约
contract KittyAccessControl {
//合约升级广播
event ContractUpgrade(address newContract);
//定义了三个地址 ceo地址 cfo地址 coo地址
address public ceoAddress;
address public cfoAddress;
address public cooAddress;
//定义合同维护状态 flase 不在维护中
bool public paused = false;
//ceo地址函数修改器 用作权限验证
modifier onlyCEO() {
//校验调用方是ceo地址
require(msg.sender == ceoAddress);
_;
}
//同理
modifier onlyCFO() {
require(msg.sender == cfoAddress);
_;
}
//同理
modifier onlyCOO() {
require(msg.sender == cooAddress);
_;
}
//同理
modifier onlyCLevel() {
// 校验调用方是ceo地址/cfo地址/coo地址
require(
msg.sender == cooAddress ||
msg.sender == ceoAddress ||
msg.sender == cfoAddress
);
_;
}
//设置ceo地址
//权限修饰符有 public、private、external internal
//public修饰的函数 任何用户或者合约都能调用和访问
//external修饰的函数只能在合约之外调用,不能被合约内的其他函数调用
//onlyCEO 使用onlyCEO函数修改器,表示只有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;
}
//同理
function setCOO(address _newCOO) external onlyCEO {
require(_newCOO != address(0));
cooAddress = _newCOO;
}
//合约维护相关操作
//函数修改器 限制当前不在维护中
modifier whenNotPaused() {
require(!paused);
_;
}
//同理 在维护中
modifier whenPaused {
require(paused);
_;
}
//将合约状态改为维护中--whenNotPaused 验证当前是非维护状态才可以操作
//ceo cfo coo账户可操作
function pause() external onlyCLevel whenNotPaused {
paused = true;
}
//将合约置于非维护状态 维护状态才可以操作
//只有ceo可操作
function unpause() public onlyCEO whenPaused {
paused = false;
}
}
//猫基础合约
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;
//猫可以再次从事繁殖活动的剩余时间
//母猫则为怀孕中到生产的剩余时间 此字段参与小猫的基因生成算法
//这里时间的算法是根据区块数 和 区块时间间隔
//比如 当前区块数为 10 产生一个区块要15秒 冷却时间为1分钟
//那么应该是4个区块后 该猫冷却结束 那么此值应设为14
//判断冷却时间是否结束的方式为 判断该值是否小于当前的区块数
uint64 cooldownEndBlock;
//猫父母的ID,对于第0代猫设置为0
uint32 matronId; //母
uint32 sireId; //父
//猫怀孕时,此值为公猫的id,未怀孕为0,可以用来分辨此猫是否怀孕中
uint32 siringWithId;
//繁殖冷却时间的数组下标
//第0代的猫从0开始,其他猫被初始化为(猫的代数 / 2) 最大为13
//每成功繁殖一次加1
uint16 cooldownIndex;
//猫的代数
//合约产出的0代猫代数为0 其他猫的代数为--父母中较大的代数再加1
uint16 generation;
}
//繁殖冷却时间数组 母猫称为怀孕时间,最大冷却时间为7天
//结合Kitty.cooldownIndex使用
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;
//猫的数组,数组下表就是猫的id
//id为0的猫是所有0代猫的父母 既父又母 基因无效
Kitty[] kitties;
//猫的id对应猫主人的地址的map
mapping(uint256 => address) public kittyIndexToOwner;
//账户地址对应token数量的map
mapping(address => uint256) ownershipTokenCount;
//猫的id对应已授权地址(approve)的map 未授权过的值为0
mapping(uint256 => address) public kittyIndexToApproved;
//公猫的id对应已授权共同繁殖的母猫的主人地址的map 未授权过的值为0
//比如 666:0x123 说明id为666的猫已经向0x123地址授权共同繁殖,0x123则可选一只自己的猫与666进行繁殖
mapping(uint256 => address) public sireAllowedToAddress;
//销售合约地址 外部合约
SaleClockAuction public saleAuction;
//拍卖合约地址 外部合约
SiringClockAuction public siringAuction;
//将token发送给_to地址
function _transfer(address _from, address _to, uint256 _tokenId) internal {
//to地址对应token数量 + 1
ownershipTokenCount[_to]++;
//猫的id对应的地址改为to地址
kittyIndexToOwner[_tokenId] = _to;
//from地址不是0 意义不大
if (_from != address(0)) {
//from地址token数量 -1
ownershipTokenCount[_from]--;
//删除此猫已授权的繁殖授权和交易授权
delete sireAllowedToAddress[_tokenId];
delete kittyIndexToApproved[_tokenId];
}
Transfer(_from, _to, _tokenId);
}
//生成猫
//_matronId 母亲的id _sireId 父亲的id
//_generation 猫的代数 _genes 基因 _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)));
//猫的繁殖冷却时间下标 为猫的代数 / 2 最大为13
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;
}
}
//外部合约的引用 计算元数据
contract ERC721Metadata {
//获取元数据
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;
}
}
}
//继承了KittyBase和ERC721
contract KittyOwnership is KittyBase, ERC721 {
//代币名和简称
string public constant name = "CryptoKitties";
string public constant symbol = "CK";
// The contract that will return kitty metadata
ERC721Metadata public erc721Metadata;
bytes4 constant InterfaceSignature_ERC165 =
bytes4(keccak256('supportsInterface(bytes4)'));
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)'));
//为此合约的任何标准接口实现返回true
function supportsInterface(bytes4 _interfaceID) external view returns(bool) {
return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
}
//设置erc721Metadata
//仅ceo地址可操作
function setMetadataAddress(address _contractAddress) public onlyCEO {
erc721Metadata = ERC721Metadata(_contractAddress);
}
//验证tokenId的猫属于claimantd地址
function _owns(address _claimant, uint256 _tokenId) internal view returns(bool) {
return kittyIndexToOwner[_tokenId] == _claimant;
}
//验证tokenId的猫已经被授权给claimant地址了
function _approvedFor(address _claimant, uint256 _tokenId) internal view returns(bool) {
return kittyIndexToApproved[_tokenId] == _claimant;
}
//将tokenId的猫授权给approved地址
function _approve(uint256 _tokenId, address _approved) internal {
kittyIndexToApproved[_tokenId] = _approved;
}
//owner地址猫的数量
function balanceOf(address _owner) public view returns(uint256 count) {
return ownershipTokenCount[_owner];
}
//实现transfer 将tokenId的猫转给to地址
function transfer(
address _to,
uint256 _tokenId
)
external
whenNotPaused {
//验证地址有效
require(_to != address(0));
//to地址不能是本合约地址
require(_to != address(this));
//不能转给拍卖合同
require(_to != address(saleAuction));
require(_to != address(siringAuction));
//验证发送方是猫的原主人
require(_owns(msg.sender, _tokenId));
//转账操作
_transfer(msg.sender, _to, _tokenId);
}
//实现approve 将tokenId的猫授权给approved地址
function approve(
address _to,
uint256 _tokenId
)
external
whenNotPaused {
//验证发送方是猫的原主人
require(_owns(msg.sender, _tokenId));
//将tokenId的猫授权给approved地址
_approve(_tokenId, _to);
Approval(msg.sender, _to, _tokenId);
}
//实现transferFrom 将token从_from账户发送给_to账户
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
external
whenNotPaused {
//验证to地址有效
require(_to != address(0));
//to地址不能是本合约地址
require(_to != address(this));
//验证已授权
require(_approvedFor(msg.sender, _tokenId));
//验证猫的主人是from地址
require(_owns(_from, _tokenId));
//交易操作
_transfer(_from, _to, _tokenId);
}
//猫的总数
function totalSupply() public view returns(uint) {
return kitties.length - 1;
}
//查询猫的主人(token所属的地址)
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) {
//查询该地址猫的数量
uint256 tokenCount = balanceOf(_owner);
//数量为0直接返回
if (tokenCount == 0) {
return new uint256[](0);
} else {
//返回结果数组
uint256[] memory result = new uint256[](tokenCount);
//整个合约猫的数量
uint256 totalCats = totalSupply();
//返回结果数组的下标,每找到一只owner地址的猫后加一
uint256 resultIndex = 0;
//下标 从1开始检索 因为0猫不属于任何账户
uint256 catId;
//遍历
for (catId = 1; catId <= totalCats; catId++) {
//判断第catId只猫是否属于owner
if (kittyIndexToOwner[catId] == _owner) {
//第catId只猫属于owner 将猫的id加到返回结果数组中
//返回结果数组的下标+1
result[resultIndex] = catId;
resultIndex++;
}
}
//返回结果
return result;
}
}
/// @dev Adapted from memcpy() by @arachnid (Nick Johnson <arachnid@notdot.net>)
/// This method is licenced under the Apache License.
/// Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol
function _memcpy(uint _dest, uint _src, uint _len) private view {
// Copy word-length chunks while possible
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))
}
}
/// @dev Adapted from toString(slice) by @arachnid (Nick Johnson <arachnid@notdot.net>)
/// This method is licenced under the Apache License.
/// 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;
}
/// @notice Returns a URI pointing to a metadata package for this token conforming to
/// ERC-721 (https://github.com/ethereum/EIPs/issues/721)
/// @param _tokenId The ID number of the Kitty whose metadata should be returned.
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);
}
}
//生育合约 包含繁殖、怀孕、出生
contract KittyBreeding is KittyOwnership {
//怀孕广播
event Pregnant(address owner, uint256 matronId, uint256 sireId, uint256 cooldownEndBlock);
//繁殖费用 coo有权调整
//2 finney = 2 * 10的15次方
uint256 public autoBirthFee = 2 finney;
//怀孕的猫的数量
uint256 public pregnantKitties;
//基因遗传算法合约
GeneScienceInterface public geneScience;
//设置/更换基因遗传算法合约 仅ceo可操作
function setGeneScienceAddress(address _address) external onlyCEO {
GeneScienceInterface candidateContract = GeneScienceInterface(_address);
//校验地址是基因合同的地址
//https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol
require(candidateContract.isGeneScience());
//设置基因遗传算法合约地址
geneScience = candidateContract;
}
//判断当前的猫是否可以进行繁殖 有三个条件 血缘 繁殖冷却 授权
//判断当前的猫是否可以进行繁殖 时间角度 仅合约内调用
function _isReadyToBreed(Kitty _kit) internal view returns(bool) {
//非怀孕期间 且 繁殖冷却结束 上面cooldownEndBlock解释过此处算法 cooldownEndBlock小于当前的区块数
return (_kit.siringWithId == 0) && (_kit.cooldownEndBlock <= uint64(block.number));
}
//检查id为sireId和matronId的猫是否可以进行繁殖 授权角度 仅合约内调用
//条件是 两只猫属于同一主人 或者 sireId猫已经向matronId所属地址授权共同繁殖
function _isSiringPermitted(uint256 _sireId, uint256 _matronId) internal view returns(bool) {
address matronOwner = kittyIndexToOwner[_matronId];
address sireOwner = kittyIndexToOwner[_sireId];
return (matronOwner == sireOwner || sireAllowedToAddress[_sireId] == matronOwner);
}
//繁殖后计算冷却时间的方法
function _triggerCooldown(Kitty storage _kitten) internal {
//上面cooldownEndBlock解释过此处算法
//冷却时间/区块间隔时间 为预估多少区块后冷却结束 再加上当前区块 为猫繁殖冷却结束区块数
_kitten.cooldownEndBlock = uint64((cooldowns[_kitten.cooldownIndex] / secondsPerBlock) + block.number);
//繁殖冷却时间的数组下标 +1
if (_kitten.cooldownIndex < 13) {
_kitten.cooldownIndex += 1;
}
}
//将id为sireId的猫授权给addr共同繁殖
function approveSiring(address _addr, uint256 _sireId)
external
whenNotPaused {
require(_owns(msg.sender, _sireId));
//加入授权mapping中
sireAllowedToAddress[_sireId] = _addr;
}
//更新autoBirthFee
function setAutoBirthFee(uint256 val) external onlyCOO {
autoBirthFee = val;
}
//判断母猫是否可以生产,条件是怀孕中 & 生产时间已到
function _isReadyToGiveBirth(Kitty _matron) private view returns(bool) {
return (_matron.siringWithId != 0) && (_matron.cooldownEndBlock <= uint64(block.number));
}
//判断当前的猫是否可以进行繁殖 此方法对外开放
function isReadyToBreed(uint256 _kittyId)
public
view
returns(bool) {
require(_kittyId > 0);
Kitty storage kit = kitties[_kittyId];
return _isReadyToBreed(kit);
}
//查询猫是否怀孕
function isPregnant(uint256 _kittyId)
public
view
returns(bool) {
require(_kittyId > 0);
return kitties[_kittyId].siringWithId != 0;
}
//检查两只猫是否可以进行繁殖 血缘角度 仅合约内调用
function _isValidMatingPair(
Kitty storage _matron,
uint256 _matronId,
Kitty storage _sire,
uint256 _sireId
)
private
view
returns(bool) {
//传入两只猫id不能相同
if (_matronId == _sireId) {
return false;
}
//不能与父母猫繁殖
if (_matron.matronId == _sireId || _matron.sireId == _sireId) {
return false;
}
if (_sire.matronId == _matronId || _sire.sireId == _matronId) {
return false;
}
//任何一只猫的母猫是0代猫 可以直接通过
if (_sire.matronId == 0 || _matron.matronId == 0) {
return true;
}
//两只猫的父母要是四个不同的猫,这两只猫不能是兄弟姐妹
if (_sire.matronId == _matron.matronId || _sire.matronId == _matron.sireId) {
return false;
}
if (_sire.sireId == _matron.matronId || _sire.sireId == _matron.sireId) {
return false;
}
return true;
}
//检查两只猫是否可以进行繁殖 通过猫的id检查血缘角度
function _canBreedWithViaAuction(uint256 _matronId, uint256 _sireId)
internal
view
returns(bool) {
Kitty storage matron = kitties[_matronId];
Kitty storage sire = kitties[_sireId];
return _isValidMatingPair(matron, _matronId, sire, _sireId);
}
//检查两只猫是否可以进行繁殖 授权角度 和 血缘角度
function canBreedWith(uint256 _matronId, uint256 _sireId)
external
view
returns(bool) {
require(_matronId > 0);
require(_sireId > 0);
Kitty storage matron = kitties[_matronId];
Kitty storage sire = kitties[_sireId];
return _isValidMatingPair(matron, _matronId, sire, _sireId) &&
_isSiringPermitted(_sireId, _matronId);
}
//繁殖方法 这里也只是准备工作 不是最终方法 所以没有校验条件
function _breedWith(uint256 _matronId, uint256 _sireId) internal {
//两只猫对象
Kitty storage sire = kitties[_sireId];
Kitty storage matron = kitties[_matronId];
//设置母猫siringWithId为公猫id
matron.siringWithId = uint32(_sireId);
//更改两只猫繁殖冷却时间/怀孕时间
_triggerCooldown(sire);
_triggerCooldown(matron);
//删除两只猫的共同繁殖授权
delete sireAllowedToAddress[_matronId];
delete sireAllowedToAddress[_sireId];
//怀孕的猫的数量+1
pregnantKitties++;
//怀孕广播
Pregnant(kittyIndexToOwner[_matronId], _matronId, _sireId, matron.cooldownEndBlock);
}
//繁殖
function breedWithAuto(uint256 _matronId, uint256 _sireId)
external
payable
whenNotPaused {
//余额大于繁殖费用
require(msg.value >= autoBirthFee);
//母猫所有权校验
require(_owns(msg.sender, _matronId));
//无需校验父母猫是否再拍卖中,
//因为对于公猫 拍卖会清除猫的共同繁殖授权 对于母猫 繁殖需要母猫的主人来调用
//拍卖合同不会进行繁殖和授权操作 所以不用判断这个条件
//授权角度校验
require(_isSiringPermitted(_sireId, _matronId));
//公猫
Kitty storage matron = kitties[_matronId];
//繁殖冷却角度校验公猫
require(_isReadyToBreed(matron));
//母猫
Kitty storage sire = kitties[_sireId];
//繁殖冷却角度校验母猫
require(_isReadyToBreed(sire));
//血缘角度校验
require(_isValidMatingPair(
matron,
_matronId,
sire,
_sireId
));
//繁殖
_breedWith(_matronId, _sireId);
}
//怀孕母猫生产,根据基因生产一个小猫归母猫主人所有
//可以调用此方法为其他账户地址的小猫生产,但小猫是属于母猫主人的,而非调用地址
function giveBirth(uint256 _matronId)
external
whenNotPaused
returns(uint256) {
//怀孕母猫
Kitty storage matron = kitties[_matronId];
//判断猫的有效性
require(matron.birthTime != 0);
//判断母猫是否可以生产
require(_isReadyToGiveBirth(matron));
//查出父猫
uint256 sireId = matron.siringWithId;
Kitty storage sire = kitties[sireId];
//小猫的代数 取父母中较大的代数 后面会+1
uint16 parentGen = matron.generation;
if (sire.generation > matron.generation) {
parentGen = sire.generation;
}
//生成小猫的基因
uint256 childGenes = geneScience.mixGenes(matron.genes, sire.genes, matron.cooldownEndBlock - 1);
//母猫主人地址
address owner = kittyIndexToOwner[_matronId];
//生成小猫
uint256 kittenId = _createKitty(_matronId, matron.siringWithId, parentGen + 1, childGenes, owner);
//清楚母猫的怀孕标记
delete matron.siringWithId;
//怀孕的猫的数量-1
pregnantKitties--;
// Send the balance fee to the person who made birth happen.
msg.sender.send(autoBirthFee);
//返回小猫id
return kittenId;
}
}
//拍卖合约基础
contract ClockAuctionBase {
//拍卖结构体
struct Auction {
//卖家地址
address seller;
//起拍价 单位wei
uint128 startingPrice;
//结束价 单位wei
uint128 endingPrice;
//拍卖时长
uint64 duration;
//起拍时间
uint64 startedAt;
}
//定于拍卖合约地址
ERC721 public nonFungibleContract;
//拍卖佣金比例
//0-10000表示1%--100%
uint256 public ownerCut;
//tokenId对应拍卖结构的map
mapping(uint256 => Auction) tokenIdToAuction;
event AuctionCreated(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration);
event AuctionSuccessful(uint256 tokenId, uint256 totalPrice, address winner);
event AuctionCancelled(uint256 tokenId);
//验证token的所有权是否为claimant
function _owns(address _claimant, uint256 _tokenId) internal view returns(bool) {
return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
}
//将tokenId托管到拍卖账户
function _escrow(address _owner, uint256 _tokenId) internal {
nonFungibleContract.transferFrom(_owner, this, _tokenId);
}
//将tokenId转给receiver
function _transfer(address _receiver, uint256 _tokenId) internal {
nonFungibleContract.transfer(_receiver, _tokenId);
}
//添加拍卖
function _addAuction(uint256 _tokenId, Auction _auction) internal {
//校验拍卖时长大于一分钟
require(_auction.duration >= 1 minutes);
//放入拍卖map
tokenIdToAuction[_tokenId] = _auction;
AuctionCreated(
uint256(_tokenId),
uint256(_auction.startingPrice),
uint256(_auction.endingPrice),
uint256(_auction.duration)
);
}
///取消拍卖
function _cancelAuction(uint256 _tokenId, address _seller) internal {
_removeAuction(_tokenId);
_transfer(_seller, _tokenId);
AuctionCancelled(_tokenId);
}
//计算价格
function _bid(uint256 _tokenId, uint256 _bidAmount)
internal
returns(uint256) {
//拍卖结构体
Auction storage auction = tokenIdToAuction[_tokenId];
//校验拍卖有效性
require(_isOnAuction(auction));
uint256 price = _currentPrice(auction);
//检查出价大于当前价格
require(_bidAmount >= price);
//卖家地址
address seller = auction.seller;
//删除拍卖,防止重复出价
_removeAuction(_tokenId);
if (price > 0) {
//计算佣金
uint256 auctioneerCut = _computeCut(price);
//计算扣除佣金后的
uint256 sellerProceeds = price - auctioneerCut;
//发送给卖价
seller.transfer(sellerProceeds);
}
//计算溢价
uint256 bidExcess = _bidAmount - price;
//退还溢价
msg.sender.transfer(bidExcess);
AuctionSuccessful(_tokenId, price, msg.sender);
return price;
}
//移除拍卖
function _removeAuction(uint256 _tokenId) internal {
delete tokenIdToAuction[_tokenId];
}
//拍卖是否在进行中
function _isOnAuction(Auction storage _auction) internal view returns(bool) {
return (_auction.startedAt > 0);
}
//计算当前价
function _currentPrice(Auction storage _auction)
internal
view
returns(uint256) {
//距离拍卖开始过了多久 单位秒
uint256 secondsPassed = 0;
//secondsPassed = 当前时间 - 起拍时间
if (now > _auction.startedAt) {
secondsPassed = now - _auction.startedAt;
}
return _computeCurrentPrice(
_auction.startingPrice,
_auction.endingPrice,
_auction.duration,
secondsPassed
);
}
//计算拍卖的当前价格
function _computeCurrentPrice(
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration,
uint256 _secondsPassed
)
internal
pure
returns(uint256) {
//secondsPassed >= _duration 说明拍卖结束
if (_secondsPassed >= _duration) {
//返回成交价
return _endingPrice;
} else {
//拍卖价格变动金额 结束价 - 起拍价 通常是负值
int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice);
// 变动金额 * 拍卖开始时间 / 拍卖总时长
int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration);
//起拍价 + currentPriceChange
int256 currentPrice = int256(_startingPrice) + currentPriceChange;
return uint256(currentPrice);
}
}
//计算
function _computeCut(uint256 _price) internal view returns(uint256) {
//削减金额
return _price * ownerCut / 10000;
}
}
//维护合约
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
//函数修改器 限制当前不在维护中
modifier whenNotPaused() {
require(!paused);
_;
}
//同理 维护中
modifier whenPaused {
require(paused);
_;
}
//将合约置于维护状态 owner账户可操作
function pause() onlyOwner whenNotPaused returns(bool) {
paused = true;
Pause();
return true;
}
//将合约置于非维护状态 owner账户可操作
function unpause() onlyOwner whenPaused returns(bool) {
paused = false;
Unpause();
return true;
}
}
//Clock auction
contract ClockAuction is Pausable, ClockAuctionBase {
/// @dev The ERC-165 interface signature for ERC-721.
/// Ref: https://github.com/ethereum/EIPs/issues/165
/// Ref: https://github.com/ethereum/EIPs/issues/721
bytes4 constant InterfaceSignature_ERC721 = bytes4(0x9a20483d);
//初始化拍卖合约
function ClockAuction(address _nftAddress, uint256 _cut) public {
//确保佣金比例在正常范围内 并初始化佣金比例
require(_cut <= 10000);
ownerCut = _cut;
//初始化拍卖合约
ERC721 candidateContract = ERC721(_nftAddress);
require(candidateContract.supportsInterface(InterfaceSignature_ERC721));
nonFungibleContract = candidateContract;
}
//提现余额
function withdrawBalance() external {
address nftAddress = address(nonFungibleContract);
//验证身份
require(
msg.sender == owner ||
msg.sender == nftAddress
);
//余额提现
bool res = nftAddress.send(this.balance);
}
//创建拍卖
function createAuction(
uint256 _tokenId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration,
address _seller
)
external
whenNotPaused {
//校验变量值有效性和卖价token有效性
require(_startingPrice == uint256(uint128(_startingPrice)));
require(_endingPrice == uint256(uint128(_endingPrice)));
require(_duration == uint256(uint64(_duration)));
require(_owns(msg.sender, _tokenId));
//托管到拍卖合同
_escrow(msg.sender, _tokenId);
//创建拍卖结构体
Auction memory auction = Auction(
_seller,
uint128(_startingPrice),
uint128(_endingPrice),
uint64(_duration),
uint64(now)
);
//添加拍卖
_addAuction(_tokenId, auction);
}
//竞价
function bid(uint256 _tokenId)
external
payable
whenNotPaused {
//竞价资金处理
_bid(_tokenId, msg.value);
//token转移
_transfer(msg.sender, _tokenId);
}
//取消拍卖
function cancelAuction(uint256 _tokenId)
external {
Auction storage auction = tokenIdToAuction[_tokenId];
require(_isOnAuction(auction));
address seller = auction.seller;
require(msg.sender == seller);
_cancelAuction(_tokenId, seller);
}
//合约维护中情况下取消拍卖
function cancelAuctionWhenPaused(uint256 _tokenId)
whenPaused
onlyOwner
external {
Auction storage auction = tokenIdToAuction[_tokenId];
require(_isOnAuction(auction));
_cancelAuction(_tokenId, auction.seller);
}
//获取拍卖信息
function getAuction(uint256 _tokenId)
external
view
returns
(
address seller,
uint256 startingPrice,
uint256 endingPrice,
uint256 duration,
uint256 startedAt
) {
Auction storage auction = tokenIdToAuction[_tokenId];
//拍卖进行中 返回拍卖信息
require(_isOnAuction(auction));
return (
auction.seller,
auction.startingPrice,
auction.endingPrice,
auction.duration,
auction.startedAt
);
}
//获取拍卖的当前价格
function getCurrentPrice(uint256 _tokenId)
external
view
returns(uint256) {
Auction storage auction = tokenIdToAuction[_tokenId];
require(_isOnAuction(auction));
return _currentPrice(auction);
}
}
//繁殖拍卖
contract SiringClockAuction is ClockAuction {
//合约标识
bool public isSiringClockAuction = true;
//构造方法使用ClockAuction构造方法
function SiringClockAuction(address _nftAddr, uint256 _cut) public
ClockAuction(_nftAddr, _cut) {}
//创建拍卖
function createAuction(
uint256 _tokenId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration,
address _seller
)
external {
//校验变量值有效性和调用方
require(_startingPrice == uint256(uint128(_startingPrice)));
require(_endingPrice == uint256(uint128(_endingPrice)));
require(_duration == uint256(uint64(_duration)));
require(msg.sender == address(nonFungibleContract));
//托管到拍卖合同
_escrow(_seller, _tokenId);
//创建拍卖结构体
Auction memory auction = Auction(
_seller,
uint128(_startingPrice),
uint128(_endingPrice),
uint64(_duration),
uint64(now)
);
//添加拍卖
_addAuction(_tokenId, auction);
}
//竞价
function bid(uint256 _tokenId)
external
payable {
//合约调用
require(msg.sender == address(nonFungibleContract));
//卖家
address seller = tokenIdToAuction[_tokenId].seller;
竞价
_bid(_tokenId, msg.value);
//token返还卖家
_transfer(seller, _tokenId);
}
}
//猫咪出售拍卖
contract SaleClockAuction is ClockAuction {
//合约标识
bool public isSaleClockAuction = true;
//记录最后五只0代猫卖出的价格
uint256 public gen0SaleCount;
uint256[5] public lastGen0SalePrices;
//构造方法使用ClockAuction构造方法
function SaleClockAuction(address _nftAddr, uint256 _cut) public
ClockAuction(_nftAddr, _cut) {}
//创建拍卖
function createAuction(
uint256 _tokenId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration,
address _seller
)
external {
//校验变量值有效性和调用方
require(_startingPrice == uint256(uint128(_startingPrice)));
require(_endingPrice == uint256(uint128(_endingPrice)));
require(_duration == uint256(uint64(_duration)));
require(msg.sender == address(nonFungibleContract));
//托管到拍卖合同
_escrow(_seller, _tokenId);
//创建拍卖结构体
Auction memory auction = Auction(
_seller,
uint128(_startingPrice),
uint128(_endingPrice),
uint64(_duration),
uint64(now)
);
//添加拍卖
_addAuction(_tokenId, auction);
}
//竞价
function bid(uint256 _tokenId)
external
payable {
// _bid verifies token ID size
//卖家地址
address seller = tokenIdToAuction[_tokenId].seller;
//竞价 返回成交价格
uint256 price = _bid(_tokenId, msg.value);
//把token转移给竞价方
_transfer(msg.sender, _tokenId);
// If not a gen0 auction, exit
//如果是0代猫
if (seller == address(nonFungibleContract)) {
// Track gen0 sale prices
//此0代猫的价格记录到lastGen0SalePrices中
//因为总数是逐个递增,求余可以保证是逐个覆盖,数组内保持最后出售的五只零代猫的价格
lastGen0SalePrices[gen0SaleCount % 5] = price;
//0代猫出售总是+1
gen0SaleCount++;
}
}
//0代猫的均价
function averageGen0SalePrice() external view returns(uint256) {
uint256 sum = 0;
//五只0代猫的价格相加后除以5
for (uint256 i = 0; i < 5; i++) {
sum += lastGen0SalePrices[i];
}
return sum / 5;
}
}
contract KittyAuction is KittyBreeding {
//设置猫咪出售拍卖合约地址
function setSaleAuctionAddress(address _address) external onlyCEO {
SaleClockAuction candidateContract = SaleClockAuction(_address);
//校验合约标识
require(candidateContract.isSaleClockAuction());
//设置地址
saleAuction = candidateContract;
}
//设置繁殖拍卖合约地址
function setSiringAuctionAddress(address _address) external onlyCEO {
SiringClockAuction candidateContract = SiringClockAuction(_address);//校验合约标识
require(candidateContract.isSiringClockAuction());
//设置地址
siringAuction = candidateContract;
}
//创建销售拍卖结构体
//发起token拍卖
function createSaleAuction(
uint256 _kittyId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration
)
external
whenNotPaused {
//校验token所有权
require(_owns(msg.sender, _kittyId));
//处于孕期的猫不可以进行繁殖拍卖
require(!isPregnant(_kittyId));
_approve(_kittyId, saleAuction);
//授权给拍卖合同
//创建拍卖
saleAuction.createAuction(
_kittyId,
_startingPrice,
_endingPrice,
_duration,
msg.sender
);
}
//创建繁殖拍卖
function createSiringAuction(
uint256 _kittyId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration
)
external
whenNotPaused {
//校验token所有权
require(_owns(msg.sender, _kittyId));
//校验token当前可以进行繁殖
require(isReadyToBreed(_kittyId));
//授权给繁殖拍卖合约 todo
_approve(_kittyId, siringAuction);
//创建繁殖拍卖
siringAuction.createAuction(
_kittyId,
_startingPrice,
_endingPrice,
_duration,
msg.sender
);
}
//繁殖拍卖竞价
function bidOnSiringAuction(
uint256 _sireId,
uint256 _matronId
)
external
payable
whenNotPaused {
//校验猫咪所有权和猫咪可繁殖条件
require(_owns(msg.sender, _matronId));
require(isReadyToBreed(_matronId));
require(_canBreedWithViaAuction(_matronId, _sireId));
//获取当前价格
uint256 currentPrice = siringAuction.getCurrentPrice(_sireId);
require(msg.value >= currentPrice + autoBirthFee);
siringAuction.bid.value(msg.value - autoBirthFee)(_sireId);
_breedWith(uint32(_matronId), uint32(_sireId));
}
//拍卖合约余额提现
function withdrawAuctionBalances() external onlyCLevel {
saleAuction.withdrawBalance();
siringAuction.withdrawBalance();
}
}
/// @title all functions related to creating kittens
contract KittyMinting is KittyAuction {
//合约所有者可以创建猫的数量
uint256 public constant PROMO_CREATION_LIMIT = 5000;
uint256 public constant GEN0_CREATION_LIMIT = 45000;
//0代猫最低起拍价常量
uint256 public constant GEN0_STARTING_PRICE = 10 finney;
//0代猫拍卖时长为1天
uint256 public constant GEN0_AUCTION_DURATION = 1 days;
//记录已创建0代猫和促销猫的数量
uint256 public promoCreatedCount;
uint256 public gen0CreatedCount;
//创建促销猫 coo
function createPromoKitty(uint256 _genes, address _owner) external onlyCOO {
address kittyOwner = _owner;
if (kittyOwner == address(0)) {
kittyOwner = cooAddress;
}
require(promoCreatedCount < PROMO_CREATION_LIMIT);
promoCreatedCount++;
_createKitty(0, 0, 0, _genes, kittyOwner);
}
//创建0代猫并拍卖 coo
function createGen0Auction(uint256 _genes) external onlyCOO {
require(gen0CreatedCount < GEN0_CREATION_LIMIT);
uint256 kittyId = _createKitty(0, 0, 0, _genes, address(this));
_approve(kittyId, saleAuction);
saleAuction.createAuction(
kittyId,
_computeNextGen0Price(),
0,
GEN0_AUCTION_DURATION,
address(this)
);
gen0CreatedCount++;
}
//计算0代猫起拍价
function _computeNextGen0Price() internal view returns(uint256) {
//计算上五只0代猫的均价
uint256 avePrice = saleAuction.averageGen0SalePrice();
//数值有效性校验
require(avePrice == uint256(uint128(avePrice)));
//价格=均价的1.5倍
uint256 nextPrice = avePrice + (avePrice / 2);
//价格不低于最低起拍价
if (nextPrice < GEN0_STARTING_PRICE) {
nextPrice = GEN0_STARTING_PRICE;
}
return nextPrice;
}
}
contract KittyCore is KittyMinting {
// 我们有几个单独实例化的兄弟合同处理拍卖和我们的超级绝密遗传组合算法。
// 我们使用继承将核心契约分解为多个文件,每个文件代表CK功能的主要方面。
// 分类如下:
// - KittyBase:
// 这是我们定义在整个核心功能中共享的最基本代码的地方。
// 这包括我们的主要数据存储、常量和数据类型,以及用于管理这些项的内部函数。
// - KittyAccessControl:
// 该契约管理只能由特定角色执行的操作的各种地址和约束。也就是CEO, CFO和COO。
// - KittyOwnership:
// 这提供了基本NFT交易所需的方法,遵循ERC-721规范草案
// - KittyBreeding:
// 这个文件包含了将猫繁殖在一起的必要方法,包括跟踪配种报价,并依赖外部基因组合合同。
// - KittyAuctions:
// 这里我们有拍卖或竞拍猫或繁殖拍卖的公共方法。
// 实际的拍卖功能是在两个兄弟合同中处理的(一个用于销售,一个用于繁殖),
// 而拍卖的创作和竞价大多是通过核心合同的这一层面来进行中介的。
// - KittyMinting:
// 包含了我们用来创建新的第0代猫的功能。
// 我们可以提供多达5000只“促销”猫,可以分发出去(特别是当社区是新的时候),
// 而其他所有的游戏只能先创建好,然后通过算法确定的起始价格立即拍卖。
// 不管它们是如何产生的,
// 0代猫的数量有一个严格的限制,在那之后,一切都取决于社区的繁殖,繁殖,繁殖!
//升级新合约
address public newContractAddress;
function KittyCore() public {
//合约状态初始化为维护中
paused = true;
//初始化ceo coo地址
ceoAddress = msg.sender;
cooAddress = msg.sender;
//生成0猫
_createKitty(0, 0, 0, uint256(-1), address(0));
}
//合约升级 ceo可操作 维护中可操作
function setNewAddress(address _v2Address) external onlyCEO whenPaused {
newContractAddress = _v2Address;
ContractUpgrade(_v2Address);
}
//拒绝接受初拍卖合约以外地址向本地址发送以太币,避免事故
function() external payable {
require(
msg.sender == address(saleAuction) ||
msg.sender == address(siringAuction)
);
}
//获取猫的所有信息
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
) {
Kitty storage kit = kitties[_id];
//是否在孕期
isGestating = (kit.siringWithId != 0);
//是否可以繁殖
isReady = (kit.cooldownEndBlock <= block.number);
//繁殖冷却下标
cooldownIndex = uint256(kit.cooldownIndex);
//繁殖冷却剩余时间
nextActionAt = uint256(kit.cooldownEndBlock);
//怀孕对象id
siringWithId = uint256(kit.siringWithId);
//出生时间
birthTime = uint256(kit.birthTime);
//母亲id
matronId = uint256(kit.matronId);
//父猫id
sireId = uint256(kit.sireId);
//代数
generation = uint256(kit.generation);
//基因
genes = kit.genes;
}
//关闭合约维护状态
function unpause() public onlyCEO whenPaused {
//外部合约要已经设置完
require(saleAuction != address(0));
require(siringAuction != address(0));
require(geneScience != address(0));
//新合约是0 说明此合约未升级 有效
require(newContractAddress == address(0));
super.unpause();
}
//余额提现
function withdrawBalance() external onlyCFO {
uint256 balance = this.balance;
//合约内怀孕母猫繁殖需要的费用
uint256 subtractFees = (pregnantKitties + 1) * autoBirthFee;
if (balance > subtractFees) {
//扣除繁殖所需费用 剩下的余额提现到cfo账户
cfoAddress.send(balance - subtractFees);
}
}
}