Transaction Hash:
Block:
7524088 at Apr-07-2019 11:50:58 PM +UTC
Transaction Fee:
0.000178627 ETH
$0.35
Gas Used:
178,627 Gas / 1 Gwei
Emitted Events:
| 104 |
AxieCore.Approval( _owner=[Sender] 0xe800419e5c0b3d355cc7f2c5273e8f2a6b553558, _approved=0x00000000...000000000, _tokenId=20264 )
|
| 105 |
AxieCore.Transfer( _from=[Sender] 0xe800419e5c0b3d355cc7f2c5273e8f2a6b553558, _to=[Receiver] AxieSiringClockAuction, _tokenId=20264 )
|
| 106 |
AxieSiringClockAuction.AuctionCreated( _axieId=20264, _startingPrice=38000000000000000, _endingPrice=38000000000000000, _duration=86400, _seller=[Sender] 0xe800419e5c0b3d355cc7f2c5273e8f2a6b553558 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x4Bb96091...F90f81B01
Miner
| (Ethpool 2) | 291.361206727955488195 Eth | 291.361385354955488195 Eth | 0.000178627 | |
| 0x60cE035D...98948973b | |||||
| 0xe800419E...a6B553558 |
0.109457316993452386 Eth
Nonce: 1525
|
0.109278689993452386 Eth
Nonce: 1526
| 0.000178627 | ||
| 0xF5b0A3eF...F3FFEcb8d |
Execution Trace
AxieSiringClockAuction.createAuction( _axieId=20264, _startingPrice=38000000000000000, _endingPrice=38000000000000000, _duration=86400 )
-
AxieCore.ownerOf( _tokenId=20264 ) => ( 0xe800419E5c0b3d355cC7f2C5273E8f2a6B553558 )
Axie Infinity: Breeding Contract #2 (Deprecated).bcf7bd0b( )-
0x10e304a53351b272dc415ad049ad06565ebdfe34.893bb0bf( )
-
AxieCore.transferFrom( _from=0xe800419E5c0b3d355cC7f2C5273E8f2a6B553558, _to=0x60cE035Dc589C3fd185B224A7Ca03C598948973b, _tokenId=20264 )
0xe8bd438d0383cf4d19641eaa4793eddc6cebeaf1.f7ebc39a( )-
AxieCore.CALL( )
-
createAuction[AxieSiringClockAuction (ln:456)]
ownerOf[AxieSiringClockAuction (ln:470)]requireEnoughExpForBreeding[AxieSiringClockAuction (ln:471)]_escrow[AxieSiringClockAuction (ln:473)]transferFrom[AxieSiringClockAuction (ln:675)]
Auction[AxieSiringClockAuction (ln:475)]_addAuction[AxieSiringClockAuction (ln:483)]AuctionCreated[AxieSiringClockAuction (ln:647)]
File 1 of 2: AxieSiringClockAuction
File 2 of 2: AxieCore
pragma solidity ^0.4.23;
// File: contracts/breeding/AxieIncubatorInterface.sol
interface AxieIncubatorInterface {
function breedingFee() external view returns (uint256);
function requireEnoughExpForBreeding(
uint256 _axieId
)
external
view;
function breedAxies(
uint256 _sireId,
uint256 _matronId,
uint256 _birthPlace
)
external
payable
returns (uint256 _axieId);
}
// File: contracts/erc/erc721/IERC721Base.sol
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// Note: the ERC-165 identifier for this interface is 0x6466353c
interface IERC721Base /* is IERC165 */ {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner) external view returns (uint256);
/// @notice Find the owner of an NFT
/// @param _tokenId The identifier for an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId) external view returns (address);
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param _data Additional data with no specified format, sent in call to `_to`
// solium-disable-next-line arg-overflow
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to []
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Set or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint256 _tokenId) external payable;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all your asset.
/// @dev Emits the ApprovalForAll event
/// @param _operator Address to add to the set of authorized operators.
/// @param _approved True if the operators is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint256 _tokenId) external view returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
// File: zeppelin/contracts/ownership/Ownable.sol
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) onlyOwner public {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
// File: zeppelin/contracts/lifecycle/Pausable.sol
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
// File: zeppelin/contracts/ownership/HasNoContracts.sol
/**
* @title Contracts that should not own Contracts
* @author Remco Bloemen <remco@2π.com>
* @dev Should contracts (anything Ownable) end up being owned by this contract, it allows the owner
* of this contract to reclaim ownership of the contracts.
*/
contract HasNoContracts is Ownable {
/**
* @dev Reclaim ownership of Ownable contracts
* @param contractAddr The address of the Ownable to be reclaimed.
*/
function reclaimContract(address contractAddr) external onlyOwner {
Ownable contractInst = Ownable(contractAddr);
contractInst.transferOwnership(owner);
}
}
// File: zeppelin/contracts/token/ERC20Basic.sol
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
uint256 public totalSupply;
function balanceOf(address who) public constant returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
// File: zeppelin/contracts/token/ERC20.sol
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public constant returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: zeppelin/contracts/token/SafeERC20.sol
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
function safeTransfer(ERC20Basic token, address to, uint256 value) internal {
assert(token.transfer(to, value));
}
function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
assert(token.transferFrom(from, to, value));
}
function safeApprove(ERC20 token, address spender, uint256 value) internal {
assert(token.approve(spender, value));
}
}
// File: zeppelin/contracts/ownership/CanReclaimToken.sol
/**
* @title Contracts that should be able to recover tokens
* @author SylTi
* @dev This allow a contract to recover any ERC20 token received in a contract by transferring the balance to the contract owner.
* This will prevent any accidental loss of tokens.
*/
contract CanReclaimToken is Ownable {
using SafeERC20 for ERC20Basic;
/**
* @dev Reclaim all ERC20Basic compatible tokens
* @param token ERC20Basic The address of the token contract
*/
function reclaimToken(ERC20Basic token) external onlyOwner {
uint256 balance = token.balanceOf(this);
token.safeTransfer(owner, balance);
}
}
// File: zeppelin/contracts/ownership/HasNoTokens.sol
/**
* @title Contracts that should not own Tokens
* @author Remco Bloemen <remco@2π.com>
* @dev This blocks incoming ERC23 tokens to prevent accidental loss of tokens.
* Should tokens (any ERC20Basic compatible) end up in the contract, it allows the
* owner to reclaim the tokens.
*/
contract HasNoTokens is CanReclaimToken {
/**
* @dev Reject all ERC23 compatible tokens
* @param from_ address The address that is transferring the tokens
* @param value_ uint256 the amount of the specified token
* @param data_ Bytes The data passed from the caller.
*/
function tokenFallback(address from_, uint256 value_, bytes data_) external {
revert();
}
}
// File: contracts/marketplace/AxieSiringClockAuction.sol
/// @title Clock auction for Axie siring.
contract AxieSiringClockAuction is HasNoContracts, HasNoTokens, Pausable {
// Represents an auction on an NFT.
struct Auction {
// Current owner of NFT.
address seller;
// Price (in wei) at beginning of auction.
uint128 startingPrice;
// Price (in wei) at end of auction.
uint128 endingPrice;
// Duration (in seconds) of auction.
uint64 duration;
// Time when auction started.
// NOTE: 0 if this auction has been concluded.
uint64 startedAt;
}
// Cut owner takes on each auction, measured in basis points (1/100 of a percent).
// Values 0-10,000 map to 0%-100%.
uint256 public ownerCut;
IERC721Base coreContract;
AxieIncubatorInterface incubatorContract;
// Map from Axie ID to their corresponding auction.
mapping (uint256 => Auction) public auctions;
event AuctionCreated(
uint256 indexed _axieId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration,
address _seller
);
event AuctionSuccessful(
uint256 indexed _sireId,
uint256 indexed _matronId,
uint256 _totalPrice,
address _winner
);
event AuctionCancelled(uint256 indexed _axieId);
/// @dev Constructor creates a reference to the NFT ownership contract
/// and verifies the owner cut is in the valid range.
/// @param _ownerCut - percent cut the owner takes on each auction, must be
/// between 0-10,000.
constructor(uint256 _ownerCut) public {
require(_ownerCut <= 10000);
ownerCut = _ownerCut;
}
function () external payable onlyOwner {
}
// Modifiers to check that inputs can be safely stored with a certain
// number of bits. We use constants and multiple modifiers to save gas.
modifier canBeStoredWith64Bits(uint256 _value) {
require(_value <= 18446744073709551615);
_;
}
modifier canBeStoredWith128Bits(uint256 _value) {
require(_value < 340282366920938463463374607431768211455);
_;
}
function reclaimEther() external onlyOwner {
owner.transfer(address(this).balance);
}
function setCoreContract(address _coreAddress) external onlyOwner {
coreContract = IERC721Base(_coreAddress);
}
function setIncubatorContract(address _incubatorAddress) external onlyOwner {
incubatorContract = AxieIncubatorInterface(_incubatorAddress);
}
/// @dev Returns auction info for an NFT on auction.
/// @param _axieId - ID of NFT on auction.
function getAuction(
uint256 _axieId
)
external
view
returns (
address seller,
uint256 startingPrice,
uint256 endingPrice,
uint256 duration,
uint256 startedAt
)
{
Auction storage _auction = auctions[_axieId];
require(_isOnAuction(_auction));
return (
_auction.seller,
_auction.startingPrice,
_auction.endingPrice,
_auction.duration,
_auction.startedAt
);
}
/// @dev Returns the current price of an auction.
/// @param _axieId - ID of the Axie price we are checking.
function getCurrentPrice(
uint256 _axieId
)
external
view
returns (uint256)
{
Auction storage _auction = auctions[_axieId];
require(_isOnAuction(_auction));
return _getCurrentPrice(_auction);
}
/// @dev Creates and begins a new auction.
/// @param _axieId - ID of Axie to auction, sender must be owner.
/// @param _startingPrice - Price of item (in wei) at beginning of auction.
/// @param _endingPrice - Price of item (in wei) at end of auction.
/// @param _duration - Length of time to move between starting
/// price and ending price (in seconds).
function createAuction(
uint256 _axieId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration
)
external
whenNotPaused
canBeStoredWith128Bits(_startingPrice)
canBeStoredWith128Bits(_endingPrice)
canBeStoredWith64Bits(_duration)
{
address _seller = msg.sender;
require(coreContract.ownerOf(_axieId) == _seller);
incubatorContract.requireEnoughExpForBreeding(_axieId); // Validate EXP for breeding.
_escrow(_seller, _axieId);
Auction memory _auction = Auction(
_seller,
uint128(_startingPrice),
uint128(_endingPrice),
uint64(_duration),
uint64(now)
);
_addAuction(
_axieId,
_auction,
_seller
);
}
/// @dev Bids on an siring auction and completing it.
/// @param _sireId - ID of Axie to bid on siring.
/// @param _matronId - ID of matron Axie.
function bidOnSiring(
uint256 _sireId,
uint256 _matronId,
uint256 _birthPlace
)
external
payable
whenNotPaused
returns (uint256 /* _axieId */)
{
Auction storage _auction = auctions[_sireId];
require(_isOnAuction(_auction));
require(msg.sender == coreContract.ownerOf(_matronId));
// Save seller address here since `_bid` will clear it.
address _seller = _auction.seller;
// _bid will throw if the bid or funds transfer fails.
_bid(_sireId, _matronId, msg.value, _auction);
uint256 _axieId = incubatorContract.breedAxies.value(
incubatorContract.breedingFee()
)(
_sireId,
_matronId,
_birthPlace
);
_transfer(_seller, _sireId);
return _axieId;
}
/// @dev Cancels an auction that hasn't been won yet.
/// Returns the NFT to original owner.
/// @notice This is a state-modifying function that can
/// be called while the contract is paused.
/// @param _axieId - ID of Axie on auction.
function cancelAuction(uint256 _axieId) external {
Auction storage _auction = auctions[_axieId];
require(_isOnAuction(_auction));
require(msg.sender == _auction.seller);
_cancelAuction(_axieId, _auction.seller);
}
/// @dev Cancels an auction when the contract is paused.
/// Only the owner may do this, and NFTs are returned to
/// the seller. This should only be used in emergencies.
/// @param _axieId - ID of the NFT on auction to cancel.
function cancelAuctionWhenPaused(
uint256 _axieId
)
external
whenPaused
onlyOwner
{
Auction storage _auction = auctions[_axieId];
require(_isOnAuction(_auction));
_cancelAuction(_axieId, _auction.seller);
}
/// @dev Returns true if the NFT is on auction.
/// @param _auction - Auction to check.
function _isOnAuction(Auction storage _auction) internal view returns (bool) {
return (_auction.startedAt > 0);
}
/// @dev Returns current price of an NFT on auction. Broken into two
/// functions (this one, that computes the duration from the auction
/// structure, and the other that does the price computation) so we
/// can easily test that the price computation works correctly.
function _getCurrentPrice(
Auction storage _auction
)
internal
view
returns (uint256)
{
uint256 _secondsPassed = 0;
// A bit of insurance against negative values (or wraparound).
// Probably not necessary (since Ethereum guarantees that the
// now variable doesn't ever go backwards).
if (now > _auction.startedAt) {
_secondsPassed = now - _auction.startedAt;
}
return _computeCurrentPrice(
_auction.startingPrice,
_auction.endingPrice,
_auction.duration,
_secondsPassed
);
}
/// @dev Computes the current price of an auction. Factored out
/// from _currentPrice so we can run extensive unit tests.
/// When testing, make this function external and turn on
/// `Current price computation` test suite.
function _computeCurrentPrice(
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration,
uint256 _secondsPassed
)
internal
pure
returns (uint256)
{
// NOTE: We don't use SafeMath (or similar) in this function because
// all of our external functions carefully cap the maximum values for
// time (at 64-bits) and currency (at 128-bits). _duration is
// also known to be non-zero (see the require() statement in
// _addAuction()).
if (_secondsPassed >= _duration) {
// We've reached the end of the dynamic pricing portion
// of the auction, just return the end price.
return _endingPrice;
} else {
// Starting price can be higher than ending price (and often is!), so
// this delta can be negative.
int256 _totalPriceChange = int256(_endingPrice) - int256(_startingPrice);
// This multiplication can't overflow, _secondsPassed will easily fit within
// 64-bits, and _totalPriceChange will easily fit within 128-bits, their product
// will always fit within 256-bits.
int256 _currentPriceChange = _totalPriceChange * int256(_secondsPassed) / int256(_duration);
// _currentPriceChange can be negative, but if so, will have a magnitude
// less that _startingPrice. Thus, this result will always end up positive.
int256 _currentPrice = int256(_startingPrice) + _currentPriceChange;
return uint256(_currentPrice);
}
}
/// @dev Adds an auction to the list of open auctions. Also fires the
/// AuctionCreated event.
/// @param _axieId The ID of the Axie to be put on auction.
/// @param _auction Auction to add.
function _addAuction(
uint256 _axieId,
Auction memory _auction,
address _seller
)
internal
{
// Require that all auctions have a duration of
// at least one minute. (Keeps our math from getting hairy!).
require(_auction.duration >= 1 minutes);
auctions[_axieId] = _auction;
emit AuctionCreated(
_axieId,
uint256(_auction.startingPrice),
uint256(_auction.endingPrice),
uint256(_auction.duration),
_seller
);
}
/// @dev Removes an auction from the list of open auctions.
/// @param _axieId - ID of NFT on auction.
function _removeAuction(uint256 _axieId) internal {
delete auctions[_axieId];
}
/// @dev Cancels an auction unconditionally.
function _cancelAuction(uint256 _axieId, address _seller) internal {
_removeAuction(_axieId);
_transfer(_seller, _axieId);
emit AuctionCancelled(_axieId);
}
/// @dev Escrows the NFT, assigning ownership to this contract.
/// Throws if the escrow fails.
/// @param _owner - Current owner address of Axie to escrow.
/// @param _axieId - ID of Axie whose approval to verify.
function _escrow(address _owner, uint256 _axieId) internal {
// It will throw if transfer fails.
coreContract.transferFrom(_owner, this, _axieId);
}
/// @dev Transfers an NFT owned by this contract to another address.
/// Returns true if the transfer succeeds.
/// @param _receiver - Address to transfer NFT to.
/// @param _axieId - ID of Axie to transfer.
function _transfer(address _receiver, uint256 _axieId) internal {
// It will throw if transfer fails
coreContract.transferFrom(this, _receiver, _axieId);
}
/// @dev Computes owner's cut of a sale.
/// @param _price - Sale price of NFT.
function _computeCut(uint256 _price) internal view returns (uint256) {
// NOTE: We don't use SafeMath (or similar) in this function because
// all of our entry functions carefully cap the maximum values for
// currency (at 128-bits), and ownerCut <= 10000 (see the require()
// statement in the ClockAuction constructor). The result of this
// function is always guaranteed to be <= _price.
return _price * ownerCut / 10000;
}
/// @dev Computes the price and transfers winnings.
/// Does NOT transfer ownership of Axie.
function _bid(
uint256 _sireId,
uint256 _matronId,
uint256 _bidAmount,
Auction storage _auction
)
internal
returns (uint256)
{
// Check that the incoming bid is higher than the current price.
uint256 _price = _getCurrentPrice(_auction);
uint256 _priceWithFee = _price + incubatorContract.breedingFee();
// Technically this shouldn't happen as `_price` fits in 128 bits.
// However, we could set `breedingFee` to a very large number accidentally.
assert(_priceWithFee >= _price);
require(_bidAmount >= _priceWithFee);
// Grab a reference to the seller before the auction struct
// gets deleted.
address _seller = _auction.seller;
// The bid is good! Remove the auction before sending the fees
// to the sender so we can't have a reentrancy attack.
_removeAuction(_sireId);
// Transfer proceeds to seller (if there are any!)
if (_price > 0) {
// Calculate the auctioneer's cut.
// (NOTE: _computeCut() is guaranteed to return a
// value <= price, so this subtraction can't go negative.)
uint256 _auctioneerCut = _computeCut(_price);
uint256 _sellerProceeds = _price - _auctioneerCut;
// NOTE: Doing a transfer() in the middle of a complex
// method like this is generally discouraged because of
// reentrancy attacks and DoS attacks if the seller is
// a contract with an invalid fallback function. We explicitly
// guard against reentrancy attacks by removing the auction
// before calling transfer(), and the only thing the seller
// can DoS is the sale of their own asset! (And if it's an
// accident, they can call cancelAuction().)
_seller.transfer(_sellerProceeds);
}
if (_bidAmount > _priceWithFee) {
// Calculate any excess funds included with the bid. If the excess
// is anything worth worrying about, transfer it back to bidder.
// NOTE: We checked above that the bid amount is greater than or
// equal to the price so this cannot underflow.
uint256 _bidExcess = _bidAmount - _priceWithFee;
// Return the funds. Similar to the previous transfer, this is
// not susceptible to a re-entry attack because the auction is
// removed before any transfers occur.
msg.sender.transfer(_bidExcess);
}
// Tell the world!
emit AuctionSuccessful(
_sireId,
_matronId,
_price,
msg.sender
);
return _price;
}
}File 2 of 2: AxieCore
pragma solidity ^0.4.19;
// File: contracts/erc/erc165/IERC165.sol
/// @title ERC-165 Standard Interface Detection
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
interface IERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
// File: contracts/erc/erc165/ERC165.sol
contract ERC165 is IERC165 {
/// @dev You must not set element 0xffffffff to true
mapping (bytes4 => bool) internal supportedInterfaces;
function ERC165() internal {
supportedInterfaces[0x01ffc9a7] = true; // ERC-165
}
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
return supportedInterfaces[interfaceID];
}
}
// File: contracts/erc/erc721/IERC721Base.sol
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// Note: the ERC-165 identifier for this interface is 0x6466353c
interface IERC721Base /* is IERC165 */ {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner) external view returns (uint256);
/// @notice Find the owner of an NFT
/// @param _tokenId The identifier for an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId) external view returns (address);
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param _data Additional data with no specified format, sent in call to `_to`
// solium-disable-next-line arg-overflow
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to []
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Set or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint256 _tokenId) external payable;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all your asset.
/// @dev Emits the ApprovalForAll event
/// @param _operator Address to add to the set of authorized operators.
/// @param _approved True if the operators is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint256 _tokenId) external view returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
// File: contracts/erc/erc721/IERC721Enumerable.sol
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// Note: the ERC-165 identifier for this interface is 0x780e9d63
interface IERC721Enumerable /* is IERC721Base */ {
/// @notice Count NFTs tracked by this contract
/// @return A count of valid NFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function totalSupply() external view returns (uint256);
/// @notice Enumerate valid NFTs
/// @dev Throws if `_index` >= `totalSupply()`.
/// @param _index A counter less than `totalSupply()`
/// @return The token identifier for the `_index`th NFT,
/// (sort order not specified)
function tokenByIndex(uint256 _index) external view returns (uint256);
/// @notice Enumerate NFTs assigned to an owner
/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
/// `_owner` is the zero address, representing invalid NFTs.
/// @param _owner An address where we are interested in NFTs owned by them
/// @param _index A counter less than `balanceOf(_owner)`
/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
/// (sort order not specified)
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _tokenId);
}
// File: contracts/erc/erc721/IERC721TokenReceiver.sol
/// @dev Note: the ERC-165 identifier for this interface is 0xf0b9e5ba
interface IERC721TokenReceiver {
/// @notice Handle the receipt of an NFT
/// @dev The ERC721 smart contract calls this function on the recipient
/// after a `transfer`. This function MAY throw to revert and reject the
/// transfer. This function MUST use 50,000 gas or less. Return of other
/// than the magic value MUST result in the transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _from The sending address
/// @param _tokenId The NFT identifier which is being transfered
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
/// unless throwing
function onERC721Received(address _from, uint256 _tokenId, bytes _data) external returns (bytes4);
}
// File: contracts/core/dependency/AxieManager.sol
interface AxieSpawningManager {
function isSpawningAllowed(uint256 _genes, address _owner) external returns (bool);
function isRebirthAllowed(uint256 _axieId, uint256 _genes) external returns (bool);
}
interface AxieRetirementManager {
function isRetirementAllowed(uint256 _axieId, bool _rip) external returns (bool);
}
interface AxieMarketplaceManager {
function isTransferAllowed(address _from, address _to, uint256 _axieId) external returns (bool);
}
interface AxieGeneManager {
function isEvolvementAllowed(uint256 _axieId, uint256 _newGenes) external returns (bool);
}
// File: contracts/core/dependency/AxieDependency.sol
contract AxieDependency {
address public whitelistSetterAddress;
AxieSpawningManager public spawningManager;
AxieRetirementManager public retirementManager;
AxieMarketplaceManager public marketplaceManager;
AxieGeneManager public geneManager;
mapping (address => bool) public whitelistedSpawner;
mapping (address => bool) public whitelistedByeSayer;
mapping (address => bool) public whitelistedMarketplace;
mapping (address => bool) public whitelistedGeneScientist;
function AxieDependency() internal {
whitelistSetterAddress = msg.sender;
}
modifier onlyWhitelistSetter() {
require(msg.sender == whitelistSetterAddress);
_;
}
modifier whenSpawningAllowed(uint256 _genes, address _owner) {
require(
spawningManager == address(0) ||
spawningManager.isSpawningAllowed(_genes, _owner)
);
_;
}
modifier whenRebirthAllowed(uint256 _axieId, uint256 _genes) {
require(
spawningManager == address(0) ||
spawningManager.isRebirthAllowed(_axieId, _genes)
);
_;
}
modifier whenRetirementAllowed(uint256 _axieId, bool _rip) {
require(
retirementManager == address(0) ||
retirementManager.isRetirementAllowed(_axieId, _rip)
);
_;
}
modifier whenTransferAllowed(address _from, address _to, uint256 _axieId) {
require(
marketplaceManager == address(0) ||
marketplaceManager.isTransferAllowed(_from, _to, _axieId)
);
_;
}
modifier whenEvolvementAllowed(uint256 _axieId, uint256 _newGenes) {
require(
geneManager == address(0) ||
geneManager.isEvolvementAllowed(_axieId, _newGenes)
);
_;
}
modifier onlySpawner() {
require(whitelistedSpawner[msg.sender]);
_;
}
modifier onlyByeSayer() {
require(whitelistedByeSayer[msg.sender]);
_;
}
modifier onlyMarketplace() {
require(whitelistedMarketplace[msg.sender]);
_;
}
modifier onlyGeneScientist() {
require(whitelistedGeneScientist[msg.sender]);
_;
}
/*
* @dev Setting the whitelist setter address to `address(0)` would be a irreversible process.
* This is to lock changes to Axie's contracts after their development is done.
*/
function setWhitelistSetter(address _newSetter) external onlyWhitelistSetter {
whitelistSetterAddress = _newSetter;
}
function setSpawningManager(address _manager) external onlyWhitelistSetter {
spawningManager = AxieSpawningManager(_manager);
}
function setRetirementManager(address _manager) external onlyWhitelistSetter {
retirementManager = AxieRetirementManager(_manager);
}
function setMarketplaceManager(address _manager) external onlyWhitelistSetter {
marketplaceManager = AxieMarketplaceManager(_manager);
}
function setGeneManager(address _manager) external onlyWhitelistSetter {
geneManager = AxieGeneManager(_manager);
}
function setSpawner(address _spawner, bool _whitelisted) external onlyWhitelistSetter {
require(whitelistedSpawner[_spawner] != _whitelisted);
whitelistedSpawner[_spawner] = _whitelisted;
}
function setByeSayer(address _byeSayer, bool _whitelisted) external onlyWhitelistSetter {
require(whitelistedByeSayer[_byeSayer] != _whitelisted);
whitelistedByeSayer[_byeSayer] = _whitelisted;
}
function setMarketplace(address _marketplace, bool _whitelisted) external onlyWhitelistSetter {
require(whitelistedMarketplace[_marketplace] != _whitelisted);
whitelistedMarketplace[_marketplace] = _whitelisted;
}
function setGeneScientist(address _geneScientist, bool _whitelisted) external onlyWhitelistSetter {
require(whitelistedGeneScientist[_geneScientist] != _whitelisted);
whitelistedGeneScientist[_geneScientist] = _whitelisted;
}
}
// File: contracts/core/AxieAccessControl.sol
contract AxieAccessControl {
address public ceoAddress;
address public cfoAddress;
address public cooAddress;
function AxieAccessControl() internal {
ceoAddress = msg.sender;
}
modifier onlyCEO() {
require(msg.sender == ceoAddress);
_;
}
modifier onlyCFO() {
require(msg.sender == cfoAddress);
_;
}
modifier onlyCOO() {
require(msg.sender == cooAddress);
_;
}
modifier onlyCLevel() {
require(
// solium-disable operator-whitespace
msg.sender == ceoAddress ||
msg.sender == cfoAddress ||
msg.sender == cooAddress
// solium-enable operator-whitespace
);
_;
}
function setCEO(address _newCEO) external onlyCEO {
require(_newCEO != address(0));
ceoAddress = _newCEO;
}
function setCFO(address _newCFO) external onlyCEO {
cfoAddress = _newCFO;
}
function setCOO(address _newCOO) external onlyCEO {
cooAddress = _newCOO;
}
function withdrawBalance() external onlyCFO {
cfoAddress.transfer(this.balance);
}
}
// File: contracts/core/lifecycle/AxiePausable.sol
contract AxiePausable is AxieAccessControl {
bool public paused = false;
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused {
require(paused);
_;
}
function pause() external onlyCLevel whenNotPaused {
paused = true;
}
function unpause() public onlyCEO whenPaused {
paused = false;
}
}
// File: zeppelin/contracts/math/SafeMath.sol
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal constant returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal constant returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
// File: contracts/core/erc721/AxieERC721BaseEnumerable.sol
contract AxieERC721BaseEnumerable is ERC165, IERC721Base, IERC721Enumerable, AxieDependency, AxiePausable {
using SafeMath for uint256;
// @dev Total amount of tokens.
uint256 private _totalTokens;
// @dev Mapping from token index to ID.
mapping (uint256 => uint256) private _overallTokenId;
// @dev Mapping from token ID to index.
mapping (uint256 => uint256) private _overallTokenIndex;
// @dev Mapping from token ID to owner.
mapping (uint256 => address) private _tokenOwner;
// @dev For a given owner and a given operator, store whether
// the operator is allowed to manage tokens on behalf of the owner.
mapping (address => mapping (address => bool)) private _tokenOperator;
// @dev Mapping from token ID to approved address.
mapping (uint256 => address) private _tokenApproval;
// @dev Mapping from owner to list of owned token IDs.
mapping (address => uint256[]) private _ownedTokens;
// @dev Mapping from token ID to index in the owned token list.
mapping (uint256 => uint256) private _ownedTokenIndex;
function AxieERC721BaseEnumerable() internal {
supportedInterfaces[0x6466353c] = true; // ERC-721 Base
supportedInterfaces[0x780e9d63] = true; // ERC-721 Enumerable
}
// solium-disable function-order
modifier mustBeValidToken(uint256 _tokenId) {
require(_tokenOwner[_tokenId] != address(0));
_;
}
function _isTokenOwner(address _ownerToCheck, uint256 _tokenId) private view returns (bool) {
return _tokenOwner[_tokenId] == _ownerToCheck;
}
function _isTokenOperator(address _operatorToCheck, uint256 _tokenId) private view returns (bool) {
return whitelistedMarketplace[_operatorToCheck] ||
_tokenOperator[_tokenOwner[_tokenId]][_operatorToCheck];
}
function _isApproved(address _approvedToCheck, uint256 _tokenId) private view returns (bool) {
return _tokenApproval[_tokenId] == _approvedToCheck;
}
modifier onlyTokenOwner(uint256 _tokenId) {
require(_isTokenOwner(msg.sender, _tokenId));
_;
}
modifier onlyTokenOwnerOrOperator(uint256 _tokenId) {
require(_isTokenOwner(msg.sender, _tokenId) || _isTokenOperator(msg.sender, _tokenId));
_;
}
modifier onlyTokenAuthorized(uint256 _tokenId) {
require(
// solium-disable operator-whitespace
_isTokenOwner(msg.sender, _tokenId) ||
_isTokenOperator(msg.sender, _tokenId) ||
_isApproved(msg.sender, _tokenId)
// solium-enable operator-whitespace
);
_;
}
// ERC-721 Base
function balanceOf(address _owner) external view returns (uint256) {
require(_owner != address(0));
return _ownedTokens[_owner].length;
}
function ownerOf(uint256 _tokenId) external view mustBeValidToken(_tokenId) returns (address) {
return _tokenOwner[_tokenId];
}
function _addTokenTo(address _to, uint256 _tokenId) private {
require(_to != address(0));
_tokenOwner[_tokenId] = _to;
uint256 length = _ownedTokens[_to].length;
_ownedTokens[_to].push(_tokenId);
_ownedTokenIndex[_tokenId] = length;
}
function _mint(address _to, uint256 _tokenId) internal {
require(_tokenOwner[_tokenId] == address(0));
_addTokenTo(_to, _tokenId);
_overallTokenId[_totalTokens] = _tokenId;
_overallTokenIndex[_tokenId] = _totalTokens;
_totalTokens = _totalTokens.add(1);
Transfer(address(0), _to, _tokenId);
}
function _removeTokenFrom(address _from, uint256 _tokenId) private {
require(_from != address(0));
uint256 _tokenIndex = _ownedTokenIndex[_tokenId];
uint256 _lastTokenIndex = _ownedTokens[_from].length.sub(1);
uint256 _lastTokenId = _ownedTokens[_from][_lastTokenIndex];
_tokenOwner[_tokenId] = address(0);
// Insert the last token into the position previously occupied by the removed token.
_ownedTokens[_from][_tokenIndex] = _lastTokenId;
_ownedTokenIndex[_lastTokenId] = _tokenIndex;
// Resize the array.
delete _ownedTokens[_from][_lastTokenIndex];
_ownedTokens[_from].length--;
// Remove the array if no more tokens are owned to prevent pollution.
if (_ownedTokens[_from].length == 0) {
delete _ownedTokens[_from];
}
// Update the index of the removed token.
delete _ownedTokenIndex[_tokenId];
}
function _burn(uint256 _tokenId) internal {
address _from = _tokenOwner[_tokenId];
require(_from != address(0));
_removeTokenFrom(_from, _tokenId);
_totalTokens = _totalTokens.sub(1);
uint256 _tokenIndex = _overallTokenIndex[_tokenId];
uint256 _lastTokenId = _overallTokenId[_totalTokens];
delete _overallTokenIndex[_tokenId];
delete _overallTokenId[_totalTokens];
_overallTokenId[_tokenIndex] = _lastTokenId;
_overallTokenIndex[_lastTokenId] = _tokenIndex;
Transfer(_from, address(0), _tokenId);
}
function _isContract(address _address) private view returns (bool) {
uint _size;
// solium-disable-next-line security/no-inline-assembly
assembly { _size := extcodesize(_address) }
return _size > 0;
}
function _transferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes _data,
bool _check
)
internal
mustBeValidToken(_tokenId)
onlyTokenAuthorized(_tokenId)
whenTransferAllowed(_from, _to, _tokenId)
{
require(_isTokenOwner(_from, _tokenId));
require(_to != address(0));
require(_to != _from);
_removeTokenFrom(_from, _tokenId);
delete _tokenApproval[_tokenId];
Approval(_from, address(0), _tokenId);
_addTokenTo(_to, _tokenId);
if (_check && _isContract(_to)) {
IERC721TokenReceiver(_to).onERC721Received.gas(50000)(_from, _tokenId, _data);
}
Transfer(_from, _to, _tokenId);
}
// solium-disable arg-overflow
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable {
_transferFrom(_from, _to, _tokenId, _data, true);
}
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable {
_transferFrom(_from, _to, _tokenId, "", true);
}
function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
_transferFrom(_from, _to, _tokenId, "", false);
}
// solium-enable arg-overflow
function approve(
address _approved,
uint256 _tokenId
)
external
payable
mustBeValidToken(_tokenId)
onlyTokenOwnerOrOperator(_tokenId)
whenNotPaused
{
address _owner = _tokenOwner[_tokenId];
require(_owner != _approved);
require(_tokenApproval[_tokenId] != _approved);
_tokenApproval[_tokenId] = _approved;
Approval(_owner, _approved, _tokenId);
}
function setApprovalForAll(address _operator, bool _approved) external whenNotPaused {
require(_tokenOperator[msg.sender][_operator] != _approved);
_tokenOperator[msg.sender][_operator] = _approved;
ApprovalForAll(msg.sender, _operator, _approved);
}
function getApproved(uint256 _tokenId) external view mustBeValidToken(_tokenId) returns (address) {
return _tokenApproval[_tokenId];
}
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
return _tokenOperator[_owner][_operator];
}
// ERC-721 Enumerable
function totalSupply() external view returns (uint256) {
return _totalTokens;
}
function tokenByIndex(uint256 _index) external view returns (uint256) {
require(_index < _totalTokens);
return _overallTokenId[_index];
}
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _tokenId) {
require(_owner != address(0));
require(_index < _ownedTokens[_owner].length);
return _ownedTokens[_owner][_index];
}
}
// File: contracts/erc/erc721/IERC721Metadata.sol
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f
interface IERC721Metadata /* is IERC721Base */ {
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external pure returns (string _name);
/// @notice An abbreviated name for NFTs in this contract
function symbol() external pure returns (string _symbol);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
/// Metadata JSON Schema".
function tokenURI(uint256 _tokenId) external view returns (string);
}
// File: contracts/core/erc721/AxieERC721Metadata.sol
contract AxieERC721Metadata is AxieERC721BaseEnumerable, IERC721Metadata {
string public tokenURIPrefix = "https://axieinfinity.com/erc/721/axies/";
string public tokenURISuffix = ".json";
function AxieERC721Metadata() internal {
supportedInterfaces[0x5b5e139f] = true; // ERC-721 Metadata
}
function name() external pure returns (string) {
return "Axie";
}
function symbol() external pure returns (string) {
return "AXIE";
}
function setTokenURIAffixes(string _prefix, string _suffix) external onlyCEO {
tokenURIPrefix = _prefix;
tokenURISuffix = _suffix;
}
function tokenURI(
uint256 _tokenId
)
external
view
mustBeValidToken(_tokenId)
returns (string)
{
bytes memory _tokenURIPrefixBytes = bytes(tokenURIPrefix);
bytes memory _tokenURISuffixBytes = bytes(tokenURISuffix);
uint256 _tmpTokenId = _tokenId;
uint256 _length;
do {
_length++;
_tmpTokenId /= 10;
} while (_tmpTokenId > 0);
bytes memory _tokenURIBytes = new bytes(_tokenURIPrefixBytes.length + _length + 5);
uint256 _i = _tokenURIBytes.length - 6;
_tmpTokenId = _tokenId;
do {
_tokenURIBytes[_i--] = byte(48 + _tmpTokenId % 10);
_tmpTokenId /= 10;
} while (_tmpTokenId > 0);
for (_i = 0; _i < _tokenURIPrefixBytes.length; _i++) {
_tokenURIBytes[_i] = _tokenURIPrefixBytes[_i];
}
for (_i = 0; _i < _tokenURISuffixBytes.length; _i++) {
_tokenURIBytes[_tokenURIBytes.length + _i - 5] = _tokenURISuffixBytes[_i];
}
return string(_tokenURIBytes);
}
}
// File: contracts/core/erc721/AxieERC721.sol
// solium-disable-next-line no-empty-blocks
contract AxieERC721 is AxieERC721BaseEnumerable, AxieERC721Metadata {
}
// File: contracts/core/AxieCore.sol
// solium-disable-next-line no-empty-blocks
contract AxieCore is AxieERC721 {
struct Axie {
uint256 genes;
uint256 bornAt;
}
Axie[] axies;
event AxieSpawned(uint256 indexed _axieId, address indexed _owner, uint256 _genes);
event AxieRebirthed(uint256 indexed _axieId, uint256 _genes);
event AxieRetired(uint256 indexed _axieId);
event AxieEvolved(uint256 indexed _axieId, uint256 _oldGenes, uint256 _newGenes);
function AxieCore() public {
axies.push(Axie(0, now)); // The void Axie
_spawnAxie(0, msg.sender); // Will be Puff
_spawnAxie(0, msg.sender); // Will be Kotaro
_spawnAxie(0, msg.sender); // Will be Ginger
_spawnAxie(0, msg.sender); // Will be Stella
}
function getAxie(
uint256 _axieId
)
external
view
mustBeValidToken(_axieId)
returns (uint256 /* _genes */, uint256 /* _bornAt */)
{
Axie storage _axie = axies[_axieId];
return (_axie.genes, _axie.bornAt);
}
function spawnAxie(
uint256 _genes,
address _owner
)
external
onlySpawner
whenSpawningAllowed(_genes, _owner)
returns (uint256)
{
return _spawnAxie(_genes, _owner);
}
function rebirthAxie(
uint256 _axieId,
uint256 _genes
)
external
onlySpawner
mustBeValidToken(_axieId)
whenRebirthAllowed(_axieId, _genes)
{
Axie storage _axie = axies[_axieId];
_axie.genes = _genes;
_axie.bornAt = now;
AxieRebirthed(_axieId, _genes);
}
function retireAxie(
uint256 _axieId,
bool _rip
)
external
onlyByeSayer
whenRetirementAllowed(_axieId, _rip)
{
_burn(_axieId);
if (_rip) {
delete axies[_axieId];
}
AxieRetired(_axieId);
}
function evolveAxie(
uint256 _axieId,
uint256 _newGenes
)
external
onlyGeneScientist
mustBeValidToken(_axieId)
whenEvolvementAllowed(_axieId, _newGenes)
{
uint256 _oldGenes = axies[_axieId].genes;
axies[_axieId].genes = _newGenes;
AxieEvolved(_axieId, _oldGenes, _newGenes);
}
function _spawnAxie(uint256 _genes, address _owner) private returns (uint256 _axieId) {
Axie memory _axie = Axie(_genes, now);
_axieId = axies.push(_axie) - 1;
_mint(_owner, _axieId);
AxieSpawned(_axieId, _owner, _genes);
}
}