merge bootstrap

This commit is contained in:
Ricardo Guilherme Schmidt 2019-02-14 02:12:41 -02:00
commit 0e01f27410
No known key found for this signature in database
GPG Key ID: BFB3F5C8ED618A94
48 changed files with 1678 additions and 489 deletions

3
.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

9
.eslintrc.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": "airbnb",
"plugins": [
"react"
],
"rules": {
"react/prop-types": 0
}
}

3
.gitignore vendored
View File

@ -6,8 +6,7 @@ __pycache__/
# embark
.embark/
chains.json
config/production/password
config/livenet/password
.password
# egg-related
viper.egg-info/

View File

@ -1,7 +1,9 @@
# status.im contracts
Requires https://github.com/creationix/nvm
Usage:
```
nvm install v8.9.4
nvm use v8.9.4
npm install -g embark
git clone https://github.com/status-im/contracts.git
cd contracts
@ -16,4 +18,4 @@ Usage:
| token/TestToken | Yes | Yes | Yes |
| token/ERC20Token | No | Yes | Yes |
| deploy/Instance | No | No | No |
| deploy/Factory | No | No | No |
| deploy/Factory | No | No | No |

View File

@ -0,0 +1,50 @@
import EmbarkJS from 'Embark/EmbarkJS';
import StatusRoot from 'Embark/contracts/StatusRoot';
import MiniMeToken from 'Embark/contracts/MiniMeToken';
import React from 'react';
import { Form, FormGroup, FormControl, HelpBlock, Button } from 'react-bootstrap';
import ERC20TokenUI from './erc20token';
class TestTokenUI extends React.Component {
constructor(props) {
super(props);
this.state = {
amountToMint: 100,
}
}
handleMintAmountChange(e){
this.setState({amountToMint: e.target.value});
}
async mint(e){
e.preventDefault();
await EmbarkJS.enableEthereum();
var value = parseInt(this.state.amountToMint, 10);
StatusRoot.methods.mint(value).send({ gas: 1000000 })
console.log(StatusRoot.options.address +".mint("+value+").send({from: " + web3.eth.defaultAccount + "})");
}
render(){
return (<React.Fragment>
<h3> Test Status Network</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>
<ERC20TokenUI address={ MiniMeToken.options.address } />
</React.Fragment>
);
}
}
export default TestTokenUI;

View File

@ -7,21 +7,16 @@ class ERC20TokenUI extends React.Component {
constructor(props) {
super(props);
ERC20Token.options.address = props.address;
this.state = {
balanceOf: 0,
transferTo: "",
transferAmount: 0,
logs: []
accountBalance: 0,
accountB: web3.eth.defaultAccount,
}
}
contractAddress(e){
e.preventDefault();
var tokenAddress = e.target.value;
ERC20Token.options.address = tokenAddress;
}
update_transferTo(e){
this.setState({transferTo: e.target.value});
}
@ -33,99 +28,87 @@ class ERC20TokenUI extends React.Component {
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+")");
this._addToLog(ERC20Token.options.address+".methods.transfer(" + to + ", "+amount+").send({from: " + web3.eth.defaultAccount + "})");
var tx = ERC20Token.methods.transfer(to, amount);
tx.estimateGas().then((r) => {
tx.send({gas: r, from: web3.eth.defaultAccount});
});
}
approve(e){
var to = this.state.transferTo;
var amount = this.state.transferAmount;
this._addToLog(ERC20Token.options.address+".methods.approve(" + to + ", "+amount+").send({from: " + web3.eth.defaultAccount + "})");
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 + ")");
this._addToLog(ERC20Token.options.address+".methods.balanceOf(" + who + ").call()");
ERC20Token.methods.balanceOf(who).call()
.then(_value => this.setState({balanceOf: _value}))
}
getDefaultAccountBalance(){
this._addToLog(ERC20Token.options.address + ".methods.balanceOf(" + web3.eth.defaultAccount + ").call()");
ERC20Token.methods.balanceOf(web3.eth.defaultAccount).call()
.then(_value => this.setState({accountBalance: _value}))
}
_addToLog(txt){
this.state.logs.push(txt);
this.setState({logs: this.state.logs});
console.log(txt);
}
render(){
return (<React.Fragment>
<h2> Set token contract address</h2>
<Form inline>
<FormGroup>
render() {
return (
<React.Fragment>
<h3> Read account token balance</h3>
<Form inline>
<FormGroup>
<label>
Of:
<FormControl
type="text"
onChange={(e) => this.contractAddress(e)} />
</FormGroup>
</Form>
defaultValue={this.state.accountB}
onChange={(e) => this.balanceOf(e)} />
</label>
<label>
<HelpBlock><span className="balanceOf">{this.state.balanceOf}</span></HelpBlock>
</label>
</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>
<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>
</React.Fragment>
);
}
);
}
}
export default ERC20TokenUI;
export default ERC20TokenUI;

View File

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

View File

@ -1,8 +1,18 @@
div {
margin: 15px;
.navbar {
}
.accounts {
float: right;
margin-right: 17px;
font-family: monospace;
}
.identicon {
border-radius: 50%;
}
.logs {
background-color: black;
font-size: 14px;

View File

@ -1,14 +1,11 @@
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 TestStatusNetworkUI from './components/TestStatusNetwork';
import './dapp.css';
class App extends React.Component {
class DApp extends React.Component {
constructor(props) {
super(props);
@ -17,13 +14,11 @@ class App extends React.Component {
}
componentDidMount(){
__embarkContext.execWhenReady(() => {
});
}
_renderStatus(title, available){
_renderStatus(title, available) {
let className = available ? 'pull-right status-online' : 'pull-right status-offline';
return <React.Fragment>
{title}
@ -32,17 +27,16 @@ class App extends React.Component {
}
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>);
return (
<div>
<Tabs defaultActiveKey={1} id="uncontrolled-tab-example">
<Tab eventKey={1} title="TestStatusNetwork">
<TestStatusNetworkUI />
</Tab>
</Tabs>
</div>);
}
}
ReactDOM.render(<App></App>, document.getElementById('app'));
export default DApp;

View File

@ -7,6 +7,6 @@
<body class="container">
<div id="app">
</div>
<script src="js/dapp.js"></script>
<script src="js/index.js" type="text/javascript"></script>
</body>
</html>

9
app/index.js Normal file
View File

@ -0,0 +1,9 @@
import React from 'react';
import { render } from 'react-dom';
import DApp from './dapp';
import './dapp.css';
render(
<DApp />,
document.getElementById('app')
);

73
config/blockchain.js Normal file
View File

@ -0,0 +1,73 @@
module.exports = {
development: {
enabled: true,
networkType: "custom", // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
networkId: "1337", // Network id used when networkType is custom
isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
genesisBlock: "config/development/genesis.json", // Genesis block to initiate on first creation of a development node
datadir: ".embark/development/datadir", // Data directory for the databases and keystore
mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
rpcHost: "localhost", // HTTP-RPC server listening interface (default: "localhost")
rpcPort: 8545, // HTTP-RPC server listening port (default: 8545)
rpcCorsDomain: "auto", // Comma separated list of domains from which to accept cross origin requests (browser enforced)
// When set to "auto", Embark will automatically set the cors to the address of the webserver
proxy: true, // Proxy is used to present meaningful information about transactions
accounts: [
{
nodeAccounts: true,
password: "config/development/.password"
}
],
targetGasLimit: 8000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
wsRPC: true, // Enable the WS-RPC server
wsOrigins: "auto", // Origins from which to accept websockets requests
// When set to "auto", Embark will automatically set the cors to the address of the webserver
wsHost: "localhost", // WS-RPC server listening interface (default: "localhost")
wsPort: 8546, // WS-RPC server listening port (default: 8546)
simulatorBlocktime: 0 // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
},
testnet: {
enabled: true,
networkType: "testnet",
syncMode: "light",
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
accounts: [
{
nodeAccounts: true,
password: "config/testnet/.password"
}
],
},
livenet: {
enabled: false,
networkType: "livenet",
syncMode: "light",
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
accounts: [
{
nodeAccounts: true,
password: "config/livenet/.password"
}
],
},
rinkeby: {
enabled: true,
networkType: "rinkeby",
syncMode: "light",
rpcHost: "localhost",
rpcPort: 8545,
rpcCorsDomain: "http://localhost:8000",
accounts: [
{
nodeAccounts: true,
password: "config/rinkeby/.password"
}
],
}
};

View File

@ -1,56 +0,0 @@
{
"development": {
"enabled": true,
"networkType": "custom",
"genesisBlock": "config/development/genesis.json",
"datadir": ".embark/development/datadir",
"mineWhenNeeded": true,
"nodiscover": true,
"maxpeers": 0,
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "auto",
"account": {
"password": "config/development/password"
},
"targetGasLimit": 8000000,
"wsOrigins": "auto",
"wsRPC": true,
"wsHost": "localhost",
"wsPort": 8546,
"simulatorMnemonic": "example exile argue silk regular smile grass bomb merge arm assist farm",
"simulatorBlocktime": 0
},
"testnet": {
"enabled": true,
"networkType": "testnet",
"light": true,
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
"account": {
"password": "config/testnet/password"
}
},
"livenet": {
"enabled": true,
"networkType": "livenet",
"light": true,
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
"account": {
"password": "config/livenet/password"
}
},
"privatenet": {
"enabled": true,
"networkType": "custom",
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
"datadir": "yourdatadir",
"networkId": "123",
"bootnodes": ""
}
}

12
config/communication.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
default: {
enabled: true,
provider: "whisper", // Communication provider. Currently, Embark only supports whisper
available_providers: ["whisper"], // Array of available providers
connection: {
host: "localhost", // Host of the blockchain node
port: 8546, // Port of the blockchain node
type: "ws" // Type of connection (ws or rpc)
}
}
};

View File

@ -1,12 +0,0 @@
{
"default": {
"enabled": true,
"provider": "whisper",
"available_providers": ["whisper", "orbit"],
"connection": {
"host": "localhost",
"port": 8546,
"type": "ws"
}
}
}

83
config/contracts.js Normal file
View File

@ -0,0 +1,83 @@
module.exports = {
// default applies to all environments
default: {
// Blockchain node to deploy the contracts
deployment: {
host: "localhost", // Host of the blockchain node
port: 8545, // Port of the blockchain node
type: "rpc" // Type of connection (ws or rpc),
},
// order of connections the dapp should connect to
dappConnection: [
"$WEB3", // uses pre existing web3 object if available (e.g in Mist)
"ws://localhost:8546",
"http://localhost:8545"
],
gas: "auto",
strategy: "explicit",
contracts: {
"MiniMeTokenFactory": {
"deploy": true
},
"MiniMeToken": {
"deploy": true,
"args":["$MiniMeTokenFactory", "0x0", "0x0", "Status Test Token", 18, "STT", true],
},
"StatusRoot": {
"instanceOf": "TestStatusNetwork",
"deploy": true,
"args": ["0x0", "$MiniMeToken"],
"onDeploy": [
"await MiniMeToken.methods.changeController(StatusRoot.address).send()",
"await StatusRoot.methods.setOpen(true).send()",
]
}
}
},
development: {
deployment: {
accounts: [
{
privateKey: "b2ab40d549e67ba67f278781fec03b3a90515ad4d0c898a6326dd958de1e46fa",
balance: "5 ether" // You can set the balance of the account in the dev environment
// Balances are in Wei, but you can specify the unit with its name
}
]
}
},
testnet: {
contracts: {
"MiniMeTokenFactory": {
"deploy": false,
"address": "0x6bFa86A71A7DBc68566d5C741F416e3009804279"
},
"MiniMeToken": {
"deploy": false,
"address": "0xc55cF4B03948D7EBc8b9E8BAD92643703811d162"
},
"StatusRoot": {
"instanceOf": "TestStatusNetwork",
"deploy": false,
"address": "0x34358C45FbA99ef9b78cB501584E8cBFa6f85Cef"
}
}
},
rinkeby: {
contracts: {
"MiniMeTokenFactory": {
"deploy": false,
"address": "0x5bA5C786845CaacD45f5952E1135F4bFB8855469"
},
"MiniMeToken": {
"deploy": false,
"address": "0x43d5adC3B49130A575ae6e4b00dFa4BC55C71621"
},
"StatusRoot": {
"instanceOf": "TestStatusNetwork",
"deploy": false,
"address": "0xEdEB948dE35C6ac414359f97329fc0b4be70d3f1"
}
}
}
}

View File

@ -1,32 +0,0 @@
{
"default": {
"versions": {
"web3.js": "1.0.0-beta",
"solc": "0.4.23"
},
"deployment": {
"host": "localhost",
"port": 8545,
"type": "rpc"
},
"dappConnection": [
"$WEB3",
"http://localhost:8545"
],
"gas": "auto",
"contracts": {
"ERC20Receiver": {
"deploy": false
},
"Factory": {
"deploy": false
},
"Instance": {
"deploy": false
},
"UpdatableInstance": {
"deploy": false
}
}
}
}

View File

@ -1 +0,0 @@
dev_password

6
config/namesystem.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
default: {
available_providers: ["ens"],
provider: "ens"
}
};

59
config/storage.js Normal file
View File

@ -0,0 +1,59 @@
module.exports = {
// default applies to all environments
default: {
enabled: true,
ipfs_bin: "ipfs",
available_providers: ["ipfs"],
upload: {
provider: "ipfs",
host: "localhost",
port: 5001
},
dappConnection: [
{
provider:"ipfs",
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
]
// Configuration to start Swarm in the same terminal as `embark run`
/*,account: {
address: "YOUR_ACCOUNT_ADDRESS", // Address of account accessing Swarm
password: "PATH/TO/PASSWORD/FILE" // File containing the password of the account
},
swarmPath: "PATH/TO/SWARM/EXECUTABLE" // Path to swarm executable (default: swarm)*/
},
// default environment, merges with the settings in default
// assumed to be the intended environment by `embark run`
development: {
enabled: true,
upload: {
provider: "ipfs",
host: "localhost",
port: 5001,
getUrl: "http://localhost:8080/ipfs/"
}
},
// merges with the settings in default
// used with "embark run privatenet"
privatenet: {
},
// merges with the settings in default
// used with "embark run testnet"
testnet: {
},
// merges with the settings in default
// used with "embark run livenet"
livenet: {
},
// you can name an environment with specific settings and then specify with
// "embark run custom_name"
//custom_name: {
//}
};

View File

@ -1,20 +0,0 @@
{
"default": {
"versions": {
"ipfs-api": "17.2.4"
},
"enabled": true,
"ipfs_bin": "ipfs",
"provider": "ipfs",
"available_providers": ["ipfs"],
"host": "localhost",
"port": 5001
},
"development": {
"enabled": true,
"provider": "ipfs",
"host": "localhost",
"port": 5001,
"getUrl": "http://localhost:8080/ipfs/"
}
}

View File

@ -1 +0,0 @@
test_password

5
config/webserver.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
enabled: true,
host: "localhost",
port: 8000
};

View File

@ -1,5 +0,0 @@
{
"enabled": true,
"host": "localhost",
"port": 8000
}

View File

@ -1,14 +1,14 @@
pragma solidity ^0.4.23;
pragma solidity >=0.5.0 <0.6.0;
contract Controlled {
/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController {
require(msg.sender == controller);
require(msg.sender == controller, "Unauthorized");
_;
}
address public controller;
address payable public controller;
constructor() internal {
controller = msg.sender;
@ -16,7 +16,7 @@ contract Controlled {
/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(address _newController) public onlyController {
function changeController(address payable _newController) public onlyController {
controller = _newController;
}
}

View File

@ -1,13 +1,11 @@
pragma solidity ^0.4.21;
pragma solidity >=0.5.0 <0.6.0;
/**
* @notice Uses ethereum signed messages
*/
contract MessageSigned {
constructor() internal {
}
constructor() internal {}
/**
* @notice recovers address who signed the message
@ -16,10 +14,10 @@ contract MessageSigned {
*/
function recoverAddress(
bytes32 _signHash,
bytes _messageSignature
bytes memory _messageSignature
)
pure
internal
pure
returns(address)
{
uint8 v;
@ -42,19 +40,19 @@ contract MessageSigned {
function getSignHash(
bytes32 _hash
)
pure
internal
pure
returns (bytes32 signHash)
{
signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
signHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash));
}
/**
* @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
*/
function signatureSplit(bytes _signature)
pure
function signatureSplit(bytes memory _signature)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
{
// The signature format is a compact form of:
@ -71,7 +69,7 @@ contract MessageSigned {
v := and(mload(add(_signature, 65)), 0xff)
}
require(v == 27 || v == 28);
require(v == 27 || v == 28, "Bad signature");
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity >=0.5.0 <0.6.0;
/// @dev `Owned` is a base level contract that assigns an `owner` that can be
/// later changed
@ -7,23 +7,23 @@ contract Owned {
/// @dev `owner` is the only address that can call a function with this
/// modifier
modifier onlyOwner() {
require(msg.sender == owner);
require(msg.sender == owner, "Unauthorized");
_;
}
address public owner;
address payable public owner;
/// @notice The Constructor assigns the message sender to be `owner`
constructor() internal {
owner = msg.sender;
}
address public newOwner;
address payable public newOwner;
/// @notice `owner` can step down and assign some other address to this role
/// @param _newOwner The address of the new owner. 0x0 can be used to create
/// an unowned neutral vault, however that cannot be undone
function changeOwner(address _newOwner) public onlyOwner {
function changeOwner(address payable _newOwner) public onlyOwner {
newOwner = _newOwner;
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity >=0.5.0 <0.6.0;
/**
* Math operations with safety checks

View File

@ -0,0 +1,90 @@
pragma solidity >=0.5.0 <0.6.0;
import "../token/TokenController.sol";
import "../common/Owned.sol";
import "../token/ERC20Token.sol";
import "../token/MiniMeToken.sol";
/**
* @title SNTController
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
* @notice enables economic abstraction for SNT
*/
contract SNTController is TokenController, Owned {
MiniMeToken public snt;
event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount);
event ControllerChanged(address indexed _newController);
/**
* @notice Constructor
* @param _owner Authority address
* @param _snt SNT token
*/
constructor(address payable _owner, MiniMeToken _snt) internal {
if(_owner != address(0)){
owner = _owner;
}
snt = _snt;
}
/**
* @notice The owner of this contract can change the controller of the SNT token
* Please, be sure that the owner is a trusted agent or 0x0 address.
* @param _newController The address of the new controller
*/
function changeController(address payable _newController) public onlyOwner {
snt.changeController(_newController);
emit ControllerChanged(_newController);
}
/**
* @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 onlyOwner {
if (snt.controller() == address(this)) {
snt.claimTokens(_token);
}
if (_token == address(0)) {
address(owner).transfer(address(this).balance);
return;
}
ERC20Token token = ERC20Token(_token);
uint256 balance = token.balanceOf(address(this));
token.transfer(owner, balance);
emit ClaimedTokens(_token, owner, balance);
}
/**
* @notice payment by address coming from controlled token
* @dev In between the offering and the network. Default settings for allowing token transfers.
*/
function proxyPayment(address) external payable returns (bool) {
//Uncomment above line when using parameters
//require(msg.sender == address(snt), "Unauthorized");
return false;
}
/**
* @notice register and authorizes transfer from token
* @dev called by snt when a transfer is made
*/
function onTransfer(address, address, uint256) external returns (bool) {
//Uncomment above line when using parameters
//require(msg.sender == address(snt), "Unauthorized");
return true;
}
/**
* @notice register and authorizes approve from token
* @dev called by snt when an approval is made
*/
function onApprove(address, address, uint256) external returns (bool) {
//Uncomment above line when using parameters
//require(msg.sender == address(snt), "Unauthorized");
return true;
}
}

View File

@ -0,0 +1,23 @@
pragma solidity >=0.5.0 <0.6.0;
import "./SNTController.sol";
/**
* @dev Status Network is implemented here
*/
contract StatusNetwork is SNTController {
/**
* @notice Constructor
* @param _owner Authority address
* @param _snt SNT token
*/
constructor(
address payable _owner,
MiniMeToken _snt
)
public
SNTController(_owner, _snt)
{ }
}

View File

@ -0,0 +1,55 @@
pragma solidity >=0.5.0 <0.6.0;
import "./StatusNetwork.sol";
/**
* @title SNTController
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
* @notice Test net version of SNTController which allow public mint
*/
contract TestStatusNetwork is StatusNetwork {
bool public open = false;
/**
* @notice Constructor
* @param _owner Authority address
* @param _snt SNT token
*/
constructor(address payable _owner, MiniMeToken _snt)
public
StatusNetwork(_owner, _snt)
{ }
function () external {
_generateTokens(msg.sender, 1000 * (10 ** uint(snt.decimals())));
}
function mint(uint256 _amount) external {
_generateTokens(msg.sender, _amount);
}
function generateTokens(address _who, uint _amount) external {
_generateTokens(_who, _amount);
}
function destroyTokens(address _who, uint _amount) external onlyOwner {
snt.destroyTokens(_who, _amount);
}
function setOpen(bool _open) external onlyOwner {
open = _open;
}
function _generateTokens(address _who, uint _amount) private {
require(msg.sender == owner || open, "Test Mint Disabled");
address statusNetwork = snt.controller();
if(statusNetwork == address(this)){
snt.generateTokens(_who, _amount);
} else {
TestStatusNetwork(statusNetwork).generateTokens(_who, _amount);
}
}
}

View File

@ -0,0 +1,5 @@
pragma solidity >=0.5.0 <0.6.0;
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 _amount, address _token, bytes memory _data) public;
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity >=0.5.0 <0.6.0;
import "./ERC20Token.sol";
@ -43,7 +43,7 @@ contract ERC20Receiver {
)
external
{
require(_token.allowance(msg.sender, address(this)) >= _amount);
require(_token.allowance(msg.sender, address(this)) >= _amount, "Bad argument");
_depositToken(msg.sender, _token, _amount);
}
@ -65,7 +65,7 @@ contract ERC20Receiver {
)
private
{
require(_amount > 0);
require(_amount > 0, "Bad argument");
if (_token.transferFrom(_from, address(this), _amount)) {
tokenBalances[address(_token)][_from] += _amount;
emit TokenDeposited(address(_token), _from, _amount);
@ -79,10 +79,10 @@ contract ERC20Receiver {
)
private
{
require(_amount > 0);
require(tokenBalances[address(_token)][_from] >= _amount);
require(_amount > 0, "Bad argument");
require(tokenBalances[address(_token)][_from] >= _amount, "Insufficient funds");
tokenBalances[address(_token)][_from] -= _amount;
require(_token.transfer(_from, _amount));
require(_token.transfer(_from, _amount), "Transfer fail");
emit TokenWithdrawn(address(_token), _from, _amount);
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity >=0.5.0 <0.6.0;
// Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/issues/20

View File

@ -0,0 +1,633 @@
pragma solidity >=0.5.0 <0.6.0;
/*
Copyright 2016, Jordi Baylina
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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.
*/
import "../common/Controlled.sol";
import "./TokenController.sol";
import "./ApproveAndCallFallBack.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"
*/
contract MiniMeToken is Controlled {
string public name; //The Token's name: e.g. DigixDAO Tokens
uint8 public decimals; //Number of decimals of the smallest unit
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
*/
struct Checkpoint {
// `fromBlock` is the block number that the value was generated from
uint128 fromBlock;
// `value` is the amount of tokens at a specific block number
uint128 value;
}
// `parentToken` is the Token address that was cloned to produce this token;
// it will be 0x0 for a token that was not cloned
MiniMeToken public parentToken;
// `parentSnapShotBlock` is the block number from the Parent Token that was
// used to determine the initial distribution of the Clone Token
uint public parentSnapShotBlock;
// `creationBlock` is the block number that the Clone Token was created
uint public creationBlock;
// `balances` is the map that tracks the balance of each address, in this
// contract when the balance changes the block number that the change
// occurred is also included in the map
mapping (address => Checkpoint[]) balances;
// `allowed` tracks any extra transfer rights as in all ERC20 tokens
mapping (address => mapping (address => uint256)) allowed;
// Tracks the history of the `totalSupply` of the token
Checkpoint[] totalSupplyHistory;
// Flag that determines if the token is transferable or not.
bool public transfersEnabled;
// The factory used to create new clone tokens
MiniMeTokenFactory public tokenFactory;
////////////////
// 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
*/
constructor(
address _tokenFactory,
address _parentToken,
uint _parentSnapShotBlock,
string memory _tokenName,
uint8 _decimalUnits,
string memory _tokenSymbol,
bool _transfersEnabled
)
public
{
tokenFactory = MiniMeTokenFactory(_tokenFactory);
name = _tokenName; // Set the name
decimals = _decimalUnits; // Set the decimals
symbol = _tokenSymbol; // Set the symbol
parentToken = MiniMeToken(address(uint160(_parentToken)));
parentSnapShotBlock = _parentSnapShotBlock;
transfersEnabled = _transfersEnabled;
creationBlock = block.number;
}
///////////////////
// 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
*/
function transfer(address _to, uint256 _amount) public returns (bool success) {
require(transfersEnabled, "Transfers disabled");
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
*/
function transferFrom(
address _from,
address _to,
uint256 _amount
)
public
returns (bool success)
{
// The controller of this contract can move tokens around at will,
// this is important to recognize! Confirm that you trust the
// controller of this contract, which in most situations should be
// another open source smart contract or 0x0
if (msg.sender != controller) {
require(transfersEnabled, "Transfers disabled");
// The standard ERC 20 transferFrom functionality
if (allowed[_from][msg.sender] < _amount) {
return false;
}
allowed[_from][msg.sender] -= _amount;
}
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
*/
function doTransfer(
address _from,
address _to,
uint _amount
)
internal
returns(bool)
{
if (_amount == 0) {
return true;
}
require(parentSnapShotBlock < block.number, "Invalid block.number");
// Do not allow transfer to 0x0 or the token contract itself
require((_to != address(0)) && (_to != address(this)), "Invalid _to");
// 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), "Unauthorized transfer");
}
// 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
uint256 previousBalanceTo = balanceOfAt(_to, block.number);
require(previousBalanceTo + _amount >= previousBalanceTo, "Balance overflow"); // Check for overflow
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
// An event to make the transfer easy to find on the blockchain
emit Transfer(_from, _to, _amount);
return true;
}
function doApprove(
address _from,
address _spender,
uint256 _amount
)
internal
returns (bool)
{
require(transfersEnabled, "Transfers disabled");
// 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[_from][_spender] == 0), "Reset allowance first");
// Alerts the token controller of the approve function call
if (isContract(controller)) {
require(TokenController(controller).onApprove(_from, _spender, _amount), "Unauthorized approve");
}
allowed[_from][_spender] = _amount;
emit Approval(_from, _spender, _amount);
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) 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) {
return 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
)
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
*/
function approveAndCall(
address _spender,
uint256 _amount,
bytes memory _extraData
)
public
returns (bool success)
{
require(doApprove(msg.sender, _spender, _amount), "Approve failed");
ApproveAndCallFallBack(_spender).receiveApproval(
msg.sender,
_amount,
address(this),
_extraData
);
return true;
}
/**
* @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);
}
////////////////
// 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`
*/
function balanceOfAt(
address _owner,
uint _blockNumber
)
public
view
returns (uint)
{
// These next few lines are used when the balance of the token is
// requested before a check point was ever created for this token, it
// 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 (address(parentToken) != address(0)) {
return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
} else {
// Has no parent
return 0;
}
// This will return the expected balance during normal situations
} else {
return getValueAt(balances[_owner], _blockNumber);
}
}
/**
* @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 (address(parentToken) != address(0)) {
return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
} else {
return 0;
}
// This will return the expected totalSupply during normal situations
} else {
return getValueAt(totalSupplyHistory, _blockNumber);
}
}
////////////////
// 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
*/
function createCloneToken(
string memory _cloneTokenName,
uint8 _cloneDecimalUnits,
string memory _cloneTokenSymbol,
uint _snapshotBlock,
bool _transfersEnabled
)
public
returns(address)
{
uint snapshotBlock = _snapshotBlock;
if (snapshotBlock == 0) {
snapshotBlock = block.number;
}
MiniMeToken cloneToken = tokenFactory.createCloneToken(
address(this),
snapshotBlock,
_cloneTokenName,
_cloneDecimalUnits,
_cloneTokenSymbol,
_transfersEnabled
);
cloneToken.changeController(msg.sender);
// An event to make the token easy to find on the blockchain
emit NewCloneToken(address(cloneToken), snapshotBlock);
return address(cloneToken);
}
////////////////
// 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
*/
function generateTokens(
address _owner,
uint _amount
)
public
onlyController
returns (bool)
{
uint curTotalSupply = totalSupplyAt(block.number);
require(curTotalSupply + _amount >= curTotalSupply, "Total overflow"); // Check for overflow
uint previousBalanceTo = balanceOfAt(_owner, block.number);
require(previousBalanceTo + _amount >= previousBalanceTo, "Balance overflow"); // Check for overflow
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
emit Transfer(address(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
*/
function destroyTokens(
address _owner,
uint _amount
)
public
onlyController
returns (bool)
{
uint curTotalSupply = totalSupplyAt(block.number);
require(curTotalSupply >= _amount, "No enough supply");
uint previousBalanceFrom = balanceOfAt(_owner, block.number);
require(previousBalanceFrom >= _amount, "No enough balance");
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
emit Transfer(_owner, address(0), _amount);
return true;
}
////////////////
// Enable tokens transfers
////////////////
/**
* @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;
}
////////////////
// 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
*/
function getValueAt(
Checkpoint[] storage checkpoints,
uint _block
)
internal
view
returns (uint)
{
if (checkpoints.length == 0) {
return 0;
}
// Shortcut for the actual value
if (_block >= checkpoints[checkpoints.length-1].fromBlock) {
return checkpoints[checkpoints.length-1].value;
}
if (_block < checkpoints[0].fromBlock) {
return 0;
}
// Binary search of the value in the array
uint min = 0;
uint max = checkpoints.length-1;
while (max > min) {
uint mid = (max + min + 1) / 2;
if (checkpoints[mid].fromBlock<=_block) {
min = mid;
} else {
max = mid-1;
}
}
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
*/
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);
}
}
/**
* @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 == address(0)){
return false;
}
assembly {
size := extcodesize(_addr)
}
return size>0;
}
/**
* @dev Helper function to return a min betwen the two uints
*/
function min(uint a, uint b) internal pure 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
*/
function () external payable {
require(isContract(controller), "Deposit unallowed");
require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender), "Deposit denied");
}
//////////
// 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.
*/
function claimTokens(address _token) public onlyController {
if (_token == address(0)) {
controller.transfer(address(this).balance);
return;
}
MiniMeToken token = MiniMeToken(address(uint160(_token)));
uint balance = token.balanceOf(address(this));
token.transfer(controller, balance);
emit ClaimedTokens(_token, controller, balance);
}
////////////////
// Events
////////////////
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 Approval(
address indexed _owner,
address indexed _spender,
uint256 _amount
);
}

