Transaction Hash:
Block:
11900973 at Feb-21-2021 03:03:11 PM +UTC
Transaction Fee:
0.010920820137357555 ETH
$17.38
Gas Used:
94,145 Gas / 116.000001459 Gwei
Emitted Events:
126 |
AxieClockAuction.AuctionSuccessful( _nftAddress=AxieCore, _tokenId=35317, _totalPrice=65000000000000000, _winner=[Sender] 0x9ac20c4922fca379bb95083eb441c393ea88033e )
|
127 |
AxieCore.Approval( _owner=[Receiver] AxieClockAuction, _approved=0x00000000...000000000, _tokenId=35317 )
|
128 |
AxieCore.Transfer( _from=[Receiver] AxieClockAuction, _to=[Sender] 0x9ac20c4922fca379bb95083eb441c393ea88033e, _tokenId=35317 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x829BD824...93333A830
Miner
| (F2Pool Old) | 2,703.770732561392719996 Eth | 2,703.781653381530077551 Eth | 0.010920820137357555 | |
0x9AC20C49...3ea88033E |
1.178634776353029822 Eth
Nonce: 16
|
1.102713956215672267 Eth
Nonce: 17
| 0.075920820137357555 | ||
0xF4985070...c9a2dd9e2 | (Axie Infinity: Axie Clock Auction) | 619.15278107469293991 Eth | 619.15554357469293991 Eth | 0.0027625 | |
0xF5b0A3eF...F3FFEcb8d | |||||
0xFe24CEA9...f94379401 | 0.003179745815726841 Eth | 0.065417245815726841 Eth | 0.0622375 |
Execution Trace
ETH 0.065004960317460317
AxieClockAuction.bid( _nftAddress=0xF5b0A3eFB8e8E4c201e2A935F110eAaF3FFEcb8d, _tokenId=35317 )
- ETH 0.0622375
0xfe24cea99cd00b20be072aeb5b79628f94379401.CALL( )
- ETH 0.000004960317460317
0x9ac20c4922fca379bb95083eb441c393ea88033e.CALL( )
AxieCore.transferFrom( _from=0xF4985070Ce32b6B1994329DF787D1aCc9a2dd9e2, _to=0x9AC20C4922fcA379BB95083Eb441C393ea88033E, _tokenId=35317 )
0xe8bd438d0383cf4d19641eaa4793eddc6cebeaf1.f7ebc39a( )
-
AxieCore.CALL( )
-
bid[AxieClockAuction (ln:387)]
_bid[AxieClockAuction (ln:396)]
_isOnAuction[AxieClockAuction (ln:614)]
_getCurrentPrice[AxieClockAuction (ln:618)]
_computeCurrentPrice[AxieClockAuction (ln:465)]
_removeAuction[AxieClockAuction (ln:627)]
_computeCut[AxieClockAuction (ln:634)]
transfer[AxieClockAuction (ln:645)]
transfer[AxieClockAuction (ln:658)]
AuctionSuccessful[AxieClockAuction (ln:662)]
_transfer[AxieClockAuction (ln:397)]
_getNftContract[AxieClockAuction (ln:580)]
transferFrom[AxieClockAuction (ln:583)]
File 1 of 2: AxieClockAuction
File 2 of 2: AxieCore
pragma solidity ^0.4.19; // 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/HasNoEther.sol /** * @title Contracts that should not own Ether * @author Remco Bloemen <remco@2π.com> * @dev This tries to block incoming ether to prevent accidental loss of Ether. Should Ether end up * in the contract, it will allow the owner to reclaim this ether. * @notice Ether can still be send to this contract by: * calling functions labeled `payable` * `selfdestruct(contract_address)` * mining directly to the contract address */ contract HasNoEther is Ownable { /** * @dev Constructor that rejects incoming Ether * @dev The `payable` flag is added so we can access `msg.value` without compiler warning. If we * leave out payable, then Solidity will allow inheriting contracts to implement a payable * constructor. By doing it this way we prevent a payable constructor from working. Alternatively * we could use assembly to access msg.value. */ function HasNoEther() payable { require(msg.value == 0); } /** * @dev Disallows direct send by settings a default function without the `payable` flag. */ function() external { } /** * @dev Transfer all Ether held by the contract to the owner. */ function reclaimEther() external onlyOwner { assert(owner.send(this.balance)); } } // File: contracts/marketplace/AxieClockAuction.sol /// @title Clock auction for non-fungible tokens. contract AxieClockAuction is HasNoEther, 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; // Map from token ID to their corresponding auction. mapping (address => mapping (uint256 => Auction)) public auctions; event AuctionCreated( address indexed _nftAddress, uint256 indexed _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration, address _seller ); event AuctionSuccessful( address indexed _nftAddress, uint256 indexed _tokenId, uint256 _totalPrice, address _winner ); event AuctionCancelled( address indexed _nftAddress, uint256 indexed _tokenId ); /// @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. function AxieClockAuction(uint256 _ownerCut) public { require(_ownerCut <= 10000); ownerCut = _ownerCut; } /// @dev DON'T give me your money. function () external {} // 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); _; } /// @dev Returns auction info for an NFT on auction. /// @param _nftAddress - Address of the NFT. /// @param _tokenId - ID of NFT on auction. function getAuction( address _nftAddress, uint256 _tokenId ) external view returns ( address seller, uint256 startingPrice, uint256 endingPrice, uint256 duration, uint256 startedAt ) { Auction storage _auction = auctions[_nftAddress][_tokenId]; require(_isOnAuction(_auction)); return ( _auction.seller, _auction.startingPrice, _auction.endingPrice, _auction.duration, _auction.startedAt ); } /// @dev Returns the current price of an auction. /// @param _nftAddress - Address of the NFT. /// @param _tokenId - ID of the token price we are checking. function getCurrentPrice( address _nftAddress, uint256 _tokenId ) external view returns (uint256) { Auction storage _auction = auctions[_nftAddress][_tokenId]; require(_isOnAuction(_auction)); return _getCurrentPrice(_auction); } /// @dev Creates and begins a new auction. /// @param _nftAddress - address of a deployed contract implementing /// the Nonfungible Interface. /// @param _tokenId - ID of token 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( address _nftAddress, uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration ) external whenNotPaused canBeStoredWith128Bits(_startingPrice) canBeStoredWith128Bits(_endingPrice) canBeStoredWith64Bits(_duration) { address _seller = msg.sender; require(_owns(_nftAddress, _seller, _tokenId)); _escrow(_nftAddress, _seller, _tokenId); Auction memory _auction = Auction( _seller, uint128(_startingPrice), uint128(_endingPrice), uint64(_duration), uint64(now) ); _addAuction(_nftAddress, _tokenId, _auction, _seller); } /// @dev Bids on an open auction, completing the auction and transferring /// ownership of the NFT if enough Ether is supplied. /// @param _nftAddress - address of a deployed contract implementing /// the Nonfungible Interface. /// @param _tokenId - ID of token to bid on. function bid( address _nftAddress, uint256 _tokenId ) external payable whenNotPaused { // _bid will throw if the bid or funds transfer fails _bid(_nftAddress, _tokenId, msg.value); _transfer(_nftAddress, msg.sender, _tokenId); } /// @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 _nftAddress - Address of the NFT. /// @param _tokenId - ID of token on auction function cancelAuction(address _nftAddress, uint256 _tokenId) external { Auction storage _auction = auctions[_nftAddress][_tokenId]; require(_isOnAuction(_auction)); require(msg.sender == _auction.seller); _cancelAuction(_nftAddress, _tokenId, _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 _nftAddress - Address of the NFT. /// @param _tokenId - ID of the NFT on auction to cancel. function cancelAuctionWhenPaused( address _nftAddress, uint256 _tokenId ) external whenPaused onlyOwner { Auction storage _auction = auctions[_nftAddress][_tokenId]; require(_isOnAuction(_auction)); _cancelAuction(_nftAddress, _tokenId, _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 Gets the NFT object from an address, validating that implementsERC721 is true. /// @param _nftAddress - Address of the NFT. function _getNftContract(address _nftAddress) internal pure returns (IERC721Base) { IERC721Base candidateContract = IERC721Base(_nftAddress); // require(candidateContract.implementsERC721()); return candidateContract; } /// @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 Returns true if the claimant owns the token. /// @param _nftAddress - The address of the NFT. /// @param _claimant - Address claiming to own the token. /// @param _tokenId - ID of token whose ownership to verify. function _owns(address _nftAddress, address _claimant, uint256 _tokenId) private view returns (bool) { IERC721Base _nftContract = _getNftContract(_nftAddress); return (_nftContract.ownerOf(_tokenId) == _claimant); } /// @dev Adds an auction to the list of open auctions. Also fires the /// AuctionCreated event. /// @param _tokenId The ID of the token to be put on auction. /// @param _auction Auction to add. function _addAuction( address _nftAddress, uint256 _tokenId, Auction _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[_nftAddress][_tokenId] = _auction; AuctionCreated( _nftAddress, _tokenId, uint256(_auction.startingPrice), uint256(_auction.endingPrice), uint256(_auction.duration), _seller ); } /// @dev Removes an auction from the list of open auctions. /// @param _tokenId - ID of NFT on auction. function _removeAuction(address _nftAddress, uint256 _tokenId) internal { delete auctions[_nftAddress][_tokenId]; } /// @dev Cancels an auction unconditionally. function _cancelAuction(address _nftAddress, uint256 _tokenId, address _seller) internal { _removeAuction(_nftAddress, _tokenId); _transfer(_nftAddress, _seller, _tokenId); AuctionCancelled(_nftAddress, _tokenId); } /// @dev Escrows the NFT, assigning ownership to this contract. /// Throws if the escrow fails. /// @param _nftAddress - The address of the NFT. /// @param _owner - Current owner address of token to escrow. /// @param _tokenId - ID of token whose approval to verify. function _escrow(address _nftAddress, address _owner, uint256 _tokenId) private { IERC721Base _nftContract = _getNftContract(_nftAddress); // It will throw if transfer fails _nftContract.transferFrom(_owner, this, _tokenId); } /// @dev Transfers an NFT owned by this contract to another address. /// Returns true if the transfer succeeds. /// @param _nftAddress - The address of the NFT. /// @param _receiver - Address to transfer NFT to. /// @param _tokenId - ID of token to transfer. function _transfer(address _nftAddress, address _receiver, uint256 _tokenId) internal { IERC721Base _nftContract = _getNftContract(_nftAddress); // It will throw if transfer fails _nftContract.transferFrom(this, _receiver, _tokenId); } /// @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 token. function _bid( address _nftAddress, uint256 _tokenId, uint256 _bidAmount ) internal returns (uint256) { // Get a reference to the auction struct Auction storage _auction = auctions[_nftAddress][_tokenId]; // Explicitly check that this auction is currently live. // (Because of how Ethereum mappings work, we can't just count // on the lookup above failing. An invalid _tokenId will just // return an auction object that is all zeros.) require(_isOnAuction(_auction)); // Check that the incoming bid is higher than the current // price uint256 _price = _getCurrentPrice(_auction); require(_bidAmount >= _price); // 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(_nftAddress, _tokenId); // 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 > _price) { // 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 - _price; // 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! AuctionSuccessful(_nftAddress, _tokenId, _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); } }