Skip to content

Commit e9f0b74

Browse files
committed
Implement ERC721 token with metadata
1 parent 2e1a695 commit e9f0b74

File tree

4 files changed

+158
-0
lines changed

4 files changed

+158
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
pragma solidity ^0.4.18;
2+
3+
import "./ERC721TokenMock.sol";
4+
import "../token/ERC721/ERC721TokenMetadata.sol";
5+
6+
/**
7+
* @title ERC721TokenMetadataMock
8+
* This mock just provides a public mint and burn functions for testing purposes.
9+
*/
10+
contract ERC721TokenMetadataMock is ERC721TokenMetadata, ERC721TokenMock {
11+
function ERC721TokenMetadataMock(string name, string symbol)
12+
ERC721TokenMock(name, symbol)
13+
ERC721TokenMetadata(name, symbol)
14+
public
15+
{ }
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pragma solidity ^0.4.18;
2+
3+
import "./ERC721.sol";
4+
5+
6+
/**
7+
* @title Full ERC721 interface with metadata
8+
* @dev see https://github.com/ethereum/eips/issues/721 and https://github.com/ethereum/EIPs/pull/841
9+
*/
10+
contract ERC721Metadata is ERC721 {
11+
event MetadataUpdated(address _owner, uint256 _tokenId, string _newMetadata);
12+
13+
function setTokenMetadata(uint256 _tokenId, string _metadata) public;
14+
function tokenMetadata(uint256 _tokenId) public view returns (string infoUrl);
15+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
pragma solidity ^0.4.18;
2+
3+
import "./ERC721Token.sol";
4+
import "./ERC721Metadata.sol";
5+
6+
7+
/**
8+
* @title Full ERC721 Token with metadata
9+
* This implementation includes all the functionality of the ERC721 standard besides metadata functionality
10+
* @dev see https://github.com/ethereum/eips/issues/721 and https://github.com/ethereum/EIPs/pull/841
11+
*/
12+
contract ERC721TokenMetadata is ERC721Metadata, ERC721Token {
13+
// Tokens metadata
14+
mapping (uint256 => string) private _metadata;
15+
16+
/**
17+
* @dev Constructor function
18+
*/
19+
function ERC721TokenMetadata(string name, string symbol) ERC721Token(name, symbol) public {}
20+
21+
/**
22+
* @dev Gets the metadata of the given token ID
23+
* @param _tokenId uint256 ID of the token to query the metadata of
24+
* @return string representing the metadata of the given token ID
25+
*/
26+
function tokenMetadata(uint256 _tokenId) public view returns (string) {
27+
return _metadata[_tokenId];
28+
}
29+
30+
/**
31+
* @dev Sets the metadata of the given token ID
32+
* @param _tokenId uint256 ID of the token to set the metadata of
33+
* @param _newMetadata string representing the new metadata to be set
34+
*/
35+
function setTokenMetadata(uint256 _tokenId, string _newMetadata) public onlyOwnerOf(_tokenId) {
36+
_metadata[_tokenId] = _newMetadata;
37+
MetadataUpdated(msg.sender, _tokenId, _newMetadata);
38+
}
39+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import assertRevert from '../../helpers/assertRevert';
2+
const BigNumber = web3.BigNumber;
3+
const ERC721TokenMetadata = artifacts.require('ERC721TokenMetadataMock.sol');
4+
5+
require('chai')
6+
.use(require('chai-as-promised'))
7+
.use(require('chai-bignumber')(BigNumber))
8+
.should();
9+
10+
contract('ERC721TokenMetadata', accounts => {
11+
let token = null;
12+
const _name = 'Non Fungible Token';
13+
const _symbol = 'NFT';
14+
const _firstTokenId = 1;
15+
const _secondTokenId = 2;
16+
const _unknownTokenId = 3;
17+
const _creator = accounts[0];
18+
19+
beforeEach(async function () {
20+
token = await ERC721TokenMetadata.new(_name, _symbol, { from: _creator });
21+
await token.mint(_creator, _firstTokenId, { from: _creator });
22+
await token.mint(_creator, _secondTokenId, { from: _creator });
23+
});
24+
25+
describe('tokenMetadata', function () {
26+
describe('when no metadata was set', function () {
27+
it('the given token has no metadata', async function () {
28+
const metadata = await token.tokenMetadata(_firstTokenId);
29+
30+
metadata.should.be.equal('');
31+
});
32+
});
33+
34+
describe('when some metadata was set', function () {
35+
it('returns the metadata of the given token', async function () {
36+
await token.setTokenMetadata(_firstTokenId, 'dummy metadata', { from: _creator });
37+
38+
const metadata = await token.tokenMetadata(_firstTokenId);
39+
40+
metadata.should.be.equal('dummy metadata');
41+
});
42+
});
43+
});
44+
45+
describe('setTokenMetadata', function () {
46+
describe('when the sender is not the token owner', function () {
47+
const sender = accounts[1];
48+
49+
it('reverts', async function () {
50+
await assertRevert(token.setTokenMetadata(_firstTokenId, 'new metadata', { from: sender }));
51+
});
52+
});
53+
54+
describe('when the sender is the owner of the token', function () {
55+
const sender = _creator;
56+
57+
describe('when the given token ID was tracked by this contract before', function () {
58+
const tokenId = _firstTokenId;
59+
60+
it('updates the metadata of the given token ID', async function () {
61+
await token.setTokenMetadata(_firstTokenId, 'new metadata', { from: sender });
62+
63+
const metadata = await token.tokenMetadata(tokenId);
64+
65+
metadata.should.be.equal('new metadata');
66+
});
67+
68+
it('emits a metadata updated event', async function () {
69+
const { logs } = await token.setTokenMetadata(tokenId, 'new metadata', { from: sender });
70+
71+
logs.length.should.be.equal(1);
72+
logs[0].event.should.be.eq('MetadataUpdated');
73+
logs[0].args._owner.should.be.equal(sender);
74+
logs[0].args._tokenId.should.be.bignumber.equal(tokenId);
75+
logs[0].args._newMetadata.should.be.equal('new metadata');
76+
});
77+
});
78+
79+
describe('when the given token ID was not tracked by this contract before', function () {
80+
const tokenId = _unknownTokenId;
81+
82+
it('reverts', async function () {
83+
await assertRevert(token.setTokenMetadata(tokenId, 'new metadata'), { from: sender });
84+
});
85+
});
86+
});
87+
});
88+
});

0 commit comments

Comments
 (0)