Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests and continuous integration #10

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
build
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: node_js
node_js:
- "9"

services:
- docker

before_install:
- docker run -d -it -p 9090:9090 -e "showQueryString=true" -e "showBody=true" -e "formatJson=true" -e "allowTvmTransferTrc10=1" --name tron trontools/quickstart
- sleep 120

script:
- yarn install
- yarn test
30 changes: 30 additions & 0 deletions contracts/mocks/BaseCappedTokenMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
pragma solidity ^0.4.23;

import "./../tokens/TRC20/TRC20Capped.sol";
import "./../tokens/TRC20/TRC20Detailed.sol";
import "./../ownership/Ownable.sol";

/**
* @title TRC20Detailed token
* @dev The decimals are only for visualization purposes.
* All the operations are done using the smallest and indivisible token unit,
* just as on TRON all the operations are done in sun.
*
* Example inherits from basic TRC20 implementation but can be modified to
* extend from other ITRC20-based tokens:
* https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1536
*/
contract BaseCappedTokenMock is TRC20Detailed, TRC20Capped, Ownable {

string constant _name = "TOKEN_MOCK";
string constant _symbol = "TWMCK";
uint8 constant _decimals = 6;
uint256 constant _totalSupply = 20000000000 * (10 ** uint256(_decimals));

constructor()
TRC20Detailed(_name, _symbol, _decimals)
TRC20Capped(_totalSupply)
public {
_mint(msg.sender, _totalSupply);
}
}
23 changes: 23 additions & 0 deletions contracts/mocks/OwnableMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

pragma solidity ^0.4.23;

import "./../tokens/TRC20/TRC20.sol";
import "./../ownership/Ownable.sol";

/**
* @title TRC20Detailed token
* @dev The decimals are only for visualization purposes.
* All the operations are done using the smallest and indivisible token unit,
* just as on TRON all the operations are done in sun.
*
* Example inherits from basic TRC20 implementation but can be modified to
* extend from other ITRC20-based tokens:
* https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1536
*/
contract OwnableMock is TRC20, Ownable {

constructor()
TRC20()
public {
}
}
72 changes: 72 additions & 0 deletions contracts/ownership/Ownable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
pragma solidity ^0.4.23;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which contract is using Ownable? We should probably only do tests for the existing contracts, then add contract/test pairs from then on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, my idea was to show how to test ownable contracts. No problem.

Copy link
Contributor Author

@dbuarque dbuarque Feb 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @taiyangc, I'm using on BaseCappedTokenMock.sol file, can you take a look, please?


/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}

/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}

/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}

/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}

/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}

/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
46 changes: 46 additions & 0 deletions contracts/ownership/Secondary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
pragma solidity ^0.4.23;

/**
* @title Secondary
* @dev A Secondary contract can only be used by its primary account (the one that created it)
*/
contract Secondary {
address private _primary;

event PrimaryTransferred(
address recipient
);

/**
* @dev Sets the primary account to the one that is creating the Secondary contract.
*/
constructor () internal {
_primary = msg.sender;
emit PrimaryTransferred(_primary);
}

/**
* @dev Reverts if called from any account other than the primary.
*/
modifier onlyPrimary() {
require(msg.sender == _primary);
_;
}

/**
* @return the address of the primary.
*/
function primary() public view returns (address) {
return _primary;
}

/**
* @dev Transfers contract to a new primary.
* @param recipient The address of new primary.
*/
function transferPrimary(address recipient) public onlyPrimary {
require(recipient != address(0));
_primary = recipient;
emit PrimaryTransferred(_primary);
}
}
15 changes: 6 additions & 9 deletions migrations/2_deploy_contracts.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
var TRC20 = artifacts.require("./TRC20.sol");
var TRC20Detailed = artifacts.require("./TRC20Detailed.sol");
var TRC20Mintable = artifacts.require("./TRC20Mintable.sol");
var TRC20Burnable = artifacts.require("./TRC20Burnable.sol");
/* global artifacts */
var OwnableMock = artifacts.require('./OwnableMock.sol')
var BaseCappedTokenMock = artifacts.require('./BaseCappedTokenMock.sol')

