Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions contracts/mocks/ERC721DeedMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pragma solidity ^0.4.18;

import "../token/ERC721/ERC721Deed.sol";

/**
* @title ERC721DeedMock
* This mock just provides a public mint and burn functions for testing purposes.
*/


contract ERC721DeedMock is ERC721Deed {

// ERC-165 interface implementation

// 0x01ffc9a7
bytes4 internal constant INTERFACE_SIGNATURE_ERC165 = bytes4(keccak256("supportsInterface(bytes4)"));

// 0xda671b9b
bytes4 internal constant INTERFACE_SIGNATURE_ERC721 = bytes4(keccak256("ownerOf(uint256)")) ^ bytes4(keccak256("countOfDeeds()")) ^ bytes4(keccak256("countOfDeedsByOwner(address)")) ^ bytes4(keccak256("deedOfOwnerByIndex(address,uint256)")) ^ bytes4(keccak256("approve(address,uint256)")) ^ bytes4(keccak256("takeOwnership(uint256)"));

function ERC721DeedMock() ERC721Deed() public {}

function supportsInterface(bytes4 _interfaceID) external pure returns (bool) {
return (
_interfaceID == INTERFACE_SIGNATURE_ERC165 || _interfaceID == INTERFACE_SIGNATURE_ERC721
);
}

function mint(address _to, uint256 _tokenId) public {
super._mint(_to, _tokenId);
}

function burn(uint256 _tokenId) public {
super._burn(_tokenId);
}
}
105 changes: 92 additions & 13 deletions contracts/token/ERC721/ERC721.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,95 @@
pragma solidity ^0.4.18;

/**
* @title ERC721 interface
* @dev see https://github.com/ethereum/eips/issues/721
*/
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);

function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
/// @title Interface for contracts conforming to ERC-721: Deed Standard
/// @author William Entriken (https://phor.net), et. al.
/// Slightly altered by Nastassia Sachs (https://github.com/nastassiasachs)
/// @dev Specification at https://github.com/ethereum/eips/XXXFinalUrlXXX

interface ERC721 {

// COMPLIANCE WITH ERC-165 (DRAFT) /////////////////////////////////////////

/// @dev ERC-165 (draft) interface signature for itself
// bytes4 internal constant INTERFACE_SIGNATURE_ERC165 = // 0x01ffc9a7
// bytes4(keccak256('supportsInterface(bytes4)'));

/// @dev ERC-165 (draft) interface signature for ERC721
// bytes4 internal constant INTERFACE_SIGNATURE_ERC721 = // 0xda671b9b
// bytes4(keccak256('ownerOf(uint256)')) ^
// bytes4(keccak256('countOfDeeds()')) ^
// bytes4(keccak256('countOfDeedsByOwner(address)')) ^
// bytes4(keccak256('deedOfOwnerByIndex(address,uint256)')) ^
// bytes4(keccak256('approve(address,uint256)')) ^
// bytes4(keccak256('takeOwnership(uint256)'));

/// @notice Query a contract to see if it supports a certain interface
/// @dev Returns `true` the interface is supported and `false` otherwise,
/// returns `true` for INTERFACE_SIGNATURE_ERC165 and
/// INTERFACE_SIGNATURE_ERC721, see ERC-165 for other interface signatures.
function supportsInterface(bytes4 _interfaceID) external pure returns (bool);

// PUBLIC QUERY FUNCTIONS //////////////////////////////////////////////////

/// @notice Find the owner of a deed
/// @param _deedId The identifier for a deed we are inspecting
/// @dev Deeds assigned to zero address are considered invalid, and
/// queries about them do throw.
/// @return The non-zero address of the owner of deed `_deedId`, or `throw`
/// if deed `_deedId` is not tracked by this contract
function ownerOf(uint256 _deedId) external view returns (address _owner);

/// @notice Count deeds tracked by this contract
/// @return A count of valid deeds tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function countOfDeeds() external view returns (uint256 _count);

/// @notice Count all deeds assigned to an owner
/// @dev Throws if `_owner` is the zero address, representing invalid deeds.
/// @param _owner An address where we are interested in deeds owned by them
/// @return The number of deeds owned by `_owner`, possibly zero
function countOfDeedsByOwner(address _owner) external view returns (uint256 _count);

/// @notice Enumerate deeds assigned to an owner
/// @dev Throws if `_index` >= `countOfDeedsByOwner(_owner)` or if
/// `_owner` is the zero address, representing invalid deeds.
/// @param _owner An address where we are interested in deeds owned by them
/// @param _index A counter less than `countOfDeedsByOwner(_owner)`
/// @return The identifier for the `_index`th deed assigned to `_owner`,
/// (sort order not specified)
function deedOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _deedId);

// TRANSFER MECHANISM //////////////////////////////////////////////////////

