Skip to content
This repository has been archived by the owner on Jan 12, 2019. It is now read-only.

ChainableXcert #14

Merged
merged 4 commits into from
Apr 7, 2018
Merged
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
4 changes: 3 additions & 1 deletion test/mocks/XcertMock.sol → contracts/mocks/XcertMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ pragma solidity ^0.4.19;

import "../../contracts/tokens/BurnableXcert.sol";
import "../../contracts/tokens/SettableTransferXcert.sol";
import "../../contracts/tokens/ChainableXcert.sol";

contract XcertMock is BurnableXcert, SettableTransferXcert {
contract XcertMock is BurnableXcert, SettableTransferXcert, ChainableXcert {

function XcertMock(string _name, string _symbol)
BurnableXcert(_name, _symbol)
SettableTransferXcert(_name, _symbol)
ChainableXcert(_name, _symbol)
public
{

Expand Down
68 changes: 68 additions & 0 deletions contracts/tokens/ChainableXcert.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
pragma solidity ^0.4.19;

import "./Xcert.sol";

contract ChainableXcert is Xcert {

/*
* @dev This emits when additional proof is chained.
*/
event ChainedProof(uint256 indexed tokenId, string proof, uint256 index);

function ChainableXcert(string _name,
string _symbol)
Xcert(_name, _symbol)
public
{
supportedInterfaces[0xc1dcb551] = true; // ChainableXcert
}

/*
* @dev Adds new proof to chain of proofs for token.
* @param _tokenId Id of Xcert that we want to add proof to.
* @param _proof New proof we want to add to NFToken.
*/
function chain(uint256 _tokenId,
string _proof)
validNFToken(_tokenId)
onlyOwner
external
{

require(bytes(_proof).length > 0);

idToProof[_tokenId].push(_proof);

ChainedProof(_tokenId, _proof, idToProof[_tokenId].length.sub(1));
}

/*
* @dev Gets proof by index.
* @param _tokenId Id of the Xcert we want to get proof of.
* @param _index Index of the proof we want to get.
*/
function getProofByIndex(uint256 _tokenId,
uint256 _index)
validNFToken(_tokenId)
external
view
returns (string)
{
require(_index < idToProof[_tokenId].length);
return idToProof[_tokenId][_index];
}

/*
* @dev Gets the count of all proofs for a Xcert.
* @param _tokenId Id of the Xcert we want to get proof of.
*/
function getProofCount(uint256 _tokenId)
validNFToken(_tokenId)
external
view
returns (uint256)
{
return idToProof[_tokenId].length;
}

}
12 changes: 3 additions & 9 deletions contracts/tokens/Xcert.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ contract Xcert is Ownable, ERC721, ERC721Metadata, ERC165 {
/*
* @dev Mapping from NFToken ID to proof.
*/
mapping (uint256 => string) internal idToProof;
mapping (uint256 => string[]) internal idToProof;

/*
* @dev Mapping of supported intefraces.
Expand Down Expand Up @@ -103,11 +103,6 @@ contract Xcert is Ownable, ERC721, ERC721Metadata, ERC165 {
*/
event MintAuthorizedAddress(address indexed _target, bool _authorized);

/*
* @dev this emits everytime a new Xcert contract is deployed.
*/
event XcertContractDeployed(address _contractAddress, string _name, string _symbol);

/*
* @dev Guarantees that the msg.sender is an owner or operator of the given NFToken.
* @param _tokenId ID of the NFToken to validate.
Expand Down Expand Up @@ -164,7 +159,6 @@ contract Xcert is Ownable, ERC721, ERC721Metadata, ERC165 {
supportedInterfaces[0x80ac58cd] = true; // ERC721
supportedInterfaces[0x5b5e139f] = true; // ERC721Metadata
supportedInterfaces[0x58c66b9f] = true; // Xcert
XcertContractDeployed(address(this), _name, _symbol);
}

/*
Expand Down Expand Up @@ -391,7 +385,7 @@ contract Xcert is Ownable, ERC721, ERC721Metadata, ERC165 {
require(bytes(_proof).length > 0);

idToUri[_id] = _uri;
idToProof[_id] = _proof;
idToProof[_id].push(_proof);
addNFToken(_to, _id);

Transfer(address(0), _to, _id);
Expand All @@ -408,7 +402,7 @@ contract Xcert is Ownable, ERC721, ERC721Metadata, ERC165 {
view
returns (string)
{
return idToProof[_tokenId];
return idToProof[_tokenId][idToProof[_tokenId].length.sub(1)];
}

/*
Expand Down
8 changes: 8 additions & 0 deletions contracts/utils/Selector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.4.19;
import "../tokens/Xcert.sol";
import "../tokens/BurnableXcert.sol";
import "../tokens/SettableTransferXcert.sol";
import "../tokens/ChainableXcert.sol";

contract Selector {

Expand All @@ -23,4 +24,11 @@ contract Selector {
SettableTransferXcert i;
return i.setTransferable.selector;
}

function calculateChainableXcertSelector() public pure returns (bytes4) {
ChainableXcert i;
return i.chain.selector
^ i.getProofByIndex.selector
^ i.getProofCount.selector;
}
}
44 changes: 44 additions & 0 deletions test/tokens/ChainableXcert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const ChainableXcert = artifacts.require('ChainableXcert');
const util = require('ethjs-util');
const assertRevert = require('../helpers/assertRevert');

contract('ChainableXcert', (accounts) => {
let xcert;
let id1 = web3.sha3('test1');
let mockProof = "1e205550c271490347e5e2393a02e94d284bbe9903f023ba098355b8d75974c8";
let mockProof2 = "1e205550c271490347e5e2393a02e94d284bbe9903f023ba098355b8d75974d5";

beforeEach(async function () {
xcert = await ChainableXcert.new('Foo', 'F');
await xcert.mint(accounts[0], id1, mockProof, 'url1');
});

it('correctly chains additional proof', async () => {
await xcert.chain(id1, mockProof2);
var proof = await xcert.getProof(id1);
assert.equal(proof, mockProof2);
});

it('reverts trying to chain empty proof', async () => {
await assertRevert(xcert.chain(id1, ""));
});

it('correctly gets proof by index', async () => {
await xcert.chain(id1, mockProof2);
var proof = await xcert.getProofByIndex(id1, 1);
assert.equal(proof, mockProof2);
});

it('reverts trying to get proof for none existant index', async () => {
await assertRevert(xcert.getProofByIndex(id1, 1));
});

it('returns correct proof count', async () => {
var proofCount = await xcert.getProofCount(id1);
assert.equal(proofCount, 1);

await xcert.chain(id1, mockProof2);
proofCount = await xcert.getProofCount(id1);
assert.equal(proofCount, 2);
});
});
10 changes: 8 additions & 2 deletions test/tokens/XcertMock.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,28 @@ contract('XcertMock', (accounts) => {
let id3 = web3.sha3('test3');
let id4 = web3.sha3('test4');
let mockProof = "1e205550c271490347e5e2393a02e94d284bbe9903f023ba098355b8d75974c8";
let mockProof2 = "1e205550c271490347e5e2393a02e94d284bbe9903f023ba098355b8d75974d7";

beforeEach(async function () {
xcert = await Xcert.new('Foo', 'F');
await xcert.mint(accounts[0], id1, mockProof, 'url1');
});

it('destroys NFToken id 1', async () => {
await xcert.mint(accounts[0], id1, mockProof, 'url1');
await xcert.burn(id1);
const count = await xcert.balanceOf(accounts[0]);
assert.equal(count, 0);
});

it('reverts trying to transfer when transfers are disabled', async () => {
await xcert.mint(accounts[0], id1, mockProof, 'url1');
await xcert.setTransferable(false);
await assertRevert(xcert.transferFrom(accounts[0], accounts[1], id1));
});

it('correctly chains additional proof', async () => {
await xcert.chain(id1, mockProof2);
var proof = await xcert.getProof(id1);
assert.equal(proof, mockProof2);
});

});
43 changes: 43 additions & 0 deletions test/utils/Selector.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const Xcert = artifacts.require('Xcert');
const BurnableXcert = artifacts.require('BurnableXcert');
const ChainableXcert = artifacts.require('ChainableXcert');
const SettableTransferXcert = artifacts.require('SettableTransferXcert');
const Selector = artifacts.require('Selector');

contract('Selector', (accounts) => {

let selector;

beforeEach(async function () {
selector = await Selector.new();
});

it('Checks Xcert selector', async () => {
var xcert = await Xcert.new('Foo', 'F');
var bytes = await selector.calculateXcertSelector();
var supports = await xcert.supportsInterface(bytes);
assert.equal(supports, true);
});

it('Checks BurnableXcert selector', async () => {
var xcert = await BurnableXcert.new('Foo', 'F');
var bytes = await selector.calculateBurnableXcertSelector();
var supports = await xcert.supportsInterface(bytes);
assert.equal(supports, true);
});

it('Checks SettableTransferXcert selector', async () => {
var xcert = await SettableTransferXcert.new('Foo', 'F');
var bytes = await selector.calculateSettableTransferXcertSelector();
var supports = await xcert.supportsInterface(bytes);
assert.equal(supports, true);
});

it('Checks ChainableXcert selector', async () => {
var xcert = await ChainableXcert.new('Foo', 'F');
var bytes = await selector.calculateChainableXcertSelector();
var supports = await xcert.supportsInterface(bytes);
assert.equal(supports, true);
});

});