Skip to content

Instantly share code, notes, and snippets.

@ptescher
Created March 23, 2018 20:58
Show Gist options
  • Save ptescher/3a4ecd53f32d2be9111b1ab6fc240ca7 to your computer and use it in GitHub Desktop.
Save ptescher/3a4ecd53f32d2be9111b1ab6fc240ca7 to your computer and use it in GitHub Desktop.
pragma solidity 0.4.19;
/**
* Interface for required functionality in the ERC721 standard
* for non-fungible tokens.
*
* Author: Nadav Hollander (nadav at dharma.io)
*/
contract ERC721 {
// Function
function totalSupply() public view returns (uint256 _totalSupply);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint _tokenId) public view returns (address _owner);
function approve(address _to, uint _tokenId) public;
function getApproved(uint _tokenId) public view returns (address _approved);
function transferFrom(address _from, address _to, uint _tokenId) public;
function transfer(address _to, uint _tokenId) public;
function implementsERC721() public view returns (bool _implementsERC721);
// Events
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}
/**
* Interface for optional functionality in the ERC721 standard
* for non-fungible tokens.
*
* Author: Nadav Hollander (nadav at dharma.io)
*/
contract DetailedERC721 is ERC721 {
function name() public view returns (string _name);
function symbol() public view returns (string _symbol);
function tokenMetadata(uint _tokenId) public view returns (string _infoUrl);
function tokenOfOwnerByIndex(address _owner, uint _index) public view returns (uint _tokenId);
}
/**
* @title NonFungibleToken
*
* Generic implementation for both required and optional functionality in
* the ERC721 standard for non-fungible tokens.
*
* Heavily inspired by Decentraland's generic implementation:
* https://github.com/decentraland/land/blob/master/contracts/BasicNFT.sol
*
* Standard Author: dete
* Implementation Author: Nadav Hollander <nadav at dharma.io>
*/
contract NonFungibleToken is DetailedERC721 {
string public name;
string public symbol;
uint public numTokensTotal;
mapping(uint => address) internal tokenIdToOwner;
mapping(uint => address) internal tokenIdToApprovedAddress;
mapping(uint => string) internal tokenIdToMetadata;
mapping(address => uint[]) internal ownerToTokensOwned;
mapping(uint => uint) internal tokenIdToOwnerArrayIndex;
event Transfer(
address indexed _from,
address indexed _to,
uint256 _tokenId
);
event Approval(
address indexed _owner,
address indexed _approved,
uint256 _tokenId
);
modifier onlyExtantToken(uint _tokenId) {
require(ownerOf(_tokenId) != address(0));
_;
}
function name()
public
view
returns (string _name)
{
return name;
}
function symbol()
public
view
returns (string _symbol)
{
return symbol;
}
function totalSupply()
public
view
returns (uint256 _totalSupply)
{
return numTokensTotal;
}
function balanceOf(address _owner)
public
view
returns (uint _balance)
{
return ownerToTokensOwned[_owner].length;
}
function ownerOf(uint _tokenId)
public
view
returns (address _owner)
{
return _ownerOf(_tokenId);
}
function tokenMetadata(uint _tokenId)
public
view
returns (string _infoUrl)
{
return tokenIdToMetadata[_tokenId];
}
function approve(address _to, uint _tokenId)
public
onlyExtantToken(_tokenId)
{
require(msg.sender == ownerOf(_tokenId));
require(msg.sender != _to);
if (_getApproved(_tokenId) != address(0) ||
_to != address(0)) {
_approve(_to, _tokenId);
Approval(msg.sender, _to, _tokenId);
}
}
function transferFrom(address _from, address _to, uint _tokenId)
public
onlyExtantToken(_tokenId)
{
require(getApproved(_tokenId) == msg.sender);
require(ownerOf(_tokenId) == _from);
require(_to != address(0));
_clearApprovalAndTransfer(_from, _to, _tokenId);
Approval(_from, 0, _tokenId);
Transfer(_from, _to, _tokenId);
}
function transfer(address _to, uint _tokenId)
public
onlyExtantToken(_tokenId)
{
require(ownerOf(_tokenId) == msg.sender);
require(_to != address(0));
_clearApprovalAndTransfer(msg.sender, _to, _tokenId);
Approval(msg.sender, 0, _tokenId);
Transfer(msg.sender, _to, _tokenId);
}
function tokenOfOwnerByIndex(address _owner, uint _index)
public
view
returns (uint _tokenId)
{
return _getOwnerTokenByIndex(_owner, _index);
}
function getOwnerTokens(address _owner)
public
view
returns (uint[] _tokenIds)
{
return _getOwnerTokens(_owner);
}
function implementsERC721()
public
view
returns (bool _implementsERC721)
{
return true;
}
function getApproved(uint _tokenId)
public
view
returns (address _approved)
{
return _getApproved(_tokenId);
}
function _clearApprovalAndTransfer(address _from, address _to, uint _tokenId)
internal
{
_clearTokenApproval(_tokenId);
_removeTokenFromOwnersList(_from, _tokenId);
_setTokenOwner(_tokenId, _to);
_addTokenToOwnersList(_to, _tokenId);
}
function _ownerOf(uint _tokenId)
internal
view
returns (address _owner)
{
return tokenIdToOwner[_tokenId];
}
function _approve(address _to, uint _tokenId)
internal
{
tokenIdToApprovedAddress[_tokenId] = _to;
}
function _getApproved(uint _tokenId)
internal
view
returns (address _approved)
{
return tokenIdToApprovedAddress[_tokenId];
}
function _getOwnerTokens(address _owner)
internal
view
returns (uint[] _tokens)
{
return ownerToTokensOwned[_owner];
}
function _getOwnerTokenByIndex(address _owner, uint _index)
internal
view
returns (uint _tokens)
{
return ownerToTokensOwned[_owner][_index];
}
function _clearTokenApproval(uint _tokenId)
internal
{
tokenIdToApprovedAddress[_tokenId] = address(0);
}
function _setTokenOwner(uint _tokenId, address _owner)
internal
{
tokenIdToOwner[_tokenId] = _owner;
}
function _addTokenToOwnersList(address _owner, uint _tokenId)
internal
{
ownerToTokensOwned[_owner].push(_tokenId);
tokenIdToOwnerArrayIndex[_tokenId] =
ownerToTokensOwned[_owner].length - 1;
}
function _removeTokenFromOwnersList(address _owner, uint _tokenId)
internal
{
uint length = ownerToTokensOwned[_owner].length;
uint index = tokenIdToOwnerArrayIndex[_tokenId];
uint swapToken = ownerToTokensOwned[_owner][length - 1];
ownerToTokensOwned[_owner][index] = swapToken;
tokenIdToOwnerArrayIndex[swapToken] = index;
delete ownerToTokensOwned[_owner][length - 1];
ownerToTokensOwned[_owner].length--;
}
function _insertTokenMetadata(uint _tokenId, string _metadata)
internal
{
tokenIdToMetadata[_tokenId] = _metadata;
}
}
/**
* @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;
}
}
/**
* @title MintableNonFungibleToken
*
* Superset of the ERC721 standard that allows for the minting
* of non-fungible tokens.
*/
contract MintableNonFungibleToken is NonFungibleToken {
using SafeMath for uint;
event Mint(address indexed _to, uint256 indexed _tokenId);
modifier onlyNonexistentToken(uint _tokenId) {
require(tokenIdToOwner[_tokenId] == address(0));
_;
}
function mint(address _owner, uint256 _tokenId)
internal
onlyNonexistentToken(_tokenId)
{
_setTokenOwner(_tokenId, _owner);
_addTokenToOwnersList(_owner, _tokenId);
numTokensTotal = numTokensTotal.add(1);
Mint(_owner, _tokenId);
}
}
/**
* @title LimitedMintableNonFungibleToken
*
* Superset of the ERC721 standard that allows for the minting
* of non-fungible tokens, but limited to n tokens.
*/
contract LimitedMintableNonFungibleToken is MintableNonFungibleToken {
uint public mintLimit;
function LimitedMintableNonFungibleToken(uint _mintLimit) public {
mintLimit = _mintLimit;
}
function mint(uint256 _tokenId) public onlyNonexistentToken(_tokenId) {
require(ownerToTokensOwned[msg.sender].length < mintLimit);
_setTokenOwner(_tokenId, msg.sender);
_addTokenToOwnersList(msg.sender, _tokenId);
numTokensTotal = numTokensTotal.add(1);
Mint(msg.sender, _tokenId);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment