Merge branch '145-identity' into 150-gas-abstraction

This commit is contained in:
Ricardo Guilherme Schmidt 2018-05-14 18:04:28 -03:00
commit a3a6732e97
32 changed files with 1391 additions and 485 deletions

1
.gitignore vendored
View File

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

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;

53
app/dapp.css Normal file
View File

@ -0,0 +1,53 @@
div {
margin: 15px;
}
.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;
}

48
app/dapp.js Normal file
View File

@ -0,0 +1,48 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Tabs, Tab } from 'react-bootstrap';
import EmbarkJS from 'Embark/EmbarkJS';
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><h3>Status.im Contracts</h3>
<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,31 @@
],
"gas": "auto",
"contracts": {
"ERC20Receiver": {
"deploy": false
},
"Factory": {
"deploy": false
},
"Instance": {
"deploy": false
},
"UpdatableInstance": {
"deploy": false
},
"DelayedUpdatableInstance": {
"deploy": false
},
"Identity": {
"deploy": false
},
"IdentityKernel": {
"deploy": false
},
"IdentityFactory": {
"deploy": true
}
}
}
}

View File

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

View File

@ -14,7 +14,7 @@ contract DelayedUpdatableInstance is DelayedUpdatableInstanceStorage, DelegatedC
event UpdateCancelled();
event UpdateConfirmed(address oldKernel, address newKernel);
function DelayedUpdatableInstance(address _kernel) public {
constructor(address _kernel) public {
kernel = _kernel;
}

View File

@ -17,4 +17,6 @@ contract DelayedUpdatableInstanceStorage {
uint256 activation;
}
// protected zone end
constructor() internal { }
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.21;
pragma solidity ^0.4.23;
/**
@ -8,6 +8,10 @@ pragma solidity ^0.4.21;
* Important to avoid overwriting wrong storage pointers is that never define storage to this contract
*/
contract DelegatedCall {
constructor() internal {
}
/**
* @dev delegates the call of this function
*/

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.23;
import "../common/Controlled.sol";
@ -19,7 +19,7 @@ contract Factory is Controlled {
uint256 latestUpdate;
address latestKernel;
function Factory(address _kernel)
constructor(address _kernel)
public
{
_setKernel(_kernel);

View File

@ -10,7 +10,7 @@ import "./DelegatedCall.sol";
*/
contract Instance is InstanceStorage, DelegatedCall {
function Instance(address _kernel) public {
constructor(address _kernel) public {
kernel = _kernel;
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.23;
/**
@ -12,4 +12,5 @@ contract InstanceStorage {
// protected zone start (InstanceStorage vars)
address public kernel;
// protected zone end
constructor() internal { }
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.23;
import "./Instance.sol";
@ -12,7 +12,7 @@ contract UpdatableInstance is Instance {
event InstanceUpdated(address oldKernel, address newKernel);
function UpdatableInstance(address _kernel)
constructor(address _kernel)
Instance(_kernel)
public
{
@ -21,7 +21,7 @@ contract UpdatableInstance is Instance {
function updateUpdatableInstance(address _kernel) external {
require(msg.sender == address(this));
InstanceUpdated(kernel, _kernel);
emit InstanceUpdated(kernel, _kernel);
kernel = _kernel;
}

View File

@ -11,19 +11,21 @@ contract ERC725 {
event KeyRemoved(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType);
event ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
event Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
event ExecutionFailed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
event Approved(uint256 indexed executionId, bool approved);
struct Key {
uint256 purpose; //e.g., MANAGEMENT_KEY = 1, ACTION_KEY = 2, etc.
uint256[] purposes; //e.g., MANAGEMENT_KEY = 1, ACTION_KEY = 2, etc.
uint256 keyType; // e.g. 1 = ECDSA, 2 = RSA, etc.
bytes32 key;
}
function getKey(bytes32 _key, uint256 _purpose) public view returns(uint256 purpose, uint256 keyType, bytes32 key);
function getKeyPurpose(bytes32 _key) public view returns(uint256[] purpose);
function getKeysByPurpose(uint256 _purpose) public view returns(bytes32[] keys);
function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success);
function removeKey(bytes32 _key, uint256 _purpose) public returns (bool success);
function execute(address _to, uint256 _value, bytes _data) public returns (uint256 executionId);
function approve(uint256 _id, bool _approve) public returns (bool success);
function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success);
function removeKey(bytes32 _key, uint256 _purpose) public returns (bool success);
function getKey(bytes32 _key) public view returns(uint256[] purposes, uint256 keyType, bytes32 key);
function getKeyPurpose(bytes32 _key) public view returns(uint256[] purpose);
function getKeysByPurpose(uint256 _purpose) public view returns(bytes32[] keys);
function keyHasPurpose(bytes32 _key, uint256 purpose) public view returns(bool exists);
}

View File

@ -2,22 +2,22 @@ pragma solidity ^0.4.21;
contract ERC735 {
event ClaimRequested(bytes32 indexed claimRequestId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimRemoved(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimChanged(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimRequested(bytes32 indexed claimRequestId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimAdded(bytes32 indexed claimId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimRemoved(bytes32 indexed claimId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
event ClaimChanged(bytes32 indexed claimId, uint256 indexed topic, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
struct Claim {
uint256 claimType;
uint256 topic;
uint256 scheme;
address issuer; // msg.sender
bytes signature; // this.address + claimType + data
bytes signature; // this.address + topic + data
bytes data;
string uri;
}
function getClaim(bytes32 _claimId) public view returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri);
function getClaimIdsByType(uint256 _claimType) public view returns(bytes32[] claimIds);
function addClaim(uint256 _claimType, uint256 _scheme, address _issuer, bytes _signature, bytes _data, string _uri) public returns (bytes32 claimRequestId);
function getClaim(bytes32 _claimId) public view returns(uint256 topic, uint256 scheme, address issuer, bytes signature, bytes data, string uri);
function getClaimIdsByTopic(uint256 _topic) public view returns(bytes32[] claimIds);
function addClaim(uint256 _topic, uint256 _scheme, address _issuer, bytes _signature, bytes _data, string _uri) public returns (bytes32 claimRequestId);
function removeClaim(bytes32 _claimId) public returns (bool success);
}

File diff suppressed because it is too large Load Diff

View File

@ -9,25 +9,49 @@ contract IdentityFactory is Factory {
event IdentityCreated(address instance);
function IdentityFactory(bytes _infohash)
constructor()
public
Factory(new IdentityKernel())
Factory(new IdentityKernel())
{
}
function createIdentity()
external
returns (address)
{
return createIdentity(msg.sender);
{
bytes32[] memory initKeys = new bytes32[](2);
uint256[] memory initPurposes = new uint256[](2);
uint256[] memory initTypes = new uint256[](2);
initKeys[0] = keccak256(msg.sender);
initKeys[1] = initKeys[0];
initPurposes[0] = 0;
initPurposes[1] = 1;
initTypes[0] = 0;
initTypes[1] = 0;
return createIdentity(
initKeys,
initPurposes,
initTypes,
1,
1,
0
);
}
function createIdentity(address _idOwner)
function createIdentity(
bytes32[] _keys,
uint256[] _purposes,
uint256[] _types,
uint256 _managerThreshold,
uint256 _actorThreshold,
address _recoveryContract
)
public
returns (address)
{
IdentityKernel instance = IdentityKernel(new DelayedUpdatableInstance(address(latestKernel)));
instance.initIdentity(_idOwner);
instance.initIdentity(_keys,_purposes,_types,_managerThreshold,_actorThreshold,_recoveryContract);
emit IdentityCreated(address(instance));
return instance;
}

View File

@ -5,7 +5,34 @@ import "./Identity.sol";
contract IdentityKernel is DelayedUpdatableInstanceStorage, Identity {
function initIdentity(address _caller) external {
_constructIdentity(_caller);
constructor()
Identity(
new bytes32[](0),
new uint256[](0),
new uint256[](0),
0,
0,
0
)
public
{
}
function initIdentity(
bytes32[] _keys,
uint256[] _purposes,
uint256[] _types,
uint256 _managerThreshold,
uint256 _actorThreshold,
address _recoveryContract
) external {
_constructIdentity(
_keys,
_purposes,
_types,
_managerThreshold,
_actorThreshold,
_recoveryContract);
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.23;
contract TestContract {
@ -6,7 +6,7 @@ contract TestContract {
event TestFunctionExecuted();
function test() public {
TestFunctionExecuted();
emit TestFunctionExecuted();
}
/*

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.17;
pragma solidity ^0.4.23;
import "../identity/IdentityKernel.sol";
@ -8,6 +8,6 @@ contract UpdatedIdentityKernel is IdentityKernel {
event TestFunctionExecuted(uint256 minApprovalsByManagementKeys);
function test() public {
TestFunctionExecuted(purposeThreshold[MANAGEMENT_KEY]);
emit TestFunctionExecuted(purposeThreshold[MANAGEMENT_KEY]);
}
}

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"
"web3-eth-abi": "^1.0.0-beta.34",
"react": "^16.3.2",
"react-bootstrap": "^0.32.1",
"react-dom": "^16.3.2"
}
}

81
test/erc20token.js Normal file
View File

@ -0,0 +1,81 @@
describe("ERC20Token", async function() {
this.timeout(0);
var ERC20Token;
var accountsArr;
before(function(done) {
this.timeout(0);
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
});

View File

@ -1,11 +1,3 @@
const assert = require('assert');
const Embark = require('embark');
let EmbarkSpec = Embark.initTests();
let web3 = EmbarkSpec.web3;
const identityJson = require('../dist/contracts/Identity.json');
const updatedIdentityKernelJson = require('../dist/contracts/UpdatedIdentityKernel.json');
const TestUtils = require("../utils/testUtils.js")
const idUtils = require("../utils/identityUtils.js")

View File

@ -1,8 +1,4 @@
const assert = require('assert');
const Embark = require('embark');
let EmbarkSpec = Embark.initTests();
let web3 = EmbarkSpec.web3;
const TestUtils = require("../utils/testUtils.js");
const idUtils = require('../utils/identityUtils.js');
@ -11,14 +7,15 @@ describe("Identity", function() {
let accounts;
beforeEach( function(done) {
before( function(done) {
this.timeout(0);
EmbarkSpec = Embark.initTests();
web3 = EmbarkSpec.web3;
EmbarkSpec.deployAll({
"Identity": {},
"Identity": {
args: [
[],[],[],0,0,0
]
},
"TestContract": {}
}, (_accounts) => {
accounts = _accounts;
@ -28,27 +25,31 @@ describe("Identity", function() {
describe("Identity()", () => {
it("initialize with msg.sender as management key", async () => {
var result = await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[0]), idUtils.purposes.MANAGEMENT).call()
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
idUtils.purposes.MANAGEMENT,
Identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY");
result,
true,
Identity.address + ".keyHasPurpose("+web3.utils.soliditySha3(accounts[0])+","+idUtils.purposes.MANAGEMENT+") is not MANAGEMENT_KEY");
});
});
describe("addKey(address _key, uint256 _type)", () => {
it("MANAGEMENT_KEY add a new address as ACTION_KEY", async () => {
assert.equal(
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION).call(),
false);
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS)
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION, idUtils.types.ADDRESS)
).send({from: accounts[0]});
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.ACTION,
Identity.address+".getKeyPurpose("+accounts[1]+") is not ACTION_KEY");
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION).call(),
true);
});
it("should not add key by non manager", async () => {
@ -56,7 +57,7 @@ describe("Identity", function() {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
.send({from: accounts[2]});
assert.fail('should have reverted before');
} catch(error) {
@ -64,9 +65,9 @@ describe("Identity", function() {
}
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
Identity.address+".getKeyPurpose("+accounts[1]+") is not correct");
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT).call(),
false)
});
@ -74,14 +75,14 @@ describe("Identity", function() {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[2]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
try {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
.send({from: accounts[2]});
assert.fail('should have reverted before');
} catch(error) {
@ -89,20 +90,19 @@ describe("Identity", function() {
}
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
Identity.address+".getKeyType("+accounts[1]+") is not correct");
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT).call(),
false);
});
it("fire KeyAdded(address indexed key, uint256 indexed type)", async () => {
xit("fire KeyAdded(address indexed key, uint256 indexed type)", async () => {
let receipt = await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
.send({from: accounts[0]});
const keyAdded = TestUtils.eventValues(receipt, "KeyAdded");
assert(keyAdded.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
assert(keyAdded.key, web3.utils.soliditySha3(accounts[1]), "Key is not correct")
assert(keyAdded.keyType, idUtils.types.ADDRESS, "Type is not correct")
});
});
@ -113,33 +113,33 @@ describe("Identity", function() {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[5]), idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
.send({from: accounts[0]});
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT))
idUtils.encode.removeKey(web3.utils.soliditySha3(accounts[5]), idUtils.purposes.MANAGEMENT))
.send({from: accounts[0]});
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
Identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[5]), idUtils.purposes.MANAGEMENT).call(),
false,
Identity.address+".keyHasPurpose("+web3.utils.soliditySha3(accounts[5])+","+idUtils.purposes.MANAGEMENT+") is not false")
});
it("other key should not remove a key", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
.send({from: accounts[0]});
try {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT))
idUtils.encode.removeKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT))
.send({from: accounts[2]});
assert.fail('should have reverted before');
} catch(error) {
@ -147,29 +147,29 @@ describe("Identity", function() {
}
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.MANAGEMENT,
Identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.MANAGEMENT).call(),
true,
Identity.address+".keyHasPurpose("+accounts[1]+","+idUtils.purposes.MANAGEMENT+") is not true")
});
it("actor key should not remove key", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[2]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
try {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION))
idUtils.encode.removeKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION))
.send({from: accounts[2]});
assert.fail('should have reverted before');
@ -178,86 +178,67 @@ describe("Identity", function() {
}
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.ACTION,
Identity.address+".getKeyType("+accounts[1]+") is not 0")
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION).call(),
true,
Identity.address+".keyHasPurpose("+accounts[1]+","+idUtils.purposes.ACTION+") is not true")
});
it("MANAGEMENT_KEY should not remove itself MANAGEMENT_KEY when there is no other MANAGEMENT_KEY", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.removeKey(accounts[0], idUtils.purposes.MANAGEMENT))
idUtils.encode.removeKey(web3.utils.soliditySha3(accounts[0]), idUtils.purposes.MANAGEMENT))
.send({from: accounts[0]});
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
idUtils.purposes.MANAGEMENT,
Identity.address+".getKeyType("+accounts[0]+") is not 1")
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[0]), idUtils.purposes.MANAGEMENT).call(),
true,
Identity.address+".keyHasPurpose("+web3.utils.soliditySha3(accounts[0])+") is not true")
});
it("fire KeyRemoved(address indexed key, uint256 indexed type)", async () => {
xit("fire KeyRemoved(address indexed key, uint256 indexed type)", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
let receipt = await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION))
idUtils.encode.removeKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION))
.send({from: accounts[0]});
const keyRemoved = TestUtils.eventValues(receipt, "KeyRemoved");
assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct");
assert(keyRemoved.key, web3.utils.soliditySha3(accounts[1]), "Key is not correct");
assert(keyRemoved.keyType, idUtils.types.ADDRESS, "Type is not correct");
});
});
describe("getKeyPurpose(address _key)", () => {
it("should start only with initializer as only key", async () => {
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
idUtils.purposes.MANAGEMENT,
Identity.address+".getKeyPurpose("+accounts[0]+") is not correct")
describe("keyHasPurpose(address _key)", () => {
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
Identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 2 after addKey type 2", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.ACTION,
Identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 3 after addKey type 3", async () => {
assert.equal(
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.CLAIM_SIGNER).call(),
false,
Identity.address+".keyHasPurpose("+accounts[1]+","+idUtils.purposes.CLAIM_SIGNER+") is not false")
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.CLAIM_SIGNER, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.CLAIM_SIGNER, idUtils.types.ADDRESS))
.send({from: accounts[0]});
assert.equal(
await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.CLAIM_SIGNER,
Identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
await Identity.methods.keyHasPurpose(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.CLAIM_SIGNER).call(),
true,
Identity.address+".keyHasPurpose("+accounts[1]+","+idUtils.purposes.CLAIM_SIGNER+") is not true")
});
});
});
/*
describe("getKeysByType(uint256 _type)", () => {
@ -278,7 +259,7 @@ describe("Identity", function() {
describe("execute(address _to, uint256 _value, bytes _data)", () => {
let functionPayload;
it("Identity should receive ether", async() => {
xit("Identity should receive ether", async() => {
const amountToSend = web3.utils.toWei('0.05', "ether");
@ -295,7 +276,7 @@ describe("Identity", function() {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
@ -346,11 +327,11 @@ describe("Identity", function() {
});
it("ACTION_KEY should send ether from contract", async () => {
xit("ACTION_KEY should send ether from contract", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
// Adding funds to contract
@ -374,11 +355,11 @@ describe("Identity", function() {
assert(web3.utils.toBN(a2Balance1).toString(), web3.utils.toBN(a2Balance0).add(web3.utils.toBN(amountToSend)).toString(), accounts[2] + " did not receive ether");
});
it("fire ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
xit("fire ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
let receipt = await Identity.methods.execute(
@ -393,11 +374,11 @@ describe("Identity", function() {
assert(executionRequested.data, functionPayload, "Data is not correct");
});
it("fire Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
xit("fire Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
await Identity.methods.execute(
Identity.address,
0,
idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
idUtils.encode.addKey(web3.utils.soliditySha3(accounts[1]), idUtils.purposes.ACTION, idUtils.types.ADDRESS))
.send({from: accounts[0]});
let receipt = await Identity.methods.execute(
@ -447,7 +428,7 @@ describe("Identity", function() {
});
it("fire Approved(uint256 indexed executionId, bool approved)", async () => {
xit("fire Approved(uint256 indexed executionId, bool approved)", async () => {
});

34
test/testtoken.js Normal file
View File

@ -0,0 +1,34 @@
describe("TestToken", async function() {
this.timeout(0);
var accountsArr;
before(function(done) {
this.timeout(0);
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,105 @@ 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.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');
}