View File

@ -0,0 +1,49 @@
pragma solidity >=0.5.0 <0.6.0;
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
*/
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
*/
function createCloneToken(
address _parentToken,
uint _snapshotBlock,
string memory _tokenName,
uint8 _decimalUnits,
string memory _tokenSymbol,
bool _transfersEnabled
) public returns (MiniMeToken) {
MiniMeToken newToken = new MiniMeToken(
address(this),
_parentToken,
_snapshotBlock,
_tokenName,
_decimalUnits,
_tokenSymbol,
_transfersEnabled
);
newToken.changeController(msg.sender);
return newToken;
}
}

View File

@ -1,10 +1,10 @@
pragma solidity ^0.4.23;
pragma solidity >=0.5.0 <0.6.0;
import "./ERC20Token.sol";
contract StandardToken is ERC20Token {
uint256 private supply;
uint256 public totalSupply;
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
@ -63,14 +63,6 @@ contract StandardToken is ERC20Token {
return balances[_owner];
}
function totalSupply()
external
view
returns(uint256 currentTotalSupply)
{
return supply;
}
function mint(
address _to,
uint256 _amount
@ -78,8 +70,8 @@ contract StandardToken is ERC20Token {
internal
{
balances[_to] += _amount;
supply += _amount;
emit Transfer(0x0, _to, _amount);
totalSupply += _amount;
emit Transfer(address(0x0), _to, _amount);
}
function transfer(

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity >=0.5.0 <0.6.0;
import "./StandardToken.sol";

View File

@ -0,0 +1,33 @@
pragma solidity >=0.5.0 <0.6.0;
/**
* @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);
/**
* @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 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);
}

View File

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

View File

@ -4,7 +4,8 @@
"description": "",
"scripts": {
"solidity-coverage": "./node_modules/.bin/solidity-coverage",
"test": "embark test"
"test": "embark test",
"lint": "eslint"
},
"repository": {
"type": "git",
@ -16,9 +17,17 @@
"url": "https://github.com/status-im/contracts/issues"
},
"homepage": "https://github.com/status-im/contracts#readme",
"dependencies": {
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.2.3"
},
"dependencies": {
"elliptic-curve": "^0.1.0",
"ethereumjs-util": "^5.1.5",
"react": "^16.3.2",
"react-bootstrap": "^0.32.1",
"react-dom": "^16.3.2"
"react-blockies": "^1.4.0",
"react-bootstrap": "0.32.1",
"react-dom": "^16.3.2",
"web3": "^1.0.0-beta.34"
}
}

View File

@ -0,0 +1,31 @@
exports.Test = (Controlled) => {
describe("Controlled", async function() {
this.timeout(0);
var accounts;
before(function(done) {
web3.eth.getAccounts().then(function (res) {
accounts = res;
done();
});
});
it("should start with msg.sender as controller", async function() {
var controller = await Controlled.methods.controller().call();
assert(controller, accounts[0]);
});
it("should allow controller to set new controller", async function() {
await Controlled.methods.changeController(accounts[1]).send({from: accounts[0]});
var controller = await Controlled.methods.controller().call();
assert(controller, accounts[1]);
});
it("should set back to original controller", async function() {
await Controlled.methods.changeController(accounts[0]).send({from: accounts[1]});
var controller = await Controlled.methods.controller().call();
assert(controller, accounts[0]);
});
});
}

View File

@ -0,0 +1,85 @@
const ERC20Receiver = require('Embark/contracts/ERC20Receiver');
exports.config = {
contracts : {
"ERC20Receiver": {
}
}
}
exports.Test = (ERC20Token) => {
describe("ERC20Token", function() {
var accounts;
before(function(done) {
web3.eth.getAccounts().then(function (res) {
accounts = res;
done();
});
});
it("should transfer 1 token", async function() {
let initialBalance0 = await ERC20Token.methods.balanceOf(accounts[0]).call();
let initialBalance1 = await ERC20Token.methods.balanceOf(accounts[1]).call();
await ERC20Token.methods.transfer(accounts[1],1).send({from: accounts[0]});
let result0 = await ERC20Token.methods.balanceOf(accounts[0]).call();
let result1 = await ERC20Token.methods.balanceOf(accounts[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(accounts[2],10000000).send({from: accounts[0]});
let result = await ERC20Token.methods.allowance(accounts[0], accounts[2]).call();
assert.equal(result, 10000000);
});
it("should consume allowance amount", async function() {
let initialAllowance = await ERC20Token.methods.allowance(accounts[0], accounts[2]).call();
await ERC20Token.methods.transferFrom(accounts[0], accounts[0],1).send({from: accounts[2]});
let result = await ERC20Token.methods.allowance(accounts[0], accounts[2]).call();
assert.equal(result, +initialAllowance-1);
});
it("should transfer approved amount", async function() {
let initialBalance0 = await ERC20Token.methods.balanceOf(accounts[0]).call();
let initialBalance1 = await ERC20Token.methods.balanceOf(accounts[1]).call();
await ERC20Token.methods.transferFrom(accounts[0], accounts[1],1).send({from: accounts[2]});
let result0 = await ERC20Token.methods.balanceOf(accounts[0]).call();
let result1 = await ERC20Token.methods.balanceOf(accounts[1]).call();
assert.equal(result0, +initialBalance0-1);
assert.equal(result1, +initialBalance1+1);
});
it("should unset approved amount", async function() {
await ERC20Token.methods.approve(accounts[2],0).send({from: accounts[0]});
let result = await ERC20Token.methods.allowance(accounts[0], accounts[2]).call();
assert.equal(result, 0);
});
it("should deposit approved amount to contract ERC20Receiver", async function() {
//ERC20Receiver = await ERC20Receiver.deploy().send();
//console.log(ERC20Receiver.address);
await ERC20Token.methods.approve(ERC20Receiver.address, 10).send({from: accounts[0]});
await ERC20Receiver.methods.depositToken(ERC20Token.address, 10).send({from: accounts[0]});
let result = await ERC20Receiver.methods.tokenBalanceOf(ERC20Token.address, accounts[0]).call();
assert.equal(result, 10, "ERC20Receiver.tokenBalanceOf("+ERC20Token.address+","+accounts[0]+") wrong");
});
it("should witdraw approved amount from contract ERC20Receiver", async function() {
let tokenBalance = await ERC20Receiver.methods.tokenBalanceOf(ERC20Token.address, accounts[0]).call();
await ERC20Receiver.methods.withdrawToken(ERC20Token.address, tokenBalance).send({from: accounts[0]});
tokenBalance = await ERC20Receiver.methods.tokenBalanceOf(ERC20Token.address, accounts[0]).call();
assert.equal(tokenBalance, 0, "ERC20Receiver.tokenBalanceOf("+ERC20Token.address+","+accounts[0]+") wrong");
});
//TODO: include checks for expected events fired
});
}

View File

@ -1,81 +0,0 @@
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
});

123
test/minimetoken.js Normal file
View File

@ -0,0 +1,123 @@
const utils = require('../utils/testUtils')
const MiniMeToken = require('Embark/contracts/MiniMeToken');
const ERC20TokenSpec = require('./abstract/erc20tokenspec');
const ControlledSpec = require('./abstract/controlled');
config({
contracts: {
"MiniMeTokenFactory": {
},
"MiniMeToken": {
"args": [
"$MiniMeTokenFactory",
utils.zeroAddress,
0,
"TestMiniMeToken",
18,
"TST",
true
]
},
...ERC20TokenSpec.config.contracts
}
});
contract("MiniMeToken", function() {
this.timeout(0);
var accounts;
before(function(done) {
web3.eth.getAccounts().then(function (res) {
accounts = res;
done();
});
});
var miniMeTokenClone;
const b = [];
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.returnValues[0];
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);
});
it("should mint balances for ERC20TokenSpec", async function() {
let initialBalance = 7 * 10 ^ 18;
for(i=0;i<accounts.length;i++){
await MiniMeToken.methods.generateTokens(accounts[i], initialBalance).send({from: accounts[0]})
//assert.equal(await TestToken.methods.balanceOf(accounts[i]).call(), initialBalance);
}
})
ERC20TokenSpec.Test(MiniMeToken);
ControlledSpec.Test(MiniMeToken);
});

53
test/teststatusnetwork.js Normal file
View File

@ -0,0 +1,53 @@
const Utils = require('../utils/testUtils');
const MiniMeToken = require('Embark/contracts/MiniMeToken');
const TestStatusNetwork = require('Embark/contracts/TestStatusNetwork');
const ERC20TokenSpec = require('./abstract/erc20tokenspec');
config({
contracts: {
"MiniMeTokenFactory": {},
"MiniMeToken": {
"args":["$MiniMeTokenFactory", "0x0", "0x0", "Status Test Token", 18, "STT", true],
},
"TestStatusNetwork": {
"deploy": true,
"args": ["0x0", "$MiniMeToken"],
"onDeploy": [
"await MiniMeToken.methods.changeController(TestStatusNetwork.address).send()",
"await TestStatusNetwork.methods.setOpen(true).send()",
]
}
}
});
contract("TestStatusNetwork", function() {
this.timeout(0);
var accounts;
before(function(done) {
web3.eth.getAccounts().then(function (res) {
accounts = res;
done();
});
});
it("should increase totalSupply in mint", async function() {
let initialSupply = await MiniMeToken.methods.totalSupply().call();
await TestStatusNetwork.methods.mint(100).send();
let result = await MiniMeToken.methods.totalSupply().call();
assert.equal(result, +initialSupply+100);
});
it("should increase accountBalance in mint", async function() {
let initialBalance = await MiniMeToken.methods.balanceOf(accounts[0]).call();
await TestStatusNetwork.methods.mint(100).send({from: accounts[0]});
let result = await MiniMeToken.methods.balanceOf(accounts[0]).call();
assert.equal(result, +initialBalance+100);
});
it("should burn account supply", async function() {
let initialBalance = await MiniMeToken.methods.balanceOf(accounts[0]).call();
await TestStatusNetwork.methods.destroyTokens(accounts[0], initialBalance).send({from: accounts[0]});
assert.equal(await MiniMeToken.methods.totalSupply().call(), 0);
assert.equal(await MiniMeToken.methods.balanceOf(accounts[0]).call(), 0);
})
});

View File

@ -1,34 +0,0 @@
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

@ -187,6 +187,15 @@ 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());
};
@ -195,3 +204,36 @@ 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()
},
async (error) => {
if (error) {
console.log(error);
return reject(err);
}
await web3.currentProvider.sendAsync(
{
jsonrpc: '2.0',
method: 'evm_mine',
params: [],
id: new Date().getSeconds()
}, (error) => {
if (error) {
console.log(error);
return reject(err);
}
resolve();
}
)
}
)
});
}