/// @dev This event emits when ownership of any deed changes by any
/// mechanism. This event emits when deeds are created (`from` == 0) and
/// destroyed (`to` == 0). Exception: during contract creation, any
/// transfers may occur without emitting `Transfer`. At the time of any transfer,
/// the "approved taker" is implicitly reset to the zero address.
event Transfer(address indexed _from, address indexed _to, uint256 indexed _deedId);

/// @dev The Approve event emits to log the "approved taker" for a deed -- whether
/// set for the first time, reaffirmed by setting the same value, or setting to
/// a new value. The "approved taker" is the zero address if nobody can take the
/// deed now or it is an address if that address can call `takeOwnership` to attempt
/// taking the deed. Any change to the "approved taker" for a deed SHALL cause
/// Approve to emit. However, an exception, the Approve event will not emit when
/// Transfer emits, this is because Transfer implicitly denotes the "approved taker"
/// is reset to the zero address.
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _deedId);

/// @notice Set the "approved taker" for your deed, or revoke approval by
/// setting the zero address. You may `approve` any number of times while
/// the deed is assigned to you, only the most recent approval matters. Emits
/// an Approval event.
/// @dev Throws if `msg.sender` does not own deed `_deedId` or if `_to` ==
/// `msg.sender` or if `_deedId` is not a valid deed.
/// @param _deedId The deed for which you are granting approval
function approve(address _to, uint256 _deedId) external payable;

/// @notice Become owner of a deed for which you are currently approved
/// @dev Throws if `msg.sender` is not approved to become the owner of
/// `deedId` or if `msg.sender` currently owns `_deedId` or if `_deedId is not a
/// valid deed.
/// @param _deedId The deed that is being transferred
function takeOwnership(uint256 _deedId) external payable;
}
218 changes: 218 additions & 0 deletions contracts/token/ERC721/ERC721Deed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
pragma solidity ^0.4.18;

import "./ERC721.sol";
import "../../math/SafeMath.sol";

/**
* @title ERC721Deed
* Generic implementation for the required functionality of the ERC721 standard
* @author Nastassia Sachs (https://github.com/nastassiasachs)
* Based on OpenZeppelin's ERC721Token
*/


contract ERC721Deed is ERC721 {
using SafeMath for uint256;

// Total amount of deeds
uint256 private totalDeeds;

// Mapping from deed ID to owner
mapping (uint256 => address) private deedOwner;

// Mapping from deed ID to approved address
mapping (uint256 => address) private deedApprovedFor;

// Mapping from owner to list of owned deed IDs
mapping (address => uint256[]) private ownedDeeds;

// Mapping from deed ID to index of the owner deeds list
mapping(uint256 => uint256) private ownedDeedsIndex;

/**
* @dev Guarantees msg.sender is owner of the given deed
* @param _deedId uint256 ID of the deed to validate its ownership belongs to msg.sender
*/
modifier onlyOwnerOf(uint256 _deedId) {
require(deedOwner[_deedId] == msg.sender);
_;
}

/**
* @dev Gets the owner of the specified deed ID
* @param _deedId uint256 ID of the deed to query the owner of
* @return owner address currently marked as the owner of the given deed ID
*/
function ownerOf(uint256 _deedId) external view returns (address _owner) {
require(deedOwner[_deedId] != address(0));
_owner = deedOwner[_deedId];
}

/**
* @dev Gets the total amount of deeds stored by the contract
* @return uint256 representing the total amount of deeds
*/
function countOfDeeds() external view returns (uint256) {
return totalDeeds;
}

/**
* @dev Gets the number of deeds of the specified address
* @param _owner address to query the number of deeds
* @return uint256 representing the number of deeds owned by the passed address
*/
function countOfDeedsByOwner(address _owner) external view returns (uint256 _count) {
require(_owner != address(0));
_count = ownedDeeds[_owner].length;
}

/**
* @dev Gets the deed ID of the specified address at the specified index
* @param _owner address for the deed's owner
* @param _index uint256 for the n-th deed in the list of deeds owned by this owner
* @return uint256 representing the ID of the deed
*/
function deedOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _deedId) {
require(_owner != address(0));
require(_index < ownedDeeds[_owner].length);
_deedId = ownedDeeds[_owner][_index];
}

/**
* @dev Gets all deed IDs of the specified address
* @param _owner address for the deed's owner
* @return uint256[] representing all deed IDs owned by the passed address
*/
function deedsOf(address _owner) external view returns (uint256[] _ownedDeedIds) {
require(_owner != address(0));
_ownedDeedIds = ownedDeeds[_owner];
}

/**
* @dev Approves another address to claim for the ownership of the given deed ID
* @param _to address to be approved for the given deed ID
* @param _deedId uint256 ID of the deed to be approved
*/
function approve(address _to, uint256 _deedId) external onlyOwnerOf(_deedId) payable {
require(_to != msg.sender);
if (_to != address(0) || approvedFor(_deedId) != address(0)) {
Approval(msg.sender, _to, _deedId);
}
deedApprovedFor[_deedId] = _to;
}

