Add default implementation for metadata URI

This allows token implementation to be non-abstract
pull/803/head
Santiago Palladino 7 years ago
parent 54a1d2eacc
commit 851685c40e
  1. 27
      contracts/mocks/ERC721TokenMock.sol
  2. 30
      contracts/token/ERC721/ERC721Token.sol
  3. 24
      test/token/ERC721/ERC721Token.test.js

@ -5,7 +5,7 @@ import "../token/ERC721/ERC721Token.sol";
/**
* @title ERC721TokenMock
* This mock just provides a public mint and burn functions for testing purposes,
* and a mock metadata URI implementation
* and a public setter for metadata URI
*/
contract ERC721TokenMock is ERC721Token {
function ERC721TokenMock(string name, string symbol)
@ -21,28 +21,7 @@ contract ERC721TokenMock is ERC721Token {
doBurn(ownerOf(_tokenId), _tokenId);
}
// Mock implementation for testing.
// Do not use this code in production!
function tokenURI(uint256 _tokenId) public view returns (string) {
require(exists(_tokenId));
bytes memory uri = new bytes(78 + 7);
uint256 i;
uint256 value = _tokenId;
uri[0] = "m";
uri[1] = "o";
uri[2] = "c";
uri[3] = "k";
uri[4] = ":";
uri[5] = "/";
uri[6] = "/";
for (i = 0; i < 78; i++) {
uri[6 + 78 - i] = byte(value % 10 + 48);
value = value / 10;
}
return string(uri);
function setTokenURI(uint256 _tokenId, string _uri) public {
doSetTokenURI(_tokenId, _uri);
}
}

@ -30,6 +30,9 @@ contract ERC721Token is ERC721, ERC721BasicToken {
// Mapping from token id to position in the allTokens array
mapping(uint256 => uint256) internal allTokensIndex;
// Optional mapping for token URIs
mapping(uint256 => string) internal tokenURIs;
/**
* @dev Constructor function
*/
@ -63,6 +66,27 @@ contract ERC721Token is ERC721, ERC721BasicToken {
return symbol_;
}
/**
* @dev Returns an URI for a given token ID
* @dev Throws if the token ID does not exist. May return an empty string.
* @param _tokenId uint256 ID of the token to query
*/
function tokenURI(uint256 _tokenId) public view returns (string) {
require(exists(_tokenId));
return tokenURIs[_tokenId];
}
/**
* @dev Internal function to set the token URI for a given token
* @dev Reverts if the token ID does not exist
* @param _tokenId uint256 ID of the token to set its URI
* @param _uri string URI to assign
*/
function doSetTokenURI(uint256 _tokenId, string _uri) internal {
require(exists(_tokenId));
tokenURIs[_tokenId] = _uri;
}
/**
* @dev Gets the token ID at a given index of the tokens list of the requested owner
* @param _owner address owning the tokens list to be accessed
@ -152,6 +176,12 @@ contract ERC721Token is ERC721, ERC721BasicToken {
function doBurn(address _owner, uint256 _tokenId) internal {
super.doBurn(_owner, _tokenId);
// Clear metadata (if any)
if (bytes(tokenURIs[_tokenId]).length == 0) {
delete tokenURIs[_tokenId];
}
// Reorg all tokens array
uint256 tokenIndex = allTokensIndex[_tokenId];
uint256 lastTokenIndex = allTokens.length.sub(1);
uint256 lastToken = allTokens[lastTokenIndex];

@ -77,6 +77,8 @@ contract('ERC721Token', function (accounts) {
});
describe('metadata', function () {
const sampleUri = 'mock://mytoken';
it('has a name', async function () {
const name = await this.token.name();
name.should.be.equal(name);
@ -87,10 +89,26 @@ contract('ERC721Token', function (accounts) {
symbol.should.be.equal(symbol);
});
it('returns metadata for a token id', async function () {
it('sets and returns metadata for a token id', async function () {
await this.token.setTokenURI(firstTokenId, sampleUri);
const uri = await this.token.tokenURI(firstTokenId);
uri.should.be.equal(sampleUri);
});
it('can burn token with metadata', async function () {
await this.token.setTokenURI(firstTokenId, sampleUri);
await this.token.burn(firstTokenId);
const exists = await this.token.exists(firstTokenId);
exists.should.be.false;
});
it('returns empty metadata for token', async function () {
const uri = await this.token.tokenURI(firstTokenId);
const expected = `mock://${firstTokenId.toString().padStart(78, 0)}`;
uri.should.be.equal(expected);
uri.should.be.equal('');
});
it('reverts when querying metadata for non existant token id', async function () {
await assertRevert(this.token.tokenURI(500));
});
});

Loading…
Cancel
Save