Transaction Hash:
Block:
5632551 at May-18-2018 02:28:57 AM +UTC
Transaction Fee:
0.001064097 ETH
$1.93
Gas Used:
118,233 Gas / 9 Gwei
Emitted Events:
108 |
Marketplace.AuctionCreated( id=755C53BFB4983F4871E33F1D6807C7333101A49FAF0A4F0AFB141B77C34C8813, assetId=2722258935367507707706996859454145691716, seller=[Sender] 0xe010b3bcbb3d077a82985194800227914393360a, priceInWei=860000000000000000000000, expiresAt=1679097600000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 3,318.825343684709294139 Eth | 3,318.826407781709294139 Eth | 0.001064097 | |
0xB3BCa6F5...A8A1b98f8 | |||||
0xe010B3bc...14393360a |
0.08674251115 Eth
Nonce: 318
|
0.08567841415 Eth
Nonce: 319
| 0.001064097 |
Execution Trace
Marketplace.createOrder( assetId=2722258935367507707706996859454145691716, priceInWei=860000000000000000000000, expiresAt=1679097600000 )
LANDProxy.6352211e( )
-
LANDRegistry.ownerOf( assetId=2722258935367507707706996859454145691716 ) => ( 0xe010B3bcbB3D077A82985194800227914393360a )
-
LANDProxy.2972b0f0( )
-
LANDRegistry.isAuthorized( operator=0xB3BCa6F5052c7e24726b44da7403b56A8A1b98f8, assetId=2722258935367507707706996859454145691716 ) => ( True )
-
createOrder[Marketplace (ln:265)]
ownerOf[Marketplace (ln:266)]
isAuthorized[Marketplace (ln:268)]
add[Marketplace (ln:270)]
Auction[Marketplace (ln:279)]
transferFrom[Marketplace (ln:289)]
AuctionCreated[Marketplace (ln:296)]
File 1 of 3: Marketplace
File 2 of 3: LANDProxy
File 3 of 3: LANDRegistry
pragma solidity 0.4.21; // File: zeppelin-solidity/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() public { 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) public onlyOwner { require(newOwner != address(0)); OwnershipTransferred(owner, newOwner); owner = newOwner; } } // File: zeppelin-solidity/contracts/lifecycle/Destructible.sol /** * @title Destructible * @dev Base contract that can be destroyed by owner. All funds in contract will be sent to the owner. */ contract Destructible is Ownable { function Destructible() public payable { } /** * @dev Transfers the current balance to the owner and terminates the contract. */ function destroy() onlyOwner public { selfdestruct(owner); } function destroyAndSend(address _recipient) onlyOwner public { selfdestruct(_recipient); } } // File: zeppelin-solidity/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-solidity/contracts/math/SafeMath.sol /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure 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; } /** * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } // File: contracts/marketplace/Marketplace.sol /** * @title Interface for contracts conforming to ERC-20 */ contract ERC20Interface { function transferFrom(address from, address to, uint tokens) public returns (bool success); } /** * @title Interface for contracts conforming to ERC-721 */ contract ERC721Interface { function ownerOf(uint256 assetId) public view returns (address); function safeTransferFrom(address from, address to, uint256 assetId) public; function isAuthorized(address operator, uint256 assetId) public view returns (bool); } contract Marketplace is Ownable, Pausable, Destructible { using SafeMath for uint256; ERC20Interface public acceptedToken; ERC721Interface public nonFungibleRegistry; struct Auction { // Auction ID bytes32 id; // Owner of the NFT address seller; // Price (in wei) for the published item uint256 price; // Time when this sale ends uint256 expiresAt; } mapping (uint256 => Auction) public auctionByAssetId; uint256 public ownerCutPercentage; uint256 public publicationFeeInWei; /* EVENTS */ event AuctionCreated( bytes32 id, uint256 indexed assetId, address indexed seller, uint256 priceInWei, uint256 expiresAt ); event AuctionSuccessful( bytes32 id, uint256 indexed assetId, address indexed seller, uint256 totalPrice, address indexed winner ); event AuctionCancelled( bytes32 id, uint256 indexed assetId, address indexed seller ); event ChangedPublicationFee(uint256 publicationFee); event ChangedOwnerCut(uint256 ownerCut); /** * @dev Constructor for this contract. * @param _acceptedToken - Address of the ERC20 accepted for this marketplace * @param _nonFungibleRegistry - Address of the ERC721 registry contract. */ function Marketplace(address _acceptedToken, address _nonFungibleRegistry) public { acceptedToken = ERC20Interface(_acceptedToken); nonFungibleRegistry = ERC721Interface(_nonFungibleRegistry); } /** * @dev Sets the publication fee that's charged to users to publish items * @param publicationFee - Fee amount in wei this contract charges to publish an item */ function setPublicationFee(uint256 publicationFee) onlyOwner public { publicationFeeInWei = publicationFee; ChangedPublicationFee(publicationFeeInWei); } /** * @dev Sets the share cut for the owner of the contract that's * charged to the seller on a successful sale. * @param ownerCut - Share amount, from 0 to 100 */ function setOwnerCut(uint8 ownerCut) onlyOwner public { require(ownerCut < 100); ownerCutPercentage = ownerCut; ChangedOwnerCut(ownerCutPercentage); } /** * @dev Cancel an already published order * @param assetId - ID of the published NFT * @param priceInWei - Price in Wei for the supported coin. * @param expiresAt - Duration of the auction (in hours) */ function createOrder(uint256 assetId, uint256 priceInWei, uint256 expiresAt) public whenNotPaused { address assetOwner = nonFungibleRegistry.ownerOf(assetId); require(msg.sender == assetOwner); require(nonFungibleRegistry.isAuthorized(address(this), assetId)); require(priceInWei > 0); require(expiresAt > now.add(1 minutes)); bytes32 auctionId = keccak256( block.timestamp, assetOwner, assetId, priceInWei ); auctionByAssetId[assetId] = Auction({ id: auctionId, seller: assetOwner, price: priceInWei, expiresAt: expiresAt }); // Check if there's a publication fee and // transfer the amount to marketplace owner. if (publicationFeeInWei > 0) { require(acceptedToken.transferFrom( msg.sender, owner, publicationFeeInWei )); } AuctionCreated( auctionId, assetId, assetOwner, priceInWei, expiresAt ); } /** * @dev Cancel an already published order * can only be canceled by seller or the contract owner. * @param assetId - ID of the published NFT */ function cancelOrder(uint256 assetId) public whenNotPaused { require(auctionByAssetId[assetId].seller == msg.sender || msg.sender == owner); bytes32 auctionId = auctionByAssetId[assetId].id; address auctionSeller = auctionByAssetId[assetId].seller; delete auctionByAssetId[assetId]; AuctionCancelled(auctionId, assetId, auctionSeller); } /** * @dev Executes the sale for a published NTF * @param assetId - ID of the published NFT */ function executeOrder(uint256 assetId, uint256 price) public whenNotPaused { address seller = auctionByAssetId[assetId].seller; require(seller != address(0)); require(seller != msg.sender); require(auctionByAssetId[assetId].price == price); require(now < auctionByAssetId[assetId].expiresAt); require(seller == nonFungibleRegistry.ownerOf(assetId)); uint saleShareAmount = 0; if (ownerCutPercentage > 0) { // Calculate sale share saleShareAmount = price.mul(ownerCutPercentage).div(100); // Transfer share amount for marketplace Owner. acceptedToken.transferFrom( msg.sender, owner, saleShareAmount ); } // Transfer sale amount to seller acceptedToken.transferFrom( msg.sender, seller, price.sub(saleShareAmount) ); // Transfer asset owner nonFungibleRegistry.safeTransferFrom( seller, msg.sender, assetId ); bytes32 auctionId = auctionByAssetId[assetId].id; delete auctionByAssetId[assetId]; AuctionSuccessful(auctionId, assetId, seller, price, msg.sender); } }
File 2 of 3: LANDProxy
pragma solidity ^0.4.18; // File: contracts/land/LANDStorage.sol contract LANDStorage { mapping (address => uint) latestPing; uint256 constant clearLow = 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000; uint256 constant clearHigh = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; uint256 constant factor = 0x100000000000000000000000000000000; mapping (address => bool) authorizedDeploy; } // File: contracts/upgradable/OwnableStorage.sol contract OwnableStorage { address public owner; function OwnableStorage() internal { owner = msg.sender; } } // File: contracts/upgradable/ProxyStorage.sol contract ProxyStorage { /** * Current contract to which we are proxing */ address public currentContract; address public proxyOwner; } // File: erc821/contracts/AssetRegistryStorage.sol contract AssetRegistryStorage { string internal _name; string internal _symbol; string internal _description; /** * Stores the total count of assets managed by this registry */ uint256 internal _count; /** * Stores an array of assets owned by a given account */ mapping(address => uint256[]) internal _assetsOf; /** * Stores the current holder of an asset */ mapping(uint256 => address) internal _holderOf; /** * Stores the index of an asset in the `_assetsOf` array of its holder */ mapping(uint256 => uint256) internal _indexOfAsset; /** * Stores the data associated with an asset */ mapping(uint256 => string) internal _assetData; /** * For a given account, for a given opperator, store whether that operator is * allowed to transfer and modify assets on behalf of them. */ mapping(address => mapping(address => bool)) internal _operators; /** * Simple reentrancy lock */ bool internal _reentrancy; } // File: contracts/Storage.sol contract Storage is ProxyStorage, OwnableStorage, AssetRegistryStorage, LANDStorage { } // File: contracts/upgradable/DelegateProxy.sol contract DelegateProxy { /** * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!) * @param _dst Destination address to perform the delegatecall * @param _calldata Calldata for the delegatecall */ function delegatedFwd(address _dst, bytes _calldata) internal { require(isContract(_dst)); assembly { let result := delegatecall(sub(gas, 10000), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0) let size := returndatasize let ptr := mload(0x40) returndatacopy(ptr, 0, size) // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. // if the call returned error data, forward it switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } function isContract(address _target) constant internal returns (bool) { uint256 size; assembly { size := extcodesize(_target) } return size > 0; } } // File: contracts/upgradable/IApplication.sol contract IApplication { function initialize(bytes data) public; } // File: contracts/upgradable/Ownable.sol contract Ownable is Storage { event OwnerUpdate(address _prevOwner, address _newOwner); function bytesToAddress (bytes b) pure public returns (address) { uint result = 0; for (uint i = b.length-1; i+1 > 0; i--) { uint c = uint(b[i]); uint to_inc = c * ( 16 ** ((b.length - i-1) * 2)); result += to_inc; } return address(result); } modifier onlyOwner { assert(msg.sender == owner); _; } function initialize(bytes data) public { owner = bytesToAddress(data); } function transferOwnership(address _newOwner) public onlyOwner { require(_newOwner != owner); owner = _newOwner; } } // File: contracts/upgradable/Proxy.sol contract Proxy is Storage, DelegateProxy { event Upgrade(address indexed newContract, bytes initializedWith); event OwnerUpdate(address _prevOwner, address _newOwner); function Proxy() public { proxyOwner = msg.sender; } modifier onlyProxyOwner() { require(msg.sender == proxyOwner); _; } function transferOwnership(address _newOwner) public onlyProxyOwner { require(_newOwner != proxyOwner); OwnerUpdate(proxyOwner, _newOwner); proxyOwner = _newOwner; } function upgrade(IApplication newContract, bytes data) public onlyProxyOwner { currentContract = newContract; IApplication(this).initialize(data); Upgrade(newContract, data); } function () payable public { require(currentContract != 0); // if app code hasn't been set yet, don't call delegatedFwd(currentContract, msg.data); } } // File: contracts/upgradable/LANDProxy.sol contract LANDProxy is Storage, Proxy { }
File 3 of 3: LANDRegistry
pragma solidity ^0.4.23; // File: contracts/land/LANDStorage.sol contract LANDStorage { mapping (address => uint) public latestPing; uint256 constant clearLow = 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000; uint256 constant clearHigh = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; uint256 constant factor = 0x100000000000000000000000000000000; mapping (address => bool) public authorizedDeploy; mapping (uint256 => address) public updateOperator; } // File: contracts/upgradable/OwnableStorage.sol contract OwnableStorage { address public owner; function OwnableStorage() internal { owner = msg.sender; } } // File: contracts/upgradable/ProxyStorage.sol contract ProxyStorage { /** * Current contract to which we are proxing */ address public currentContract; address public proxyOwner; } // File: erc821/contracts/AssetRegistryStorage.sol contract AssetRegistryStorage { string internal _name; string internal _symbol; string internal _description; /** * Stores the total count of assets managed by this registry */ uint256 internal _count; /** * Stores an array of assets owned by a given account */ mapping(address => uint256[]) internal _assetsOf; /** * Stores the current holder of an asset */ mapping(uint256 => address) internal _holderOf; /** * Stores the index of an asset in the `_assetsOf` array of its holder */ mapping(uint256 => uint256) internal _indexOfAsset; /** * Stores the data associated with an asset */ mapping(uint256 => string) internal _assetData; /** * For a given account, for a given operator, store whether that operator is * allowed to transfer and modify assets on behalf of them. */ mapping(address => mapping(address => bool)) internal _operators; /** * Approval array */ mapping(uint256 => address) internal _approval; } // File: contracts/Storage.sol contract Storage is ProxyStorage, OwnableStorage, AssetRegistryStorage, LANDStorage { } // File: contracts/upgradable/IApplication.sol contract IApplication { function initialize(bytes data) public; } // File: contracts/upgradable/Ownable.sol contract Ownable is Storage { event OwnerUpdate(address _prevOwner, address _newOwner); function bytesToAddress (bytes b) pure public returns (address) { uint result = 0; for (uint i = b.length-1; i+1 > 0; i--) { uint c = uint(b[i]); uint to_inc = c * ( 16 ** ((b.length - i-1) * 2)); result += to_inc; } return address(result); } modifier onlyOwner { assert(msg.sender == owner); _; } function transferOwnership(address _newOwner) public onlyOwner { require(_newOwner != owner); owner = _newOwner; } } // File: contracts/land/ILANDRegistry.sol interface ILANDRegistry { // LAND can be assigned by the owner function assignNewParcel(int x, int y, address beneficiary) external; function assignMultipleParcels(int[] x, int[] y, address beneficiary) external; // After one year, land can be claimed from an inactive public key function ping() external; // LAND-centric getters function encodeTokenId(int x, int y) pure external returns (uint256); function decodeTokenId(uint value) pure external returns (int, int); function exists(int x, int y) view external returns (bool); function ownerOfLand(int x, int y) view external returns (address); function ownerOfLandMany(int[] x, int[] y) view external returns (address[]); function landOf(address owner) view external returns (int[], int[]); function landData(int x, int y) view external returns (string); // Transfer LAND function transferLand(int x, int y, address to) external; function transferManyLand(int[] x, int[] y, address to) external; // Update LAND function updateLandData(int x, int y, string data) external; function updateManyLandData(int[] x, int[] y, string data) external; // Events event Update( uint256 indexed assetId, address indexed holder, address indexed operator, string data ); } // File: erc821/contracts/ERC165.sol interface ERC165 { function supportsInterface(bytes4 interfaceID) external view returns (bool); } // File: contracts/land/IMetadataHolder.sol contract IMetadataHolder is ERC165 { function getMetadata(uint256 /* assetId */) external view returns (string); } // File: erc821/contracts/IERC721Base.sol interface IERC721Base { function totalSupply() external view returns (uint256); // function exists(uint256 assetId) external view returns (bool); function ownerOf(uint256 assetId) external view returns (address); function balanceOf(address holder) external view returns (uint256); function safeTransferFrom(address from, address to, uint256 assetId) external; function safeTransferFrom(address from, address to, uint256 assetId, bytes userData) external; function transferFrom(address from, address to, uint256 assetId) external; function approve(address operator, uint256 assetId) external; function setApprovalForAll(address operator, bool authorized) external; function getApprovedAddress(uint256 assetId) external view returns (address); function isApprovedForAll(address operator, address assetOwner) external view returns (bool); function isAuthorized(address operator, uint256 assetId) external view returns (bool); event Transfer( address indexed from, address indexed to, uint256 indexed assetId, address operator, bytes userData ); event ApprovalForAll( address indexed operator, address indexed holder, bool authorized ); event Approval( address indexed owner, address indexed operator, uint256 indexed assetId ); } // File: erc821/contracts/IERC721Receiver.sol interface IERC721Receiver { function onERC721Received( address _oldOwner, uint256 _tokenId, bytes _userData ) external returns (bytes4); } // File: zeppelin-solidity/contracts/math/SafeMath.sol /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure 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; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } // File: erc821/contracts/ERC721Base.sol contract ERC721Base is AssetRegistryStorage, IERC721Base, ERC165 { using SafeMath for uint256; // // Global Getters // /** * @dev Gets the total amount of assets stored by the contract * @return uint256 representing the total amount of assets */ function totalSupply() external view returns (uint256) { return _totalSupply(); } function _totalSupply() internal view returns (uint256) { return _count; } // // Asset-centric getter functions // /** * @dev Queries what address owns an asset. This method does not throw. * In order to check if the asset exists, use the `exists` function or check if the * return value of this call is `0`. * @return uint256 the assetId */ function ownerOf(uint256 assetId) external view returns (address) { return _ownerOf(assetId); } function _ownerOf(uint256 assetId) internal view returns (address) { return _holderOf[assetId]; } // // Holder-centric getter functions // /** * @dev Gets the balance of the specified address * @param owner address to query the balance of * @return uint256 representing the amount owned by the passed address */ function balanceOf(address owner) external view returns (uint256) { return _balanceOf(owner); } function _balanceOf(address owner) internal view returns (uint256) { return _assetsOf[owner].length; } // // Authorization getters // /** * @dev Query whether an address has been authorized to move any assets on behalf of someone else * @param operator the address that might be authorized * @param assetHolder the address that provided the authorization * @return bool true if the operator has been authorized to move any assets */ function isApprovedForAll(address operator, address assetHolder) external view returns (bool) { return _isApprovedForAll(operator, assetHolder); } function _isApprovedForAll(address operator, address assetHolder) internal view returns (bool) { return _operators[assetHolder][operator]; } /** * @dev Query what address has been particularly authorized to move an asset * @param assetId the asset to be queried for * @return bool true if the asset has been approved by the holder */ function getApprovedAddress(uint256 assetId) external view returns (address) { return _getApprovedAddress(assetId); } function _getApprovedAddress(uint256 assetId) internal view returns (address) { return _approval[assetId]; } /** * @dev Query if an operator can move an asset. * @param operator the address that might be authorized * @param assetId the asset that has been `approved` for transfer * @return bool true if the asset has been approved by the holder */ function isAuthorized(address operator, uint256 assetId) external view returns (bool) { return _isAuthorized(operator, assetId); } function _isAuthorized(address operator, uint256 assetId) internal view returns (bool) { require(operator != 0); address owner = _ownerOf(assetId); if (operator == owner) { return true; } return _isApprovedForAll(operator, owner) || _getApprovedAddress(assetId) == operator; } // // Authorization // /** * @dev Authorize a third party operator to manage (send) msg.sender's asset * @param operator address to be approved * @param authorized bool set to true to authorize, false to withdraw authorization */ function setApprovalForAll(address operator, bool authorized) external { return _setApprovalForAll(operator, authorized); } function _setApprovalForAll(address operator, bool authorized) internal { if (authorized) { require(!_isApprovedForAll(operator, msg.sender)); _addAuthorization(operator, msg.sender); } else { require(_isApprovedForAll(operator, msg.sender)); _clearAuthorization(operator, msg.sender); } emit ApprovalForAll(operator, msg.sender, authorized); } /** * @dev Authorize a third party operator to manage one particular asset * @param operator address to be approved * @param assetId asset to approve */ function approve(address operator, uint256 assetId) external { address holder = _ownerOf(assetId); require(operator != holder); if (_getApprovedAddress(assetId) != operator) { _approval[assetId] = operator; emit Approval(holder, operator, assetId); } } function _addAuthorization(address operator, address holder) private { _operators[holder][operator] = true; } function _clearAuthorization(address operator, address holder) private { _operators[holder][operator] = false; } // // Internal Operations // function _addAssetTo(address to, uint256 assetId) internal { _holderOf[assetId] = to; uint256 length = _balanceOf(to); _assetsOf[to].push(assetId); _indexOfAsset[assetId] = length; _count = _count.add(1); } function _removeAssetFrom(address from, uint256 assetId) internal { uint256 assetIndex = _indexOfAsset[assetId]; uint256 lastAssetIndex = _balanceOf(from).sub(1); uint256 lastAssetId = _assetsOf[from][lastAssetIndex]; _holderOf[assetId] = 0; // Insert the last asset into the position previously occupied by the asset to be removed _assetsOf[from][assetIndex] = lastAssetId; // Resize the array _assetsOf[from][lastAssetIndex] = 0; _assetsOf[from].length--; // Remove the array if no more assets are owned to prevent pollution if (_assetsOf[from].length == 0) { delete _assetsOf[from]; } // Update the index of positions for the asset _indexOfAsset[assetId] = 0; _indexOfAsset[lastAssetId] = assetIndex; _count = _count.sub(1); } function _clearApproval(address holder, uint256 assetId) internal { if (_ownerOf(assetId) == holder && _approval[assetId] != 0) { _approval[assetId] = 0; emit Approval(holder, 0, assetId); } } // // Supply-altering functions // function _generate(uint256 assetId, address beneficiary) internal { require(_holderOf[assetId] == 0); _addAssetTo(beneficiary, assetId); emit Transfer(0, beneficiary, assetId, msg.sender, ''); } function _destroy(uint256 assetId) internal { address holder = _holderOf[assetId]; require(holder != 0); _removeAssetFrom(holder, assetId); emit Transfer(holder, 0, assetId, msg.sender, ''); } // // Transaction related operations // modifier onlyHolder(uint256 assetId) { require(_ownerOf(assetId) == msg.sender); _; } modifier onlyAuthorized(uint256 assetId) { require(_isAuthorized(msg.sender, assetId)); _; } modifier isCurrentOwner(address from, uint256 assetId) { require(_ownerOf(assetId) == from); _; } modifier isDestinataryDefined(address destinatary) { require(destinatary != 0); _; } modifier destinataryIsNotHolder(uint256 assetId, address to) { require(_ownerOf(assetId) != to); _; } /** * @dev Alias of `safeTransferFrom(from, to, assetId, '')` * * @param from address that currently owns an asset * @param to address to receive the ownership of the asset * @param assetId uint256 ID of the asset to be transferred */ function safeTransferFrom(address from, address to, uint256 assetId) external { return _doTransferFrom(from, to, assetId, '', msg.sender, true); } /** * @dev Securely transfers the ownership of a given asset from one address to * another address, calling the method `onNFTReceived` on the target address if * there's code associated with it * * @param from address that currently owns an asset * @param to address to receive the ownership of the asset * @param assetId uint256 ID of the asset to be transferred * @param userData bytes arbitrary user information to attach to this transfer */ function safeTransferFrom(address from, address to, uint256 assetId, bytes userData) external { return _doTransferFrom(from, to, assetId, userData, msg.sender, true); } /** * @dev Transfers the ownership of a given asset from one address to another address * Warning! This function does not attempt to verify that the target address can send * tokens. * * @param from address sending the asset * @param to address to receive the ownership of the asset * @param assetId uint256 ID of the asset to be transferred */ function transferFrom(address from, address to, uint256 assetId) external { return _doTransferFrom(from, to, assetId, '', msg.sender, false); } function _doTransferFrom( address from, address to, uint256 assetId, bytes userData, address operator, bool doCheck ) onlyAuthorized(assetId) internal { _moveToken(from, to, assetId, userData, operator, doCheck); } function _moveToken( address from, address to, uint256 assetId, bytes userData, address operator, bool doCheck ) isDestinataryDefined(to) destinataryIsNotHolder(assetId, to) isCurrentOwner(from, assetId) internal { address holder = _holderOf[assetId]; _removeAssetFrom(holder, assetId); _clearApproval(holder, assetId); _addAssetTo(to, assetId); if (doCheck && _isContract(to)) { // Equals to bytes4(keccak256("onERC721Received(address,uint256,bytes)")) bytes4 ERC721_RECEIVED = bytes4(0xf0b9e5ba); require( IERC721Receiver(to).onERC721Received( holder, assetId, userData ) == ERC721_RECEIVED ); } emit Transfer(holder, to, assetId, operator, userData); } /** * Internal function that moves an asset from one holder to another */ /** * @dev Returns `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise * @param _interfaceID The interface identifier, as specified in ERC-165 */ function supportsInterface(bytes4 _interfaceID) external view returns (bool) { if (_interfaceID == 0xffffffff) { return false; } return _interfaceID == 0x01ffc9a7 || _interfaceID == 0x80ac58cd; } // // Utilities // function _isContract(address addr) internal view returns (bool) { uint size; assembly { size := extcodesize(addr) } return size > 0; } } // File: erc821/contracts/IERC721Enumerable.sol contract IERC721Enumerable { /** * @notice Enumerate active tokens * @dev Throws if `index` >= `totalSupply()`, otherwise SHALL NOT throw. * @param index A counter less than `totalSupply()` * @return The identifier for the `index`th asset, (sort order not * specified) */ // TODO (eordano): Not implemented // function tokenByIndex(uint256 index) public view returns (uint256 _assetId); /** * @notice Count of owners which own at least one asset * Must not throw. * @return A count of the number of owners which own asset */ // TODO (eordano): Not implemented // function countOfOwners() public view returns (uint256 _count); /** * @notice Enumerate owners * @dev Throws if `index` >= `countOfOwners()`, otherwise must not throw. * @param index A counter less than `countOfOwners()` * @return The address of the `index`th owner (sort order not specified) */ // TODO (eordano): Not implemented // function ownerByIndex(uint256 index) public view returns (address owner); /** * @notice Get all tokens of a given address * @dev This is not intended to be used on-chain * @param owner address of the owner to query * @return a list of all assetIds of a user */ function tokensOf(address owner) external view returns (uint256[]); /** * @notice Enumerate tokens assigned to an owner * @dev Throws if `index` >= `balanceOf(owner)` or if * `owner` is the zero address, representing invalid assets. * Otherwise this must not throw. * @param owner An address where we are interested in assets owned by them * @param index A counter less than `balanceOf(owner)` * @return The identifier for the `index`th asset assigned to `owner`, * (sort order not specified) */ function tokenOfOwnerByIndex( address owner, uint256 index ) external view returns (uint256 tokenId); } // File: erc821/contracts/ERC721Enumerable.sol contract ERC721Enumerable is AssetRegistryStorage, IERC721Enumerable { /** * @notice Get all tokens of a given address * @dev This is not intended to be used on-chain * @param owner address of the owner to query * @return a list of all assetIds of a user */ function tokensOf(address owner) external view returns (uint256[]) { return _assetsOf[owner]; } /** * @notice Enumerate tokens assigned to an owner * @dev Throws if `index` >= `balanceOf(owner)` or if * `owner` is the zero address, representing invalid assets. * Otherwise this must not throw. * @param owner An address where we are interested in assets owned by them * @param index A counter less than `balanceOf(owner)` * @return The identifier for the `index`th asset assigned to `owner`, * (sort order not specified) */ function tokenOfOwnerByIndex( address owner, uint256 index ) external view returns (uint256 assetId) { require(index < _assetsOf[owner].length); require(index < (1<<127)); return _assetsOf[owner][index]; } } // File: erc821/contracts/IERC721Metadata.sol contract IERC721Metadata { /** * @notice A descriptive name for a collection of NFTs in this contract */ function name() external view returns (string); /** * @notice An abbreviated name for NFTs in this contract */ function symbol() external view returns (string); /** * @notice A description of what this DAR is used for */ function description() external view returns (string); /** * Stores arbitrary info about a token */ function tokenMetadata(uint256 assetId) external view returns (string); } // File: erc821/contracts/ERC721Metadata.sol contract ERC721Metadata is AssetRegistryStorage, IERC721Metadata { function name() external view returns (string) { return _name; } function symbol() external view returns (string) { return _symbol; } function description() external view returns (string) { return _description; } function tokenMetadata(uint256 assetId) external view returns (string) { return _assetData[assetId]; } function _update(uint256 assetId, string data) internal { _assetData[assetId] = data; } } // File: erc821/contracts/FullAssetRegistry.sol contract FullAssetRegistry is ERC721Base, ERC721Enumerable, ERC721Metadata { constructor() public { } /** * @dev Method to check if an asset identified by the given id exists under this DAR. * @return uint256 the assetId */ function exists(uint256 assetId) external view returns (bool) { return _exists(assetId); } function _exists(uint256 assetId) internal view returns (bool) { return _holderOf[assetId] != 0; } function decimals() external pure returns (uint256) { return 0; } } // File: contracts/land/LANDRegistry.sol contract LANDRegistry is Storage, Ownable, FullAssetRegistry, ILANDRegistry { bytes4 public GET_METADATA = bytes4(keccak256("getMetadata(uint256)")); function initialize(bytes) external { _name = "Decentraland LAND"; _symbol = "LAND"; _description = "Contract that stores the Decentraland LAND registry"; } modifier onlyProxyOwner() { require(msg.sender == proxyOwner, "this function can only be called by the proxy owner"); _; } // // LAND Create and destroy // modifier onlyOwnerOf(uint256 assetId) { require(msg.sender == _ownerOf(assetId), "this function can only be called by the owner of the asset"); _; } modifier onlyUpdateAuthorized(uint256 tokenId) { require(msg.sender == _ownerOf(tokenId) || _isUpdateAuthorized(msg.sender, tokenId), "msg.sender is not authorized to update"); _; } function isUpdateAuthorized(address operator, uint256 assetId) external view returns (bool) { return _isUpdateAuthorized(operator, assetId); } function _isUpdateAuthorized(address operator, uint256 assetId) internal view returns (bool) { return operator == _ownerOf(assetId) || updateOperator[assetId] == operator; } function authorizeDeploy(address beneficiary) external onlyProxyOwner { authorizedDeploy[beneficiary] = true; } function forbidDeploy(address beneficiary) external onlyProxyOwner { authorizedDeploy[beneficiary] = false; } function assignNewParcel(int x, int y, address beneficiary) external onlyProxyOwner { _generate(_encodeTokenId(x, y), beneficiary); } function assignMultipleParcels(int[] x, int[] y, address beneficiary) external onlyProxyOwner { for (uint i = 0; i < x.length; i++) { _generate(_encodeTokenId(x[i], y[i]), beneficiary); } } // // Inactive keys after 1 year lose ownership // function ping() external { latestPing[msg.sender] = now; } function setLatestToNow(address user) external { require(msg.sender == proxyOwner || _isApprovedForAll(msg.sender, user)); latestPing[user] = now; } // // LAND Getters // function encodeTokenId(int x, int y) pure external returns (uint) { return _encodeTokenId(x, y); } function _encodeTokenId(int x, int y) pure internal returns (uint result) { require(-1000000 < x && x < 1000000 && -1000000 < y && y < 1000000); return _unsafeEncodeTokenId(x, y); } function _unsafeEncodeTokenId(int x, int y) pure internal returns (uint) { return ((uint(x) * factor) & clearLow) | (uint(y) & clearHigh); } function decodeTokenId(uint value) pure external returns (int, int) { return _decodeTokenId(value); } function _unsafeDecodeTokenId(uint value) pure internal returns (int x, int y) { x = expandNegative128BitCast((value & clearLow) >> 128); y = expandNegative128BitCast(value & clearHigh); } function _decodeTokenId(uint value) pure internal returns (int x, int y) { (x, y) = _unsafeDecodeTokenId(value); require(-1000000 < x && x < 1000000 && -1000000 < y && y < 1000000); } function expandNegative128BitCast(uint value) pure internal returns (int) { if (value & (1<<127) != 0) { return int(value | clearLow); } return int(value); } function exists(int x, int y) view external returns (bool) { return _exists(x, y); } function _exists(int x, int y) view internal returns (bool) { return _exists(_encodeTokenId(x, y)); } function ownerOfLand(int x, int y) view external returns (address) { return _ownerOfLand(x, y); } function _ownerOfLand(int x, int y) view internal returns (address) { return _ownerOf(_encodeTokenId(x, y)); } function ownerOfLandMany(int[] x, int[] y) view external returns (address[]) { require(x.length > 0); require(x.length == y.length); address[] memory addrs = new address[](x.length); for (uint i = 0; i < x.length; i++) { addrs[i] = _ownerOfLand(x[i], y[i]); } return addrs; } function landOf(address owner) external view returns (int[], int[]) { uint256 len = _assetsOf[owner].length; int[] memory x = new int[](len); int[] memory y = new int[](len); int assetX; int assetY; for (uint i = 0; i < len; i++) { (assetX, assetY) = _decodeTokenId(_assetsOf[owner][i]); x[i] = assetX; y[i] = assetY; } return (x, y); } function tokenMetadata(uint256 assetId) external view returns (string) { return _tokenMetadata(assetId); } function _tokenMetadata(uint256 assetId) internal view returns (string) { address _owner = _ownerOf(assetId); if (_isContract(_owner)) { if (ERC165(_owner).supportsInterface(GET_METADATA)) { return IMetadataHolder(_owner).getMetadata(assetId); } } return _assetData[assetId]; } function landData(int x, int y) external view returns (string) { return _tokenMetadata(_encodeTokenId(x, y)); } // // LAND Transfer // function transferLand(int x, int y, address to) external { uint256 tokenId = _encodeTokenId(x, y); _doTransferFrom(_ownerOf(tokenId), to, tokenId, '', msg.sender, true); } function transferManyLand(int[] x, int[] y, address to) external { require(x.length > 0); require(x.length == y.length); for (uint i = 0; i < x.length; i++) { uint256 tokenId = _encodeTokenId(x[i], y[i]); _doTransferFrom(_ownerOf(tokenId), to, tokenId, '', msg.sender, true); } } function setUpdateOperator(uint256 assetId, address operator) external onlyOwnerOf(assetId) { updateOperator[assetId] = operator; } // // LAND Update // function updateLandData(int x, int y, string data) external onlyUpdateAuthorized (_encodeTokenId(x, y)) { return _updateLandData(x, y, data); } function _updateLandData(int x, int y, string data) internal onlyUpdateAuthorized (_encodeTokenId(x, y)) { uint256 assetId = _encodeTokenId(x, y); _update(assetId, data); emit Update(assetId, _holderOf[assetId], msg.sender, data); } function updateManyLandData(int[] x, int[] y, string data) external { require(x.length > 0); require(x.length == y.length); for (uint i = 0; i < x.length; i++) { _updateLandData(x[i], y[i], data); } } function _doTransferFrom( address from, address to, uint256 assetId, bytes userData, address operator, bool doCheck ) internal { updateOperator[assetId] = address(0); super._doTransferFrom(from, to, assetId, userData, operator, doCheck); } function _isContract(address addr) internal view returns (bool) { uint size; assembly { size := extcodesize(addr) } return size > 0; } }