/**
* @dev Claims the ownership of a given deed ID
* @param _deedId uint256 ID of the deed being claimed by the msg.sender
*/
function takeOwnership(uint256 _deedId) external payable {
require(approvedFor(_deedId) == msg.sender);
clearApprovalAndTransfer(deedOwner[_deedId], msg.sender, _deedId);
}

/**
* @dev Gets the approved address to take ownership of a given deed ID
* @param _deedId uint256 ID of the deed to query the approval of
* @return address currently approved to take ownership of the given deed ID
*/
function approvedFor(uint256 _deedId) public view returns (address) {
return deedApprovedFor[_deedId];
}

/**
* @dev Transfers the ownership of a given deed ID to another address
* @param _to address to receive the ownership of the given deed ID
* @param _deedId uint256 ID of the deed to be transferred
*/
function transfer(address _to, uint256 _deedId) public onlyOwnerOf(_deedId) {
clearApprovalAndTransfer(msg.sender, _to, _deedId);
}

/**
* @dev Mint deed function
* @param _to The address that will own the minted deed
*/
function _mint(address _to, uint256 _deedId) internal {
require(_to != address(0));
addDeed(_to, _deedId);
Transfer(0x0, _to, _deedId);
}

/**
* @dev Burns a specific deed
* @param _deedId uint256 ID of the deed being burned by the msg.sender
*/
function _burn(uint256 _deedId) onlyOwnerOf(_deedId) internal {
if (approvedFor(_deedId) != 0) {
clearApproval(msg.sender, _deedId);
}
removeDeed(msg.sender, _deedId);
Transfer(msg.sender, 0x0, _deedId);
}

/**
* @dev Internal function to clear current approval and transfer the ownership of a given deed ID
* @param _from address which you want to send deeds from
* @param _to address which you want to transfer the deed to
* @param _deedId uint256 ID of the deed to be transferred
*/
function clearApprovalAndTransfer(address _from, address _to, uint256 _deedId) internal {
require(_to != address(0));
require(_to != _from);
require(deedOwner[_deedId] == _from);

clearApproval(_from, _deedId);
removeDeed(_from, _deedId);
addDeed(_to, _deedId);
Transfer(_from, _to, _deedId);
}

/**
* @dev Internal function to clear current approval of a given deed ID
* @param _deedId uint256 ID of the deed to be transferred
*/
function clearApproval(address _owner, uint256 _deedId) private {
require(deedOwner[_deedId] == _owner);
deedApprovedFor[_deedId] = 0;
Approval(_owner, 0, _deedId);
}

/**
* @dev Internal function to add a deed ID to the list of a given address
* @param _to address representing the new owner of the given deed ID
* @param _deedId uint256 ID of the deed to be added to the deeds list of the given address
*/
function addDeed(address _to, uint256 _deedId) private {
require(deedOwner[_deedId] == address(0));
deedOwner[_deedId] = _to;
uint256 length = ownedDeeds[_to].length;
ownedDeeds[_to].push(_deedId);
ownedDeedsIndex[_deedId] = length;
totalDeeds = totalDeeds.add(1);
}

/**
* @dev Internal function to remove a deed ID from the list of a given address
* @param _from address representing the previous owner of the given deed ID
* @param _deedId uint256 ID of the deed to be removed from the deeds list of the given address
*/
function removeDeed(address _from, uint256 _deedId) private {
require(deedOwner[_deedId] == _from);

uint256 deedIndex = ownedDeedsIndex[_deedId];
uint256 lastDeedIndex = ownedDeeds[_from].length.sub(1);
uint256 lastDeed = ownedDeeds[_from][lastDeedIndex];

deedOwner[_deedId] = 0;
ownedDeeds[_from][deedIndex] = lastDeed;
ownedDeeds[_from][lastDeedIndex] = 0;
// Note that this will handle single-element arrays. In that case, both deedIndex and lastDeedIndex are going to
// be zero. Then we can make sure that we will remove _deedId from the ownedDeeds list since we are first swapping
// the lastDeed to the first position, and then dropping the element placed in the last position of the list

ownedDeeds[_from].length--;
ownedDeedsIndex[_deedId] = 0;
ownedDeedsIndex[lastDeed] = deedIndex;
totalDeeds = totalDeeds.sub(1);
}
}
2 changes: 1 addition & 1 deletion contracts/token/ERC721/ERC721Token.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity ^0.4.18;

import "./ERC721.sol";
import "./ERC721_deprecated.sol";
import "../../math/SafeMath.sol";

/**
Expand Down
16 changes: 16 additions & 0 deletions contracts/token/ERC721/ERC721_deprecated.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pragma solidity ^0.4.18;

/**
* @title ERC721 interface
* @dev see https://github.com/ethereum/eips/issues/721
*/
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);

function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
}
Loading