Contract Name:
CryptoHandles
Contract Source Code:
File 1 of 1 : CryptoHandles
/**
* @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;
}
}
/**
* @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 ERC721Token
* Generic implementation for the required functionality of the ERC721 standard
*/
contract ERC721Token is ERC721 {
using SafeMath for uint256;
// Total amount of tokens
uint256 internal totalTokens;
// Mapping from token ID to owner
mapping (uint256 => address) internal tokenOwner;
// Mapping from token ID to approved address
mapping (uint256 => address) internal tokenApprovals;
// Mapping from owner to list of owned token IDs
mapping (address => uint256[]) internal ownedTokens;
// Mapping from token ID to index of the owner tokens list
mapping(uint256 => uint256) internal ownedTokensIndex;
/**
* @dev Guarantees msg.sender is owner of the given token
* @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender
*/
modifier onlyOwnerOf(uint256 _tokenId) {
require(ownerOf(_tokenId) == msg.sender);
_;
}
/**
* @dev Gets the total amount of tokens stored by the contract
* @return uint256 representing the total amount of tokens
*/
function totalSupply() public view returns (uint256) {
return totalTokens;
}
/**
* @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) public view returns (uint256) {
return ownedTokens[_owner].length;
}
/**
* @dev Gets the list of tokens owned by a given address
* @param _owner address to query the tokens of
* @return uint256[] representing the list of tokens owned by the passed address
*/
function tokensOf(address _owner) public view returns (uint256[]) {
return ownedTokens[_owner];
}
/**
* @dev Gets the owner of the specified token ID
* @param _tokenId uint256 ID of the token to query the owner of
* @return owner address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 _tokenId) public view returns (address) {
address owner = tokenOwner[_tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Gets the approved address to take ownership of a given token ID
* @param _tokenId uint256 ID of the token to query the approval of
* @return address currently approved to take ownership of the given token ID
*/
function approvedFor(uint256 _tokenId) public view returns (address) {
return tokenApprovals[_tokenId];
}
/**
* @dev Transfers the ownership of a given token ID to another address
* @param _to address to receive the ownership of the given token ID
* @param _tokenId uint256 ID of the token to be transferred
*/
function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
clearApprovalAndTransfer(msg.sender, _to, _tokenId);
}
/**
* @dev Approves another address to claim for the ownership of the given token ID
* @param _to address to be approved for the given token ID
* @param _tokenId uint256 ID of the token to be approved
*/
function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
address owner = ownerOf(_tokenId);
require(_to != owner);
if (approvedFor(_tokenId) != 0 || _to != 0) {
tokenApprovals[_tokenId] = _to;
Approval(owner, _to, _tokenId);
}
}
/**
* @dev Claims the ownership of a given token ID
* @param _tokenId uint256 ID of the token being claimed by the msg.sender
*/
function takeOwnership(uint256 _tokenId) public {
require(isApprovedFor(msg.sender, _tokenId));
clearApprovalAndTransfer(ownerOf(_tokenId), msg.sender, _tokenId);
}
/**
* @dev Mint token function
* @param _to The address that will own the minted token
* @param _tokenId uint256 ID of the token to be minted by the msg.sender
*/
function _mint(address _to, uint256 _tokenId) internal {
require(_to != address(0));
addToken(_to, _tokenId);
Transfer(0x0, _to, _tokenId);
}
/**
* @dev Burns a specific token
* @param _tokenId uint256 ID of the token being burned by the msg.sender
*/
function _burn(uint256 _tokenId) onlyOwnerOf(_tokenId) internal {
if (approvedFor(_tokenId) != 0) {
clearApproval(msg.sender, _tokenId);
}
removeToken(msg.sender, _tokenId);
Transfer(msg.sender, 0x0, _tokenId);
}
/**
* @dev Tells whether the msg.sender is approved for the given token ID or not
* This function is not private so it can be extended in further implementations like the operatable ERC721
* @param _owner address of the owner to query the approval of
* @param _tokenId uint256 ID of the token to query the approval of
* @return bool whether the msg.sender is approved for the given token ID or not
*/
function isApprovedFor(address _owner, uint256 _tokenId) internal view returns (bool) {
return approvedFor(_tokenId) == _owner;
}
/**
* @dev Internal function to clear current approval and transfer the ownership of a given token ID
* @param _from address which you want to send tokens from
* @param _to address which you want to transfer the token to
* @param _tokenId uint256 ID of the token to be transferred
*/
function clearApprovalAndTransfer(address _from, address _to, uint256 _tokenId) internal {
require(_to != address(0));
require(_to != ownerOf(_tokenId));
require(ownerOf(_tokenId) == _from);
clearApproval(_from, _tokenId);
removeToken(_from, _tokenId);
addToken(_to, _tokenId);
Transfer(_from, _to, _tokenId);
}
/**
* @dev Internal function to clear current approval of a given token ID
* @param _tokenId uint256 ID of the token to be transferred
*/
function clearApproval(address _owner, uint256 _tokenId) private {
require(ownerOf(_tokenId) == _owner);
tokenApprovals[_tokenId] = 0;
Approval(_owner, 0, _tokenId);
}
/**
* @dev Internal function to add a token ID to the list of a given address
* @param _to address representing the new owner of the given token ID
* @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function addToken(address _to, uint256 _tokenId) private {
require(tokenOwner[_tokenId] == address(0));
tokenOwner[_tokenId] = _to;
uint256 length = balanceOf(_to);
ownedTokens[_to].push(_tokenId);
ownedTokensIndex[_tokenId] = length;
totalTokens = totalTokens.add(1);
}
/**
* @dev Internal function to remove a token ID from the list of a given address
* @param _from address representing the previous owner of the given token ID
* @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function removeToken(address _from, uint256 _tokenId) private {
require(ownerOf(_tokenId) == _from);
uint256 tokenIndex = ownedTokensIndex[_tokenId];
uint256 lastTokenIndex = balanceOf(_from).sub(1);
uint256 lastToken = ownedTokens[_from][lastTokenIndex];
tokenOwner[_tokenId] = 0;
ownedTokens[_from][tokenIndex] = lastToken;
ownedTokens[_from][lastTokenIndex] = 0;
// Note that this will handle single-element arrays. In that case, both tokenIndex and lastTokenIndex are going to
// be zero. Then we can make sure that we will remove _tokenId from the ownedTokens list since we are first swapping
// the lastToken to the first position, and then dropping the element placed in the last position of the list
ownedTokens[_from].length--;
ownedTokensIndex[_tokenId] = 0;
ownedTokensIndex[lastToken] = tokenIndex;
totalTokens = totalTokens.sub(1);
}
}
contract AuctionHouse {
address owner;
function AuctionHouse() {
owner = msg.sender;
}
// 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 = 375; // Default is 3.75%
// Map from token ID to their corresponding auction.
mapping (address => mapping (uint256 => Auction)) tokenIdToAuction;
// Allowed tokens
mapping (address => bool) supportedTokens;
event AuctionCreated(address indexed tokenAddress, uint256 indexed tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration, address seller);
event AuctionSuccessful(address indexed tokenAddress, uint256 indexed tokenId, uint256 totalPrice, address winner);
event AuctionCancelled(address indexed tokenAddress, uint256 indexed tokenId, address seller);
// Admin
// Change owner of the contract
function changeOwner(address newOwner) external {
require(msg.sender == owner);
owner = newOwner;
}
// Add or remove supported tokens
function setSupportedToken(address tokenAddress, bool supported) external {
require(msg.sender == owner);
supportedTokens[tokenAddress] = supported;
}
// Set the owner cut for auctions
function setOwnerCut(uint256 cut) external {
require(msg.sender == owner);
require(cut <= 10000);
ownerCut = cut;
}
// Withdraw sales fees
function withdraw() external {
require(msg.sender == owner);
owner.transfer(this.balance);
}
/// @dev Returns true if the claimant owns the token.
/// @param _claimant - Address claiming to own the token.
/// @param _tokenId - ID of token whose ownership to verify.
function _owns(address _tokenAddress, address _claimant, uint256 _tokenId) internal view returns (bool) {
return (ERC721Token(_tokenAddress).ownerOf(_tokenId) == _claimant);
}
/// @dev Escrows the NFT, assigning ownership to this contract.
/// Throws if the escrow fails.
/// @param _tokenId - ID of token whose approval to verify.
function _escrow(address _tokenAddress, uint256 _tokenId) internal {
// it will throw if transfer fails
ERC721Token token = ERC721Token(_tokenAddress);
if (token.ownerOf(_tokenId) != address(this)) {
token.takeOwnership(_tokenId);
}
}
/// @dev Transfers an NFT owned by this contract to another address.
/// Returns true if the transfer succeeds.
/// @param _receiver - Address to transfer NFT to.
/// @param _tokenId - ID of token to transfer.
function _transfer(address _tokenAddress, address _receiver, uint256 _tokenId) internal {
// it will throw if transfer fails
ERC721Token(_tokenAddress).transfer(_receiver, _tokenId);
}
/// @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 _tokenAddress, uint256 _tokenId, Auction _auction) internal {
// Require that all auctions have a duration of
// at least one minute. (Keeps our math from getting hairy!)
require(_auction.duration >= 1 minutes);
tokenIdToAuction[_tokenAddress][_tokenId] = _auction;
AuctionCreated(
address(_tokenAddress),
uint256(_tokenId),
uint256(_auction.startingPrice),
uint256(_auction.endingPrice),
uint256(_auction.duration),
address(_auction.seller)
);
}
/// @dev Cancels an auction unconditionally.
function _cancelAuction(address _tokenAddress, uint256 _tokenId, address _seller) internal {
_removeAuction(_tokenAddress, _tokenId);
_transfer(_tokenAddress, _seller, _tokenId);
AuctionCancelled(_tokenAddress, _tokenId, _seller);
}
/// @dev Computes the price and transfers winnings.
/// Does NOT transfer ownership of token.
function _bid(address _tokenAddress, uint256 _tokenId, uint256 _bidAmount)
internal
returns (uint256)
{
// Get a reference to the auction struct
Auction storage auction = tokenIdToAuction[_tokenAddress][_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 bid is greater than or equal to the current price
uint256 price = _currentPrice(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(_tokenAddress, _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);
}
// 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(_tokenAddress, _tokenId, price, msg.sender);
return price;
}
/// @dev Removes an auction from the list of open auctions.
/// @param _tokenId - ID of NFT on auction.
function _removeAuction(address _tokenAddress, uint256 _tokenId) internal {
delete tokenIdToAuction[_tokenAddress][_tokenId];
}
/// @dev Returns true if the NFT is on auction.
/// @param _auction - Auction to check.
function _isOnAuction(Auction storage _auction) internal view returns (bool) {
return (_auction.startedAt > 0);
}
/// @dev Returns current price of an NFT on auction. Broken into two
/// functions (this one, that computes the duration from the auction
/// structure, and the other that does the price computation) so we
/// can easily test that the price computation works correctly.
function _currentPrice(Auction storage _auction)
internal
view
returns (uint256)
{
uint256 secondsPassed = 0;
// A bit of insurance against negative values (or wraparound).
// Probably not necessary (since Ethereum guarnatees 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 public 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 public 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 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 Creates and begins a new auction.
/// @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).
/// @param _seller - Seller, if not the message sender
function createAuction(
address _tokenAddress,
uint256 _tokenId,
uint256 _startingPrice,
uint256 _endingPrice,
uint256 _duration,
address _seller
)
public
{
// Check this token is supported
require(supportedTokens[_tokenAddress]);
// Auctions must be made by the token contract or the token owner
require(msg.sender == _tokenAddress || _owns(_tokenAddress, msg.sender, _tokenId));
// Sanity check that no inputs overflow how many bits we've allocated
// to store them in the auction struct.
require(_startingPrice == uint256(uint128(_startingPrice)));
require(_endingPrice == uint256(uint128(_endingPrice)));
require(_duration == uint256(uint64(_duration)));
_escrow(_tokenAddress, _tokenId);
Auction memory auction = Auction(
_seller,
uint128(_startingPrice),
uint128(_endingPrice),
uint64(_duration),
uint64(now)
);
_addAuction(_tokenAddress, _tokenId, auction);
}
/// @dev Bids on an open auction, completing the auction and transferring
/// ownership of the NFT if enough Ether is supplied.
/// @param _tokenId - ID of token to bid on.
function bid(address _tokenAddress, uint256 _tokenId)
external
payable
{
// Check this token is supported
require(supportedTokens[_tokenAddress]);
// _bid will throw if the bid or funds transfer fails
_bid(_tokenAddress, _tokenId, msg.value);
_transfer(_tokenAddress, 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 _tokenId - ID of token on auction
function cancelAuction(address _tokenAddress, uint256 _tokenId)
external
{
// We don't check if a token is supported here because we may remove supported
// This allows users to cancel auctions for tokens that have been removed
Auction storage auction = tokenIdToAuction[_tokenAddress][_tokenId];
require(_isOnAuction(auction));
address seller = auction.seller;
require(msg.sender == seller);
_cancelAuction(_tokenAddress, _tokenId, seller);
}
/// @dev Returns auction info for an NFT on auction.
/// @param _tokenId - ID of NFT on auction.
function getAuction(address _tokenAddress, uint256 _tokenId)
external
view
returns
(
address seller,
uint256 startingPrice,
uint256 endingPrice,
uint256 duration,
uint256 startedAt
) {
// Check this token is supported
require(supportedTokens[_tokenAddress]);
Auction storage auction = tokenIdToAuction[_tokenAddress][_tokenId];
require(_isOnAuction(auction));
return (
auction.seller,
auction.startingPrice,
auction.endingPrice,
auction.duration,
auction.startedAt
);
}
/// @dev Returns the current price of an auction.
/// @param _tokenId - ID of the token price we are checking.
function getCurrentPrice(address _tokenAddress, uint256 _tokenId)
external
view
returns (uint256)
{
// Check this token is supported
require(supportedTokens[_tokenAddress]);
Auction storage auction = tokenIdToAuction[_tokenAddress][_tokenId];
require(_isOnAuction(auction));
return _currentPrice(auction);
}
}
contract CryptoHandles is ERC721Token {
address public owner;
uint256 public defaultBuyNowPrice = 100 finney;
uint256 public defaultAuctionPrice = 1 ether;
uint256 public defaultAuctionDuration = 1 days;
AuctionHouse public auctions;
mapping (uint => bytes32) handles;
mapping (bytes32 => uint) reverse;
event SetRecord(bytes32 indexed handle, string indexed key, string value);
function CryptoHandles(address auctionAddress) {
owner = msg.sender;
auctions = AuctionHouse(auctionAddress);
}
/**
* @dev Change owner of the contract
*/
function changeOwner(address newOwner) external {
require(msg.sender == owner);
owner = newOwner;
}
/**
* @dev Withdraw funds
*/
function withdraw() external {
require(msg.sender == owner);
owner.transfer(this.balance);
}
/**
* @dev Set buy now price
*/
function setBuyNowPrice(uint price) external {
require(msg.sender == owner);
defaultBuyNowPrice = price;
}
/**
* @dev Set buy now price
*/
function setAuctionPrice(uint price) external {
require(msg.sender == owner);
defaultAuctionPrice = price;
}
/**
* @dev Set duration
*/
function setAuctionDuration(uint duration) external {
require(msg.sender == owner);
defaultAuctionDuration = duration;
}
/**
* @dev Accept proceeds from auction sales
*/
function() public payable {}
/**
* @dev Create a new handle if the handle is valid and not owned
* @param _handle bytes32 handle to register
*/
function create(bytes32 _handle) external payable {
require(isHandleValid(_handle));
require(isHandleAvailable(_handle));
uint _tokenId = totalTokens;
handles[_tokenId] = _handle;
reverse[_handle] = _tokenId;
// handle buy now
if (msg.value == defaultBuyNowPrice) {
_mint(msg.sender, _tokenId);
} else {
// otherwise start an auction
require(msg.value == 0);
// mint the token to the address
_mint(address(auctions), _tokenId);
auctions.createAuction(
address(this),
_tokenId,
defaultAuctionPrice,
0,
defaultAuctionDuration,
address(this)
);
}
}
/**
* @dev Checks if a handle is valid: a-z, 0-9, _
* @param _handle bytes32 to check validity
*/
function isHandleValid(bytes32 _handle) public pure returns (bool) {
if (_handle == 0x0) {
return false;
}
bool padded;
for (uint i = 0; i < 32; i++) {
byte char = byte(bytes32(uint(_handle) * 2 ** (8 * i)));
// null for padding
if (char == 0x0) {
padded = true;
continue;
}
// numbers 0-9
if (char >= 0x30 && char <= 0x39 && !padded) {
continue;
}
// lowercase letters a-z
if (char >= 0x61 && char <= 0x7A && !padded) {
continue;
}
// underscores _
if (char == 0x5F && !padded) {
continue;
}
return false;
}
return true;
}
/**
* @dev Checks if a handle is available
* @param _handle bytes32 handle to check availability
*/
function isHandleAvailable(bytes32 _handle) public view returns (bool) {
// Get the tokenId for a given handle
uint tokenId = reverse[_handle];
if (handles[tokenId] != _handle) {
return true;
}
}
/**
* @dev Approve the AuctionHouse and start an auction
* @param _tokenId uint256
* @param _startingPrice uint256
* @param _endingPrice uint256
* @param _duration uint256
*/
function approveAndAuction(uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration) external {
require(ownerOf(_tokenId) == msg.sender);
tokenApprovals[_tokenId] = address(auctions);
auctions.createAuction(
address(this),
_tokenId,
_startingPrice,
_endingPrice,
_duration,
msg.sender
);
}
/**
* @dev Get tokenId for a given handle
* @param _handle bytes32 handle
*/
function tokenIdForHandle(bytes32 _handle) public view returns (uint) {
// Handle 0 index
uint tokenId = reverse[_handle];
require(handles[tokenId] == _handle);
return tokenId;
}
/**
* @dev Get handle for a given tokenId
* @param _tokenId uint
*/
function handleForTokenId(uint _tokenId) public view returns (bytes32) {
bytes32 handle = handles[_tokenId];
require(handle != 0x0);
return handle;
}
/**
* @dev Get the handle owner
* @param _handle bytes32 handle to check availability
*/
function getHandleOwner(bytes32 _handle) public view returns (address) {
// Handle 0 index
uint tokenId = reverse[_handle];
require(handles[tokenId] == _handle);
return ownerOf(tokenId);
}
/// Records for a handle
mapping(bytes32 => mapping(string => string)) internal records;
function setRecord(bytes32 _handle, string _key, string _value) external {
uint tokenId = reverse[_handle];
require(ownerOf(tokenId) == msg.sender);
records[_handle][_key] = _value;
SetRecord(_handle, _key, _value);
}
function getRecord(bytes32 _handle, string _key) external view returns (string) {
return records[_handle][_key];
}
}