Merge branch 'bootstrap' into minimetoken

This commit is contained in:
Ricardo Guilherme Schmidt 2018-05-19 16:17:29 -03:00
commit cfb26fc1c3
20 changed files with 917 additions and 17 deletions

1
.gitignore vendored
View File

@ -36,6 +36,7 @@ coverage.json
# node
node_modules/
npm-debug.log
package-lock.json
# other
.vs/

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# status.im contracts
Usage:
```
npm install -g embark
git clone https://github.com/status-im/contracts.git
cd contracts
npm install
embark simulator
embark test
embark run
```
| Contract | Deploy | Test | UI |
| -------------------------------------- | ------ | ---- | --- |
| token/TestToken | Yes | Yes | Yes |
| token/ERC20Token | No | Yes | Yes |

View File

@ -0,0 +1,37 @@
.identicon {
border-radius: 50%;
}
.selectedIdenticon {
border-radius: 50%;
overflow: hidden;
float: left;
margin: 7px 0;
}
.accountHexString {
margin-left: 7px;
width: 267px;
overflow: hidden;
text-overflow: ellipsis;
display:inline-block;
}
.accountBalance {
margin-left: 10px;
overflow: hidden;
display: inline-block;
width:77px;
text-align: center;
text-overflow: ellipsis;
}
.accountList {
float: left;
margin-left: 10px;
}
.account {
display: flex;
align-items: center;
}

View File

@ -0,0 +1,112 @@
import web3 from "Embark/web3"
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import { Nav, MenuItem , NavDropdown} from 'react-bootstrap';
import Blockies from 'react-blockies';
import './accountlist.css';
class AccList extends React.Component {
constructor(props) {
super(props);
this.state = {
classNameNavDropdown: props.classNameNavDropdown,
defaultAccount: "0x0000000000000000000000000000000000000000",
addresses: [],
balances: []
}
__embarkContext.execWhenReady(() => {
this.load()
});
}
load() {
web3.eth.getAccounts((err, addresses) => {
if (addresses) {
var defaultAccount = web3.eth.defaultAccount;
if(!defaultAccount){
web3.eth.defaultAccount = addresses[0];
}
var balances = [];
balances.length == addresses.length;
addresses.forEach((address, index) => {
web3.eth.getBalance(address, 'latest', (err, balance) => {
balances[index] = balance;
if(index+1 == balances.length){
this.setState({
balances: balances
});
}
})
})
this.setState({
defaultAccount: defaultAccount,
addresses: addresses
});
} else {
console.log("No addresses available.");
}
})
}
setDefaultAccount(index) {
var defaultAcc = this.state.addresses[index];
if(defaultAcc){
web3.eth.defaultAccount = defaultAcc;
this.setState({defaultAccount: defaultAcc });
} else {
console.log("invalid account")
}
}
render() {
var accsTitle;
var accsList = [];
if (this.state.addresses) {
accsTitle = this.state.defaultAccount;
this.state.addresses.forEach(
(name, index) => {
accsList.push(
<MenuItem key={index} onClick={(e) => this.setDefaultAccount(index) }>
<div className="account">
<div className="accountIdenticon">
<Blockies seed={name} />
</div>
<div className="accountHexString">
{name}
</div>
<div className="accountBalance">
Ξ {this.state.balances[index] / (10**18)}
</div>
</div>
</MenuItem>);
}
)
}
return (
<React.Fragment>
<div className="accounts">
<div className="selectedIdenticon">
<Blockies seed={ this.state.defaultAccount } />
</div>
<div className="accountList">
<Nav>
<NavDropdown key={1} title={accsTitle} id="basic-nav-dropdown" className={ this.state.classNameNavDropdown }>
{accsList}
</NavDropdown>
</Nav>
</div>
</div>
</React.Fragment>
)
}
}
export default AccList;

View File

@ -0,0 +1,131 @@
import EmbarkJS from 'Embark/EmbarkJS';
import ERC20Token from 'Embark/contracts/ERC20Token';
import React from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
class ERC20TokenUI extends React.Component {
constructor(props) {
super(props);
this.state = {
balanceOf: 0,
transferTo: "",
transferAmount: 0,
logs: []
}
}
contractAddress(e){
e.preventDefault();
var tokenAddress = e.target.value;
ERC20Token.options.address = tokenAddress;
}
update_transferTo(e){
this.setState({transferTo: e.target.value});
}
update_transferAmount(e){
this.setState({transferAmount: e.target.value});
}
transfer(e){
var to = this.state.transferTo;
var amount = this.state.transferAmount;
var tx = ERC20Token.methods.transfer(to, amount).send({from: web3.eth.defaultAccount});
this._addToLog(ERC20Token.options.address+".transfer(" + to + ", "+amount+")");
}
approve(e){
var to = this.state.transferTo;
var amount = this.state.transferAmount;
var tx = ERC20Token.methods.approve(to, amount).send({from: web3.eth.defaultAccount});
this._addToLog(ERC20Token.options.address+".approve(" + to + ", "+amount+")");
}
balanceOf(e){
e.preventDefault();
var who = e.target.value;
if (EmbarkJS.isNewWeb3()) {
ERC20Token.methods.balanceOf(who).call()
.then(_value => this.setState({balanceOf: _value}))
} else {
ERC20Token.balanceOf(who)
.then(_value => this.x({balanceOf: _value}));
}
this._addToLog(ERC20Token.options.address+".balanceOf(" + who + ")");
}
_addToLog(txt){
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
render(){
return (<React.Fragment>
<h2> Set token contract address</h2>
<Form inline>
<FormGroup>
<FormControl
type="text"
onChange={(e) => this.contractAddress(e)} />
</FormGroup>
</Form>
<h3> Read account token balance</h3>
<Form inline>
<FormGroup>
<label>
Of:
<FormControl
type="text"
defaultValue={this.state.accountB}
onChange={(e) => this.balanceOf(e)} />
</label>
<label>
<HelpBlock><span className="balanceOf">{this.state.balanceOf}</span></HelpBlock>
</label>
</FormGroup>
</Form>
<h3> Transfer/Approve token balance</h3>
<Form inline>
<FormGroup>
<label>
To:
<FormControl
type="text"
defaultValue={this.state.transferTo}
onChange={(e) => this.update_transferTo(e) } />
</label>
<label>
Amount:
<FormControl
type="text"
defaultValue={this.state.transferAmount}
onChange={(e) => this.update_transferAmount(e) } />
</label>
<Button bsStyle="primary" onClick={(e) => this.transfer(e)}>Transfer</Button>
<Button bsStyle="primary" onClick={(e) => this.approve(e)}>Approve</Button>
</FormGroup>
</Form>
<h3> Contract Calls </h3>
<p>Javascript calls being made: </p>
<div className="logs">
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default ERC20TokenUI;

View File

@ -0,0 +1,88 @@
import EmbarkJS from 'Embark/EmbarkJS';
import TestToken from 'Embark/contracts/TestToken';
import React from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
class TestTokenUI extends React.Component {
constructor(props) {
super(props);
this.state = {
amountToMint: 100,
accountBalance: 0,
accountB: web3.eth.defaultAccount,
balanceOf: 0,
logs: []
}
}
handleMintAmountChange(e){
this.setState({amountToMint: e.target.value});
}
mint(e){
e.preventDefault();
var value = parseInt(this.state.amountToMint, 10);
if (EmbarkJS.isNewWeb3()) {
TestToken.methods.mint(value).send({from: web3.eth.defaultAccount});
} else {
TestToken.mint(value);
this._addToLog("#blockchain", "TestToken.mint(" + value + ")");
}
this._addToLog(TestToken.options.address +".mint("+value+").send({from: " + web3.eth.defaultAccount + "})");
}
getBalance(e){
e.preventDefault();
if (EmbarkJS.isNewWeb3()) {
TestToken.methods.balanceOf(web3.eth.defaultAccount).call()
.then(_value => this.setState({accountBalance: _value}))
} else {
TestToken.balanceOf(web3.eth.defaultAccount)
.then(_value => this.x({valueGet: _value}))
}
this._addToLog(TestToken.options.address + ".balanceOf(" + web3.eth.defaultAccount + ")");
}
_addToLog(txt){
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
}
render(){
return (<React.Fragment>
<h3> 1. Mint Test Token</h3>
<Form inline>
<FormGroup>
<FormControl
type="text"
defaultValue={this.state.amountToMint}
onChange={(e) => this.handleMintAmountChange(e)} />
<Button bsStyle="primary" onClick={(e) => this.mint(e)}>Mint</Button>
</FormGroup>
</Form>
<h3> 2. Read your account token balance </h3>
<Form inline>
<FormGroup>
<HelpBlock>Your test token balance is <span className="accountBalance">{this.state.accountBalance}</span></HelpBlock>
<Button bsStyle="primary" onClick={(e) => this.getBalance(e)}>Get Balance</Button>
</FormGroup>
</Form>
<h3> 3. Contract Calls </h3>
<p>Javascript calls being made: </p>
<div className="logs">
{
this.state.logs.map((item, i) => <p key={i}>{item}</p>)
}
</div>
</React.Fragment>
);
}
}
export default TestTokenUI;

View File

@ -0,0 +1,33 @@
import EmbarkJS from 'Embark/EmbarkJS';
import React from 'react';
import { Navbar, NavItem, Nav, MenuItem , NavDropdown} from 'react-bootstrap';
import AccountList from './accountList';
class TopNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render(){
return (
<React.Fragment>
<Navbar>
<Navbar.Header>
<Navbar.Brand>
<a href="#home">Status.im Demo</a>
</Navbar.Brand>
</Navbar.Header>
<AccountList classNameNavDropdown="pull-right" />
</Navbar>
</React.Fragment>
);
}
}
export default TopNavbar;

63
app/dapp.css Normal file
View File

@ -0,0 +1,63 @@
.navbar {
}
.accounts {
float: right;
margin-right: 17px;
font-family: monospace;
}
.identicon {
border-radius: 50%;
}
.logs {
background-color: black;
font-size: 14px;
color: white;
font-weight: bold;
padding: 10px;
border-radius: 8px;
}
.tab-content {
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 10px;
margin: 0px;
}
.nav-tabs {
margin-bottom: 0;
}
.status-offline {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: red;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
.status-online {
vertical-align: middle;
margin-left: 5px;
margin-top: 4px;
width: 12px;
height: 12px;
background: mediumseagreen;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
input.form-control {
margin: 5px;
}

51
app/dapp.js Normal file
View File

@ -0,0 +1,51 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Tabs, Tab } from 'react-bootstrap';
import EmbarkJS from 'Embark/EmbarkJS';
import TopNavbar from './components/topnavbar';
import TestTokenUI from './components/testtoken';
import ERC20TokenUI from './components/erc20token';
import './dapp.css';
class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount(){
__embarkContext.execWhenReady(() => {
});
}
_renderStatus(title, available) {
let className = available ? 'pull-right status-online' : 'pull-right status-offline';
return <React.Fragment>
{title}
<span className={className}></span>
</React.Fragment>;
}
render(){
return (
<div>
<TopNavbar />
<Tabs defaultActiveKey={1} id="uncontrolled-tab-example">
<Tab eventKey={1} title="TestToken">
<TestTokenUI />
</Tab>
<Tab eventKey={2} title="ERC20Token">
<ERC20TokenUI />
</Tab>
</Tabs>
</div>);
}
}
ReactDOM.render(<App></App>, document.getElementById('app'));

12
app/index.html Normal file
View File

@ -0,0 +1,12 @@
<html>
<head>
<title>Status.im - Contracts</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body class="container">
<div id="app">
</div>
<script src="js/dapp.js"></script>
</body>
</html>

View File

@ -9,12 +9,12 @@
"maxpeers": 0,
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
"rpcCorsDomain": "auto",
"account": {
"password": "config/development/password"
},
"targetGasLimit": 8000000,
"wsOrigins": "http://localhost:8000",
"wsOrigins": "auto",
"wsRPC": true,
"wsHost": "localhost",
"wsPort": 8546,

View File

@ -15,6 +15,9 @@
],
"gas": "auto",
"contracts": {
"ERC20Receiver": {
"deploy": false
}
}
}
}

View File

@ -1,6 +1,8 @@
{
"config": {
"homesteadBlock": 1
"homesteadBlock": 1,
"byzantiumBlock": 1,
"daoForkSupport": true
},
"nonce": "0x0000000000000042",
"difficulty": "0x0",

View File

@ -0,0 +1,90 @@
pragma solidity ^0.4.23;
import "./ERC20Token.sol";
contract ERC20Receiver {
event TokenDeposited(address indexed token, address indexed sender, uint256 amount);
event TokenWithdrawn(address indexed token, address indexed sender, uint256 amount);
mapping (address => mapping(address => uint256)) tokenBalances;
constructor() public {
}
function depositToken(
ERC20Token _token
)
external
{
_depositToken(
msg.sender,
_token,
_token.allowance(
msg.sender,
address(this)
)
);
}
function withdrawToken(
ERC20Token _token,
uint256 _amount
)
external
{
_withdrawToken(msg.sender, _token, _amount);
}
function depositToken(
ERC20Token _token,
uint256 _amount
)
external
{
require(_token.allowance(msg.sender, address(this)) >= _amount);
_depositToken(msg.sender, _token, _amount);
}
function tokenBalanceOf(
ERC20Token _token,
address _from
)
external
view
returns(uint256 fromTokenBalance)
{
return tokenBalances[address(_token)][_from];
}
function _depositToken(
address _from,
ERC20Token _token,
uint256 _amount
)
private
{
require(_amount > 0);
if (_token.transferFrom(_from, address(this), _amount)) {
tokenBalances[address(_token)][_from] += _amount;
emit TokenDeposited(address(_token), _from, _amount);
}
}
function _withdrawToken(
address _from,
ERC20Token _token,
uint256 _amount
)
private
{
require(_amount > 0);
require(tokenBalances[address(_token)][_from] >= _amount);
tokenBalances[address(_token)][_from] -= _amount;
require(_token.transfer(_from, _amount));
emit TokenWithdrawn(address(_token), _from, _amount);
}
}

View File

@ -66,7 +66,7 @@ contract StandardToken is ERC20Token {
function totalSupply()
external
view
returns(uint256 supply)
returns(uint256 currentTotalSupply)
{
return supply;
}

View File

@ -1,10 +1,17 @@
{
"contracts": ["contracts/**"],
"app": {
"js/dapp.js": ["app/dapp.js"],
"index.html": "app/index.html",
"images/": ["app/images/**"]
},
"buildDir": "dist/",
"config": "config/",
"plugins": {
},
"versions": {
"solc": "0.4.23"
"web3": "1.0.0-beta",
"solc": "0.4.23",
"ipfs-api": "17.2.4"
},
"plugins": {
}
}

View File

@ -1,6 +1,7 @@
{
"name": "status-contracts",
"version": "1.0.0",
"version": "0.0.1",
"description": "",
"scripts": {
"solidity-coverage": "./node_modules/.bin/solidity-coverage",
"test": "embark test"
@ -15,13 +16,10 @@
"url": "https://github.com/status-im/contracts/issues"
},
"homepage": "https://github.com/status-im/contracts#readme",
"devDependencies": {
"solidity-coverage": "^0.5.0",
"elliptic": "^6.4.0"
},
"dependencies": {
"embark": "^2.7.0",
"elliptic-curve": "^0.1.0",
"ethereumjs-util": "^5.1.5"
"react": "^16.3.2",
"react-blockies": "^1.3.0",
"react-bootstrap": "^0.32.1",
"react-dom": "^16.3.2"
}
}

80
test/erc20token.js Normal file
View File

@ -0,0 +1,80 @@
describe("ERC20Token", async function() {
this.timeout(0);
var ERC20Token;
var accountsArr;
before(function(done) {
var contractsConfig = {
"TestToken": { },
"ERC20Receiver": { }
};
EmbarkSpec.deployAll(contractsConfig, async function(accounts) {
ERC20Token = TestToken;
accountsArr = accounts;
for(i=0;i<accountsArr.length;i++){
await ERC20Token.methods.mint(7 * 10 ^ 18).send({from: accountsArr[i]})
}
done()
});
});
it("should transfer 1 token", async function() {
let initialBalance0 = await ERC20Token.methods.balanceOf(accountsArr[0]).call();
let initialBalance1 = await ERC20Token.methods.balanceOf(accountsArr[1]).call();
await ERC20Token.methods.transfer(accountsArr[1],1).send({from: accountsArr[0]});
let result0 = await ERC20Token.methods.balanceOf(accountsArr[0]).call();
let result1 = await ERC20Token.methods.balanceOf(accountsArr[1]).call();
assert.equal(result0, +initialBalance0-1);
assert.equal(result1, +initialBalance1+1);
});
it("should set approved amount", async function() {
await ERC20Token.methods.approve(accountsArr[2],10000000).send({from: accountsArr[0]});
let result = await ERC20Token.methods.allowance(accountsArr[0], accountsArr[2]).call();
assert.equal(result, 10000000);
});
it("should consume allowance amount", async function() {
let initialAllowance = await ERC20Token.methods.allowance(accountsArr[0], accountsArr[2]).call();
await ERC20Token.methods.transferFrom(accountsArr[0], accountsArr[0],1).send({from: accountsArr[2]});
let result = await ERC20Token.methods.allowance(accountsArr[0], accountsArr[2]).call();
assert.equal(result, +initialAllowance-1);
});
it("should transfer approved amount", async function() {
let initialBalance0 = await ERC20Token.methods.balanceOf(accountsArr[0]).call();
let initialBalance1 = await ERC20Token.methods.balanceOf(accountsArr[1]).call();
await ERC20Token.methods.transferFrom(accountsArr[0], accountsArr[1],1).send({from: accountsArr[2]});
let result0 = await ERC20Token.methods.balanceOf(accountsArr[0]).call();
let result1 = await ERC20Token.methods.balanceOf(accountsArr[1]).call();
assert.equal(result0, +initialBalance0-1);
assert.equal(result1, +initialBalance1+1);
});
it("should unset approved amount", async function() {
await ERC20Token.methods.approve(accountsArr[2],0).send({from: accountsArr[0]});
let result = await ERC20Token.methods.allowance(accountsArr[0], accountsArr[2]).call();
assert.equal(result, 0);
});
it("should deposit approved amount to contract ERC20Receiver", async function() {
await ERC20Token.methods.approve(ERC20Receiver.address,10).send({from: accountsArr[0]});
await ERC20Receiver.methods.depositToken(ERC20Token.address).send({from: accountsArr[0]});
let result = await ERC20Receiver.methods.tokenBalanceOf(ERC20Token.address, accountsArr[0]).call();
assert.equal(result, 10, "ERC20Receiver.tokenBalanceOf("+ERC20Token.address+","+accountsArr[0]+") wrong");
});
it("should witdraw approved amount from contract ERC20Receiver", async function() {
let tokenBalance = await ERC20Receiver.methods.tokenBalanceOf(ERC20Token.address, accountsArr[0]).call();
await ERC20Receiver.methods.withdrawToken(ERC20Token.address, tokenBalance).send({from: accountsArr[0]});
tokenBalance = await ERC20Receiver.methods.tokenBalanceOf(ERC20Token.address, accountsArr[0]).call();
assert.equal(tokenBalance, 0, "ERC20Receiver.tokenBalanceOf("+ERC20Token.address+","+accountsArr[0]+") wrong");
});
//TODO: include checks for expected events fired
});

33
test/testtoken.js Normal file
View File

@ -0,0 +1,33 @@
describe("TestToken", async function() {
this.timeout(0);
var accountsArr;
before(function(done) {
var contractsConfig = {
"TestToken": {
}
};
EmbarkSpec.deployAll(contractsConfig, async function(accounts) {
accountsArr = accounts
for(i=0;i<accountsArr.length;i++) {
await TestToken.methods.mint(7 * 10 ^ 18).send({from: accountsArr[i]})
}
done()
});
});
it("should increase totalSupply in mint", async function() {
let initialSupply = await TestToken.methods.totalSupply().call();
await TestToken.methods.mint(100).send({from: accountsArr[0]});
let result = await TestToken.methods.totalSupply().call();
assert.equal(result, +initialSupply+100);
});
it("should increase accountBalance in mint", async function() {
let initialBalance = await TestToken.methods.balanceOf(accountsArr[0]).call();
await TestToken.methods.mint(100).send({from: accountsArr[0]});
let result = await TestToken.methods.balanceOf(accountsArr[0]).call();
assert.equal(result, +initialBalance+100);
});
});

View File

@ -85,8 +85,6 @@ var callbackToResolve = function (resolve, reject) {
};
};
exports.promisify = (func) =>
(...args) => {
return new Promise((resolve, reject) => {
@ -95,3 +93,147 @@ exports.promisify = (func) =>
});
}
// This has been tested with the real Ethereum network and Testrpc.
// Copied and edited from: https://gist.github.com/xavierlepretre/d5583222fde52ddfbc58b7cfa0d2d0a9
exports.assertReverts = (contractMethodCall, maxGasAvailable) => {
return new Promise((resolve, reject) => {
try {
resolve(contractMethodCall())
} catch (error) {
reject(error)
}
})
.then(tx => {
assert.equal(tx.receipt.gasUsed, maxGasAvailable, "tx successful, the max gas available was not consumed")
})
.catch(error => {
if ((error + "").indexOf("invalid opcode") < 0 && (error + "").indexOf("out of gas") < 0) {
// Checks if the error is from TestRpc. If it is then ignore it.
// Otherwise relay/throw the error produced by the above assertion.
// Note that no error is thrown when using a real Ethereum network AND the assertion above is true.
throw error
}
})
}
exports.listenForEvent = event => new Promise((resolve, reject) => {
event({}, (error, response) => {
if (!error) {
resolve(response.args)
} else {
reject(error)
}
event.stopWatching()
})
});
exports.eventValues = (receipt, eventName) => {
if(receipt.events[eventName])
return receipt.events[eventName].returnValues;
}
exports.addressToBytes32 = (address) => {
const stringed = "0000000000000000000000000000000000000000000000000000000000000000" + address.slice(2);
return "0x" + stringed.substring(stringed.length - 64, stringed.length);
}
// OpenZeppelin's expectThrow helper -
// Source: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers/expectThrow.js
exports.expectThrow = async promise => {
try {
await promise;
} catch (error) {
// TODO: Check jump destination to destinguish between a throw
// and an actual invalid jump.
const invalidOpcode = error.message.search('invalid opcode') >= 0;
// TODO: When we contract A calls contract B, and B throws, instead
// of an 'invalid jump', we get an 'out of gas' error. How do
// we distinguish this from an actual out of gas event? (The
// testrpc log actually show an 'invalid jump' event.)
const outOfGas = error.message.search('out of gas') >= 0;
const revert = error.message.search('revert') >= 0;
assert(
invalidOpcode || outOfGas || revert,
'Expected throw, got \'' + error + '\' instead',
);
return;
}
assert.fail('Expected throw not received');
};
exports.assertJump = (error) => {
assert(error.message.search('revert') > -1, 'Revert should happen');
}
var callbackToResolve = function (resolve, reject) {
return function (error, value) {
if (error) {
reject(error);
} else {
resolve(value);
}
};
};
exports.promisify = (func) =>
(...args) => {
return new Promise((resolve, reject) => {
const callback = (err, data) => err ? reject(err) : resolve(data);
func.apply(this, [...args, callback]);
});
}
exports.zeroAddress = '0x0000000000000000000000000000000000000000';
exports.zeroBytes32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
exports.timeUnits = {
seconds: 1,
minutes: 60,
hours: 60 * 60,
days: 24 * 60 * 60,
weeks: 7 * 24 * 60 * 60,
years: 365 * 24 * 60 * 60
}
exports.ensureException = function(error) {
assert(isException(error), error.toString());
};
function isException(error) {
let strError = error.toString();
return strError.includes('invalid opcode') || strError.includes('invalid JUMP') || strError.includes('revert');
}
exports.increaseTime = async (amount) => {
return new Promise(function(resolve, reject) {
web3.currentProvider.sendAsync(
{
jsonrpc: '2.0',
method: 'evm_increaseTime',
params: [+amount],
id: new Date().getSeconds()
},
(error) => {
if (error) {
console.log(error);
return reject(err);
}
web3.currentProvider.sendAsync(
{
jsonrpc: '2.0',
method: 'evm_mine',
params: [],
id: new Date().getSeconds()
}, (error) => {
if (error) {
console.log(error);
return reject(err);
}
resolve();
}
)
}
)
});
}