module.exports = function (deployer) {
deployer.deploy(TRC20, 10000);
deployer.deploy(TRC20Detailed, "TOKEN", "SYM", 18);
deployer.deploy(TRC20Mintable, 10000);
deployer.deploy(TRC20Burnable, 10000);
};
deployer.deploy(OwnableMock)
deployer.deploy(BaseCappedTokenMock)
}
21 changes: 18 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,24 @@
"test": "test"
},
"scripts": {
"dev": "lite-server",
"test": "echo \"Error: no test specified\" && exit 1",
"migrate": "bash scripts/migrate.sh"
"test": "tronbox test",
"pretest": "yarn migrate",
"migrate": "tronbox migrate --reset"
},
"dependencies": {
"bignumber.js": "^8.0.1",
"chalk": "^2.4.1",
"sleep": "^5.2.3",
"tronbox": "^2.3.9",
"tronweb": "^2.1.25"
},
"devDependencies": {
"chai": "^4.1.2",
"chai-bignumber": "^2.0.2",
"coveralls": "^3.0.1",
"pify": "^4.0.1",
"solidity-coverage": "^0.5.4",
"solium": "^1.1.8"
},
"originalAuthors": "truffle-box",
"authors": [
Expand Down
1 change: 1 addition & 0 deletions test/.git-folder-keeper
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is a placeholder file to ensure the parent directory in the git repository. Feel free to remove.
16 changes: 16 additions & 0 deletions test/helpers/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* global assert */

function isException (error) {
let strError = error.toString()
return strError.includes('VM Exception') || strError.includes('invalid opcode') || strError.includes('invalid JUMP')
}

function ensureException (error) {
assert(isException(error), error.toString())
}

module.exports = {
zeroAddress: '0x0000000000000000000000000000000000000000',
isException: isException,
ensureException: ensureException
}
13 changes: 13 additions & 0 deletions test/helpers/wait.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const sleep = require('sleep')
const chalk = require('chalk')

function log (x) {
process.stdout.write(chalk.yellow(x))
}

module.exports = function (secs) {
secs = secs || 1
// log(`Sleeping for ${secs} second${secs === 1 ? '' : 's'}...`)
sleep.sleep(secs || 1)
// log(' Slept.\n')
}
115 changes: 115 additions & 0 deletions test/ownership/Ownable.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* global artifacts, contract, it, assert, before, beforeEach, describe, tronWeb, done */
/* eslint-disable prefer-reflect */
const wait = require('./../helpers/wait')
const OwnableMock = artifacts.require('OwnableMock.sol')

contract('Ownable', (accounts) => {
let token
before(async () => {
token = await OwnableMock.deployed()
wait(3)
if (accounts.length < 3) {
// Set your own accounts if you are not using Tron Quickstart
}
})

it('should have an owner', async () => {
const owner = await token.owner()
assert.equal(accounts[0], tronWeb.address.fromHex(owner))
})

it('should change owner after transfer', async function () {
return new Promise(async (resolve, reject) => {
try {
const isOwner = await token.isOwner({ from: accounts[3] })
assert.equal(isOwner, false)

const _token = await tronWeb.contract().at(token.address)
wait(3)
const watcher = await _token.OwnershipTransferred().watch(async (err, res) => {
if (err) throw err
if (res) {
assert.equal(res.name, 'OwnershipTransferred')
assert.equal(tronWeb.address.toHex(res.contract), token.address)
assert.equal(res.result.previousOwner, tronWeb.address.toHex(accounts[0]))
assert.equal(res.result.newOwner, tronWeb.address.toHex(accounts[3]))

watcher.stop()
resolve()
}
})

await token.transferOwnership(accounts[3], { from: accounts[0] })
wait(3)
const isNewOwner = await token.isOwner({ from: accounts[3] })
assert.equal(isNewOwner, true)
} catch (e) {
reject(e)
}
})
})

it('should prevent non-owners from transfering', async function () {
try {
await token.transferOwnership(accounts[4], { from: accounts[5], shouldPollResponse: true })
assert(false, "didn't throw should prevent non-owners from transferin")
} catch (error) {
assert.equal(error, 'REVERT opcode executed')
}
})

it('should guard ownership against stuck state', async function () {
try {
await token.transferOwnership(null, { from: accounts[0] })
assert(false, "didn't throw should guard ownership against stuck state")
} catch (error) {
assert.equal(error.reason, 'invalid address')
}
})

it('should lose owner after renouncement', async function () {
return new Promise(async (resolve, reject) => {
try {
const isOwner = await token.isOwner({ from: accounts[9] })
assert.equal(isOwner, false)

const isOwner2 = await token.isOwner({ from: accounts[3] })
assert.equal(isOwner2, true)

const _token = await tronWeb.contract().at(token.address)
wait(3)
const watcher = await _token.OwnershipTransferred().watch(async (err, res) => {
if (err) throw err
if (res) {
assert.equal(res.name, 'OwnershipTransferred')
assert.equal(tronWeb.address.toHex(res.contract), token.address)
assert.equal(tronWeb.address.toHex(res.result.previousOwner), tronWeb.address.toHex(accounts[3]))
assert.equal(res.result.newOwner, '410000000000000000000000000000000000000000')

watcher.stop()
resolve()
}
})

await token.renounceOwnership({ from: accounts[3] })
wait(3)
const isOwner3 = await token.isOwner({ from: accounts[3] })
assert.equal(isOwner3, false)

const owner = await token.owner()
assert.equal(owner, '410000000000000000000000000000000000000000')
} catch (e) {
reject(e)
}
})
})

it('should prevent non-owners from renouncement', async () => {
try {
await token.renounceOwnership({ from: accounts[5], shouldPollResponse: true })
assert(false, "didn't throw should prevent non-owners from renouncement")
} catch (error) {
assert.equal(error, 'REVERT opcode executed')
}
})
})
Loading