Skip to content

Commit 41517a2

Browse files
committed
Adding fees for other functions, added more test coverage when fees are enabled
1 parent 1927016 commit 41517a2

File tree

12 files changed

+264
-205
lines changed

12 files changed

+264
-205
lines changed

README.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22
[![Build Status](https://travis-ci.org/codex-protocol/contract.codex-registry.svg?branch=master)](https://travis-ci.org/codex-protocol/contract.codex-registry)
33

44
## To do
5-
### Testing
6-
- Extensive testing on CodexTitle
7-
8-
### Features
9-
- Documentation hashes array for CodexTitle (i.e., in addition to images)
5+
- Extensive testing on ERC900
6+
- Token Ejection (similar to BiddableEscrow)
7+
- 100% code coverage
8+
- Gas optimization
9+
- TCR for providers
1010
- Add a version field to the tokens themselves so we can track if they've been upgraded or not. This is useful for cases where storage state in the new implementation needs to be migrated over.
11-
- Consider adding the burn functionality back in, but maybe restricting it to onlyOwner for now.
1211

1312
## Notes
14-
- Everything under the zeppelin-solidity directory was copied over from node_modules/zeppelin-solidity to give visibility into the contract code and so they can be pinned to a specific compiler version. No extensive changes have been made
15-
- This contract is not intended to be used directly on it's own. Users should instead communicate to the contract through instances of the Biddable Widget hosted by our consortium members
1613
- If you accidentally send ERC-20 tokens to the address where this is deployed, please email contact@codexprotocol.com to discuss retrieval
1714

1815
## Thanks to

contracts/CodexTitleAccess.sol

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ contract CodexTitleAccess is CodexTitleCore {
2121
string _providerMetadataId) // TODO: convert to bytes32
2222
public
2323
whenNotPaused
24-
canPayFees
24+
canPayFees(creationFee)
2525
{
2626
return super.mint(
2727
_to,
@@ -42,6 +42,7 @@ contract CodexTitleAccess is CodexTitleCore {
4242
uint256 _tokenId)
4343
public
4444
whenNotPaused
45+
canPayFees(transferFee)
4546
{
4647
return super.transferFrom(_from, _to, _tokenId);
4748
}
@@ -55,6 +56,7 @@ contract CodexTitleAccess is CodexTitleCore {
5556
uint256 _tokenId)
5657
public
5758
whenNotPaused
59+
canPayFees(transferFee)
5860
{
5961
return super.safeTransferFrom(_from, _to, _tokenId);
6062
}
@@ -69,6 +71,7 @@ contract CodexTitleAccess is CodexTitleCore {
6971
bytes _data)
7072
public
7173
whenNotPaused
74+
canPayFees(transferFee)
7275
{
7376
return super.safeTransferFrom(
7477
_from,
@@ -87,9 +90,10 @@ contract CodexTitleAccess is CodexTitleCore {
8790
bytes32 _newDescriptionHash,
8891
bytes32[] _newImageHashes,
8992
string _providerId, // TODO: convert to bytes32?
90-
string _providerMetadataId // TODO: convert to bytes32?
91-
)
92-
public whenNotPaused
93+
string _providerMetadataId) // TODO: convert to bytes32?
94+
public
95+
whenNotPaused
96+
canPayFees(modificationFee)
9397
{
9498
return super.modifyMetadataHashes(
9599
_tokenId,

contracts/CodexTitleFees.sol

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ contract CodexTitleFees is Pausable {
2626
// Fee to create new tokens. 10^18 = 1 token
2727
uint256 public creationFee = 0;
2828

29-
modifier canPayFees() {
29+
// Fee to transfer tokens. 10^18 = 1 token
30+
uint256 public transferFee = 0;
31+
32+
// Fee to modify tokens. 10^18 = 1 token
33+
uint256 public modificationFee = 0;
34+
35+
modifier canPayFees(uint256 baseFee) {
3036
if (feeRecipient != address(0)) {
3137
// TODO: Update the discount to be based on weight as opposed to just
3238
// a binary on/off value
33-
uint256 calculatedFee = creationFee;
39+
uint256 calculatedFee = baseFee;
3440
if (codexStakeContainer != address(0) &&
3541
codexStakeContainer.totalStakedFor(msg.sender) >= 0) {
3642

@@ -47,25 +53,29 @@ contract CodexTitleFees is Pausable {
4753

4854
/**
4955
* @dev Sets the address of the ERC20 token used for fees in the contract.
56+
* Fees are in the smallest denomination, e.g., 10^18 is 1 token.
5057
* @param _codexToken The address of the ERC20 Codex Protocol Token
5158
* @param _feeRecipient The address where the fees are sent
52-
* @param _creationFee The new creation fee. 10^18 is 1 token.
59+
* @param _creationFee The new creation fee.
60+
* @param _transferFee The new transfer fee.
61+
* @param _modificationFee The new modification fee.
5362
*/
54-
function setFees(ERC20 _codexToken, address _feeRecipient, uint256 _creationFee) external onlyOwner {
63+
function setFees(
64+
ERC20 _codexToken,
65+
address _feeRecipient,
66+
uint256 _creationFee,
67+
uint256 _transferFee,
68+
uint256 _modificationFee)
69+
external onlyOwner
70+
{
5571
codexToken = _codexToken;
5672
feeRecipient = _feeRecipient;
5773
creationFee = _creationFee;
74+
transferFee = _transferFee;
75+
modificationFee = _modificationFee;
5876
}
5977

6078
function setStakeContainer(ERC900 _codexStakeContainer) external onlyOwner {
6179
codexStakeContainer = _codexStakeContainer;
6280
}
63-
64-
/**
65-
* @dev Sets the creation fee in CODX
66-
* @param _creationFee The new creation fee. 10^18 is 1 token.
67-
*/
68-
function setCreationFee(uint256 _creationFee) external onlyOwner {
69-
creationFee = _creationFee;
70-
}
7181
}

contracts/ERC900/ERC900.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ contract ERC900 {
1717
function token() public view returns (address);
1818
function supportsHistory() public pure returns (bool);
1919

20-
// optional
20+
// NOTE: Not implementing the optional functions
2121
// function lastStakedFor(address addr) public view returns (uint256);
2222
// function totalStakedForAt(address addr, uint256 blockNumber) public view returns (uint256);
2323
// function totalStakedAt(uint256 blockNumber) public view returns (uint256);

contracts/ERC900/ERC900BasicStakeContainer.sol

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,29 @@ contract ERC900BasicStakeContainer is ERC900 {
3737
Stake personalStake;
3838
}
3939

40-
constructor(ERC20 _stakingToken) public {
41-
stakingToken = _stakingToken;
40+
modifier noExistingStake(address _address) {
41+
require(
42+
!addresses[_address].personalStake.exists,
43+
"Stake already exists");
44+
_;
4245
}
4346

44-
function stake(uint256 _amount, bytes _data) public {
45-
require(!addresses[msg.sender].personalStake.exists, "Stake already exists");
46-
47+
modifier canStake(address _address, uint256 _amount) {
4748
require(
48-
stakingToken.transferFrom(msg.sender, this, _amount),
49+
stakingToken.transferFrom(_address, this, _amount),
4950
"Stake required");
51+
_;
52+
}
5053

54+
constructor(ERC20 _stakingToken) public {
55+
stakingToken = _stakingToken;
56+
}
57+
58+
function stake(uint256 _amount, bytes _data)
59+
public
60+
noExistingStake(msg.sender)
61+
canStake(msg.sender, _amount)
62+
{
5163
addresses[msg.sender].personalStake = Stake(block.number, _amount, true);
5264
addresses[msg.sender].amountStakedFor.add(_amount);
5365

@@ -58,13 +70,11 @@ contract ERC900BasicStakeContainer is ERC900 {
5870
_data);
5971
}
6072

61-
function stakeFor(address _user, uint256 _amount, bytes _data) public {
62-
require(!addresses[msg.sender].personalStake.exists, "Stake already exists");
63-
64-
require(
65-
stakingToken.transferFrom(msg.sender, this, _amount),
66-
"Stake required");
67-
73+
function stakeFor(address _user, uint256 _amount, bytes _data)
74+
public
75+
noExistingStake(msg.sender)
76+
canStake(msg.sender, _amount)
77+
{
6878
addresses[msg.sender].personalStake = Stake(block.number, _amount, true);
6979

7080
// Notice here that we are increasing the staked amount for _user
@@ -115,4 +125,8 @@ contract ERC900BasicStakeContainer is ERC900 {
115125
function token() public view returns (address) {
116126
return stakingToken;
117127
}
128+
129+
function supportsHistory() public pure returns (bool) {
130+
return false;
131+
}
118132
}

contracts/ERC900/ERC900StakeContainer.sol

Lines changed: 0 additions & 32 deletions
This file was deleted.

migrations/6_initialize_721token.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,21 @@ module.exports = async (deployer, network, accounts) => {
2323

2424
case 'rinkeby':
2525
erc20TokenAddress = '0xb05e292f89c6a82f5ed1be694dc7b6444866b364'
26-
initialFees = 10
26+
initialFees = 10 ** 18 // 1 token
2727
break
2828

2929
default:
3030
throw new Error('No erc20TokenAddress & initialFees defined for this network')
3131
}
3232

3333
console.log(`Setting the fees to ${initialFees} at ERC-20 token address: ${erc20TokenAddress}`)
34-
await proxiedCodexTitle.setFees(erc20TokenAddress, accounts[0], initialFees)
34+
await proxiedCodexTitle.setFees(
35+
erc20TokenAddress,
36+
accounts[0],
37+
initialFees, // creationFee
38+
initialFees, // transferFee
39+
initialFees, // modificationFee
40+
)
3541
})
3642
.then(async () => {
3743

test/helpers/modifyMetadataHashes.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default async function modifyMetadataHashes({
1111
expectedImageHashes = newImageHashes,
1212
expectedDescriptionHash = newDescriptionHash,
1313

14+
feesEnabled = false,
1415
}) {
1516

1617
if (
@@ -22,6 +23,10 @@ export default async function modifyMetadataHashes({
2223
return
2324
}
2425

26+
// If fees are enabled, a Transfer event is fired in addition to the Modified event
27+
const expectedLogsLength = feesEnabled ? 2 : 1
28+
const logIndex = feesEnabled ? 1 : 0
29+
2530
const { logs } = await this.token.modifyMetadataHashes(
2631
this.tokenId,
2732
newNameHash,
@@ -39,19 +44,19 @@ export default async function modifyMetadataHashes({
3944

4045
// no Modified event is emitted when no provider details are specified
4146
if (!providerId && !providerMetadataId) {
42-
logs.length.should.be.equal(0)
47+
logs.length.should.be.equal(expectedLogsLength - 1)
4348
return
4449
}
4550

46-
logs.length.should.be.equal(1)
51+
logs.length.should.be.equal(expectedLogsLength)
4752

48-
logs[0].event.should.be.eq('Modified')
49-
logs[0].args._from.should.be.equal(this.creator)
50-
logs[0].args._tokenId.should.be.bignumber.equal(this.tokenId)
51-
logs[0].args._newNameHash.should.be.equal(tokenData[0])
52-
logs[0].args._newDescriptionHash.should.be.equal(tokenData[1])
53-
logs[0].args._newImageHashes.should.deep.equal(tokenData[2])
54-
logs[0].args._providerId.should.be.equal(providerId)
55-
logs[0].args._providerMetadataId.should.be.equal(providerMetadataId)
53+
logs[logIndex].event.should.be.eq('Modified')
54+
logs[logIndex].args._from.should.be.equal(this.creator)
55+
logs[logIndex].args._tokenId.should.be.bignumber.equal(this.tokenId)
56+
logs[logIndex].args._newNameHash.should.be.equal(tokenData[0])
57+
logs[logIndex].args._newDescriptionHash.should.be.equal(tokenData[1])
58+
logs[logIndex].args._newImageHashes.should.deep.equal(tokenData[2])
59+
logs[logIndex].args._providerId.should.be.equal(providerId)
60+
logs[logIndex].args._providerMetadataId.should.be.equal(providerMetadataId)
5661

5762
}

test/token/CodexTitle.test.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import shouldBehaveLikeERC165 from './behaviors/ERC165.behavior'
12
import shouldBehaveLikeCodexTitle from './behaviors/CodexTitle.behavior'
3+
import shouldBehaveLikeCodexTitleWithFees from './behaviors/CodexTitleFees.behavior'
24

35
const { BigNumber } = web3
46
const CodexTitle = artifacts.require('CodexTitle.sol')
@@ -9,10 +11,28 @@ require('chai')
911
.should()
1012

1113
contract('CodexTitle', function (accounts) {
14+
const metadata = {
15+
hashedMetadata: {
16+
name: web3.sha3('First token'),
17+
description: web3.sha3('This is the first token'),
18+
images: ['asdf'].map((image) => {
19+
return web3.sha3(image)
20+
}),
21+
},
22+
providerId: '1',
23+
providerMetadataId: '10',
24+
}
25+
1226
beforeEach(async function () {
1327
this.token = await CodexTitle.new()
1428
await this.token.initializeOwnable(accounts[0])
1529
})
1630

17-
shouldBehaveLikeCodexTitle(accounts)
31+
shouldBehaveLikeERC165()
32+
33+
// Base behavior, no fees
34+
shouldBehaveLikeCodexTitle(accounts, metadata)
35+
36+
// Extended functionality & base behavior with fees enabled
37+
shouldBehaveLikeCodexTitleWithFees(accounts, metadata)
1838
})

test/token/CodexTitleProxy.test.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import assertRevert from '../helpers/assertRevert'
2-
import shouldBehaveLikeERC165 from './behaviors/ERC165.behavior'
32
import shouldBehaveLikeCodexTitle from './behaviors/CodexTitle.behavior'
3+
import shouldBehaveLikeCodexTitleWithFees from './behaviors/CodexTitleFees.behavior'
44

55
const { BigNumber } = web3
66
const ERC721Token = artifacts.require('ERC721TokenMock.sol')
@@ -214,6 +214,18 @@ contract('CodexTitleProxy', async function (accounts) {
214214
})
215215

216216
describe('proxying CodexTitle', function () {
217+
const metadata = {
218+
hashedMetadata: {
219+
name: web3.sha3('First token'),
220+
description: web3.sha3('This is the first token'),
221+
images: ['asdf'].map((image) => {
222+
return web3.sha3(image)
223+
}),
224+
},
225+
providerId: '1',
226+
providerMetadataId: '10',
227+
}
228+
217229
beforeEach(async function () {
218230
const token = await CodexTitle.new()
219231
this.proxy = await CodexTitleProxy.new(token.address)
@@ -223,8 +235,8 @@ contract('CodexTitleProxy', async function (accounts) {
223235
})
224236

225237
describe('should behave', function () {
226-
shouldBehaveLikeERC165()
227-
shouldBehaveLikeCodexTitle(accounts)
238+
shouldBehaveLikeCodexTitle(accounts, metadata)
239+
shouldBehaveLikeCodexTitleWithFees(accounts, metadata)
228240
})
229241
})
230242
})

0 commit comments

Comments
 (0)