Merge branch 'minimetoken' into 172-topic-democracy
This commit is contained in:
commit
ea0ea87ba1
|
@ -36,6 +36,7 @@ coverage.json
|
|||
# node
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
package-lock.json
|
||||
|
||||
# other
|
||||
.vs/
|
||||
|
|
|
@ -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 |
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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'));
|
|
@ -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>
|
|
@ -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,
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
],
|
||||
"gas": "auto",
|
||||
"contracts": {
|
||||
"ERC20Receiver": {
|
||||
"deploy": false
|
||||
},
|
||||
"MiniMeToken": {
|
||||
"deploy": false
|
||||
},
|
||||
"MiniMeTokenFactory": {
|
||||
"deploy": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"homesteadBlock": 1
|
||||
"homesteadBlock": 1,
|
||||
"byzantiumBlock": 1,
|
||||
"daoForkSupport": true
|
||||
},
|
||||
"nonce": "0x0000000000000042",
|
||||
"difficulty": "0x0",
|
||||
|
|
|
@ -10,7 +10,7 @@ contract Controlled {
|
|||
|
||||
address public controller;
|
||||
|
||||
constructor() public {
|
||||
constructor() internal {
|
||||
controller = msg.sender;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
pragma solidity ^0.4.21;
|
||||
|
||||
/**
|
||||
* @notice Uses ethereum signed messages
|
||||
*/
|
||||
contract MessageSigned {
|
||||
|
||||
constructor() internal {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice recovers address who signed the message
|
||||
* @param _signHash operation ethereum signed message hash
|
||||
* @param _messageSignature message `_signHash` signature
|
||||
*/
|
||||
function recoverAddress(
|
||||
bytes32 _signHash,
|
||||
bytes _messageSignature
|
||||
)
|
||||
pure
|
||||
internal
|
||||
returns(address)
|
||||
{
|
||||
uint8 v;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
(v,r,s) = signatureSplit(_messageSignature);
|
||||
return ecrecover(
|
||||
_signHash,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
|
||||
* @param _hash Sign to hash.
|
||||
* @return signHash Hash to be signed.
|
||||
*/
|
||||
function getSignHash(
|
||||
bytes32 _hash
|
||||
)
|
||||
pure
|
||||
internal
|
||||
returns (bytes32 signHash)
|
||||
{
|
||||
signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
|
||||
*/
|
||||
function signatureSplit(bytes _signature)
|
||||
pure
|
||||
internal
|
||||
returns (uint8 v, bytes32 r, bytes32 s)
|
||||
{
|
||||
// The signature format is a compact form of:
|
||||
// {bytes32 r}{bytes32 s}{uint8 v}
|
||||
// Compact means, uint8 is not padded to 32 bytes.
|
||||
assembly {
|
||||
r := mload(add(_signature, 32))
|
||||
s := mload(add(_signature, 64))
|
||||
// Here we are loading the last 32 bytes, including 31 bytes
|
||||
// of 's'. There is no 'mload8' to do this.
|
||||
//
|
||||
// 'byte' is not working due to the Solidity parser, so lets
|
||||
// use the second best option, 'and'
|
||||
v := and(mload(add(_signature, 65)), 0xff)
|
||||
}
|
||||
|
||||
require(v == 27 || v == 28);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@ contract Owned {
|
|||
address public owner;
|
||||
|
||||
/// @notice The Constructor assigns the message sender to be `owner`
|
||||
constructor() public {
|
||||
constructor() internal {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
pragma solidity ^0.4.14;
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
contract ApproveAndCallFallBack {
|
||||
function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public ;
|
||||
interface ApproveAndCallFallBack {
|
||||
function receiveApproval(
|
||||
address from,
|
||||
uint256 _amount,
|
||||
address _token,
|
||||
bytes _data
|
||||
) external;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,48 +1,52 @@
|
|||
pragma solidity ^0.4.17;
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
// Abstract contract for the full ERC 20 Token standard
|
||||
// https://github.com/ethereum/EIPs/issues/20
|
||||
|
||||
contract ERC20Token {
|
||||
/* This is a slight change to the ERC20 base standard.
|
||||
function totalSupply() constant returns (uint256 supply);
|
||||
is replaced with:
|
||||
uint256 public totalSupply;
|
||||
This automatically creates a getter function for the totalSupply.
|
||||
This is moved to the base contract since public getter functions are not
|
||||
currently recognised as an implementation of the matching abstract
|
||||
function by the compiler.
|
||||
*/
|
||||
/// total amount of tokens
|
||||
uint256 public totalSupply;
|
||||
interface ERC20Token {
|
||||
|
||||
/// @notice send `_value` token to `_to` from `msg.sender`
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transfer(address _to, uint256 _value) public returns (bool success);
|
||||
/**
|
||||
* @notice send `_value` token to `_to` from `msg.sender`
|
||||
* @param _to The address of the recipient
|
||||
* @param _value The amount of token to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transfer(address _to, uint256 _value) external returns (bool success);
|
||||
|
||||
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @param _value The amount of tokens to be approved for transfer
|
||||
/// @return Whether the approval was successful or not
|
||||
function approve(address _spender, uint256 _value) public returns (bool success);
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to spend `_value` tokens
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @param _value The amount of tokens to be approved for transfer
|
||||
* @return Whether the approval was successful or not
|
||||
*/
|
||||
function approve(address _spender, uint256 _value) external returns (bool success);
|
||||
|
||||
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
/// @param _from The address of the sender
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
|
||||
/**
|
||||
* @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
* @param _from The address of the sender
|
||||
* @param _to The address of the recipient
|
||||
* @param _value The amount of token to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
|
||||
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @return The balance
|
||||
function balanceOf(address _owner) public view returns (uint256 balance);
|
||||
/**
|
||||
* @param _owner The address from which the balance will be retrieved
|
||||
* @return The balance
|
||||
*/
|
||||
function balanceOf(address _owner) external view returns (uint256 balance);
|
||||
|
||||
/// @param _owner The address of the account owning tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens allowed to spent
|
||||
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
|
||||
/**
|
||||
* @param _owner The address of the account owning tokens
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @return Amount of remaining tokens allowed to spent
|
||||
*/
|
||||
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
|
||||
|
||||
/**
|
||||
* @notice return total supply of tokens
|
||||
*/
|
||||
function totalSupply() external view returns (uint256 supply);
|
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pragma solidity ^0.4.6;
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
/*
|
||||
Copyright 2016, Jordi Baylina
|
||||
|
@ -16,14 +16,15 @@ pragma solidity ^0.4.6;
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/// @title MiniMeToken Contract
|
||||
/// @author Jordi Baylina
|
||||
/// @dev This token contract's goal is to make it easy for anyone to clone this
|
||||
/// token using the token distribution at a given block, this will allow DAO's
|
||||
/// and DApps to upgrade their features in a decentralized manner without
|
||||
/// affecting the original token
|
||||
/// @dev It is ERC20 compliant, but still needs to under go further testing.
|
||||
/**
|
||||
* @title MiniMeToken Contract
|
||||
* @author Jordi Baylina
|
||||
* @dev This token contract's goal is to make it easy for anyone to clone this
|
||||
* token using the token distribution at a given block, this will allow DAO's
|
||||
* and DApps to upgrade their features in a decentralized manner without
|
||||
* affecting the original token
|
||||
* @dev It is ERC20 compliant, but still needs to under go further testing.
|
||||
*/
|
||||
|
||||
import "../common/Controlled.sol";
|
||||
import "./TokenController.sol";
|
||||
|
@ -31,9 +32,11 @@ import "./ApproveAndCallFallBack.sol";
|
|||
import "./MiniMeTokenInterface.sol";
|
||||
import "./MiniMeTokenFactory.sol";
|
||||
|
||||
/// @dev The actual token contract, the default controller is the msg.sender
|
||||
/// that deploys the contract, so usually this token will be deployed by a
|
||||
/// token controller contract, which Giveth will call a "Campaign"
|
||||
/**
|
||||
* @dev The actual token contract, the default controller is the msg.sender
|
||||
* that deploys the contract, so usually this token will be deployed by a
|
||||
* token controller contract, which Giveth will call a "Campaign"
|
||||
*/
|
||||
contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
||||
|
||||
string public name; //The Token's name: e.g. DigixDAO Tokens
|
||||
|
@ -41,10 +44,11 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
string public symbol; //An identifier: e.g. REP
|
||||
string public version = "MMT_0.1"; //An arbitrary versioning scheme
|
||||
|
||||
|
||||
/// @dev `Checkpoint` is the structure that attaches a block number to a
|
||||
/// given value, the block number attached is the one that last changed the
|
||||
/// value
|
||||
/**
|
||||
* @dev `Checkpoint` is the structure that attaches a block number to a
|
||||
* given value, the block number attached is the one that last changed the
|
||||
* value
|
||||
*/
|
||||
struct Checkpoint {
|
||||
|
||||
// `fromBlock` is the block number that the value was generated from
|
||||
|
@ -86,20 +90,22 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// Constructor
|
||||
////////////////
|
||||
|
||||
/// @notice Constructor to create a MiniMeToken
|
||||
/// @param _tokenFactory The address of the MiniMeTokenFactory contract that
|
||||
/// will create the Clone token contracts, the token factory needs to be
|
||||
/// deployed first
|
||||
/// @param _parentToken Address of the parent token, set to 0x0 if it is a
|
||||
/// new token
|
||||
/// @param _parentSnapShotBlock Block of the parent token that will
|
||||
/// determine the initial distribution of the clone token, set to 0 if it
|
||||
/// is a new token
|
||||
/// @param _tokenName Name of the new token
|
||||
/// @param _decimalUnits Number of decimals of the new token
|
||||
/// @param _tokenSymbol Token Symbol for the new token
|
||||
/// @param _transfersEnabled If true, tokens will be able to be transferred
|
||||
function MiniMeToken(
|
||||
/**
|
||||
* @notice Constructor to create a MiniMeToken
|
||||
* @param _tokenFactory The address of the MiniMeTokenFactory contract that
|
||||
* will create the Clone token contracts, the token factory needs to be
|
||||
* deployed first
|
||||
* @param _parentToken Address of the parent token, set to 0x0 if it is a
|
||||
* new token
|
||||
* @param _parentSnapShotBlock Block of the parent token that will
|
||||
* determine the initial distribution of the clone token, set to 0 if it
|
||||
* is a new token
|
||||
* @param _tokenName Name of the new token
|
||||
* @param _decimalUnits Number of decimals of the new token
|
||||
* @param _tokenSymbol Token Symbol for the new token
|
||||
* @param _transfersEnabled If true, tokens will be able to be transferred
|
||||
*/
|
||||
constructor(
|
||||
address _tokenFactory,
|
||||
address _parentToken,
|
||||
uint _parentSnapShotBlock,
|
||||
|
@ -110,6 +116,7 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
)
|
||||
public
|
||||
{
|
||||
require(_tokenFactory != address(0)); //if not set, clone feature will not work properly
|
||||
tokenFactory = MiniMeTokenFactory(_tokenFactory);
|
||||
name = _tokenName; // Set the name
|
||||
decimals = _decimalUnits; // Set the decimals
|
||||
|
@ -125,21 +132,25 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// ERC20 Methods
|
||||
///////////////////
|
||||
|
||||
/// @notice Send `_amount` tokens to `_to` from `msg.sender`
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _amount The amount of tokens to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
/**
|
||||
* @notice Send `_amount` tokens to `_to` from `msg.sender`
|
||||
* @param _to The address of the recipient
|
||||
* @param _amount The amount of tokens to be transferred
|
||||
* @return Whether the transfer was successful or not
|
||||
*/
|
||||
function transfer(address _to, uint256 _amount) public returns (bool success) {
|
||||
require(transfersEnabled);
|
||||
return doTransfer(msg.sender, _to, _amount);
|
||||
}
|
||||
|
||||
/// @notice Send `_amount` tokens to `_to` from `_from` on the condition it
|
||||
/// is approved by `_from`
|
||||
/// @param _from The address holding the tokens being transferred
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _amount The amount of tokens to be transferred
|
||||
/// @return True if the transfer was successful
|
||||
/**
|
||||
* @notice Send `_amount` tokens to `_to` from `_from` on the condition it
|
||||
* is approved by `_from`
|
||||
* @param _from The address holding the tokens being transferred
|
||||
* @param _to The address of the recipient
|
||||
* @param _amount The amount of tokens to be transferred
|
||||
* @return True if the transfer was successful
|
||||
*/
|
||||
function transferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
|
@ -165,12 +176,14 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
return doTransfer(_from, _to, _amount);
|
||||
}
|
||||
|
||||
/// @dev This is the actual transfer function in the token contract, it can
|
||||
/// only be called by other functions in this contract.
|
||||
/// @param _from The address holding the tokens being transferred
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _amount The amount of tokens to be transferred
|
||||
/// @return True if the transfer was successful
|
||||
/**
|
||||
* @dev This is the actual transfer function in the token contract, it can
|
||||
* only be called by other functions in this contract.
|
||||
* @param _from The address holding the tokens being transferred
|
||||
* @param _to The address of the recipient
|
||||
* @param _amount The amount of tokens to be transferred
|
||||
* @return True if the transfer was successful
|
||||
*/
|
||||
function doTransfer(
|
||||
address _from,
|
||||
address _to,
|
||||
|
@ -180,106 +193,124 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
returns(bool)
|
||||
{
|
||||
|
||||
if (_amount == 0) {
|
||||
return true;
|
||||
}
|
||||
if (_amount == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
require(parentSnapShotBlock < block.number);
|
||||
require(parentSnapShotBlock < block.number);
|
||||
|
||||
// Do not allow transfer to 0x0 or the token contract itself
|
||||
require((_to != 0) && (_to != address(this)));
|
||||
// Do not allow transfer to 0x0 or the token contract itself
|
||||
require((_to != 0) && (_to != address(this)));
|
||||
|
||||
// If the amount being transfered is more than the balance of the
|
||||
// account the transfer returns false
|
||||
var previousBalanceFrom = balanceOfAt(_from, block.number);
|
||||
if (previousBalanceFrom < _amount) {
|
||||
return false;
|
||||
}
|
||||
// If the amount being transfered is more than the balance of the
|
||||
// account the transfer returns false
|
||||
uint256 previousBalanceFrom = balanceOfAt(_from, block.number);
|
||||
if (previousBalanceFrom < _amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Alerts the token controller of the transfer
|
||||
if (isContract(controller)) {
|
||||
require(TokenController(controller).onTransfer(_from, _to, _amount));
|
||||
}
|
||||
// Alerts the token controller of the transfer
|
||||
if (isContract(controller)) {
|
||||
require(TokenController(controller).onTransfer(_from, _to, _amount));
|
||||
}
|
||||
|
||||
// First update the balance array with the new value for the address
|
||||
// sending the tokens
|
||||
updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
|
||||
// First update the balance array with the new value for the address
|
||||
// sending the tokens
|
||||
updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
|
||||
|
||||
// Then update the balance array with the new value for the address
|
||||
// receiving the tokens
|
||||
var previousBalanceTo = balanceOfAt(_to, block.number);
|
||||
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
|
||||
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
|
||||
// Then update the balance array with the new value for the address
|
||||
// receiving the tokens
|
||||
uint256 previousBalanceTo = balanceOfAt(_to, block.number);
|
||||
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
|
||||
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
|
||||
|
||||
// An event to make the transfer easy to find on the blockchain
|
||||
Transfer(_from, _to, _amount);
|
||||
// An event to make the transfer easy to find on the blockchain
|
||||
emit Transfer(_from, _to, _amount);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @param _owner The address that's balance is being requested
|
||||
/// @return The balance of `_owner` at the current block
|
||||
function balanceOf(address _owner) public constant returns (uint256 balance) {
|
||||
return balanceOfAt(_owner, block.number);
|
||||
}
|
||||
|
||||
/// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
|
||||
/// its behalf. This is a modified version of the ERC20 approve function
|
||||
/// to be a little bit safer
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @param _amount The amount of tokens to be approved for transfer
|
||||
/// @return True if the approval was successful
|
||||
function approve(address _spender, uint256 _amount) public returns (bool success) {
|
||||
function doApprove(
|
||||
address _from,
|
||||
address _spender,
|
||||
uint256 _amount
|
||||
)
|
||||
internal
|
||||
returns (bool)
|
||||
{
|
||||
require(transfersEnabled);
|
||||
|
||||
// To change the approve amount you first have to reduce the addresses`
|
||||
// allowance to zero by calling `approve(_spender,0)` if it is not
|
||||
// already 0 to mitigate the race condition described here:
|
||||
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
|
||||
require((_amount == 0) || (allowed[_from][_spender] == 0));
|
||||
|
||||
// Alerts the token controller of the approve function call
|
||||
if (isContract(controller)) {
|
||||
require(TokenController(controller).onApprove(msg.sender, _spender, _amount));
|
||||
require(TokenController(controller).onApprove(_from, _spender, _amount));
|
||||
}
|
||||
|
||||
allowed[msg.sender][_spender] = _amount;
|
||||
Approval(msg.sender, _spender, _amount);
|
||||
allowed[_from][_spender] = _amount;
|
||||
emit Approval(_from, _spender, _amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev This function makes it easy to read the `allowed[]` map
|
||||
/// @param _owner The address of the account that owns the token
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens of _owner that _spender is allowed
|
||||
/// to spend
|
||||
/**
|
||||
* @param _owner The address that's balance is being requested
|
||||
* @return The balance of `_owner` at the current block
|
||||
*/
|
||||
function balanceOf(address _owner) external view returns (uint256 balance) {
|
||||
return balanceOfAt(_owner, block.number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
|
||||
* its behalf. This is a modified version of the ERC20 approve function
|
||||
* to be a little bit safer
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @param _amount The amount of tokens to be approved for transfer
|
||||
* @return True if the approval was successful
|
||||
*/
|
||||
function approve(address _spender, uint256 _amount) external returns (bool success) {
|
||||
doApprove(msg.sender, _spender, _amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev This function makes it easy to read the `allowed[]` map
|
||||
* @param _owner The address of the account that owns the token
|
||||
* @param _spender The address of the account able to transfer the tokens
|
||||
* @return Amount of remaining tokens of _owner that _spender is allowed
|
||||
* to spend
|
||||
*/
|
||||
function allowance(
|
||||
address _owner,
|
||||
address _spender
|
||||
)
|
||||
public
|
||||
constant
|
||||
external
|
||||
view
|
||||
returns (uint256 remaining)
|
||||
{
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
|
||||
/// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
|
||||
/// its behalf, and then a function is triggered in the contract that is
|
||||
/// being approved, `_spender`. This allows users to use their tokens to
|
||||
/// interact with contracts in one function call instead of two
|
||||
/// @param _spender The address of the contract able to transfer the tokens
|
||||
/// @param _amount The amount of tokens to be approved for transfer
|
||||
/// @return True if the function call was successful
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to send `_amount` tokens on
|
||||
* its behalf, and then a function is triggered in the contract that is
|
||||
* being approved, `_spender`. This allows users to use their tokens to
|
||||
* interact with contracts in one function call instead of two
|
||||
* @param _spender The address of the contract able to transfer the tokens
|
||||
* @param _amount The amount of tokens to be approved for transfer
|
||||
* @return True if the function call was successful
|
||||
*/
|
||||
function approveAndCall(
|
||||
address _spender,
|
||||
uint256 _amount,
|
||||
bytes _extraData
|
||||
)
|
||||
public
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
require(approve(_spender, _amount));
|
||||
require(doApprove(msg.sender, _spender, _amount));
|
||||
|
||||
ApproveAndCallFallBack(_spender).receiveApproval(
|
||||
msg.sender,
|
||||
|
@ -291,9 +322,11 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
return true;
|
||||
}
|
||||
|
||||
/// @dev This function makes it easy to get the total number of tokens
|
||||
/// @return The total number of tokens
|
||||
function totalSupply() public constant returns (uint) {
|
||||
/**
|
||||
* @dev This function makes it easy to get the total number of tokens
|
||||
* @return The total number of tokens
|
||||
*/
|
||||
function totalSupply() external view returns (uint) {
|
||||
return totalSupplyAt(block.number);
|
||||
}
|
||||
|
||||
|
@ -302,16 +335,18 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// Query balance and totalSupply in History
|
||||
////////////////
|
||||
|
||||
/// @dev Queries the balance of `_owner` at a specific `_blockNumber`
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @param _blockNumber The block number when the balance is queried
|
||||
/// @return The balance at `_blockNumber`
|
||||
/**
|
||||
* @dev Queries the balance of `_owner` at a specific `_blockNumber`
|
||||
* @param _owner The address from which the balance will be retrieved
|
||||
* @param _blockNumber The block number when the balance is queried
|
||||
* @return The balance at `_blockNumber`
|
||||
*/
|
||||
function balanceOfAt(
|
||||
address _owner,
|
||||
uint _blockNumber
|
||||
)
|
||||
public
|
||||
constant
|
||||
view
|
||||
returns (uint)
|
||||
{
|
||||
|
||||
|
@ -320,8 +355,7 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// requires that the `parentToken.balanceOfAt` be queried at the
|
||||
// genesis block for that token as this contains initial balance of
|
||||
// this token
|
||||
if ((balances[_owner].length == 0)
|
||||
|| (balances[_owner][0].fromBlock > _blockNumber)) {
|
||||
if ((balances[_owner].length == 0) || (balances[_owner][0].fromBlock > _blockNumber)) {
|
||||
if (address(parentToken) != 0) {
|
||||
return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
|
||||
} else {
|
||||
|
@ -335,18 +369,19 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
}
|
||||
}
|
||||
|
||||
/// @notice Total amount of tokens at a specific `_blockNumber`.
|
||||
/// @param _blockNumber The block number when the totalSupply is queried
|
||||
/// @return The total amount of tokens at `_blockNumber`
|
||||
function totalSupplyAt(uint _blockNumber) public constant returns(uint) {
|
||||
/**
|
||||
* @notice Total amount of tokens at a specific `_blockNumber`.
|
||||
* @param _blockNumber The block number when the totalSupply is queried
|
||||
* @return The total amount of tokens at `_blockNumber`
|
||||
*/
|
||||
function totalSupplyAt(uint _blockNumber) public view returns(uint) {
|
||||
|
||||
// These next few lines are used when the totalSupply of the token is
|
||||
// requested before a check point was ever created for this token, it
|
||||
// requires that the `parentToken.totalSupplyAt` be queried at the
|
||||
// genesis block for this token as that contains totalSupply of this
|
||||
// token at this block number.
|
||||
if ((totalSupplyHistory.length == 0)
|
||||
|| (totalSupplyHistory[0].fromBlock > _blockNumber)) {
|
||||
if ((totalSupplyHistory.length == 0) || (totalSupplyHistory[0].fromBlock > _blockNumber)) {
|
||||
if (address(parentToken) != 0) {
|
||||
return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
|
||||
} else {
|
||||
|
@ -363,16 +398,18 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// Clone Token Method
|
||||
////////////////
|
||||
|
||||
/// @notice Creates a new clone token with the initial distribution being
|
||||
/// this token at `_snapshotBlock`
|
||||
/// @param _cloneTokenName Name of the clone token
|
||||
/// @param _cloneDecimalUnits Number of decimals of the smallest unit
|
||||
/// @param _cloneTokenSymbol Symbol of the clone token
|
||||
/// @param _snapshotBlock Block when the distribution of the parent token is
|
||||
/// copied to set the initial distribution of the new clone token;
|
||||
/// if the block is zero than the actual block, the current block is used
|
||||
/// @param _transfersEnabled True if transfers are allowed in the clone
|
||||
/// @return The address of the new MiniMeToken Contract
|
||||
/**
|
||||
* @notice Creates a new clone token with the initial distribution being
|
||||
* this token at `_snapshotBlock`
|
||||
* @param _cloneTokenName Name of the clone token
|
||||
* @param _cloneDecimalUnits Number of decimals of the smallest unit
|
||||
* @param _cloneTokenSymbol Symbol of the clone token
|
||||
* @param _snapshotBlock Block when the distribution of the parent token is
|
||||
* copied to set the initial distribution of the new clone token;
|
||||
* if the block is zero than the actual block, the current block is used
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
* @return The address of the new MiniMeToken Contract
|
||||
*/
|
||||
function createCloneToken(
|
||||
string _cloneTokenName,
|
||||
uint8 _cloneDecimalUnits,
|
||||
|
@ -383,12 +420,13 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
public
|
||||
returns(address)
|
||||
{
|
||||
if (_snapshotBlock == 0) {
|
||||
_snapshotBlock = block.number;
|
||||
uint snapshotBlock = _snapshotBlock;
|
||||
if (snapshotBlock == 0) {
|
||||
snapshotBlock = block.number;
|
||||
}
|
||||
MiniMeToken cloneToken = tokenFactory.createCloneToken(
|
||||
this,
|
||||
_snapshotBlock,
|
||||
snapshotBlock,
|
||||
_cloneTokenName,
|
||||
_cloneDecimalUnits,
|
||||
_cloneTokenSymbol,
|
||||
|
@ -398,7 +436,7 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
cloneToken.changeController(msg.sender);
|
||||
|
||||
// An event to make the token easy to find on the blockchain
|
||||
NewCloneToken(address(cloneToken), _snapshotBlock);
|
||||
emit NewCloneToken(address(cloneToken), snapshotBlock);
|
||||
return address(cloneToken);
|
||||
}
|
||||
|
||||
|
@ -406,10 +444,12 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// Generate and destroy tokens
|
||||
////////////////
|
||||
|
||||
/// @notice Generates `_amount` tokens that are assigned to `_owner`
|
||||
/// @param _owner The address that will be assigned the new tokens
|
||||
/// @param _amount The quantity of tokens generated
|
||||
/// @return True if the tokens are generated correctly
|
||||
/**
|
||||
* @notice Generates `_amount` tokens that are assigned to `_owner`
|
||||
* @param _owner The address that will be assigned the new tokens
|
||||
* @param _amount The quantity of tokens generated
|
||||
* @return True if the tokens are generated correctly
|
||||
*/
|
||||
function generateTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
|
@ -418,21 +458,22 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
onlyController
|
||||
returns (bool)
|
||||
{
|
||||
uint curTotalSupply = totalSupply();
|
||||
uint curTotalSupply = totalSupplyAt(block.number);
|
||||
require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
|
||||
uint previousBalanceTo = balanceOf(_owner);
|
||||
uint previousBalanceTo = balanceOfAt(_owner, block.number);
|
||||
require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
|
||||
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
|
||||
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
|
||||
Transfer(0, _owner, _amount);
|
||||
emit Transfer(0, _owner, _amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// @notice Burns `_amount` tokens from `_owner`
|
||||
/// @param _owner The address that will lose the tokens
|
||||
/// @param _amount The quantity of tokens to burn
|
||||
/// @return True if the tokens are burned correctly
|
||||
/**
|
||||
* @notice Burns `_amount` tokens from `_owner`
|
||||
* @param _owner The address that will lose the tokens
|
||||
* @param _amount The quantity of tokens to burn
|
||||
* @return True if the tokens are burned correctly
|
||||
*/
|
||||
function destroyTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
|
@ -441,13 +482,13 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
onlyController
|
||||
returns (bool)
|
||||
{
|
||||
uint curTotalSupply = totalSupply();
|
||||
uint curTotalSupply = totalSupplyAt(block.number);
|
||||
require(curTotalSupply >= _amount);
|
||||
uint previousBalanceFrom = balanceOf(_owner);
|
||||
uint previousBalanceFrom = balanceOfAt(_owner, block.number);
|
||||
require(previousBalanceFrom >= _amount);
|
||||
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
|
||||
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
|
||||
Transfer(_owner, 0, _amount);
|
||||
emit Transfer(_owner, 0, _amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -455,9 +496,10 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// Enable tokens transfers
|
||||
////////////////
|
||||
|
||||
|
||||
/// @notice Enables token holders to transfer their tokens freely if true
|
||||
/// @param _transfersEnabled True if transfers are allowed in the clone
|
||||
/**
|
||||
* @notice Enables token holders to transfer their tokens freely if true
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
*/
|
||||
function enableTransfers(bool _transfersEnabled) public onlyController {
|
||||
transfersEnabled = _transfersEnabled;
|
||||
}
|
||||
|
@ -466,20 +508,22 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// Internal helper functions to query and set a value in a snapshot array
|
||||
////////////////
|
||||
|
||||
/// @dev `getValueAt` retrieves the number of tokens at a given block number
|
||||
/// @param checkpoints The history of values being queried
|
||||
/// @param _block The block number to retrieve the value at
|
||||
/// @return The number of tokens being queried
|
||||
/**
|
||||
* @dev `getValueAt` retrieves the number of tokens at a given block number
|
||||
* @param checkpoints The history of values being queried
|
||||
* @param _block The block number to retrieve the value at
|
||||
* @return The number of tokens being queried
|
||||
*/
|
||||
function getValueAt(
|
||||
Checkpoint[] storage checkpoints,
|
||||
uint _block
|
||||
)
|
||||
constant
|
||||
view
|
||||
internal
|
||||
returns (uint)
|
||||
{
|
||||
if (checkpoints.length == 0) {
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Shortcut for the actual value
|
||||
|
@ -504,44 +548,54 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
return checkpoints[min].value;
|
||||
}
|
||||
|
||||
/// @dev `updateValueAtNow` used to update the `balances` map and the
|
||||
/// `totalSupplyHistory`
|
||||
/// @param checkpoints The history of data being updated
|
||||
/// @param _value The new number of tokens
|
||||
/**
|
||||
* @dev `updateValueAtNow` used to update the `balances` map and the
|
||||
* `totalSupplyHistory`
|
||||
* @param checkpoints The history of data being updated
|
||||
* @param _value The new number of tokens
|
||||
*/
|
||||
function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal {
|
||||
if ((checkpoints.length == 0)
|
||||
|| (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
|
||||
Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ];
|
||||
newCheckPoint.fromBlock = uint128(block.number);
|
||||
newCheckPoint.value = uint128(_value);
|
||||
} else {
|
||||
Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
|
||||
oldCheckPoint.value = uint128(_value);
|
||||
}
|
||||
if (
|
||||
(checkpoints.length == 0) ||
|
||||
(checkpoints[checkpoints.length - 1].fromBlock < block.number))
|
||||
{
|
||||
Checkpoint storage newCheckPoint = checkpoints[checkpoints.length++];
|
||||
newCheckPoint.fromBlock = uint128(block.number);
|
||||
newCheckPoint.value = uint128(_value);
|
||||
} else {
|
||||
Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
|
||||
oldCheckPoint.value = uint128(_value);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Internal function to determine if an address is a contract
|
||||
/// @param _addr The address being queried
|
||||
/// @return True if `_addr` is a contract
|
||||
function isContract(address _addr) constant internal returns(bool) {
|
||||
/**
|
||||
* @dev Internal function to determine if an address is a contract
|
||||
* @param _addr The address being queried
|
||||
* @return True if `_addr` is a contract
|
||||
*/
|
||||
function isContract(address _addr) internal view returns(bool) {
|
||||
uint size;
|
||||
if (_addr == 0){
|
||||
if (_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
assembly {
|
||||
size := extcodesize(_addr)
|
||||
}
|
||||
return size>0;
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
/// @dev Helper function to return a min betwen the two uints
|
||||
/**
|
||||
* @dev Helper function to return a min betwen the two uints
|
||||
*/
|
||||
function min(uint a, uint b) internal returns (uint) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/// @notice The fallback function: If the contract's controller has not been
|
||||
/// set to 0, then the `proxyPayment` method is called which relays the
|
||||
/// ether and creates tokens as described in the token controller contract
|
||||
/**
|
||||
* @notice The fallback function: If the contract's controller has not been
|
||||
* set to 0, then the `proxyPayment` method is called which relays the
|
||||
* ether and creates tokens as described in the token controller contract
|
||||
*/
|
||||
function () public payable {
|
||||
require(isContract(controller));
|
||||
require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
|
||||
|
@ -551,20 +605,22 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
// Safety Methods
|
||||
//////////
|
||||
|
||||
/// @notice This method can be used by the controller to extract mistakenly
|
||||
/// sent tokens to this contract.
|
||||
/// @param _token The address of the token contract that you want to recover
|
||||
/// set to 0 in case you want to extract ether.
|
||||
/**
|
||||
* @notice This method can be used by the controller to extract mistakenly
|
||||
* sent tokens to this contract.
|
||||
* @param _token The address of the token contract that you want to recover
|
||||
* set to 0 in case you want to extract ether.
|
||||
*/
|
||||
function claimTokens(address _token) public onlyController {
|
||||
if (_token == 0x0) {
|
||||
controller.transfer(this.balance);
|
||||
controller.transfer(address(this).balance);
|
||||
return;
|
||||
}
|
||||
|
||||
MiniMeToken token = MiniMeToken(_token);
|
||||
uint balance = token.balanceOf(this);
|
||||
uint balance = token.balanceOf(address(this));
|
||||
token.transfer(controller, balance);
|
||||
ClaimedTokens(_token, controller, balance);
|
||||
emit ClaimedTokens(_token, controller, balance);
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
@ -572,11 +628,11 @@ contract MiniMeToken is MiniMeTokenInterface, Controlled {
|
|||
////////////////
|
||||
event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
|
||||
event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock);
|
||||
event NewCloneToken(address indexed _cloneToken, uint snapshotBlock);
|
||||
event Approval(
|
||||
address indexed _owner,
|
||||
address indexed _spender,
|
||||
uint256 _amount
|
||||
);
|
||||
);
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
pragma solidity ^0.4.11;
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
import "./MiniMeToken.sol";
|
||||
|
||||
|
@ -6,21 +6,25 @@ import "./MiniMeToken.sol";
|
|||
// MiniMeTokenFactory
|
||||
////////////////
|
||||
|
||||
/// @dev This contract is used to generate clone contracts from a contract.
|
||||
/// In solidity this is the way to create a contract from a contract of the
|
||||
/// same class
|
||||
/**
|
||||
* @dev This contract is used to generate clone contracts from a contract.
|
||||
* In solidity this is the way to create a contract from a contract of the
|
||||
* same class
|
||||
*/
|
||||
contract MiniMeTokenFactory {
|
||||
|
||||
/// @notice Update the DApp by creating a new token with new functionalities
|
||||
/// the msg.sender becomes the controller of this clone token
|
||||
/// @param _parentToken Address of the token being cloned
|
||||
/// @param _snapshotBlock Block of the parent token that will
|
||||
/// determine the initial distribution of the clone token
|
||||
/// @param _tokenName Name of the new token
|
||||
/// @param _decimalUnits Number of decimals of the new token
|
||||
/// @param _tokenSymbol Token Symbol for the new token
|
||||
/// @param _transfersEnabled If true, tokens will be able to be transferred
|
||||
/// @return The address of the new token contract
|
||||
/**
|
||||
* @notice Update the DApp by creating a new token with new functionalities
|
||||
* the msg.sender becomes the controller of this clone token
|
||||
* @param _parentToken Address of the token being cloned
|
||||
* @param _snapshotBlock Block of the parent token that will
|
||||
* determine the initial distribution of the clone token
|
||||
* @param _tokenName Name of the new token
|
||||
* @param _decimalUnits Number of decimals of the new token
|
||||
* @param _tokenSymbol Token Symbol for the new token
|
||||
* @param _transfersEnabled If true, tokens will be able to be transferred
|
||||
* @return The address of the new token contract
|
||||
*/
|
||||
function createCloneToken(
|
||||
address _parentToken,
|
||||
uint _snapshotBlock,
|
||||
|
|
|
@ -1,68 +1,39 @@
|
|||
pragma solidity ^0.4.17;
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
contract MiniMeTokenInterface {
|
||||
import "./ERC20Token.sol";
|
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||
|
||||
/// @dev This function makes it easy to get the total number of tokens
|
||||
/// @return The total number of tokens
|
||||
function totalSupply() public constant returns (uint);
|
||||
contract MiniMeTokenInterface is ERC20Token {
|
||||
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @return The balance
|
||||
function balanceOf(address _owner) public constant returns (uint256 balance);
|
||||
|
||||
/// @notice send `_value` token to `_to` from `msg.sender`
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transfer(address _to, uint256 _value) public returns (bool success);
|
||||
|
||||
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
/// @param _from The address of the sender
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
|
||||
|
||||
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @param _value The amount of tokens to be approved for transfer
|
||||
/// @return Whether the approval was successful or not
|
||||
function approve(address _spender, uint256 _value) public returns (bool success);
|
||||
|
||||
/// @param _owner The address of the account owning tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens allowed to spent
|
||||
function allowance(address _owner, address _spender) public constant returns (uint256 remaining);
|
||||
|
||||
/// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
|
||||
/// its behalf, and then a function is triggered in the contract that is
|
||||
/// being approved, `_spender`. This allows users to use their tokens to
|
||||
/// interact with contracts in one function call instead of two
|
||||
/// @param _spender The address of the contract able to transfer the tokens
|
||||
/// @param _amount The amount of tokens to be approved for transfer
|
||||
/// @return True if the function call was successful
|
||||
/**
|
||||
* @notice `msg.sender` approves `_spender` to send `_amount` tokens on
|
||||
* its behalf, and then a function is triggered in the contract that is
|
||||
* being approved, `_spender`. This allows users to use their tokens to
|
||||
* interact with contracts in one function call instead of two
|
||||
* @param _spender The address of the contract able to transfer the tokens
|
||||
* @param _amount The amount of tokens to be approved for transfer
|
||||
* @return True if the function call was successful
|
||||
*/
|
||||
function approveAndCall(
|
||||
address _spender,
|
||||
uint256 _amount,
|
||||
bytes _extraData
|
||||
)
|
||||
public
|
||||
external
|
||||
returns (bool success);
|
||||
|
||||
|
||||
/// @notice Creates a new clone token with the initial distribution being
|
||||
/// this token at `_snapshotBlock`
|
||||
/// @param _cloneTokenName Name of the clone token
|
||||
/// @param _cloneDecimalUnits Number of decimals of the smallest unit
|
||||
/// @param _cloneTokenSymbol Symbol of the clone token
|
||||
/// @param _snapshotBlock Block when the distribution of the parent token is
|
||||
/// copied to set the initial distribution of the new clone token;
|
||||
/// if the block is zero than the actual block, the current block is used
|
||||
/// @param _transfersEnabled True if transfers are allowed in the clone
|
||||
/// @return The address of the new MiniMeToken Contract
|
||||
/**
|
||||
* @notice Creates a new clone token with the initial distribution being
|
||||
* this token at `_snapshotBlock`
|
||||
* @param _cloneTokenName Name of the clone token
|
||||
* @param _cloneDecimalUnits Number of decimals of the smallest unit
|
||||
* @param _cloneTokenSymbol Symbol of the clone token
|
||||
* @param _snapshotBlock Block when the distribution of the parent token is
|
||||
* copied to set the initial distribution of the new clone token;
|
||||
* if the block is zero than the actual block, the current block is used
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
* @return The address of the new MiniMeToken Contract
|
||||
*/
|
||||
function createCloneToken(
|
||||
string _cloneTokenName,
|
||||
uint8 _cloneDecimalUnits,
|
||||
|
@ -73,10 +44,12 @@ contract MiniMeTokenInterface {
|
|||
public
|
||||
returns(address);
|
||||
|
||||
/// @notice Generates `_amount` tokens that are assigned to `_owner`
|
||||
/// @param _owner The address that will be assigned the new tokens
|
||||
/// @param _amount The quantity of tokens generated
|
||||
/// @return True if the tokens are generated correctly
|
||||
/**
|
||||
* @notice Generates `_amount` tokens that are assigned to `_owner`
|
||||
* @param _owner The address that will be assigned the new tokens
|
||||
* @param _amount The quantity of tokens generated
|
||||
* @return True if the tokens are generated correctly
|
||||
*/
|
||||
function generateTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
|
@ -84,10 +57,12 @@ contract MiniMeTokenInterface {
|
|||
public
|
||||
returns (bool);
|
||||
|
||||
/// @notice Burns `_amount` tokens from `_owner`
|
||||
/// @param _owner The address that will lose the tokens
|
||||
/// @param _amount The quantity of tokens to burn
|
||||
/// @return True if the tokens are burned correctly
|
||||
/**
|
||||
* @notice Burns `_amount` tokens from `_owner`
|
||||
* @param _owner The address that will lose the tokens
|
||||
* @param _amount The quantity of tokens to burn
|
||||
* @return True if the tokens are burned correctly
|
||||
*/
|
||||
function destroyTokens(
|
||||
address _owner,
|
||||
uint _amount
|
||||
|
@ -95,22 +70,26 @@ contract MiniMeTokenInterface {
|
|||
public
|
||||
returns (bool);
|
||||
|
||||
|
||||
/// @notice Enables token holders to transfer their tokens freely if true
|
||||
/// @param _transfersEnabled True if transfers are allowed in the clone
|
||||
/**
|
||||
* @notice Enables token holders to transfer their tokens freely if true
|
||||
* @param _transfersEnabled True if transfers are allowed in the clone
|
||||
*/
|
||||
function enableTransfers(bool _transfersEnabled) public;
|
||||
|
||||
|
||||
/// @notice This method can be used by the controller to extract mistakenly
|
||||
/// sent tokens to this contract.
|
||||
/// @param _token The address of the token contract that you want to recover
|
||||
/// set to 0 in case you want to extract ether.
|
||||
/**
|
||||
* @notice This method can be used by the controller to extract mistakenly
|
||||
* sent tokens to this contract.
|
||||
* @param _token The address of the token contract that you want to recover
|
||||
* set to 0 in case you want to extract ether.
|
||||
*/
|
||||
function claimTokens(address _token) public;
|
||||
|
||||
/// @dev Queries the balance of `_owner` at a specific `_blockNumber`
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @param _blockNumber The block number when the balance is queried
|
||||
/// @return The balance at `_blockNumber`
|
||||
/**
|
||||
* @dev Queries the balance of `_owner` at a specific `_blockNumber`
|
||||
* @param _owner The address from which the balance will be retrieved
|
||||
* @param _blockNumber The block number when the balance is queried
|
||||
* @return The balance at `_blockNumber`
|
||||
*/
|
||||
function balanceOfAt(
|
||||
address _owner,
|
||||
uint _blockNumber
|
||||
|
@ -119,11 +98,11 @@ contract MiniMeTokenInterface {
|
|||
constant
|
||||
returns (uint);
|
||||
|
||||
/// @notice Total amount of tokens at a specific `_blockNumber`.
|
||||
/// @param _blockNumber The block number when the totalSupply is queried
|
||||
/// @return The total amount of tokens at `_blockNumber`
|
||||
function totalSupplyAt(uint _blockNumber) public constant returns(uint);
|
||||
|
||||
|
||||
/**
|
||||
* @notice Total amount of tokens at a specific `_blockNumber`.
|
||||
* @param _blockNumber The block number when the totalSupply is queried
|
||||
* @return The total amount of tokens at `_blockNumber`
|
||||
*/
|
||||
function totalSupplyAt(uint _blockNumber) public view returns(uint);
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ import "./ERC20Token.sol";
|
|||
|
||||
contract StandardToken is ERC20Token {
|
||||
|
||||
uint256 private supply;
|
||||
mapping (address => uint256) balances;
|
||||
mapping (address => mapping (address => uint256)) allowed;
|
||||
|
||||
|
@ -13,14 +14,14 @@ contract StandardToken is ERC20Token {
|
|||
address _to,
|
||||
uint256 _value
|
||||
)
|
||||
public
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
return transfer(msg.sender, _to, _value);
|
||||
}
|
||||
|
||||
function approve(address _spender, uint256 _value)
|
||||
public
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
|
@ -33,7 +34,7 @@ contract StandardToken is ERC20Token {
|
|||
address _to,
|
||||
uint256 _value
|
||||
)
|
||||
public
|
||||
external
|
||||
returns (bool success)
|
||||
{
|
||||
if (balances[_from] >= _value &&
|
||||
|
@ -47,7 +48,7 @@ contract StandardToken is ERC20Token {
|
|||
}
|
||||
|
||||
function allowance(address _owner, address _spender)
|
||||
public
|
||||
external
|
||||
view
|
||||
returns (uint256 remaining)
|
||||
{
|
||||
|
@ -55,13 +56,32 @@ contract StandardToken is ERC20Token {
|
|||
}
|
||||
|
||||
function balanceOf(address _owner)
|
||||
public
|
||||
external
|
||||
view
|
||||
returns (uint256 balance)
|
||||
{
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
function totalSupply()
|
||||
external
|
||||
view
|
||||
returns(uint256 currentTotalSupply)
|
||||
{
|
||||
return supply;
|
||||
}
|
||||
|
||||
function mint(
|
||||
address _to,
|
||||
uint256 _amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
balances[_to] += _amount;
|
||||
supply += _amount;
|
||||
emit Transfer(0x0, _to, _amount);
|
||||
}
|
||||
|
||||
function transfer(
|
||||
address _from,
|
||||
address _to,
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
pragma solidity ^0.4.23;
|
||||
|
||||
import "./StandardToken.sol";
|
||||
|
||||
/**
|
||||
* @notice ERC20Token for test scripts, can be minted by anyone.
|
||||
*/
|
||||
contract TestToken is StandardToken {
|
||||
|
||||
constructor() public { }
|
||||
|
||||
/**
|
||||
* @notice any caller can mint any `_amount`
|
||||
* @param _amount how much to be minted
|
||||
*/
|
||||
function mint(uint256 _amount) public {
|
||||
mint(msg.sender, _amount);
|
||||
}
|
||||
}
|
|
@ -1,26 +1,33 @@
|
|||
pragma solidity ^0.4.14;
|
||||
pragma solidity ^0.4.23;
|
||||
/**
|
||||
* @dev The token controller contract must implement these functions
|
||||
*/
|
||||
interface TokenController {
|
||||
/**
|
||||
* @notice Called when `_owner` sends ether to the MiniMe Token contract
|
||||
* @param _owner The address that sent the ether to create tokens
|
||||
* @return True if the ether is accepted, false if it throws
|
||||
*/
|
||||
function proxyPayment(address _owner) external payable returns(bool);
|
||||
|
||||
/// @dev The token controller contract must implement these functions
|
||||
contract TokenController {
|
||||
/// @notice Called when `_owner` sends ether to the MiniMe Token contract
|
||||
/// @param _owner The address that sent the ether to create tokens
|
||||
/// @return True if the ether is accepted, false if it throws
|
||||
function proxyPayment(address _owner) payable returns(bool);
|
||||
/**
|
||||
* @notice Notifies the controller about a token transfer allowing the
|
||||
* controller to react if desired
|
||||
* @param _from The origin of the transfer
|
||||
* @param _to The destination of the transfer
|
||||
* @param _amount The amount of the transfer
|
||||
* @return False if the controller does not authorize the transfer
|
||||
*/
|
||||
function onTransfer(address _from, address _to, uint _amount) external returns(bool);
|
||||
|
||||
/// @notice Notifies the controller about a token transfer allowing the
|
||||
/// controller to react if desired
|
||||
/// @param _from The origin of the transfer
|
||||
/// @param _to The destination of the transfer
|
||||
/// @param _amount The amount of the transfer
|
||||
/// @return False if the controller does not authorize the transfer
|
||||
function onTransfer(address _from, address _to, uint _amount) returns(bool);
|
||||
|
||||
/// @notice Notifies the controller about an approval allowing the
|
||||
/// controller to react if desired
|
||||
/// @param _owner The address that calls `approve()`
|
||||
/// @param _spender The spender in the `approve()` call
|
||||
/// @param _amount The amount in the `approve()` call
|
||||
/// @return False if the controller does not authorize the approval
|
||||
function onApprove(address _owner, address _spender, uint _amount)
|
||||
/**
|
||||
* @notice Notifies the controller about an approval allowing the
|
||||
* controller to react if desired
|
||||
* @param _owner The address that calls `approve()`
|
||||
* @param _spender The spender in the `approve()` call
|
||||
* @param _amount The amount in the `approve()` call
|
||||
* @return False if the controller does not authorize the approval
|
||||
*/
|
||||
function onApprove(address _owner, address _spender, uint _amount) external
|
||||
returns(bool);
|
||||
}
|
||||
|
|
13
embark.json
13
embark.json
|
@ -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": {
|
||||
}
|
||||
}
|
||||
|
|
14
package.json
14
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
exports.Test = (contractsConfig, afterDeploy) => {
|
||||
|
||||
describe("Controlled", async function() {
|
||||
this.timeout(0);
|
||||
var Controlled;
|
||||
var accountsArr;
|
||||
before(function(done) {
|
||||
EmbarkSpec.deployAll(contractsConfig, async function(accounts) {
|
||||
Controlled = Contract;
|
||||
accountsArr = accounts;
|
||||
await afterDeploy(accounts, Contract);
|
||||
done()
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should start with msg.sender as controller", async function() {
|
||||
var controller = await Controlled.methods.controller().call();
|
||||
assert(controller, accountsArr[0]);
|
||||
});
|
||||
|
||||
it("should allow controller to set new controller", async function() {
|
||||
await Controlled.methods.changeController(accountsArr[1]).send({from: accountsArr[0]});
|
||||
var controller = await Controlled.methods.controller().call();
|
||||
assert(controller, accountsArr[1]);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
|
||||
exports.Test = (contractsConfig, afterDeploy) => {
|
||||
describe("ERC20Token", async function() {
|
||||
this.timeout(0);
|
||||
var ERC20Token;
|
||||
var accountsArr;
|
||||
before(function(done) {
|
||||
contractsConfig["ERC20Receiver"] = {};
|
||||
EmbarkSpec.deployAll(contractsConfig, async function(accounts) {
|
||||
ERC20Token = Contract;
|
||||
accountsArr = accounts;
|
||||
await afterDeploy(accounts, Contract);
|
||||
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, "account 0 balance unexpected");
|
||||
assert.equal(result1, +initialBalance1+1, "account 1 balance unexpected");
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
|
||||
});
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
const utils = require('../utils/testUtils')
|
||||
const ERC20Token = require('./erc20token');
|
||||
const Controlled = require('./controlled');
|
||||
|
||||
describe("MiniMeToken", async function() {
|
||||
this.timeout(0);
|
||||
var accounts;
|
||||
var miniMeTokenClone;
|
||||
const b = [];
|
||||
|
||||
before(function(done) {
|
||||
var contractsConfig = {
|
||||
"MiniMeTokenFactory": {
|
||||
},
|
||||
"MiniMeToken": {
|
||||
"args": [
|
||||
"$MiniMeTokenFactory",
|
||||
utils.zeroAddress,
|
||||
0,
|
||||
"TestMiniMeToken",
|
||||
18,
|
||||
"TST",
|
||||
true
|
||||
]
|
||||
}
|
||||
};
|
||||
EmbarkSpec.deployAll(contractsConfig, async function(accountsArr) {
|
||||
accounts = accountsArr
|
||||
done()
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should generate tokens for address 1', async () => {
|
||||
await MiniMeToken.methods.generateTokens(accounts[1], 10).send();
|
||||
assert.equal(await MiniMeToken.methods.totalSupply().call(), 10);
|
||||
assert.equal(await MiniMeToken.methods.balanceOf(accounts[1]).call(), 10);
|
||||
b[0] = await web3.eth.getBlockNumber();
|
||||
});
|
||||
|
||||
it('should transfer tokens from address 1 to address 3', async () => {
|
||||
await MiniMeToken.methods.transfer(accounts[3], 1).send({from: accounts[1]});
|
||||
assert.equal(await MiniMeToken.methods.totalSupply().call(), 10);
|
||||
assert.equal(await MiniMeToken.methods.balanceOf(accounts[1]).call(), 9);
|
||||
assert.equal(await MiniMeToken.methods.balanceOf(accounts[3]).call(), 1);
|
||||
b[1] = await web3.eth.getBlockNumber();
|
||||
});
|
||||
|
||||
it('should destroy 3 tokens from 1 and 1 from 2', async () => {
|
||||
await MiniMeToken.methods.destroyTokens(accounts[1], 3).send({ from: accounts[0] });
|
||||
assert.equal(await MiniMeToken.methods.totalSupply().call(), 7);
|
||||
assert.equal(await MiniMeToken.methods.balanceOf(accounts[1]).call(), 6);
|
||||
b[2] = await web3.eth.getBlockNumber();
|
||||
});
|
||||
|
||||
|
||||
it('should create the clone token', async () => {
|
||||
const miniMeTokenCloneTx = await MiniMeToken.methods.createCloneToken(
|
||||
'Clone Token 1',
|
||||
18,
|
||||
'MMTc',
|
||||
0,
|
||||
true).send({ from: accounts[0]});
|
||||
let addr = miniMeTokenCloneTx.events.NewCloneToken.raw.topics[1];
|
||||
addr = `0x${addr.slice(26)}`;
|
||||
addr = web3.utils.toChecksumAddress(addr);
|
||||
miniMeTokenClone = new web3.eth.Contract(MiniMeToken._jsonInterface, addr);
|
||||
|
||||
b[3] = await web3.eth.getBlockNumber();
|
||||
|
||||
assert.equal(await miniMeTokenClone.methods.parentToken().call(), MiniMeToken.address);
|
||||
assert.equal(await miniMeTokenClone.methods.parentSnapShotBlock().call(), b[3]);
|
||||
assert.equal(await miniMeTokenClone.methods.totalSupply().call(), 7);
|
||||
assert.equal(await MiniMeToken.methods.balanceOf(accounts[1]).call(), 6);
|
||||
|
||||
assert.equal(await miniMeTokenClone.methods.totalSupplyAt(b[2]).call(), 7);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOfAt(accounts[3], b[2]).call(), 1);
|
||||
});
|
||||
|
||||
it('should move tokens in the clone token from 2 to 3', async () => {
|
||||
|
||||
await miniMeTokenClone.methods.transfer(accounts[2], 4).send({ from: accounts[1], gas: 1000000 });
|
||||
b[4] = await web3.eth.getBlockNumber();
|
||||
|
||||
assert.equal(await MiniMeToken.methods.balanceOfAt(accounts[1], b[3]).call(), 6);
|
||||
assert.equal(await MiniMeToken.methods.balanceOfAt(accounts[2], b[3]).call(), 0);
|
||||
assert.equal(await miniMeTokenClone.methods.totalSupply().call(), 7);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOf(accounts[1]).call(), 2);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOf(accounts[2]).call(), 4);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOfAt(accounts[1], b[3]).call(), 6);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOfAt(accounts[2], b[3]).call(), 0);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOfAt(accounts[1], b[2]).call(), 6);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOfAt(accounts[2], b[2]).call(), 0);
|
||||
assert.equal(await miniMeTokenClone.methods.totalSupplyAt(b[3]).call(), 7);
|
||||
assert.equal(await miniMeTokenClone.methods.totalSupplyAt(b[2]).call(), 7);
|
||||
});
|
||||
|
||||
it('should create tokens in the child token', async () => {
|
||||
await miniMeTokenClone.methods.generateTokens(accounts[1], 10).send({ from: accounts[0], gas: 1000000});
|
||||
assert.equal(await miniMeTokenClone.methods.totalSupply().call(), 17);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOf(accounts[1]).call(), 12);
|
||||
assert.equal(await miniMeTokenClone.methods.balanceOf(accounts[2]).call(), 4);
|
||||
});
|
||||
|
||||
var erc20tokenConfig = {
|
||||
"MiniMeTokenFactory": {
|
||||
},
|
||||
"Contract": {
|
||||
"instanceOf" : "MiniMeToken",
|
||||
"args": [
|
||||
"$MiniMeTokenFactory",
|
||||
utils.zeroAddress,
|
||||
0,
|
||||
"TestMiniMeToken",
|
||||
18,
|
||||
"TST",
|
||||
true
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
ERC20Token.Test(erc20tokenConfig, async function (accounts, MiniMeToken) {
|
||||
for(i=0;i<accounts.length;i++){
|
||||
await MiniMeToken.methods.generateTokens(accounts[i], 7 * 10 ^ 18).send({from: accounts[0]})
|
||||
}
|
||||
});
|
||||
Controlled.Test(erc20tokenConfig, async function (accounts, MiniMeToken) {
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
const ERC20Token = require('./erc20token');
|
||||
|
||||
describe("TestToken", async function() {
|
||||
this.timeout(0);
|
||||
var accountsArr;
|
||||
|
||||
before(function(done) {
|
||||
var contractsConfig = {
|
||||
"TestToken": {
|
||||
}
|
||||
};
|
||||
EmbarkSpec.deployAll(contractsConfig, async function(accounts) {
|
||||
accountsArr = accounts
|
||||
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);
|
||||
});
|
||||
var erc20tokenConfig = {
|
||||
"Contract": { "instanceOf" : "TestToken" }
|
||||
}
|
||||
ERC20Token.Test(erc20tokenConfig, async function (accounts, TestToken) {
|
||||
for(i=0;i<accounts.length;i++){
|
||||
await TestToken.methods.mint(7 * 10 ^ 18).send({from: accounts[i]})
|
||||
}
|
||||
});
|
||||
});
|
|
@ -85,8 +85,98 @@ var callbackToResolve = function (resolve, reject) {
|
|||
};
|
||||
};
|
||||
|
||||
exports.promisify = (func) =>
|
||||
(...args) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = (err, data) => err ? reject(err) : resolve(data);
|
||||
func.apply(this, [...args, callback]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 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) => {
|
||||
|
@ -95,3 +185,55 @@ exports.promisify = (func) =>
|
|||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue