Merge branch 'master' into ng-accounts-backup
This commit is contained in:
17
js/src/3rdparty/etherscan/call.js
vendored
17
js/src/3rdparty/etherscan/call.js
vendored
@@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { stringify } from 'qs';
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
@@ -23,19 +25,14 @@ const options = {
|
||||
|
||||
export function call (module, action, _params, test) {
|
||||
const host = test ? 'testnet.etherscan.io' : 'api.etherscan.io';
|
||||
let params = '';
|
||||
|
||||
if (_params) {
|
||||
Object.keys(_params).map((param) => {
|
||||
const value = _params[param];
|
||||
const query = stringify(Object.assign({
|
||||
module, action
|
||||
}, _params || {}));
|
||||
|
||||
params = `${params}&${param}=${value}`;
|
||||
});
|
||||
}
|
||||
|
||||
return fetch(`http://${host}/api?module=${module}&action=${action}${params}`, options)
|
||||
return fetch(`https://${host}/api?${query}`, options)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
if (!response.ok) {
|
||||
throw { code: response.status, message: response.statusText }; // eslint-disable-line
|
||||
}
|
||||
|
||||
|
||||
5
js/src/3rdparty/etherscan/index.js
vendored
5
js/src/3rdparty/etherscan/index.js
vendored
@@ -16,10 +16,13 @@
|
||||
|
||||
import { account } from './account';
|
||||
import { stats } from './stats';
|
||||
import { txLink, addressLink } from './links';
|
||||
|
||||
const etherscan = {
|
||||
account: account,
|
||||
stats: stats
|
||||
stats: stats,
|
||||
txLink: txLink,
|
||||
addressLink: addressLink
|
||||
};
|
||||
|
||||
export default etherscan;
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { renderAccounts } from './accountSelector';
|
||||
|
||||
export default from './accountSelector';
|
||||
export {
|
||||
renderAccounts
|
||||
export const txLink = (hash, isTestnet = false) => {
|
||||
return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/tx/${hash}`;
|
||||
};
|
||||
|
||||
export const addressLink = (address, isTestnet = false) => {
|
||||
return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/address/${address}`;
|
||||
};
|
||||
@@ -135,10 +135,11 @@ APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://g
|
||||
|
||||
- [ethapi.db](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#db)
|
||||
- [ethapi.eth](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#eth)
|
||||
- [ethapi.ethcore](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#ethcore)
|
||||
- [ethapi.parity](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#parity)
|
||||
- [ethapi.net](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#net)
|
||||
- [ethapi.personal](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#personal)
|
||||
- [ethapi.shh](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#shh)
|
||||
- [ethapi.signer](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#signer)
|
||||
- [ethapi.trace](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#trace)
|
||||
- [ethapi.web3](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#web3)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { Http, Ws } from './transport';
|
||||
import Contract from './contract';
|
||||
|
||||
import { Db, Eth, Ethcore, Net, Personal, Shh, Trace, Web3 } from './rpc';
|
||||
import { Db, Eth, Parity, Net, Personal, Shh, Signer, Trace, Web3 } from './rpc';
|
||||
import Subscriptions from './subscriptions';
|
||||
import util from './util';
|
||||
import { isFunction } from './util/types';
|
||||
@@ -32,10 +32,11 @@ export default class Api {
|
||||
|
||||
this._db = new Db(transport);
|
||||
this._eth = new Eth(transport);
|
||||
this._ethcore = new Ethcore(transport);
|
||||
this._net = new Net(transport);
|
||||
this._parity = new Parity(transport);
|
||||
this._personal = new Personal(transport);
|
||||
this._shh = new Shh(transport);
|
||||
this._signer = new Signer(transport);
|
||||
this._trace = new Trace(transport);
|
||||
this._web3 = new Web3(transport);
|
||||
|
||||
@@ -50,8 +51,8 @@ export default class Api {
|
||||
return this._eth;
|
||||
}
|
||||
|
||||
get ethcore () {
|
||||
return this._ethcore;
|
||||
get parity () {
|
||||
return this._parity;
|
||||
}
|
||||
|
||||
get net () {
|
||||
@@ -66,6 +67,10 @@ export default class Api {
|
||||
return this._shh;
|
||||
}
|
||||
|
||||
get signer () {
|
||||
return this._signer;
|
||||
}
|
||||
|
||||
get trace () {
|
||||
return this._trace;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('api/Api', () => {
|
||||
});
|
||||
|
||||
describe('interface', () => {
|
||||
const api = new Api(new Api.Transport.Http(TEST_HTTP_URL));
|
||||
const api = new Api(new Api.Transport.Http(TEST_HTTP_URL, -1));
|
||||
|
||||
Object.keys(ethereumRpc).sort().forEach((endpoint) => {
|
||||
describe(endpoint, () => {
|
||||
|
||||
@@ -102,7 +102,7 @@ export default class Contract {
|
||||
options.gas = gas.toFixed(0);
|
||||
|
||||
setState({ state: 'postTransaction', gas });
|
||||
return this._api.eth.postTransaction(this._encodeOptions(this.constructors[0], options, values));
|
||||
return this._api.parity.postTransaction(this._encodeOptions(this.constructors[0], options, values));
|
||||
})
|
||||
.then((requestId) => {
|
||||
setState({ state: 'checkRequest', requestId });
|
||||
@@ -136,27 +136,30 @@ export default class Contract {
|
||||
}
|
||||
|
||||
parseEventLogs (logs) {
|
||||
return logs.map((log) => {
|
||||
const signature = log.topics[0].substr(2);
|
||||
const event = this.events.find((evt) => evt.signature === signature);
|
||||
return logs
|
||||
.map((log) => {
|
||||
const signature = log.topics[0].substr(2);
|
||||
const event = this.events.find((evt) => evt.signature === signature);
|
||||
|
||||
if (!event) {
|
||||
throw new Error(`Unable to find event matching signature ${signature}`);
|
||||
}
|
||||
if (!event) {
|
||||
console.warn(`Unable to find event matching signature ${signature}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const decoded = event.decodeLog(log.topics, log.data);
|
||||
const decoded = event.decodeLog(log.topics, log.data);
|
||||
|
||||
log.params = {};
|
||||
log.event = event.name;
|
||||
log.params = {};
|
||||
log.event = event.name;
|
||||
|
||||
decoded.params.forEach((param) => {
|
||||
const { type, value } = param.token;
|
||||
decoded.params.forEach((param) => {
|
||||
const { type, value } = param.token;
|
||||
|
||||
log.params[param.name] = { type, value };
|
||||
});
|
||||
log.params[param.name] = { type, value };
|
||||
});
|
||||
|
||||
return log;
|
||||
});
|
||||
return log;
|
||||
})
|
||||
.filter((log) => log);
|
||||
}
|
||||
|
||||
parseTransactionEvents (receipt) {
|
||||
@@ -166,7 +169,7 @@ export default class Contract {
|
||||
}
|
||||
|
||||
_pollCheckRequest = (requestId) => {
|
||||
return this._api.pollMethod('eth_checkRequest', requestId);
|
||||
return this._api.pollMethod('parity_checkRequest', requestId);
|
||||
}
|
||||
|
||||
_pollTransactionReceipt = (txhash, gas) => {
|
||||
@@ -208,7 +211,7 @@ export default class Contract {
|
||||
|
||||
if (!func.constant) {
|
||||
func.postTransaction = (options, values = []) => {
|
||||
return this._api.eth
|
||||
return this._api.parity
|
||||
.postTransaction(this._encodeOptions(func, this._addOptionsTo(options), values));
|
||||
};
|
||||
|
||||
@@ -306,7 +309,6 @@ export default class Contract {
|
||||
try {
|
||||
subscriptions[idx].callback(null, this.parseEventLogs(logs));
|
||||
} catch (error) {
|
||||
this.unsubscribe(idx);
|
||||
console.error('_sendSubscriptionChanges', error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ import Api from '../api';
|
||||
import Contract from './contract';
|
||||
import { isInstanceOf, isFunction } from '../util/types';
|
||||
|
||||
const transport = new Api.Transport.Http(TEST_HTTP_URL);
|
||||
const transport = new Api.Transport.Http(TEST_HTTP_URL, -1);
|
||||
const eth = new Api(transport);
|
||||
|
||||
describe('api/contract/Contract', () => {
|
||||
@@ -119,19 +119,6 @@ describe('api/contract/Contract', () => {
|
||||
});
|
||||
|
||||
describe('parseTransactionEvents', () => {
|
||||
it('checks for unmatched signatures', () => {
|
||||
const contract = new Contract(eth, [{ anonymous: false, name: 'Message', type: 'event' }]);
|
||||
expect(() => contract.parseTransactionEvents({
|
||||
logs: [{
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
]
|
||||
}]
|
||||
})).to.throw(/event matching signature/);
|
||||
});
|
||||
|
||||
it('parses a transaction log into the data', () => {
|
||||
const contract = new Contract(eth, [
|
||||
{
|
||||
@@ -249,9 +236,9 @@ describe('api/contract/Contract', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'eth_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'eth_checkRequest', reply: { result: null } },
|
||||
{ method: 'eth_checkRequest', reply: { result: '0x890' } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: null } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x890' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: null } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_PEND } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
@@ -266,7 +253,7 @@ describe('api/contract/Contract', () => {
|
||||
});
|
||||
|
||||
it('passes the options through to postTransaction (incl. gas calculation)', () => {
|
||||
expect(scope.body.eth_postTransaction.params).to.deep.equal([
|
||||
expect(scope.body.parity_postTransaction.params).to.deep.equal([
|
||||
{ data: '0x123', gas: '0x4b0' }
|
||||
]);
|
||||
});
|
||||
@@ -280,8 +267,8 @@ describe('api/contract/Contract', () => {
|
||||
it('fails when gasUsed == gas', () => {
|
||||
mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'eth_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'eth_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_EXCP } }
|
||||
]);
|
||||
|
||||
@@ -295,8 +282,8 @@ describe('api/contract/Contract', () => {
|
||||
it('fails when no code was deployed', () => {
|
||||
mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'eth_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'eth_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
{ method: 'eth_getCode', reply: { result: '0x' } }
|
||||
]);
|
||||
@@ -360,15 +347,15 @@ describe('api/contract/Contract', () => {
|
||||
|
||||
describe('postTransaction', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_postTransaction', reply: { result: ['hashId'] } }]);
|
||||
scope = mockHttp([{ method: 'parity_postTransaction', reply: { result: ['hashId'] } }]);
|
||||
});
|
||||
|
||||
it('encodes options and mades an eth_postTransaction call', () => {
|
||||
it('encodes options and mades an parity_postTransaction call', () => {
|
||||
return func
|
||||
.postTransaction({ someExtras: 'foo' }, VALUES)
|
||||
.then(() => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(scope.body.eth_postTransaction.params[0]).to.deep.equal({
|
||||
expect(scope.body.parity_postTransaction.params[0]).to.deep.equal({
|
||||
someExtras: 'foo',
|
||||
to: ADDR,
|
||||
data: ENCODED
|
||||
|
||||
@@ -93,6 +93,10 @@ export function inFilter (options) {
|
||||
}
|
||||
|
||||
export function inHex (str) {
|
||||
if (str && str.toString) {
|
||||
str = str.toString(16);
|
||||
}
|
||||
|
||||
if (str && str.substr(0, 2) === '0x') {
|
||||
return str.toLowerCase();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,20 @@ export function outDate (date) {
|
||||
return new Date(outNumber(date).toNumber() * 1000);
|
||||
}
|
||||
|
||||
export function outHistogram (histogram) {
|
||||
if (histogram) {
|
||||
Object.keys(histogram).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'bucketBounds':
|
||||
case 'counts':
|
||||
histogram[key] = histogram[key].map(outNumber);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return histogram;
|
||||
}
|
||||
|
||||
export function outLog (log) {
|
||||
Object.keys(log).forEach((key) => {
|
||||
switch (key) {
|
||||
@@ -139,6 +153,28 @@ export function outSignerRequest (request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
export function outSyncing (syncing) {
|
||||
if (syncing && syncing !== 'false') {
|
||||
Object.keys(syncing).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'currentBlock':
|
||||
case 'highestBlock':
|
||||
case 'startingBlock':
|
||||
case 'warpChunksAmount':
|
||||
case 'warpChunksProcessed':
|
||||
syncing[key] = outNumber(syncing[key]);
|
||||
break;
|
||||
|
||||
case 'blockGap':
|
||||
syncing[key] = syncing[key] ? syncing[key].map(outNumber) : syncing[key];
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return syncing;
|
||||
}
|
||||
|
||||
export function outTransaction (tx) {
|
||||
if (tx) {
|
||||
Object.keys(tx).forEach((key) => {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { outBlock, outAccountInfo, outAddress, outDate, outNumber, outPeers, outReceipt, outTransaction, outTrace } from './output';
|
||||
import { outBlock, outAccountInfo, outAddress, outDate, outHistogram, outNumber, outPeers, outReceipt, outSyncing, outTransaction, outTrace } from './output';
|
||||
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
|
||||
|
||||
describe('api/format/output', () => {
|
||||
@@ -120,6 +120,18 @@ describe('api/format/output', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('outHistogram', () => {
|
||||
['bucketBounds', 'counts'].forEach((type) => {
|
||||
it(`formats ${type} as number arrays`, () => {
|
||||
expect(
|
||||
outHistogram({ [type]: [0x123, 0x456, 0x789] })
|
||||
).to.deep.equal({
|
||||
[type]: [new BigNumber(0x123), new BigNumber(0x456), new BigNumber(0x789)]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outNumber', () => {
|
||||
it('returns a BigNumber equalling the value', () => {
|
||||
const bn = outNumber('0x123456');
|
||||
@@ -191,6 +203,22 @@ describe('api/format/output', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('outSyncing', () => {
|
||||
['currentBlock', 'highestBlock', 'startingBlock', 'warpChunksAmount', 'warpChunksProcessed'].forEach((input) => {
|
||||
it(`formats ${input} numbers as a number`, () => {
|
||||
expect(outSyncing({ [input]: '0x123' })).to.deep.equal({
|
||||
[input]: new BigNumber('0x123')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('formats blockGap properly', () => {
|
||||
expect(outSyncing({ blockGap: [0x123, 0x456] })).to.deep.equal({
|
||||
blockGap: [new BigNumber(0x123), new BigNumber(0x456)]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outTransaction', () => {
|
||||
['from', 'to'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import Http from '../../transport/http';
|
||||
import Db from './db';
|
||||
|
||||
const instance = new Db(new Http(TEST_HTTP_URL));
|
||||
const instance = new Db(new Http(TEST_HTTP_URL, -1));
|
||||
|
||||
describe('api/rpc/Db', () => {
|
||||
let scope;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inAddress, inBlockNumber, inData, inFilter, inHash, inHex, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAddress, outBlock, outLog, outNumber, outReceipt, outTransaction } from '../../format/output';
|
||||
import { outAddress, outBlock, outLog, outNumber, outReceipt, outSyncing, outTransaction } from '../../format/output';
|
||||
|
||||
export default class Eth {
|
||||
constructor (transport) {
|
||||
@@ -39,11 +39,6 @@ export default class Eth {
|
||||
.execute('eth_call', inOptions(options), inBlockNumber(blockNumber));
|
||||
}
|
||||
|
||||
checkRequest (requestId) {
|
||||
return this._transport
|
||||
.execute('eth_checkRequest', inNumber16(requestId));
|
||||
}
|
||||
|
||||
coinbase () {
|
||||
return this._transport
|
||||
.execute('eth_coinbase')
|
||||
@@ -267,11 +262,6 @@ export default class Eth {
|
||||
.execute('eth_pendingTransactions');
|
||||
}
|
||||
|
||||
postTransaction (options) {
|
||||
return this._transport
|
||||
.execute('eth_postTransaction', inOptions(options));
|
||||
}
|
||||
|
||||
protocolVersion () {
|
||||
return this._transport
|
||||
.execute('eth_protocolVersion');
|
||||
@@ -314,7 +304,8 @@ export default class Eth {
|
||||
|
||||
syncing () {
|
||||
return this._transport
|
||||
.execute('eth_syncing');
|
||||
.execute('eth_syncing')
|
||||
.then(outSyncing);
|
||||
}
|
||||
|
||||
uninstallFilter (filterId) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { isBigNumber } from '../../../../test/types';
|
||||
import Http from '../../transport/http';
|
||||
import Eth from './eth';
|
||||
|
||||
const instance = new Eth(new Http(TEST_HTTP_URL));
|
||||
const instance = new Eth(new Http(TEST_HTTP_URL, -1));
|
||||
|
||||
describe('rpc/Eth', () => {
|
||||
const address = '0x63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inAddress, inData, inNumber16 } from '../../format/input';
|
||||
import { outAddress, outNumber, outPeers } from '../../format/output';
|
||||
|
||||
export default class Ethcore {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
acceptNonReservedPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_acceptNonReservedPeers');
|
||||
}
|
||||
|
||||
addReservedPeer (encode) {
|
||||
return this._transport
|
||||
.execute('ethcore_addReservedPeer', encode);
|
||||
}
|
||||
|
||||
dappsPort () {
|
||||
return this._transport
|
||||
.execute('ethcore_dappsPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
defaultExtraData () {
|
||||
return this._transport
|
||||
.execute('ethcore_defaultExtraData');
|
||||
}
|
||||
|
||||
devLogs () {
|
||||
return this._transport
|
||||
.execute('ethcore_devLogs');
|
||||
}
|
||||
|
||||
devLogsLevels () {
|
||||
return this._transport
|
||||
.execute('ethcore_devLogsLevels');
|
||||
}
|
||||
|
||||
dropNonReservedPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_dropNonReservedPeers');
|
||||
}
|
||||
|
||||
extraData () {
|
||||
return this._transport
|
||||
.execute('ethcore_extraData');
|
||||
}
|
||||
|
||||
gasFloorTarget () {
|
||||
return this._transport
|
||||
.execute('ethcore_gasFloorTarget')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
generateSecretPhrase () {
|
||||
return this._transport
|
||||
.execute('ethcore_generateSecretPhrase');
|
||||
}
|
||||
|
||||
hashContent (url) {
|
||||
return this._transport
|
||||
.execute('ethcore_hashContent', url);
|
||||
}
|
||||
|
||||
minGasPrice () {
|
||||
return this._transport
|
||||
.execute('ethcore_minGasPrice')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
netChain () {
|
||||
return this._transport
|
||||
.execute('ethcore_netChain');
|
||||
}
|
||||
|
||||
netPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_netPeers')
|
||||
.then(outPeers);
|
||||
}
|
||||
|
||||
netMaxPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_netMaxPeers')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
netPort () {
|
||||
return this._transport
|
||||
.execute('ethcore_netPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
nodeName () {
|
||||
return this._transport
|
||||
.execute('ethcore_nodeName');
|
||||
}
|
||||
|
||||
phraseToAddress (phrase) {
|
||||
return this._transport
|
||||
.execute('ethcore_phraseToAddress', phrase)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
registryAddress () {
|
||||
return this._transport
|
||||
.execute('ethcore_registryAddress')
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
removeReservedPeer (encode) {
|
||||
return this._transport
|
||||
.execute('ethcore_removeReservedPeer', encode);
|
||||
}
|
||||
|
||||
rpcSettings () {
|
||||
return this._transport
|
||||
.execute('ethcore_rpcSettings');
|
||||
}
|
||||
|
||||
setAuthor (address) {
|
||||
return this._transport
|
||||
.execute('ethcore_setAuthor', inAddress(address));
|
||||
}
|
||||
|
||||
setExtraData (data) {
|
||||
return this._transport
|
||||
.execute('ethcore_setExtraData', inData(data));
|
||||
}
|
||||
|
||||
setGasFloorTarget (quantity) {
|
||||
return this._transport
|
||||
.execute('ethcore_setGasFloorTarget', inNumber16(quantity));
|
||||
}
|
||||
|
||||
setMinGasPrice (quantity) {
|
||||
return this._transport
|
||||
.execute('ethcore_setMinGasPrice', inNumber16(quantity));
|
||||
}
|
||||
|
||||
setTransactionsLimit (quantity) {
|
||||
return this._transport
|
||||
.execute('ethcore_setTransactionsLimit', inNumber16(quantity));
|
||||
}
|
||||
|
||||
signerPort () {
|
||||
return this._transport
|
||||
.execute('ethcore_signerPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
transactionsLimit () {
|
||||
return this._transport
|
||||
.execute('ethcore_transactionsLimit')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
unsignedTransactionsCount () {
|
||||
return this._transport
|
||||
.execute('ethcore_unsignedTransactionsCount')
|
||||
.then(outNumber);
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
export Db from './db';
|
||||
export Eth from './eth';
|
||||
export Ethcore from './ethcore';
|
||||
export Parity from './parity';
|
||||
export Net from './net';
|
||||
export Personal from './personal';
|
||||
export Shh from './shh';
|
||||
export Signer from './signer';
|
||||
export Trace from './trace';
|
||||
export Web3 from './web3';
|
||||
|
||||
@@ -20,7 +20,7 @@ import { isBigNumber } from '../../../../test/types';
|
||||
import Http from '../../transport/http';
|
||||
import Net from './net';
|
||||
|
||||
const instance = new Net(new Http(TEST_HTTP_URL));
|
||||
const instance = new Net(new Http(TEST_HTTP_URL, -1));
|
||||
|
||||
describe('api/rpc/Net', () => {
|
||||
describe('peerCount', () => {
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './status';
|
||||
export default from './parity';
|
||||
@@ -16,20 +16,30 @@
|
||||
|
||||
import { createHttpApi } from '../../../../test/e2e/ethapi';
|
||||
|
||||
describe('ethapi.ethcore', () => {
|
||||
describe('ethapi.parity', () => {
|
||||
const ethapi = createHttpApi();
|
||||
|
||||
describe('gasFloorTarget', () => {
|
||||
it('returns and translates the target', () => {
|
||||
return ethapi.ethcore.gasFloorTarget().then((value) => {
|
||||
return ethapi.parity.gasFloorTarget().then((value) => {
|
||||
expect(value.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('gasPriceHistogram', () => {
|
||||
it('returns and translates the target', () => {
|
||||
return ethapi.parity.gasPriceHistogram().then((result) => {
|
||||
expect(Object.keys(result)).to.deep.equal(['bucketBounds', 'counts']);
|
||||
expect(result.bucketBounds.length > 0).to.be.true;
|
||||
expect(result.counts.length > 0).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('netChain', () => {
|
||||
it('returns and the chain', () => {
|
||||
return ethapi.ethcore.netChain().then((value) => {
|
||||
return ethapi.parity.netChain().then((value) => {
|
||||
expect(value).to.equal('morden');
|
||||
});
|
||||
});
|
||||
@@ -37,7 +47,7 @@ describe('ethapi.ethcore', () => {
|
||||
|
||||
describe('netPort', () => {
|
||||
it('returns and translates the port', () => {
|
||||
return ethapi.ethcore.netPort().then((value) => {
|
||||
return ethapi.parity.netPort().then((value) => {
|
||||
expect(value.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
@@ -45,7 +55,7 @@ describe('ethapi.ethcore', () => {
|
||||
|
||||
describe('transactionsLimit', () => {
|
||||
it('returns and translates the limit', () => {
|
||||
return ethapi.ethcore.transactionsLimit().then((value) => {
|
||||
return ethapi.parity.transactionsLimit().then((value) => {
|
||||
expect(value.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
@@ -53,7 +63,7 @@ describe('ethapi.ethcore', () => {
|
||||
|
||||
describe('rpcSettings', () => {
|
||||
it('returns and translates the settings', () => {
|
||||
return ethapi.ethcore.rpcSettings().then((value) => {
|
||||
return ethapi.parity.rpcSettings().then((value) => {
|
||||
expect(value).to.be.ok;
|
||||
});
|
||||
});
|
||||
284
js/src/api/rpc/parity/parity.js
Normal file
284
js/src/api/rpc/parity/parity.js
Normal file
@@ -0,0 +1,284 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inAddress, inData, inHex, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAccountInfo, outAddress, outHistogram, outNumber, outPeers } from '../../format/output';
|
||||
|
||||
export default class Parity {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
acceptNonReservedPeers () {
|
||||
return this._transport
|
||||
.execute('parity_acceptNonReservedPeers');
|
||||
}
|
||||
|
||||
accounts () {
|
||||
return this._transport
|
||||
.execute('parity_accounts')
|
||||
.then(outAccountInfo);
|
||||
}
|
||||
|
||||
accountsInfo () {
|
||||
return this._transport
|
||||
.execute('parity_accountsInfo')
|
||||
.then(outAccountInfo);
|
||||
}
|
||||
|
||||
addReservedPeer (encode) {
|
||||
return this._transport
|
||||
.execute('parity_addReservedPeer', encode);
|
||||
}
|
||||
|
||||
changePassword (account, password, newPassword) {
|
||||
return this._transport
|
||||
.execute('parity_changePassword', inAddress(account), password, newPassword);
|
||||
}
|
||||
|
||||
checkRequest (requestId) {
|
||||
return this._transport
|
||||
.execute('parity_checkRequest', inNumber16(requestId));
|
||||
}
|
||||
|
||||
dappsPort () {
|
||||
return this._transport
|
||||
.execute('parity_dappsPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
dappsInterface () {
|
||||
return this._transport
|
||||
.execute('parity_dappsInterface');
|
||||
}
|
||||
|
||||
defaultExtraData () {
|
||||
return this._transport
|
||||
.execute('parity_defaultExtraData');
|
||||
}
|
||||
|
||||
devLogs () {
|
||||
return this._transport
|
||||
.execute('parity_devLogs');
|
||||
}
|
||||
|
||||
devLogsLevels () {
|
||||
return this._transport
|
||||
.execute('parity_devLogsLevels');
|
||||
}
|
||||
|
||||
dropNonReservedPeers () {
|
||||
return this._transport
|
||||
.execute('parity_dropNonReservedPeers');
|
||||
}
|
||||
|
||||
enode () {
|
||||
return this._transport
|
||||
.execute('parity_enode');
|
||||
}
|
||||
|
||||
extraData () {
|
||||
return this._transport
|
||||
.execute('parity_extraData');
|
||||
}
|
||||
|
||||
gasFloorTarget () {
|
||||
return this._transport
|
||||
.execute('parity_gasFloorTarget')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
gasPriceHistogram () {
|
||||
return this._transport
|
||||
.execute('parity_gasPriceHistogram')
|
||||
.then(outHistogram);
|
||||
}
|
||||
|
||||
generateSecretPhrase () {
|
||||
return this._transport
|
||||
.execute('parity_generateSecretPhrase');
|
||||
}
|
||||
|
||||
hashContent (url) {
|
||||
return this._transport
|
||||
.execute('parity_hashContent', url);
|
||||
}
|
||||
|
||||
listGethAccounts () {
|
||||
return this._transport
|
||||
.execute('parity_listGethAccounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
importGethAccounts (accounts) {
|
||||
return this._transport
|
||||
.execute('parity_importGethAccounts', (accounts || []).map(inAddress))
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
minGasPrice () {
|
||||
return this._transport
|
||||
.execute('parity_minGasPrice')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
mode () {
|
||||
return this._transport
|
||||
.execute('parity_mode');
|
||||
}
|
||||
|
||||
netChain () {
|
||||
return this._transport
|
||||
.execute('parity_netChain');
|
||||
}
|
||||
|
||||
netPeers () {
|
||||
return this._transport
|
||||
.execute('parity_netPeers')
|
||||
.then(outPeers);
|
||||
}
|
||||
|
||||
netMaxPeers () {
|
||||
return this._transport
|
||||
.execute('parity_netMaxPeers')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
netPort () {
|
||||
return this._transport
|
||||
.execute('parity_netPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
newAccountFromPhrase (phrase, password) {
|
||||
return this._transport
|
||||
.execute('parity_newAccountFromPhrase', phrase, password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
newAccountFromSecret (secret, password) {
|
||||
return this._transport
|
||||
.execute('parity_newAccountFromSecret', inHex(secret), password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
newAccountFromWallet (json, password) {
|
||||
return this._transport
|
||||
.execute('parity_newAccountFromWallet', json, password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
nextNonce (account) {
|
||||
return this._transport
|
||||
.execute('parity_nextNonce', inAddress(account))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
nodeName () {
|
||||
return this._transport
|
||||
.execute('parity_nodeName');
|
||||
}
|
||||
|
||||
phraseToAddress (phrase) {
|
||||
return this._transport
|
||||
.execute('parity_phraseToAddress', phrase)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
postTransaction (options) {
|
||||
return this._transport
|
||||
.execute('parity_postTransaction', inOptions(options));
|
||||
}
|
||||
|
||||
registryAddress () {
|
||||
return this._transport
|
||||
.execute('parity_registryAddress')
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
removeReservedPeer (encode) {
|
||||
return this._transport
|
||||
.execute('parity_removeReservedPeer', encode);
|
||||
}
|
||||
|
||||
rpcSettings () {
|
||||
return this._transport
|
||||
.execute('parity_rpcSettings');
|
||||
}
|
||||
|
||||
setAccountName (address, name) {
|
||||
return this._transport
|
||||
.execute('parity_setAccountName', inAddress(address), name);
|
||||
}
|
||||
|
||||
setAccountMeta (address, meta) {
|
||||
return this._transport
|
||||
.execute('parity_setAccountMeta', inAddress(address), JSON.stringify(meta));
|
||||
}
|
||||
|
||||
setAuthor (address) {
|
||||
return this._transport
|
||||
.execute('parity_setAuthor', inAddress(address));
|
||||
}
|
||||
|
||||
setExtraData (data) {
|
||||
return this._transport
|
||||
.execute('parity_setExtraData', inData(data));
|
||||
}
|
||||
|
||||
setGasFloorTarget (quantity) {
|
||||
return this._transport
|
||||
.execute('parity_setGasFloorTarget', inNumber16(quantity));
|
||||
}
|
||||
|
||||
setMinGasPrice (quantity) {
|
||||
return this._transport
|
||||
.execute('parity_setMinGasPrice', inNumber16(quantity));
|
||||
}
|
||||
|
||||
setMode (mode) {
|
||||
return this._transport
|
||||
.execute('parity_setMode', mode);
|
||||
}
|
||||
|
||||
setTransactionsLimit (quantity) {
|
||||
return this._transport
|
||||
.execute('parity_setTransactionsLimit', inNumber16(quantity));
|
||||
}
|
||||
|
||||
signerPort () {
|
||||
return this._transport
|
||||
.execute('parity_signerPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
testPassword (account, password) {
|
||||
return this._transport
|
||||
.execute('parity_testPassword', inAddress(account), password);
|
||||
}
|
||||
|
||||
transactionsLimit () {
|
||||
return this._transport
|
||||
.execute('parity_transactionsLimit')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
unsignedTransactionsCount () {
|
||||
return this._transport
|
||||
.execute('parity_unsignedTransactionsCount')
|
||||
.then(outNumber);
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,36 @@ import { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import { isBigNumber } from '../../../../test/types';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Ethcore from './ethcore';
|
||||
import Parity from './parity';
|
||||
|
||||
const instance = new Ethcore(new Http(TEST_HTTP_URL));
|
||||
const instance = new Parity(new Http(TEST_HTTP_URL, -1));
|
||||
|
||||
describe('api/rpc/parity', () => {
|
||||
describe('accountsInfo', () => {
|
||||
it('retrieves the available account info', () => {
|
||||
mockHttp([{ method: 'parity_accountsInfo', reply: {
|
||||
result: {
|
||||
'0x63cf90d3f0410092fc0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: '{"data":"data"}'
|
||||
}
|
||||
}
|
||||
} }]);
|
||||
|
||||
return instance.accountsInfo().then((result) => {
|
||||
expect(result).to.deep.equal({
|
||||
'0x63Cf90D3f0410092FC0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: {
|
||||
data: 'data'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('api/rpc/Ethcore', () => {
|
||||
describe('gasFloorTarget', () => {
|
||||
it('returns the gasfloor, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_gasFloorTarget', reply: { result: '0x123456' } }]);
|
||||
mockHttp([{ method: 'parity_gasFloorTarget', reply: { result: '0x123456' } }]);
|
||||
|
||||
return instance.gasFloorTarget().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
@@ -36,7 +58,7 @@ describe('api/rpc/Ethcore', () => {
|
||||
|
||||
describe('minGasPrice', () => {
|
||||
it('returns the min gasprice, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_minGasPrice', reply: { result: '0x123456' } }]);
|
||||
mockHttp([{ method: 'parity_minGasPrice', reply: { result: '0x123456' } }]);
|
||||
|
||||
return instance.minGasPrice().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
@@ -47,7 +69,7 @@ describe('api/rpc/Ethcore', () => {
|
||||
|
||||
describe('netMaxPeers', () => {
|
||||
it('returns the max peers, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_netMaxPeers', reply: { result: 25 } }]);
|
||||
mockHttp([{ method: 'parity_netMaxPeers', reply: { result: 25 } }]);
|
||||
|
||||
return instance.netMaxPeers().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
@@ -58,7 +80,7 @@ describe('api/rpc/Ethcore', () => {
|
||||
|
||||
describe('newPeers', () => {
|
||||
it('returns the peer structure, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_netPeers', reply: { result: { active: 123, connected: 456, max: 789 } } }]);
|
||||
mockHttp([{ method: 'parity_netPeers', reply: { result: { active: 123, connected: 456, max: 789 } } }]);
|
||||
|
||||
return instance.netPeers().then((peers) => {
|
||||
expect(peers.active.eq(123)).to.be.true;
|
||||
@@ -70,7 +92,7 @@ describe('api/rpc/Ethcore', () => {
|
||||
|
||||
describe('netPort', () => {
|
||||
it('returns the connected port, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_netPort', reply: { result: 33030 } }]);
|
||||
mockHttp([{ method: 'parity_netPort', reply: { result: 33030 } }]);
|
||||
|
||||
return instance.netPort().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
@@ -81,7 +103,7 @@ describe('api/rpc/Ethcore', () => {
|
||||
|
||||
describe('transactionsLimit', () => {
|
||||
it('returns the tx limit, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_transactionsLimit', reply: { result: 1024 } }]);
|
||||
mockHttp([{ method: 'parity_transactionsLimit', reply: { result: 1024 } }]);
|
||||
|
||||
return instance.transactionsLimit().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
@@ -14,107 +14,31 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inAddress, inNumber10, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAccountInfo, outAddress, outSignerRequest } from '../../format/output';
|
||||
import { inAddress, inNumber10, inOptions } from '../../format/input';
|
||||
import { outAddress } from '../../format/output';
|
||||
|
||||
export default class Personal {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
accountsInfo () {
|
||||
return this._transport
|
||||
.execute('personal_accountsInfo')
|
||||
.then(outAccountInfo);
|
||||
}
|
||||
|
||||
confirmRequest (requestId, options, password) {
|
||||
return this._transport
|
||||
.execute('personal_confirmRequest', inNumber16(requestId), options, password);
|
||||
}
|
||||
|
||||
changePassword (account, password, newPassword) {
|
||||
return this._transport
|
||||
.execute('personal_changePassword', inAddress(account), password, newPassword);
|
||||
}
|
||||
|
||||
generateAuthorizationToken () {
|
||||
return this._transport
|
||||
.execute('personal_generateAuthorizationToken');
|
||||
}
|
||||
|
||||
listAccounts () {
|
||||
return this._transport
|
||||
.execute('personal_listAccounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
listGethAccounts () {
|
||||
return this._transport
|
||||
.execute('personal_listGethAccounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
importGethAccounts (accounts) {
|
||||
return this._transport
|
||||
.execute('personal_importGethAccounts', (accounts || []).map(inAddress))
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
newAccount (password) {
|
||||
return this._transport
|
||||
.execute('personal_newAccount', password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
newAccountFromPhrase (phrase, password) {
|
||||
return this._transport
|
||||
.execute('personal_newAccountFromPhrase', phrase, password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
newAccountFromWallet (json, password) {
|
||||
return this._transport
|
||||
.execute('personal_newAccountFromWallet', json, password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
rejectRequest (requestId) {
|
||||
return this._transport
|
||||
.execute('personal_rejectRequest', inNumber16(requestId));
|
||||
}
|
||||
|
||||
requestsToConfirm () {
|
||||
return this._transport
|
||||
.execute('personal_requestsToConfirm')
|
||||
.then((requests) => (requests || []).map(outSignerRequest));
|
||||
}
|
||||
|
||||
setAccountName (address, name) {
|
||||
return this._transport
|
||||
.execute('personal_setAccountName', inAddress(address), name);
|
||||
}
|
||||
|
||||
setAccountMeta (address, meta) {
|
||||
return this._transport
|
||||
.execute('personal_setAccountMeta', inAddress(address), JSON.stringify(meta));
|
||||
}
|
||||
|
||||
signAndSendTransaction (options, password) {
|
||||
return this._transport
|
||||
.execute('personal_signAndSendTransaction', inOptions(options), password);
|
||||
}
|
||||
|
||||
signerEnabled () {
|
||||
return this._transport
|
||||
.execute('personal_signerEnabled');
|
||||
}
|
||||
|
||||
testPassword (account, password) {
|
||||
return this._transport
|
||||
.execute('personal_testPassword', inAddress(account), password);
|
||||
}
|
||||
|
||||
unlockAccount (account, password, duration = 1) {
|
||||
return this._transport
|
||||
.execute('personal_unlockAccount', inAddress(account), password, inNumber10(duration));
|
||||
|
||||
@@ -19,35 +19,13 @@ import { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import Http from '../../transport/http';
|
||||
import Personal from './personal';
|
||||
|
||||
const instance = new Personal(new Http(TEST_HTTP_URL));
|
||||
const instance = new Personal(new Http(TEST_HTTP_URL, -1));
|
||||
|
||||
describe('rpc/Personal', () => {
|
||||
const account = '0x63cf90d3f0410092fc0fca41846f596223979195';
|
||||
const checksum = '0x63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
let scope;
|
||||
|
||||
describe('accountsInfo', () => {
|
||||
it('retrieves the available account info', () => {
|
||||
scope = mockHttp([{ method: 'personal_accountsInfo', reply: {
|
||||
result: {
|
||||
'0x63cf90d3f0410092fc0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: '{"data":"data"}'
|
||||
}
|
||||
}
|
||||
} }]);
|
||||
|
||||
return instance.accountsInfo().then((result) => {
|
||||
expect(result).to.deep.equal({
|
||||
'0x63Cf90D3f0410092FC0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: {
|
||||
data: 'data'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listAccounts', () => {
|
||||
it('retrieves a list of available accounts', () => {
|
||||
scope = mockHttp([{ method: 'personal_listAccounts', reply: { result: [account] } }]);
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './events';
|
||||
export default from './signer';
|
||||
55
js/src/api/rpc/signer/signer.js
Normal file
55
js/src/api/rpc/signer/signer.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inNumber16, inData } from '../../format/input';
|
||||
import { outSignerRequest } from '../../format/output';
|
||||
|
||||
export default class Signer {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
confirmRequest (requestId, options, password) {
|
||||
return this._transport
|
||||
.execute('signer_confirmRequest', inNumber16(requestId), options, password);
|
||||
}
|
||||
|
||||
confirmRequestRaw (requestId, data) {
|
||||
return this._transport
|
||||
.execute('signer_confirmRequestRaw', inNumber16(requestId), inData(data));
|
||||
}
|
||||
|
||||
generateAuthorizationToken () {
|
||||
return this._transport
|
||||
.execute('signer_generateAuthorizationToken');
|
||||
}
|
||||
|
||||
rejectRequest (requestId) {
|
||||
return this._transport
|
||||
.execute('signer_rejectRequest', inNumber16(requestId));
|
||||
}
|
||||
|
||||
requestsToConfirm () {
|
||||
return this._transport
|
||||
.execute('signer_requestsToConfirm')
|
||||
.then((requests) => (requests || []).map(outSignerRequest));
|
||||
}
|
||||
|
||||
signerEnabled () {
|
||||
return this._transport
|
||||
.execute('signer_signerEnabled');
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import Http from '../../transport/http';
|
||||
import Trace from './trace';
|
||||
|
||||
const instance = new Trace(new Http(TEST_HTTP_URL));
|
||||
const instance = new Trace(new Http(TEST_HTTP_URL, -1));
|
||||
|
||||
describe('api/rpc/Trace', () => {
|
||||
let scope;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import Http from '../../transport/http';
|
||||
import Web3 from './web3';
|
||||
|
||||
const instance = new Web3(new Http(TEST_HTTP_URL));
|
||||
const instance = new Web3(new Http(TEST_HTTP_URL, -1));
|
||||
|
||||
describe('api/rpc/Web3', () => {
|
||||
let scope;
|
||||
|
||||
@@ -24,9 +24,9 @@ import Signer from './signer';
|
||||
const events = {
|
||||
'logging': { module: 'logging' },
|
||||
'eth_blockNumber': { module: 'eth' },
|
||||
'personal_accountsInfo': { module: 'personal' },
|
||||
'personal_listAccounts': { module: 'personal' },
|
||||
'personal_requestsToConfirm': { module: 'signer' }
|
||||
'parity_accountsInfo': { module: 'personal' },
|
||||
'eth_accounts': { module: 'personal' },
|
||||
'signer_requestsToConfirm': { module: 'signer' }
|
||||
};
|
||||
|
||||
export default class Manager {
|
||||
@@ -107,7 +107,6 @@ export default class Manager {
|
||||
callback(error, data);
|
||||
} catch (error) {
|
||||
console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error);
|
||||
this.unsubscribe(subscriptionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,18 +37,18 @@ export default class Personal {
|
||||
}
|
||||
|
||||
_listAccounts = () => {
|
||||
return this._api.personal
|
||||
.listAccounts()
|
||||
return this._api.eth
|
||||
.accounts()
|
||||
.then((accounts) => {
|
||||
this._updateSubscriptions('personal_listAccounts', null, accounts);
|
||||
this._updateSubscriptions('eth_accounts', null, accounts);
|
||||
});
|
||||
}
|
||||
|
||||
_accountsInfo = () => {
|
||||
return this._api.personal
|
||||
return this._api.parity
|
||||
.accountsInfo()
|
||||
.then((info) => {
|
||||
this._updateSubscriptions('personal_accountsInfo', null, info);
|
||||
this._updateSubscriptions('parity_accountsInfo', null, info);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,16 +59,16 @@ export default class Personal {
|
||||
}
|
||||
|
||||
switch (data.method) {
|
||||
case 'personal_importGethAccounts':
|
||||
case 'parity_importGethAccounts':
|
||||
case 'personal_newAccount':
|
||||
case 'personal_newAccountFromPhrase':
|
||||
case 'personal_newAccountFromWallet':
|
||||
case 'parity_newAccountFromPhrase':
|
||||
case 'parity_newAccountFromWallet':
|
||||
this._listAccounts();
|
||||
this._accountsInfo();
|
||||
return;
|
||||
|
||||
case 'personal_setAccountName':
|
||||
case 'personal_setAccountMeta':
|
||||
case 'parity_setAccountName':
|
||||
case 'parity_setAccountMeta':
|
||||
this._accountsInfo();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,14 +34,15 @@ function stubApi (accounts, info) {
|
||||
|
||||
return {
|
||||
_calls,
|
||||
personal: {
|
||||
parity: {
|
||||
accountsInfo: () => {
|
||||
const stub = sinon.stub().resolves(info || TEST_INFO)();
|
||||
_calls.accountsInfo.push(stub);
|
||||
return stub;
|
||||
},
|
||||
|
||||
listAccounts: () => {
|
||||
}
|
||||
},
|
||||
eth: {
|
||||
accounts: () => {
|
||||
const stub = sinon.stub().resolves(accounts || TEST_LIST)();
|
||||
_calls.listAccounts.push(stub);
|
||||
return stub;
|
||||
@@ -85,17 +86,17 @@ describe('api/subscriptions/personal', () => {
|
||||
expect(personal.isStarted).to.be.true;
|
||||
});
|
||||
|
||||
it('calls personal_accountsInfo', () => {
|
||||
it('calls parity_accountsInfo', () => {
|
||||
expect(api._calls.accountsInfo.length).to.be.ok;
|
||||
});
|
||||
|
||||
it('calls personal_listAccounts', () => {
|
||||
it('calls eth_accounts', () => {
|
||||
expect(api._calls.listAccounts.length).to.be.ok;
|
||||
});
|
||||
|
||||
it('updates subscribers', () => {
|
||||
expect(cb.firstCall).to.have.been.calledWith('personal_listAccounts', null, TEST_LIST);
|
||||
expect(cb.secondCall).to.have.been.calledWith('personal_accountsInfo', null, TEST_INFO);
|
||||
expect(cb.firstCall).to.have.been.calledWith('eth_accounts', null, TEST_LIST);
|
||||
expect(cb.secondCall).to.have.been.calledWith('parity_accountsInfo', null, TEST_INFO);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -49,10 +49,10 @@ export default class Signer {
|
||||
return;
|
||||
}
|
||||
|
||||
return this._api.personal
|
||||
return this._api.signer
|
||||
.requestsToConfirm()
|
||||
.then((requests) => {
|
||||
this._updateSubscriptions('personal_requestsToConfirm', null, requests);
|
||||
this._updateSubscriptions('signer_requestsToConfirm', null, requests);
|
||||
nextTimeout();
|
||||
})
|
||||
.catch(nextTimeout);
|
||||
@@ -65,7 +65,7 @@ export default class Signer {
|
||||
}
|
||||
|
||||
switch (data.method) {
|
||||
case 'eth_postTransaction':
|
||||
case 'parity_postTransaction':
|
||||
case 'eth_sendTranasction':
|
||||
case 'eth_sendRawTransaction':
|
||||
this._listRequests(false);
|
||||
|
||||
@@ -19,11 +19,14 @@ import JsonRpcBase from '../jsonRpcBase';
|
||||
|
||||
/* global fetch */
|
||||
export default class Http extends JsonRpcBase {
|
||||
constructor (url) {
|
||||
constructor (url, connectTimeout = 1000) {
|
||||
super();
|
||||
|
||||
this._connected = true;
|
||||
this._url = url;
|
||||
this._connectTimeout = connectTimeout;
|
||||
|
||||
this._pollConnection();
|
||||
}
|
||||
|
||||
_encodeOptions (method, params) {
|
||||
@@ -56,6 +59,8 @@ export default class Http extends JsonRpcBase {
|
||||
if (response.status !== 200) {
|
||||
this._connected = false;
|
||||
this.error(JSON.stringify({ status: response.status, statusText: response.statusText }));
|
||||
console.error(`${method}(${JSON.stringify(params)}): ${response.status}: ${response.statusText}`);
|
||||
|
||||
throw new Error(`${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
@@ -66,11 +71,26 @@ export default class Http extends JsonRpcBase {
|
||||
|
||||
if (response.error) {
|
||||
this.error(JSON.stringify(response));
|
||||
throw new Error(`${response.error.code}: ${response.error.message}`);
|
||||
console.error(`${method}(${JSON.stringify(params)}): ${response.error.code}: ${response.error.message}`);
|
||||
|
||||
throw new Error(`${method}: ${response.error.code}: ${response.error.message}`);
|
||||
}
|
||||
|
||||
this.log(JSON.stringify(response));
|
||||
return response.result;
|
||||
});
|
||||
}
|
||||
|
||||
_pollConnection = () => {
|
||||
if (this._connectTimeout <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextTimeout = () => setTimeout(this._pollConnection, this._connectTimeout);
|
||||
|
||||
this
|
||||
.execute('net_listening')
|
||||
.then(nextTimeout)
|
||||
.catch(nextTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import Http from './http';
|
||||
|
||||
const transport = new Http(TEST_HTTP_URL);
|
||||
const transport = new Http(TEST_HTTP_URL, -1);
|
||||
|
||||
describe('api/transport/Http', () => {
|
||||
describe('instance', () => {
|
||||
|
||||
@@ -107,7 +107,9 @@ export default class Ws extends JsonRpcBase {
|
||||
if (result.error) {
|
||||
this.error(event.data);
|
||||
|
||||
reject(new Error(`${result.error.code}: ${result.error.message}`));
|
||||
console.error(`${method}(${JSON.stringify(params)}): ${result.error.code}: ${result.error.message}`);
|
||||
|
||||
reject(new Error(`${method}: ${result.error.code}: ${result.error.message}`));
|
||||
delete this._messages[result.id];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import owned from './owned.json';
|
||||
import registry from './registry.json';
|
||||
import signaturereg from './signaturereg.json';
|
||||
import tokenreg from './tokenreg.json';
|
||||
import wallet from './wallet.json';
|
||||
|
||||
export {
|
||||
basiccoin,
|
||||
@@ -35,5 +36,6 @@ export {
|
||||
owned,
|
||||
registry,
|
||||
signaturereg,
|
||||
tokenreg
|
||||
tokenreg,
|
||||
wallet
|
||||
};
|
||||
|
||||
1
js/src/contracts/abi/wallet.json
Normal file
1
js/src/contracts/abi/wallet.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"removeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_numOwners","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_lastDay","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"resetSpentToday","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_spentToday","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"addOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_required","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_h","type":"bytes32"}],"name":"confirm","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_newLimit","type":"uint256"}],"name":"setDailyLimit","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"execute","outputs":[{"name":"_r","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"_operation","type":"bytes32"}],"name":"revoke","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_newRequired","type":"uint256"}],"name":"changeRequirement","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_operation","type":"bytes32"},{"name":"_owner","type":"address"}],"name":"hasConfirmed","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"ownerIndex","type":"uint256"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"}],"name":"kill","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"}],"name":"changeOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_dailyLimit","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"_owners","type":"address[]"},{"name":"_required","type":"uint256"},{"name":"_daylimit","type":"uint256"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Confirmation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Revoke","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"},{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"}],"name":"OwnerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newRequirement","type":"uint256"}],"name":"RequirementChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_from","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"SingleTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"MultiTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"initiator","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"ConfirmationNeeded","type":"event"}]
|
||||
@@ -14,22 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default class DappReg {
|
||||
constructor (api, registry) {
|
||||
this._api = api;
|
||||
@@ -69,4 +53,12 @@ export default class DappReg {
|
||||
getImage (id) {
|
||||
return this.meta(id, 'IMG');
|
||||
}
|
||||
|
||||
getContent (id) {
|
||||
return this.meta(id, 'CONTENT');
|
||||
}
|
||||
|
||||
getManifest (id) {
|
||||
return this.meta(id, 'MANIFEST');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default class Registry {
|
||||
return;
|
||||
}
|
||||
|
||||
this._api.ethcore
|
||||
this._api.parity
|
||||
.registryAddress()
|
||||
.then((address) => {
|
||||
this._instance = this._api.newContract(abis.registry, address).instance;
|
||||
|
||||
60
js/src/contracts/snippets/human-standard-token.sol
Normal file
60
js/src/contracts/snippets/human-standard-token.sol
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
This Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well as the following OPTIONAL extras intended for use by humans.
|
||||
|
||||
In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans.
|
||||
Imagine coins, currencies, shares, voting weight, etc.
|
||||
Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners.
|
||||
|
||||
1) Initial Finite Supply (upon creation one specifies how much is minted).
|
||||
2) In the absence of a token registry: Optional Decimal, Symbol & Name.
|
||||
3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred.
|
||||
|
||||
.*/
|
||||
|
||||
import "StandardToken.sol";
|
||||
|
||||
contract HumanStandardToken is StandardToken {
|
||||
|
||||
function () {
|
||||
//if ether is sent to this address, send it back.
|
||||
throw;
|
||||
}
|
||||
|
||||
/* Public variables of the token */
|
||||
|
||||
/*
|
||||
NOTE:
|
||||
The following variables are OPTIONAL vanities. One does not have to include them.
|
||||
They allow one to customise the token contract & in no way influences the core functionality.
|
||||
Some wallets/interfaces might not even bother to look at this information.
|
||||
*/
|
||||
string public name; //fancy name: eg Simon Bucks
|
||||
uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
|
||||
string public symbol; //An identifier: eg SBX
|
||||
string public version = 'H0.1'; //human 0.1 standard. Just an arbitrary versioning scheme.
|
||||
|
||||
function HumanStandardToken(
|
||||
uint256 _initialAmount,
|
||||
string _tokenName,
|
||||
uint8 _decimalUnits,
|
||||
string _tokenSymbol
|
||||
) {
|
||||
balances[msg.sender] = _initialAmount; // Give the creator all initial tokens
|
||||
totalSupply = _initialAmount; // Update total supply
|
||||
name = _tokenName; // Set the name for display purposes
|
||||
decimals = _decimalUnits; // Amount of decimals for display purposes
|
||||
symbol = _tokenSymbol; // Set the symbol for display purposes
|
||||
}
|
||||
|
||||
/* Approves and then calls the receiving contract */
|
||||
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
|
||||
//call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
|
||||
//receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
|
||||
//it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
|
||||
if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
55
js/src/contracts/snippets/standard-token.sol
Normal file
55
js/src/contracts/snippets/standard-token.sol
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
You should inherit from StandardToken or, for a token like you would want to
|
||||
deploy in something like Mist, see HumanStandardToken.sol.
|
||||
(This implements ONLY the standard functions and NOTHING else.
|
||||
If you deploy this, you won't have anything useful.)
|
||||
|
||||
Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20
|
||||
.*/
|
||||
|
||||
import "Token.sol";
|
||||
|
||||
contract StandardToken is Token {
|
||||
|
||||
function transfer(address _to, uint256 _value) returns (bool success) {
|
||||
//Default assumes totalSupply can't be over max (2^256 - 1).
|
||||
//If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap.
|
||||
//Replace the if with this one instead.
|
||||
//if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
|
||||
if (balances[msg.sender] >= _value && _value > 0) {
|
||||
balances[msg.sender] -= _value;
|
||||
balances[_to] += _value;
|
||||
Transfer(msg.sender, _to, _value);
|
||||
return true;
|
||||
} else { return false; }
|
||||
}
|
||||
|
||||
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
|
||||
//same as above. Replace this line with the following if you want to protect against wrapping uints.
|
||||
//if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
|
||||
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) {
|
||||
balances[_to] += _value;
|
||||
balances[_from] -= _value;
|
||||
allowed[_from][msg.sender] -= _value;
|
||||
Transfer(_from, _to, _value);
|
||||
return true;
|
||||
} else { return false; }
|
||||
}
|
||||
|
||||
function balanceOf(address _owner) constant returns (uint256 balance) {
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
function approve(address _spender, uint256 _value) returns (bool success) {
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
|
||||
mapping (address => uint256) balances;
|
||||
mapping (address => mapping (address => uint256)) allowed;
|
||||
}
|
||||
47
js/src/contracts/snippets/token.sol
Normal file
47
js/src/contracts/snippets/token.sol
Normal file
@@ -0,0 +1,47 @@
|
||||
// Abstract contract for the full ERC 20 Token standard
|
||||
// https://github.com/ethereum/EIPs/issues/20
|
||||
|
||||
contract Token {
|
||||
/* 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;
|
||||
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @return The balance
|
||||
function balanceOf(address _owner) 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) 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) returns (bool success);
|
||||
|
||||
/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @param _value The amount of wei to be approved for transfer
|
||||
/// @return Whether the approval was successful or not
|
||||
function approve(address _spender, uint256 _value) 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) constant returns (uint256 remaining);
|
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="/parity-logo-black-no-text.png" type="image/png">
|
||||
<title>Basic Token Deployment</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -21,3 +21,7 @@
|
||||
.iconMenu option {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
// import { api } from '../parity';
|
||||
import { api } from '../parity';
|
||||
import { attachInstances } from '../services';
|
||||
|
||||
import Header from './Header';
|
||||
@@ -83,7 +83,7 @@ export default class Application extends Component {
|
||||
Promise
|
||||
.all([
|
||||
attachInstances(),
|
||||
null // api.personal.accountsInfo()
|
||||
api.parity.accounts()
|
||||
])
|
||||
.then(([{ managerInstance, registryInstance, tokenregInstance }, accountsInfo]) => {
|
||||
accountsInfo = accountsInfo || {};
|
||||
|
||||
@@ -148,7 +148,7 @@ export default class Deployment extends Component {
|
||||
addresses={ addresses }
|
||||
onChange={ this.onChangeFrom } />
|
||||
<div className={ styles.hint }>
|
||||
the owner account to eploy from
|
||||
the owner account to deploy from
|
||||
</div>
|
||||
</div>
|
||||
<div className={ nameError ? error : styles.input }>
|
||||
@@ -296,7 +296,7 @@ export default class Deployment extends Component {
|
||||
.then((signerRequestId) => {
|
||||
this.setState({ signerRequestId, deployState: 'Transaction posted, Waiting for transaction authorization' });
|
||||
|
||||
return api.pollMethod('eth_checkRequest', signerRequestId);
|
||||
return api.pollMethod('parity_checkRequest', signerRequestId);
|
||||
})
|
||||
.then((txHash) => {
|
||||
this.setState({ txHash, deployState: 'Transaction authorized, Waiting for network confirmations' });
|
||||
|
||||
@@ -279,7 +279,7 @@ export default class Send extends Component {
|
||||
.then((signerRequestId) => {
|
||||
this.setState({ signerRequestId, sendState: 'Transaction posted, Waiting for transaction authorization' });
|
||||
|
||||
return api.pollMethod('eth_checkRequest', signerRequestId);
|
||||
return api.pollMethod('parity_checkRequest', signerRequestId);
|
||||
})
|
||||
.then((txHash) => {
|
||||
this.setState({ txHash, sendState: 'Transaction authorized, Waiting for network confirmations' });
|
||||
|
||||
@@ -100,8 +100,8 @@ export function attachInstances () {
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
api.ethcore.registryAddress(),
|
||||
api.ethcore.netChain()
|
||||
api.parity.registryAddress(),
|
||||
api.parity.netChain()
|
||||
])
|
||||
.then(([registryAddress, netChain]) => {
|
||||
const registry = api.newContract(abis.registry, registryAddress).instance;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>GAVcoin</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script src="vendor.js"></script>
|
||||
<script src="commons.js"></script>
|
||||
<script src="/parity-utils/parity.js"></script>
|
||||
<script src="gavcoin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
injectTapEventPlugin();
|
||||
|
||||
import Application from './gavcoin/Application';
|
||||
|
||||
import '../../assets/fonts/Roboto/font.css';
|
||||
import '../../assets/fonts/RobotoMono/font.css';
|
||||
import './style.css';
|
||||
import './gavcoin.html';
|
||||
|
||||
ReactDOM.render(
|
||||
<Application />,
|
||||
document.querySelector('#container')
|
||||
);
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
|
||||
import styles from './accountItem.css';
|
||||
|
||||
export default class AccountItem extends Component {
|
||||
static propTypes = {
|
||||
account: PropTypes.object,
|
||||
gavBalance: PropTypes.bool
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, gavBalance } = this.props;
|
||||
|
||||
let balance;
|
||||
let token;
|
||||
|
||||
if (gavBalance) {
|
||||
if (account.gavBalance) {
|
||||
balance = account.gavBalance;
|
||||
token = 'GAV';
|
||||
}
|
||||
} else {
|
||||
if (account.ethBalance) {
|
||||
balance = account.ethBalance;
|
||||
token = 'ETH';
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.account }>
|
||||
<div className={ styles.image }>
|
||||
<IdentityIcon address={ account.address } />
|
||||
</div>
|
||||
<div className={ styles.details }>
|
||||
<div className={ styles.name }>
|
||||
{ account.name || account.address }
|
||||
</div>
|
||||
<div className={ styles.balance }>
|
||||
{ balance }<small> { token }</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { MenuItem, SelectField } from 'material-ui';
|
||||
|
||||
import AccountItem from './AccountItem';
|
||||
|
||||
const NAME_ID = ' ';
|
||||
let lastSelectedAccount = {};
|
||||
|
||||
export default class AccountSelect extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array,
|
||||
account: PropTypes.object,
|
||||
anyAccount: PropTypes.bool,
|
||||
gavBalance: PropTypes.bool,
|
||||
onSelect: PropTypes.func,
|
||||
errorText: PropTypes.string,
|
||||
floatingLabelText: PropTypes.string,
|
||||
hintText: PropTypes.string
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.props.onSelect(lastSelectedAccount);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { account, accounts, anyAccount, errorText, floatingLabelText, gavBalance, hintText } = this.props;
|
||||
|
||||
return (
|
||||
<SelectField
|
||||
autoComplete='off'
|
||||
floatingLabelFixed
|
||||
floatingLabelText={ floatingLabelText }
|
||||
fullWidth
|
||||
hintText={ hintText }
|
||||
errorText={ errorText }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
value={ account }
|
||||
onChange={ this.onSelect }>
|
||||
{ renderAccounts(accounts, { anyAccount, gavBalance }) }
|
||||
</SelectField>
|
||||
);
|
||||
}
|
||||
|
||||
onSelect = (event, idx, account) => {
|
||||
lastSelectedAccount = account || {};
|
||||
this.props.onSelect(lastSelectedAccount);
|
||||
}
|
||||
}
|
||||
|
||||
function isPositive (numberStr) {
|
||||
return new BigNumber(numberStr.replace(',', '')).gt(0);
|
||||
}
|
||||
|
||||
export function renderAccounts (accounts, options = {}) {
|
||||
return accounts
|
||||
.filter((account) => {
|
||||
if (options.anyAccount) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (account.uuid) {
|
||||
return isPositive(account[options.gavBalance ? 'gavBalance' : 'ethBalance']);
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.map((account) => {
|
||||
const item = (
|
||||
<AccountItem
|
||||
account={ account }
|
||||
key={ account.address }
|
||||
gavBalance={ options.gavBalance || false } />
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={ account.address }
|
||||
value={ account }
|
||||
label={ item }>
|
||||
{ item }
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { TextField } from 'material-ui';
|
||||
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
import AccountSelector from '../AccountSelector';
|
||||
|
||||
import styles from './accountSelectorText.css';
|
||||
|
||||
const NAME_ID = ' ';
|
||||
|
||||
export default class AccountSelectorText extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array,
|
||||
account: PropTypes.object,
|
||||
errorText: PropTypes.string,
|
||||
gavBalance: PropTypes.bool,
|
||||
anyAccount: PropTypes.bool,
|
||||
floatingLabelText: PropTypes.string,
|
||||
hintText: PropTypes.string,
|
||||
selector: PropTypes.bool,
|
||||
onChange: PropTypes.func
|
||||
}
|
||||
|
||||
render () {
|
||||
const { selector } = this.props;
|
||||
|
||||
return selector
|
||||
? this.renderDropDown()
|
||||
: this.renderTextField();
|
||||
}
|
||||
|
||||
renderDropDown () {
|
||||
const { account, accounts, anyAccount, errorText, gavBalance, hintText, floatingLabelText, onChange } = this.props;
|
||||
|
||||
return (
|
||||
<AccountSelector
|
||||
anyAccount={ anyAccount }
|
||||
gavBalance={ gavBalance }
|
||||
accounts={ accounts }
|
||||
account={ account }
|
||||
errorText={ errorText }
|
||||
floatingLabelText={ floatingLabelText }
|
||||
hintText={ hintText }
|
||||
onSelect={ onChange } />
|
||||
);
|
||||
}
|
||||
|
||||
renderTextField () {
|
||||
const { account, errorText, hintText, floatingLabelText } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ styles.addrtext }>
|
||||
<TextField
|
||||
className={ styles.input }
|
||||
autoComplete='off'
|
||||
floatingLabelFixed
|
||||
floatingLabelText={ floatingLabelText }
|
||||
fullWidth
|
||||
hintText={ hintText }
|
||||
errorText={ errorText }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
value={ account.address || '' }
|
||||
onChange={ this.onChangeAddress } />
|
||||
{ this.renderAddressIcon() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddressIcon () {
|
||||
const { account } = this.props;
|
||||
|
||||
if (!account.address) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.addricon }>
|
||||
<IdentityIcon address={ account.address } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeAddress = (event, address) => {
|
||||
const lower = address.toLowerCase();
|
||||
const account = this.props.accounts.find((_account) => _account.address.toLowerCase() === lower);
|
||||
|
||||
this.props.onChange(account || { address });
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './accountSelectorText';
|
||||
@@ -1,47 +0,0 @@
|
||||
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity 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.
|
||||
/*
|
||||
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.accounts {
|
||||
padding: 4em 2em 0 2em;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.account {
|
||||
margin: 0.5em !important;
|
||||
background: rgb(50, 100, 150) !important;
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.account img {
|
||||
margin-bottom: -11px;
|
||||
margin-left: -11px;
|
||||
}
|
||||
|
||||
.balance {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: rgba(255, 255, 255, 0.7) !important;
|
||||
margin-right: 1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.none {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Chip } from 'material-ui';
|
||||
|
||||
import IdentityIcon from '../IdentityIcon';
|
||||
|
||||
import styles from './accounts.css';
|
||||
|
||||
export default class Accounts extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired,
|
||||
instance: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array
|
||||
}
|
||||
|
||||
render () {
|
||||
const has = this._hasAccounts();
|
||||
|
||||
return (
|
||||
<div className={ styles.accounts }>
|
||||
{ has ? this.renderAccounts() : this.renderEmpty() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderEmpty () {
|
||||
return (
|
||||
<div className={ styles.none }>
|
||||
You currently do not have any GAVcoin in any of your addresses, buy some
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccounts () {
|
||||
const { accounts } = this.props;
|
||||
|
||||
return accounts
|
||||
.filter((account) => account.hasGav)
|
||||
.map((account) => {
|
||||
return (
|
||||
<Chip
|
||||
className={ styles.account }
|
||||
key={ account.address }>
|
||||
<IdentityIcon address={ account.address } />
|
||||
<span className={ styles.name }>
|
||||
{ account.name }
|
||||
</span>
|
||||
<span className={ styles.balance }>
|
||||
{ account.gavBalance }
|
||||
</span>
|
||||
</Chip>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_hasAccounts () {
|
||||
const { accounts } = this.props;
|
||||
|
||||
if (!accounts || !accounts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return accounts.filter((account) => account.hasGav).length !== 0;
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Dialog, FlatButton, TextField } from 'material-ui';
|
||||
|
||||
import { api } from '../../parity';
|
||||
import AccountSelector from '../../AccountSelector';
|
||||
import { ERRORS, validateAccount, validatePositiveNumber } from '../validation';
|
||||
|
||||
import styles from '../actions.css';
|
||||
|
||||
const NAME_ID = ' ';
|
||||
|
||||
export default class ActionBuyIn extends Component {
|
||||
static contextTypes = {
|
||||
instance: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array,
|
||||
price: PropTypes.object,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
|
||||
state = {
|
||||
account: {},
|
||||
accountError: ERRORS.invalidAccount,
|
||||
amount: 0,
|
||||
amountError: ERRORS.invalidAmount,
|
||||
maxPrice: api.util.fromWei(this.props.price.mul(1.2)).toString(),
|
||||
maxPriceError: null,
|
||||
sending: false,
|
||||
complete: false
|
||||
}
|
||||
|
||||
render () {
|
||||
const { complete } = this.state;
|
||||
|
||||
if (complete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title='buy coins for a specific account'
|
||||
modal open
|
||||
className={ styles.dialog }
|
||||
actions={ this.renderActions() }>
|
||||
{ this.renderFields() }
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions () {
|
||||
const { complete } = this.state;
|
||||
|
||||
if (complete) {
|
||||
return (
|
||||
<FlatButton
|
||||
label='Done'
|
||||
primary
|
||||
onTouchTap={ this.props.onClose } />
|
||||
);
|
||||
}
|
||||
|
||||
const { accountError, amountError, maxPriceError, sending } = this.state;
|
||||
const hasError = !!(amountError || accountError || maxPriceError);
|
||||
|
||||
return ([
|
||||
<FlatButton
|
||||
label='Cancel'
|
||||
primary
|
||||
onTouchTap={ this.props.onClose } />,
|
||||
<FlatButton
|
||||
label='Buy'
|
||||
primary
|
||||
disabled={ hasError || sending }
|
||||
onTouchTap={ this.onSend } />
|
||||
]);
|
||||
}
|
||||
|
||||
renderFields () {
|
||||
const maxPriceLabel = `maximum price in ETH (current ${api.util.fromWei(this.props.price).toFormat(3)})`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AccountSelector
|
||||
accounts={ this.props.accounts }
|
||||
account={ this.state.account }
|
||||
errorText={ this.state.accountError }
|
||||
floatingLabelText='from account'
|
||||
hintText='the account the transaction will be made from'
|
||||
onSelect={ this.onChangeAddress } />
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
floatingLabelFixed
|
||||
floatingLabelText='amount in ETH'
|
||||
fullWidth
|
||||
hintText='the amount of ETH you wish to spend'
|
||||
errorText={ this.state.amountError }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
value={ this.state.amount }
|
||||
onChange={ this.onChangeAmount } />
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
floatingLabelFixed
|
||||
floatingLabelText={ maxPriceLabel }
|
||||
fullWidth
|
||||
hintText='the maxium price allowed for buying'
|
||||
errorText={ this.state.maxPriceError }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
value={ this.state.maxPrice }
|
||||
onChange={ this.onChangeMaxPrice } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeAddress = (account) => {
|
||||
this.setState({
|
||||
account,
|
||||
accountError: validateAccount(account)
|
||||
}, this.validateTotal);
|
||||
}
|
||||
|
||||
onChangeAmount = (event, amount) => {
|
||||
this.setState({
|
||||
amount,
|
||||
amountError: validatePositiveNumber(amount)
|
||||
}, this.validateTotal);
|
||||
}
|
||||
|
||||
onChangeMaxPrice = (event, maxPrice) => {
|
||||
this.setState({
|
||||
maxPrice,
|
||||
maxPriceError: validatePositiveNumber(maxPrice)
|
||||
});
|
||||
}
|
||||
|
||||
validateTotal = () => {
|
||||
const { account, accountError, amount, amountError } = this.state;
|
||||
|
||||
if (accountError || amountError) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (new BigNumber(amount).gt(account.ethBalance.replace(',', ''))) {
|
||||
this.setState({
|
||||
amountError: ERRORS.invalidTotal
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onSend = () => {
|
||||
const { instance } = this.context;
|
||||
const maxPrice = api.util.toWei(this.state.maxPrice);
|
||||
const values = [this.state.account.address, maxPrice.toString()];
|
||||
const options = {
|
||||
from: this.state.account.address,
|
||||
value: api.util.toWei(this.state.amount).toString()
|
||||
};
|
||||
|
||||
this.setState({
|
||||
sending: true
|
||||
});
|
||||
|
||||
instance.buyin
|
||||
.estimateGas(options, values)
|
||||
.then((gasEstimate) => {
|
||||
options.gas = gasEstimate.mul(1.2).toFixed(0);
|
||||
console.log(`buyin: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`);
|
||||
|
||||
return instance.buyin.postTransaction(options, values);
|
||||
})
|
||||
.then(() => {
|
||||
this.props.onClose();
|
||||
this.setState({
|
||||
sending: false,
|
||||
complete: true
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('error', error);
|
||||
this.setState({
|
||||
sending: false
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Dialog, FlatButton, TextField } from 'material-ui';
|
||||
|
||||
import { api } from '../../parity';
|
||||
import AccountSelector from '../../AccountSelector';
|
||||
import { ERRORS, validateAccount, validatePositiveNumber } from '../validation';
|
||||
|
||||
import styles from '../actions.css';
|
||||
|
||||
const DIVISOR = 10 ** 6;
|
||||
const NAME_ID = ' ';
|
||||
|
||||
export default class ActionRefund extends Component {
|
||||
static contextTypes = {
|
||||
instance: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array,
|
||||
price: PropTypes.object,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
|
||||
state = {
|
||||
account: {},
|
||||
accountError: ERRORS.invalidAccount,
|
||||
complete: false,
|
||||
sending: false,
|
||||
amount: 0,
|
||||
amountError: ERRORS.invalidAmount,
|
||||
price: api.util.fromWei(this.props.price).toString(),
|
||||
priceError: null
|
||||
}
|
||||
|
||||
render () {
|
||||
const { complete } = this.state;
|
||||
|
||||
if (complete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title='return coins for a refund'
|
||||
modal open
|
||||
className={ styles.dialog }
|
||||
actions={ this.renderActions() }>
|
||||
{ this.renderFields() }
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions () {
|
||||
if (this.state.complete) {
|
||||
return (
|
||||
<FlatButton
|
||||
label='Done'
|
||||
primary
|
||||
onTouchTap={ this.props.onClose } />
|
||||
);
|
||||
}
|
||||
|
||||
const hasError = !!(this.state.priceError || this.state.amountError || this.state.accountError);
|
||||
|
||||
return ([
|
||||
<FlatButton
|
||||
label='Cancel'
|
||||
primary
|
||||
onTouchTap={ this.props.onClose } />,
|
||||
<FlatButton
|
||||
label='Refund'
|
||||
primary
|
||||
disabled={ hasError || this.state.sending }
|
||||
onTouchTap={ this.onSend } />
|
||||
]);
|
||||
}
|
||||
|
||||
renderFields () {
|
||||
const priceLabel = `price in ETH (current ${api.util.fromWei(this.props.price).toFormat(3)})`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AccountSelector
|
||||
gavBalance
|
||||
accounts={ this.props.accounts }
|
||||
account={ this.state.account }
|
||||
errorText={ this.state.accountError }
|
||||
floatingLabelText='from account'
|
||||
hintText='the account the transaction will be made from'
|
||||
onSelect={ this.onChangeAddress } />
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
floatingLabelFixed
|
||||
floatingLabelText='number of coins'
|
||||
fullWidth
|
||||
hintText='the number of coins to exchange for an ETH refund'
|
||||
errorText={ this.state.amountError }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
value={ this.state.amount }
|
||||
onChange={ this.onChangeAmount } />
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
floatingLabelFixed
|
||||
floatingLabelText={ priceLabel }
|
||||
fullWidth
|
||||
hintText='the price the refund is requested at'
|
||||
errorText={ this.state.priceError }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
value={ this.state.price }
|
||||
onChange={ this.onChangePrice } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeAddress = (account) => {
|
||||
this.setState({
|
||||
account,
|
||||
accountError: validateAccount(account)
|
||||
});
|
||||
}
|
||||
|
||||
onChangeAmount = (event, amount) => {
|
||||
this.setState({
|
||||
amount,
|
||||
amountError: validatePositiveNumber(amount)
|
||||
});
|
||||
}
|
||||
|
||||
onChangePrice = (event, price) => {
|
||||
this.setState({
|
||||
price,
|
||||
priceError: validatePositiveNumber(price)
|
||||
});
|
||||
}
|
||||
|
||||
onSend = () => {
|
||||
const { instance } = this.context;
|
||||
const price = api.util.toWei(this.state.price);
|
||||
const amount = new BigNumber(this.state.amount).mul(DIVISOR);
|
||||
const values = [price.toString(), amount.toFixed(0)];
|
||||
const options = {
|
||||
from: this.state.account.address
|
||||
};
|
||||
|
||||
this.setState({
|
||||
sending: true
|
||||
});
|
||||
|
||||
instance.refund
|
||||
.estimateGas(options, values)
|
||||
.then((gasEstimate) => {
|
||||
options.gas = gasEstimate.mul(1.2).toFixed(0);
|
||||
console.log(`refund: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`);
|
||||
|
||||
return instance.refund.postTransaction(options, values);
|
||||
})
|
||||
.then(() => {
|
||||
this.props.onClose();
|
||||
this.setState({
|
||||
sending: false,
|
||||
complete: true
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('error', error);
|
||||
this.setState({
|
||||
sending: false
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './actionRefund';
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Dialog, FlatButton, TextField, Toggle } from 'material-ui';
|
||||
|
||||
import AccountSelector from '../../AccountSelector';
|
||||
import AccountSelectorText from '../../AccountSelectorText';
|
||||
import { ERRORS, validateAccount, validatePositiveNumber } from '../validation';
|
||||
|
||||
import styles from '../actions.css';
|
||||
|
||||
const DIVISOR = 10 ** 6;
|
||||
const NAME_ID = ' ';
|
||||
|
||||
export default class ActionTransfer extends Component {
|
||||
static contextTypes = {
|
||||
instance: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array,
|
||||
price: PropTypes.object,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
|
||||
state = {
|
||||
fromAccount: {},
|
||||
fromAccountError: ERRORS.invalidAccount,
|
||||
toAccount: {},
|
||||
toAccountError: ERRORS.invalidRecipient,
|
||||
inputAccount: false,
|
||||
complete: false,
|
||||
sending: false,
|
||||
amount: 0,
|
||||
amountError: ERRORS.invalidAmount
|
||||
}
|
||||
|
||||
render () {
|
||||
const { complete } = this.state;
|
||||
|
||||
if (complete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title='send coins to another account'
|
||||
modal open
|
||||
className={ styles.dialog }
|
||||
actions={ this.renderActions() }>
|
||||
{ this.renderFields() }
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
renderActions () {
|
||||
const { complete, sending, amountError, fromAccountError, toAccountError } = this.state;
|
||||
|
||||
if (complete) {
|
||||
return (
|
||||
<FlatButton
|
||||
label='Done'
|
||||
primary
|
||||
onTouchTap={ this.props.onClose } />
|
||||
);
|
||||
}
|
||||
|
||||
const hasError = !!(amountError || fromAccountError || toAccountError);
|
||||
|
||||
return ([
|
||||
<FlatButton
|
||||
label='Cancel'
|
||||
primary
|
||||
onTouchTap={ this.props.onClose } />,
|
||||
<FlatButton
|
||||
label='Transfer'
|
||||
primary
|
||||
disabled={ hasError || sending }
|
||||
onTouchTap={ this.onSend } />
|
||||
]);
|
||||
}
|
||||
|
||||
renderFields () {
|
||||
const { accounts } = this.props;
|
||||
const { fromAccount, fromAccountError, toAccount, toAccountError, inputAccount, amount, amountError } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AccountSelector
|
||||
gavBalance
|
||||
accounts={ accounts }
|
||||
account={ fromAccount }
|
||||
errorText={ fromAccountError }
|
||||
floatingLabelText='from account'
|
||||
hintText='the account the transaction will be made from'
|
||||
onSelect={ this.onChangeFromAccount } />
|
||||
<div className={ styles.overlay }>
|
||||
<AccountSelectorText
|
||||
gavBalance anyAccount
|
||||
selector={ !inputAccount }
|
||||
accounts={ accounts }
|
||||
account={ toAccount }
|
||||
errorText={ toAccountError }
|
||||
floatingLabelText='to account'
|
||||
hintText='the account the coins will be sent to'
|
||||
onChange={ this.onChangeToAccount } />
|
||||
<Toggle
|
||||
className={ styles.overlaytoggle }
|
||||
label='Edit'
|
||||
labelPosition='right'
|
||||
toggled={ inputAccount }
|
||||
onToggle={ this.onChangeToInput } />
|
||||
</div>
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
floatingLabelFixed
|
||||
floatingLabelText='number of coins'
|
||||
fullWidth
|
||||
hintText='the number of coins to transfer'
|
||||
errorText={ amountError }
|
||||
name={ NAME_ID }
|
||||
id={ NAME_ID }
|
||||
value={ amount }
|
||||
onChange={ this.onChangeAmount } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeFromAccount = (fromAccount) => {
|
||||
this.setState({
|
||||
fromAccount,
|
||||
fromAccountError: validateAccount(fromAccount)
|
||||
}, this.validateTotal);
|
||||
}
|
||||
|
||||
onChangeToAccount = (toAccount) => {
|
||||
this.setState({
|
||||
toAccount,
|
||||
toAccountError: validateAccount(toAccount)
|
||||
});
|
||||
}
|
||||
|
||||
onChangeToInput = () => {
|
||||
this.setState({
|
||||
inputAccount: !this.state.inputAccount
|
||||
});
|
||||
}
|
||||
|
||||
onChangeAmount = (event, amount) => {
|
||||
this.setState({
|
||||
amount,
|
||||
amountError: validatePositiveNumber(amount)
|
||||
}, this.validateTotal);
|
||||
}
|
||||
|
||||
validateTotal = () => {
|
||||
const { fromAccount, fromAccountError, amount, amountError } = this.state;
|
||||
|
||||
if (fromAccountError || amountError) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (new BigNumber(amount).gt(fromAccount.gavBalance.replace(',', ''))) {
|
||||
this.setState({
|
||||
amountError: ERRORS.invalidTotal
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onSend = () => {
|
||||
const { instance } = this.context;
|
||||
const amount = new BigNumber(this.state.amount).mul(DIVISOR);
|
||||
const values = [this.state.toAccount.address, amount.toFixed(0)];
|
||||
const options = {
|
||||
from: this.state.fromAccount.address
|
||||
};
|
||||
|
||||
this.setState({
|
||||
sending: true
|
||||
});
|
||||
|
||||
instance.transfer
|
||||
.estimateGas(options, values)
|
||||
.then((gasEstimate) => {
|
||||
options.gas = gasEstimate.mul(1.2).toFixed(0);
|
||||
console.log(`transfer: gas estimated as ${gasEstimate.toFixed(0)} setting to ${options.gas}`);
|
||||
|
||||
return instance.transfer.postTransaction(options, values);
|
||||
})
|
||||
.then(() => {
|
||||
this.props.onClose();
|
||||
this.setState({
|
||||
sending: false,
|
||||
complete: true
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('error', error);
|
||||
this.setState({
|
||||
sending: false
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './actionTransfer';
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './stepComplete';
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import styles from '../actions.css';
|
||||
|
||||
export default class StepComplete extends Component {
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.dialogtext }>
|
||||
Your transaction has been posted. Please visit the <a href='http://127.0.0.1:8080/#/signer' className={ styles.link } target='_blank'>Parity Signer</a> to authenticate the transfer.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity 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.
|
||||
/*
|
||||
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.actions {
|
||||
text-align: center;
|
||||
padding: 2em 2em 0 2em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.button button {
|
||||
background-color: rgba(50, 100, 150, 1) !important;
|
||||
height: 56px !important;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
|
||||
.button button[disabled] {
|
||||
background-color: rgba(50, 50, 50, 0.25) !important;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
}
|
||||
|
||||
.dialog h3 {
|
||||
color: rgba(50, 100, 150, 1) !important;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dialogtext {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.link, .link:hover, .link:visited {
|
||||
color: rgb(0, 188, 212);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.overlay svg {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.overlaytoggle {
|
||||
position: absolute !important;
|
||||
top: 40px;
|
||||
right: 0;
|
||||
display: inline-block !important;
|
||||
width: auto !important;
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { RaisedButton } from 'material-ui';
|
||||
import ActionAddShoppingCart from 'material-ui/svg-icons/action/add-shopping-cart';
|
||||
// import AvReplay from 'material-ui/svg-icons/av/replay';
|
||||
import ContentSend from 'material-ui/svg-icons/content/send';
|
||||
|
||||
import styles from './actions.css';
|
||||
|
||||
export default class Actions extends Component {
|
||||
static propTypes = {
|
||||
onAction: PropTypes.func.isRequired,
|
||||
gavBalance: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { gavBalance } = this.props;
|
||||
|
||||
return (
|
||||
<div className={ styles.actions }>
|
||||
<RaisedButton
|
||||
className={ styles.button }
|
||||
icon={ <ActionAddShoppingCart /> }
|
||||
label='buy coins'
|
||||
primary
|
||||
onTouchTap={ this.onBuyIn } />
|
||||
<RaisedButton
|
||||
disabled={ !gavBalance || gavBalance.eq(0) }
|
||||
className={ styles.button }
|
||||
icon={ <ContentSend /> }
|
||||
label='send coins'
|
||||
primary
|
||||
onTouchTap={ this.onTransfer } />
|
||||
</div>
|
||||
);
|
||||
|
||||
// <RaisedButton
|
||||
// className={ styles.button }
|
||||
// icon={ <AvReplay /> }
|
||||
// label='claim refund'
|
||||
// primary
|
||||
// onTouchTap={ this.onRefund } />
|
||||
}
|
||||
|
||||
onBuyIn = () => {
|
||||
this.props.onAction('BuyIn');
|
||||
}
|
||||
|
||||
onTransfer = () => {
|
||||
const { gavBalance } = this.props;
|
||||
|
||||
if (gavBalance && gavBalance.gt(0)) {
|
||||
this.props.onAction('Transfer');
|
||||
}
|
||||
}
|
||||
|
||||
onRefund = () => {
|
||||
this.props.onAction('Refund');
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { api } from '../parity';
|
||||
|
||||
export const ERRORS = {
|
||||
invalidAccount: 'please select an account to transact with',
|
||||
invalidRecipient: 'please select an account to send to',
|
||||
invalidAddress: 'the address is not in the correct format',
|
||||
invalidAmount: 'please enter a positive amount > 0',
|
||||
invalidTotal: 'the amount is greater than the availale balance'
|
||||
};
|
||||
|
||||
export function validatePositiveNumber (value) {
|
||||
let bn = null;
|
||||
|
||||
try {
|
||||
bn = new BigNumber(value);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (!bn || !bn.gt(0)) {
|
||||
return ERRORS.invalidAmount;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function validateAccount (account) {
|
||||
if (!account || !account.address) {
|
||||
return ERRORS.invalidAccount;
|
||||
}
|
||||
|
||||
if (!api.util.isAddressValid(account.address)) {
|
||||
return ERRORS.invalidAddress;
|
||||
}
|
||||
|
||||
account.address = api.util.toChecksumAddress(account.address);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import getMuiTheme from 'material-ui/styles/getMuiTheme';
|
||||
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
|
||||
|
||||
const muiTheme = getMuiTheme(lightBaseTheme);
|
||||
|
||||
import { api } from '../parity';
|
||||
|
||||
import * as abis from '../../../contracts/abi';
|
||||
|
||||
import Accounts from '../Accounts';
|
||||
import Actions, { ActionBuyIn, ActionRefund, ActionTransfer } from '../Actions';
|
||||
import Events from '../Events';
|
||||
import Loading from '../Loading';
|
||||
import Status from '../Status';
|
||||
|
||||
const DIVISOR = 10 ** 6;
|
||||
|
||||
export default class Application extends Component {
|
||||
static childContextTypes = {
|
||||
api: PropTypes.object,
|
||||
contract: PropTypes.object,
|
||||
instance: PropTypes.object,
|
||||
muiTheme: PropTypes.object
|
||||
};
|
||||
|
||||
state = {
|
||||
action: null,
|
||||
address: null,
|
||||
accounts: [],
|
||||
blockNumber: new BigNumber(-1),
|
||||
ethBalance: new BigNumber(0),
|
||||
gavBalance: new BigNumber(0),
|
||||
instance: null,
|
||||
loading: true,
|
||||
price: null,
|
||||
remaining: null,
|
||||
totalSupply: null
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.attachInterface();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accounts, address, blockNumber, gavBalance, loading, price, remaining, totalSupply } = this.state;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Loading />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ this.renderModals() }
|
||||
<Status
|
||||
address={ address }
|
||||
blockNumber={ blockNumber }
|
||||
gavBalance={ gavBalance }
|
||||
price={ price }
|
||||
remaining={ remaining }
|
||||
totalSupply={ totalSupply }>
|
||||
<Accounts
|
||||
accounts={ accounts } />
|
||||
</Status>
|
||||
<Actions
|
||||
gavBalance={ gavBalance }
|
||||
onAction={ this.onAction } />
|
||||
<Events
|
||||
accounts={ accounts } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderModals () {
|
||||
const { action, accounts, price } = this.state;
|
||||
|
||||
switch (action) {
|
||||
case 'BuyIn':
|
||||
return (
|
||||
<ActionBuyIn
|
||||
accounts={ accounts }
|
||||
price={ price }
|
||||
onClose={ this.onActionClose } />
|
||||
);
|
||||
case 'Refund':
|
||||
return (
|
||||
<ActionRefund
|
||||
accounts={ accounts }
|
||||
price={ price }
|
||||
onClose={ this.onActionClose } />
|
||||
);
|
||||
case 'Transfer':
|
||||
return (
|
||||
<ActionTransfer
|
||||
accounts={ accounts }
|
||||
onClose={ this.onActionClose } />
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
const { contract, instance } = this.state;
|
||||
|
||||
return {
|
||||
api,
|
||||
contract,
|
||||
instance,
|
||||
muiTheme
|
||||
};
|
||||
}
|
||||
|
||||
onAction = (action) => {
|
||||
this.setState({
|
||||
action
|
||||
});
|
||||
}
|
||||
|
||||
onActionClose = () => {
|
||||
this.setState({
|
||||
action: null
|
||||
});
|
||||
}
|
||||
|
||||
onNewBlockNumber = (_error, blockNumber) => {
|
||||
const { instance, accounts } = this.state;
|
||||
|
||||
if (_error) {
|
||||
console.error('onNewBlockNumber', _error);
|
||||
return;
|
||||
}
|
||||
|
||||
Promise
|
||||
.all([
|
||||
instance.totalSupply.call(),
|
||||
instance.remaining.call(),
|
||||
instance.price.call()
|
||||
])
|
||||
.then(([totalSupply, remaining, price]) => {
|
||||
this.setState({
|
||||
blockNumber,
|
||||
totalSupply,
|
||||
remaining,
|
||||
price
|
||||
});
|
||||
|
||||
const gavQueries = accounts.map((account) => instance.balanceOf.call({}, [account.address]));
|
||||
const ethQueries = accounts.map((account) => api.eth.getBalance(account.address));
|
||||
|
||||
return Promise.all([
|
||||
Promise.all(gavQueries),
|
||||
Promise.all(ethQueries)
|
||||
]);
|
||||
})
|
||||
.then(([gavBalances, ethBalances]) => {
|
||||
this.setState({
|
||||
ethBalance: ethBalances.reduce((total, balance) => total.add(balance), new BigNumber(0)),
|
||||
gavBalance: gavBalances.reduce((total, balance) => total.add(balance), new BigNumber(0)),
|
||||
accounts: accounts.map((account, idx) => {
|
||||
const ethBalance = ethBalances[idx];
|
||||
const gavBalance = gavBalances[idx];
|
||||
|
||||
account.ethBalance = api.util.fromWei(ethBalance).toFormat(3);
|
||||
account.gavBalance = gavBalance.div(DIVISOR).toFormat(6);
|
||||
account.hasGav = gavBalance.gt(0);
|
||||
|
||||
return account;
|
||||
})
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('onNewBlockNumber', error);
|
||||
});
|
||||
}
|
||||
|
||||
attachInterface = () => {
|
||||
api.ethcore
|
||||
.registryAddress()
|
||||
.then((registryAddress) => {
|
||||
console.log(`the registry was found at ${registryAddress}`);
|
||||
|
||||
const registry = api.newContract(abis.registry, registryAddress).instance;
|
||||
|
||||
return Promise
|
||||
.all([
|
||||
registry.getAddress.call({}, [api.util.sha3('gavcoin'), 'A']),
|
||||
api.eth.accounts(),
|
||||
null // api.personal.accountsInfo()
|
||||
]);
|
||||
})
|
||||
.then(([address, addresses, infos]) => {
|
||||
infos = infos || {};
|
||||
console.log(`gavcoin was found at ${address}`);
|
||||
|
||||
const contract = api.newContract(abis.gavcoin, address);
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
address,
|
||||
contract,
|
||||
instance: contract.instance,
|
||||
accounts: addresses.map((address) => {
|
||||
const info = infos[address] || {};
|
||||
|
||||
return {
|
||||
address,
|
||||
name: info.name,
|
||||
uuid: info.uuid
|
||||
};
|
||||
})
|
||||
});
|
||||
|
||||
api.subscribe('eth_blockNumber', this.onNewBlockNumber);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('attachInterface', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './application';
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
import { formatBlockNumber, formatCoins, formatEth } from '../../format';
|
||||
|
||||
import styles from '../events.css';
|
||||
|
||||
const EMPTY_COLUMN = (
|
||||
<td></td>
|
||||
);
|
||||
|
||||
export default class Event extends Component {
|
||||
static contextTypes = {
|
||||
accounts: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
event: PropTypes.object,
|
||||
value: PropTypes.object,
|
||||
price: PropTypes.object,
|
||||
fromAddress: PropTypes.string,
|
||||
toAddress: PropTypes.string
|
||||
}
|
||||
|
||||
render () {
|
||||
const { event, fromAddress, toAddress, price, value } = this.props;
|
||||
const { blockNumber, state, type } = event;
|
||||
const cls = `${styles.event} ${styles[state]} ${styles[type.toLowerCase()]}`;
|
||||
|
||||
return (
|
||||
<tr className={ cls }>
|
||||
{ this.renderBlockNumber(blockNumber) }
|
||||
{ this.renderType(type) }
|
||||
{ this.renderValue(value) }
|
||||
{ this.renderPrice(price) }
|
||||
{ this.renderAddress(fromAddress) }
|
||||
{ this.renderAddress(toAddress) }
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
renderBlockNumber (blockNumber) {
|
||||
return (
|
||||
<td className={ styles.blocknumber }>
|
||||
{ formatBlockNumber(blockNumber) }
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddress (address) {
|
||||
if (!address) {
|
||||
return EMPTY_COLUMN;
|
||||
}
|
||||
|
||||
return (
|
||||
<td className={ styles.account }>
|
||||
<IdentityIcon address={ address } />
|
||||
{ this.renderAddressName(address) }
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
renderAddressName (address) {
|
||||
const { accounts } = this.context;
|
||||
const account = accounts.find((_account) => _account.address === address);
|
||||
|
||||
if (account && account.name) {
|
||||
return (
|
||||
<div className={ styles.name }>
|
||||
{ account.name }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.address }>
|
||||
{ address }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderPrice (price) {
|
||||
if (!price) {
|
||||
return EMPTY_COLUMN;
|
||||
}
|
||||
|
||||
return (
|
||||
<td className={ styles.ethvalue }>
|
||||
{ formatEth(price) }<small> ETH</small>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
renderValue (value) {
|
||||
if (!value) {
|
||||
return EMPTY_COLUMN;
|
||||
}
|
||||
|
||||
return (
|
||||
<td className={ styles.gavvalue }>
|
||||
{ formatCoins(value) }<small> GAV</small>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
renderType (type) {
|
||||
return (
|
||||
<td className={ styles.type }>
|
||||
{ type }
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import Event from '../Event';
|
||||
|
||||
export default class EventBuyin extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object
|
||||
}
|
||||
|
||||
render () {
|
||||
const { event } = this.props;
|
||||
const { buyer, price, amount } = event.params;
|
||||
|
||||
return (
|
||||
<Event
|
||||
event={ event }
|
||||
fromAddress={ buyer }
|
||||
value={ amount }
|
||||
price={ price } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './eventBuyin';
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './eventNewTranch';
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import Event from '../Event';
|
||||
|
||||
export default class EventRefund extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object
|
||||
}
|
||||
|
||||
render () {
|
||||
const { event } = this.props;
|
||||
const { buyer, price, amount } = event.params;
|
||||
|
||||
return (
|
||||
<Event
|
||||
event={ event }
|
||||
fromAddress={ buyer }
|
||||
value={ amount }
|
||||
price={ price } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './eventRefund';
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import Event from '../Event';
|
||||
|
||||
export default class EventTransfer extends Component {
|
||||
static propTypes = {
|
||||
event: PropTypes.object
|
||||
}
|
||||
|
||||
render () {
|
||||
const { event } = this.props;
|
||||
const { from, to, value } = event.params;
|
||||
|
||||
return (
|
||||
<Event
|
||||
event={ event }
|
||||
fromAddress={ from }
|
||||
toAddress={ to }
|
||||
value={ value } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './eventTransfer';
|
||||
@@ -1,94 +0,0 @@
|
||||
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity 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.
|
||||
/*
|
||||
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.events {
|
||||
padding: 4em 2em;
|
||||
}
|
||||
|
||||
.list {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.list td {
|
||||
vertical-align: top;
|
||||
padding: 4px 0.5em;
|
||||
max-height: 32px;
|
||||
}
|
||||
|
||||
.event {
|
||||
line-height: 32px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.blocknumber,
|
||||
.ethvalue,
|
||||
.gavvalue {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
|
||||
.blocknumber,
|
||||
.gavvalue {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ethvalue {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.type {
|
||||
}
|
||||
|
||||
.account {
|
||||
}
|
||||
|
||||
.account img {
|
||||
margin-bottom: -11px;
|
||||
}
|
||||
|
||||
.address {
|
||||
}
|
||||
|
||||
.name {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.event div {
|
||||
display: inline;
|
||||
margin-right: 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.mined {
|
||||
}
|
||||
|
||||
.pending {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.buyin {
|
||||
}
|
||||
|
||||
.refund {
|
||||
}
|
||||
|
||||
.transfer {
|
||||
}
|
||||
|
||||
.newtranch {
|
||||
background: rgba(50, 250, 50, 0.1);
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { api } from '../parity';
|
||||
|
||||
import EventBuyin from './EventBuyin';
|
||||
import EventNewTranch from './EventNewTranch';
|
||||
import EventRefund from './EventRefund';
|
||||
import EventTransfer from './EventTransfer';
|
||||
|
||||
import styles from './events.css';
|
||||
|
||||
export default class Events extends Component {
|
||||
static childContextTypes = {
|
||||
accounts: PropTypes.array
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
contract: PropTypes.object.isRequired,
|
||||
instance: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array
|
||||
}
|
||||
|
||||
state = {
|
||||
allEvents: [],
|
||||
minedEvents: [],
|
||||
pendingEvents: []
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.setupFilters();
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.events }>
|
||||
<table className={ styles.list }>
|
||||
<tbody>
|
||||
{ this.renderEvents() }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderEvents () {
|
||||
const { allEvents } = this.state;
|
||||
|
||||
if (!allEvents.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return allEvents
|
||||
.map((event) => {
|
||||
switch (event.type) {
|
||||
case 'Buyin':
|
||||
return <EventBuyin key={ event.key } event={ event } />;
|
||||
case 'NewTranch':
|
||||
return <EventNewTranch key={ event.key } event={ event } />;
|
||||
case 'Refund':
|
||||
return <EventRefund key={ event.key } event={ event } />;
|
||||
case 'Transfer':
|
||||
return <EventTransfer key={ event.key } event={ event } />;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
const { accounts } = this.props;
|
||||
|
||||
return {
|
||||
accounts
|
||||
};
|
||||
}
|
||||
|
||||
setupFilters () {
|
||||
const { contract } = this.context;
|
||||
|
||||
const sortEvents = (a, b) => b.blockNumber.cmp(a.blockNumber) || b.logIndex.cmp(a.logIndex);
|
||||
const logToEvent = (log) => {
|
||||
const key = api.util.sha3(JSON.stringify(log));
|
||||
const { blockNumber, logIndex, transactionHash, transactionIndex, params, type } = log;
|
||||
|
||||
return {
|
||||
type: log.event,
|
||||
state: type,
|
||||
blockNumber,
|
||||
logIndex,
|
||||
transactionHash,
|
||||
transactionIndex,
|
||||
params: Object.keys(params).reduce((data, name) => {
|
||||
data[name] = params[name].value;
|
||||
return data;
|
||||
}, {}),
|
||||
key
|
||||
};
|
||||
};
|
||||
|
||||
const options = {
|
||||
fromBlock: 0,
|
||||
toBlock: 'pending',
|
||||
limit: 50
|
||||
};
|
||||
|
||||
contract.subscribe(null, options, (error, _logs) => {
|
||||
if (error) {
|
||||
console.error('setupFilters', error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_logs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const logs = _logs.map(logToEvent);
|
||||
|
||||
const minedEvents = logs
|
||||
.filter((log) => log.state === 'mined')
|
||||
.reverse()
|
||||
.concat(this.state.minedEvents)
|
||||
.sort(sortEvents);
|
||||
const pendingEvents = logs
|
||||
.filter((log) => log.state === 'pending')
|
||||
.reverse()
|
||||
.concat(this.state.pendingEvents.filter((event) => {
|
||||
return !logs.find((log) => {
|
||||
const isMined = (log.state === 'mined') && (log.transactionHash === event.transactionHash);
|
||||
const isPending = (log.state === 'pending') && (log.key === event.key);
|
||||
|
||||
return isMined || isPending;
|
||||
});
|
||||
}))
|
||||
.sort(sortEvents);
|
||||
const allEvents = pendingEvents.concat(minedEvents);
|
||||
|
||||
this.setState({
|
||||
allEvents,
|
||||
minedEvents,
|
||||
pendingEvents
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { api } from '../parity';
|
||||
import styles from './identityIcon.css';
|
||||
|
||||
export default class IdentityIcon extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { address } = this.props;
|
||||
|
||||
return (
|
||||
<img
|
||||
className={ styles.icon }
|
||||
src={ api.util.createIdentityImg(address, 4) } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './identityIcon';
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './loading';
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { CircularProgress } from 'material-ui';
|
||||
|
||||
import styles from './loading.css';
|
||||
|
||||
export default class Loading extends Component {
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.loading }>
|
||||
<CircularProgress size={ 120 } thickness={ 7 } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity 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.
|
||||
/*
|
||||
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
.status {
|
||||
background: rgba(25, 75, 125, 1);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
padding: 4em 0 2em 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 0;
|
||||
font-weight: 300;
|
||||
font-size: 2.5rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.item {
|
||||
flex: 0 1 30%;
|
||||
width: 30%;
|
||||
margin: 0 1.5%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.byline {
|
||||
font-size: 1.25em;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.heading {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.25em;
|
||||
font-size: 1.5em;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.hero {
|
||||
font-size: 4em;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { formatBlockNumber, formatCoins, formatEth } from '../format';
|
||||
|
||||
import styles from './status.css';
|
||||
|
||||
export default class Status extends Component {
|
||||
static propTypes = {
|
||||
address: PropTypes.string,
|
||||
gavBalance: PropTypes.object,
|
||||
blockNumber: PropTypes.object,
|
||||
totalSupply: PropTypes.object,
|
||||
remaining: PropTypes.object,
|
||||
price: PropTypes.object,
|
||||
children: PropTypes.node
|
||||
}
|
||||
|
||||
render () {
|
||||
const { blockNumber, gavBalance, totalSupply, remaining, price } = this.props;
|
||||
|
||||
if (!totalSupply) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.status }>
|
||||
<div className={ styles.item }>
|
||||
<div className={ styles.heading }> </div>
|
||||
<div className={ styles.hero }>
|
||||
{ formatCoins(remaining, -1) }
|
||||
</div>
|
||||
<div className={ styles.byline }>
|
||||
available for { formatEth(price) }ETH
|
||||
</div>
|
||||
</div>
|
||||
<div className={ styles.item }>
|
||||
<div className={ styles.heading }>GAVcoin</div>
|
||||
<div className={ styles.hero }>
|
||||
{ formatCoins(totalSupply, -1) }
|
||||
</div>
|
||||
<div className={ styles.byline }>
|
||||
total at { formatBlockNumber(blockNumber) }
|
||||
</div>
|
||||
</div>
|
||||
<div className={ styles.item }>
|
||||
<div className={ styles.heading }> </div>
|
||||
<div className={ styles.hero }>
|
||||
{ formatCoins(gavBalance, -1) }
|
||||
</div>
|
||||
<div className={ styles.byline }>
|
||||
coin balance
|
||||
</div>
|
||||
</div>
|
||||
{ this.props.children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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.
|
||||
|
||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { api } from '../parity';
|
||||
|
||||
const DIVISOR = 10 ** 6;
|
||||
const ZERO = new BigNumber(0);
|
||||
|
||||
export function formatBlockNumber (blockNumber) {
|
||||
return ZERO.eq(blockNumber || 0)
|
||||
? 'Pending'
|
||||
: `#${blockNumber.toFormat()}`;
|
||||
}
|
||||
|
||||
export function formatCoins (amount, decimals = 6) {
|
||||
const adjusted = amount.div(DIVISOR);
|
||||
|
||||
if (decimals === -1) {
|
||||
if (adjusted.gte(10000)) {
|
||||
decimals = 0;
|
||||
} else if (adjusted.gte(1000)) {
|
||||
decimals = 1;
|
||||
} else if (adjusted.gte(100)) {
|
||||
decimals = 2;
|
||||
} else if (adjusted.gte(10)) {
|
||||
decimals = 3;
|
||||
} else {
|
||||
decimals = 4;
|
||||
}
|
||||
}
|
||||
|
||||
return adjusted.toFormat(decimals);
|
||||
}
|
||||
|
||||
export function formatEth (eth, decimals = 3) {
|
||||
return api.util.fromWei(eth).toFormat(decimals);
|
||||
}
|
||||
@@ -4,13 +4,13 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="/parity-logo-black-no-text.png" type="image/png">
|
||||
<title>GitHub Hint</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script src="vendor.js"></script>
|
||||
<script src="commons.js"></script>
|
||||
<script src="/parity-utils/parity.js"></script>
|
||||
<script src="githubhint.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -82,6 +82,10 @@
|
||||
.capture {
|
||||
}
|
||||
|
||||
.capture+.capture {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.capture * {
|
||||
display: inline-block;
|
||||
padding: 0.75em;
|
||||
@@ -108,14 +112,36 @@
|
||||
background: #fcc;
|
||||
}
|
||||
|
||||
.hashError {
|
||||
.hashError, .hashWarning, .hashOk {
|
||||
padding-top: 0.5em;
|
||||
color: #f66;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hashOk {
|
||||
padding-top: 0.5em;
|
||||
opacity: 0.5;
|
||||
text-align: center;
|
||||
.hashError {
|
||||
color: #f66;
|
||||
}
|
||||
|
||||
.hashWarning {
|
||||
color: #f80;
|
||||
}
|
||||
|
||||
.hashOk {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.typeButtons {
|
||||
text-align: center;
|
||||
padding: 0 0 1em 0;
|
||||
}
|
||||
|
||||
.typeButtons>div {
|
||||
border-radius: 0 !important;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 5px 0 0 5px !important;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 5px 5px 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +29,21 @@ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
export default class Application extends Component {
|
||||
state = {
|
||||
fromAddress: null,
|
||||
loading: true,
|
||||
url: '',
|
||||
urlError: null,
|
||||
commit: '',
|
||||
commitError: null,
|
||||
contentHash: '',
|
||||
contentHashError: null,
|
||||
contentHashOwner: null,
|
||||
registerBusy: false,
|
||||
registerError: null,
|
||||
registerState: ''
|
||||
registerState: '',
|
||||
registerType: 'file',
|
||||
repo: '',
|
||||
repoError: null
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@@ -63,25 +70,70 @@ export default class Application extends Component {
|
||||
}
|
||||
|
||||
renderPage () {
|
||||
const { registerBusy, url, urlError, contentHash, contentHashError } = this.state;
|
||||
const { fromAddress, registerBusy, url, urlError, contentHash, contentHashError, contentHashOwner, commit, commitError, registerType, repo, repoError } = this.state;
|
||||
|
||||
let hashClass = null;
|
||||
if (contentHashError) {
|
||||
hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning;
|
||||
} else {
|
||||
hashClass = styles.hashOk;
|
||||
}
|
||||
|
||||
let valueInputs = null;
|
||||
if (registerType === 'content') {
|
||||
valueInputs = [
|
||||
<div className={ styles.capture } key='repo'>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='owner/repo'
|
||||
disabled={ registerBusy }
|
||||
value={ repo }
|
||||
className={ repoError ? styles.error : null }
|
||||
onChange={ this.onChangeRepo } />
|
||||
</div>,
|
||||
<div className={ styles.capture } key='hash'>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='commit hash sha3'
|
||||
disabled={ registerBusy }
|
||||
value={ commit }
|
||||
className={ commitError ? styles.error : null }
|
||||
onChange={ this.onChangeCommit } />
|
||||
</div>
|
||||
];
|
||||
} else {
|
||||
valueInputs = (
|
||||
<div className={ styles.capture } key='url'>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='http://domain/filename'
|
||||
disabled={ registerBusy }
|
||||
value={ url }
|
||||
className={ urlError ? styles.error : null }
|
||||
onChange={ this.onChangeUrl } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.form }>
|
||||
<div className={ styles.typeButtons }>
|
||||
<Button
|
||||
disabled={ registerBusy }
|
||||
invert={ registerType !== 'file' }
|
||||
onClick={ this.onClickTypeNormal }>File Link</Button>
|
||||
<Button
|
||||
disabled={ registerBusy }
|
||||
invert={ registerType !== 'content' }
|
||||
onClick={ this.onClickTypeContent }>Content Bundle</Button>
|
||||
</div>
|
||||
<div className={ styles.box }>
|
||||
<div className={ styles.description }>
|
||||
Provide a valid URL to register. The content information can be used in other contracts that allows for reverse lookups, e.g. image registries, dapp registries, etc.
|
||||
</div>
|
||||
<div className={ styles.capture }>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='http://domain/filename'
|
||||
disabled={ registerBusy }
|
||||
value={ url }
|
||||
className={ urlError ? styles.error : null }
|
||||
onChange={ this.onChangeUrl } />
|
||||
</div>
|
||||
<div className={ contentHashError ? styles.hashError : styles.hashOk }>
|
||||
{ valueInputs }
|
||||
<div className={ hashClass }>
|
||||
{ contentHashError || contentHash }
|
||||
</div>
|
||||
{ registerBusy ? this.renderProgress() : this.renderButtons() }
|
||||
@@ -92,7 +144,7 @@ export default class Application extends Component {
|
||||
}
|
||||
|
||||
renderButtons () {
|
||||
const { accounts, fromAddress, url, urlError, contentHashError } = this.state;
|
||||
const { accounts, fromAddress, urlError, repoError, commitError, contentHashError, contentHashOwner } = this.state;
|
||||
const account = accounts[fromAddress];
|
||||
|
||||
return (
|
||||
@@ -105,7 +157,7 @@ export default class Application extends Component {
|
||||
</div>
|
||||
<Button
|
||||
onClick={ this.onClickRegister }
|
||||
disabled={ !!contentHashError || !!urlError || url.length === 0 }>register url</Button>
|
||||
disabled={ (contentHashError && contentHashOwner !== fromAddress) || urlError || repoError || commitError }>register url</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -138,57 +190,107 @@ export default class Application extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
onClickContentHash = () => {
|
||||
this.setState({ fileHash: false, commit: '' });
|
||||
onClickTypeNormal = () => {
|
||||
const { url } = this.state;
|
||||
|
||||
this.setState({ registerType: 'file', commitError: null, repoError: null }, () => {
|
||||
this.onChangeUrl({ target: { value: url } });
|
||||
});
|
||||
}
|
||||
|
||||
onClickFileHash = () => {
|
||||
this.setState({ fileHash: true, commit: 0 });
|
||||
onClickTypeContent = () => {
|
||||
const { repo, commit } = this.state;
|
||||
|
||||
this.setState({ registerType: 'content', urlError: null }, () => {
|
||||
this.onChangeRepo({ target: { value: repo } });
|
||||
this.onChangeCommit({ target: { value: commit } });
|
||||
});
|
||||
}
|
||||
|
||||
onChangeCommit = (event) => {
|
||||
let commit = event.target.value;
|
||||
const commitError = null;
|
||||
let hasContent = false;
|
||||
|
||||
this.setState({ commit, commitError, contentHashError: null }, () => {
|
||||
const { repo } = this.state || '';
|
||||
const parts = repo.split('/');
|
||||
|
||||
hasContent = commit.length !== 0 && parts.length === 2 && parts[0].length !== 0 && parts[1].length !== 0;
|
||||
if (!commitError && hasContent) {
|
||||
this.setState({ contentHashError: 'hash lookup in progress' });
|
||||
this.lookupHash(`https://codeload.github.com/${repo}/zip/${commit}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onChangeRepo = (event) => {
|
||||
let repo = event.target.value;
|
||||
const repoError = null;
|
||||
let hasContent = false;
|
||||
|
||||
// TODO: field validation
|
||||
if (!repoError) {
|
||||
repo = repo.replace('https://github.com/', '');
|
||||
}
|
||||
|
||||
this.setState({ repo, repoError, contentHashError: null }, () => {
|
||||
const { commit } = this.state || '';
|
||||
const parts = repo.split('/');
|
||||
|
||||
hasContent = commit.length !== 0 && parts.length === 2 && parts[0].length !== 0 && parts[1].length !== 0;
|
||||
if (!repoError && hasContent) {
|
||||
this.setState({ contentHashError: 'hash lookup in progress' });
|
||||
this.lookupHash(`https://codeload.github.com/${repo}/zip/${commit}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onChangeUrl = (event) => {
|
||||
const url = event.target.value;
|
||||
let urlError = null;
|
||||
let url = event.target.value;
|
||||
const urlError = null;
|
||||
let hasContent = false;
|
||||
|
||||
if (url && url.length) {
|
||||
var re = /^https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}/g;
|
||||
urlError = re.test(url)
|
||||
? null
|
||||
: 'not matching rexex';
|
||||
// TODO: field validation
|
||||
if (!urlError) {
|
||||
const parts = url.split('/');
|
||||
hasContent = parts.length !== 0;
|
||||
|
||||
if (parts[2] === 'github.com' || parts[2] === 'raw.githubusercontent.com') {
|
||||
url = `https://raw.githubusercontent.com/${parts.slice(3).join('/')}`.replace('/blob/', '/');
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ url, urlError, contentHashError: 'hash lookup in progress' }, () => {
|
||||
this.lookupHash();
|
||||
this.setState({ url, urlError, contentHashError: null }, () => {
|
||||
if (!urlError && hasContent) {
|
||||
this.setState({ contentHashError: 'hash lookup in progress' });
|
||||
this.lookupHash(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onClickRegister = () => {
|
||||
const { url, urlError, contentHash, contentHashError, fromAddress, instance } = this.state;
|
||||
const { commit, commitError, contentHashError, contentHashOwner, fromAddress, url, urlError, registerType, repo, repoError } = this.state;
|
||||
|
||||
if (!!contentHashError || !!urlError || url.length === 0) {
|
||||
// TODO: No errors are currently set, validation to be expanded and added for each
|
||||
// field (query is fast to pick up the issues, so not burning atm)
|
||||
if ((contentHashError && contentHashOwner !== fromAddress) || repoError || urlError || commitError) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' });
|
||||
if (registerType === 'file') {
|
||||
this.registerUrl(url);
|
||||
} else {
|
||||
this.registerContent(repo, commit);
|
||||
}
|
||||
}
|
||||
|
||||
const values = [contentHash, url];
|
||||
const options = { from: fromAddress };
|
||||
|
||||
instance
|
||||
.hintURL.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
this.setState({ registerState: 'Gas estimated, Posting transaction to the network' });
|
||||
|
||||
const gasPassed = gas.mul(1.2);
|
||||
options.gas = gasPassed.toFixed(0);
|
||||
console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`);
|
||||
|
||||
return instance.hintURL.postTransaction(options, values);
|
||||
})
|
||||
trackRequest (promise) {
|
||||
return promise
|
||||
.then((signerRequestId) => {
|
||||
this.setState({ signerRequestId, registerState: 'Transaction posted, Waiting for transaction authorization' });
|
||||
|
||||
return api.pollMethod('eth_checkRequest', signerRequestId);
|
||||
return api.pollMethod('parity_checkRequest', signerRequestId);
|
||||
})
|
||||
.then((txHash) => {
|
||||
this.setState({ txHash, registerState: 'Transaction authorized, Waiting for network confirmations' });
|
||||
@@ -202,7 +304,7 @@ export default class Application extends Component {
|
||||
});
|
||||
})
|
||||
.then((txReceipt) => {
|
||||
this.setState({ txReceipt, registerBusy: false, registerState: 'Network confirmed, Received transaction receipt', url: '', contentHash: '' });
|
||||
this.setState({ txReceipt, registerBusy: false, registerState: 'Network confirmed, Received transaction receipt', url: '', commit: '', repo: '', commitError: null, contentHash: '', contentHashOwner: null, contentHashError: null });
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('onSend', error);
|
||||
@@ -210,6 +312,52 @@ export default class Application extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
registerContent (repo, commit) {
|
||||
const { contentHash, fromAddress, instance } = this.state;
|
||||
|
||||
this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' });
|
||||
|
||||
const values = [contentHash, repo, commit.substr(0, 2) === '0x' ? commit : `0x${commit}`];
|
||||
const options = { from: fromAddress };
|
||||
|
||||
this.trackRequest(
|
||||
instance
|
||||
.hint.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
this.setState({ registerState: 'Gas estimated, Posting transaction to the network' });
|
||||
|
||||
const gasPassed = gas.mul(1.2);
|
||||
options.gas = gasPassed.toFixed(0);
|
||||
console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`);
|
||||
|
||||
return instance.hint.postTransaction(options, values);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
registerUrl (url) {
|
||||
const { contentHash, fromAddress, instance } = this.state;
|
||||
|
||||
this.setState({ registerBusy: true, registerState: 'Estimating gas for the transaction' });
|
||||
|
||||
const values = [contentHash, url];
|
||||
const options = { from: fromAddress };
|
||||
|
||||
this.trackRequest(
|
||||
instance
|
||||
.hintURL.estimateGas(options, values)
|
||||
.then((gas) => {
|
||||
this.setState({ registerState: 'Gas estimated, Posting transaction to the network' });
|
||||
|
||||
const gasPassed = gas.mul(1.2);
|
||||
options.gas = gasPassed.toFixed(0);
|
||||
console.log(`gas estimated at ${gas.toFormat(0)}, passing ${gasPassed.toFormat(0)}`);
|
||||
|
||||
return instance.hintURL.postTransaction(options, values);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
onSelectFromAddress = () => {
|
||||
const { accounts, fromAddress } = this.state;
|
||||
const addresses = Object.keys(accounts);
|
||||
@@ -229,10 +377,16 @@ export default class Application extends Component {
|
||||
this.setState({ fromAddress: addresses[index] });
|
||||
}
|
||||
|
||||
lookupHash () {
|
||||
const { url, instance } = this.state;
|
||||
lookupHash (url) {
|
||||
const { instance } = this.state;
|
||||
|
||||
api.ethcore
|
||||
if (!url || !url.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`lookupHash ${url}`);
|
||||
|
||||
api.parity
|
||||
.hashContent(url)
|
||||
.then((contentHash) => {
|
||||
console.log('lookupHash', contentHash);
|
||||
@@ -243,13 +397,17 @@ export default class Application extends Component {
|
||||
|
||||
instance.entries
|
||||
.call({}, [contentHash])
|
||||
.then(([accountSlashRepo, commit, owner]) => {
|
||||
console.log('lookupHash', accountSlashRepo, api.util.bytesToHex(commit), owner);
|
||||
.then(([accountSlashRepo, commit, contentHashOwner]) => {
|
||||
console.log('lookupHash', accountSlashRepo, api.util.bytesToHex(commit), contentHashOwner);
|
||||
|
||||
if (owner !== ZERO_ADDRESS) {
|
||||
this.setState({ contentHashError: contentHash, contentHash: null });
|
||||
if (contentHashOwner !== ZERO_ADDRESS) {
|
||||
this.setState({
|
||||
contentHashError: contentHash,
|
||||
contentHashOwner,
|
||||
contentHash
|
||||
});
|
||||
} else {
|
||||
this.setState({ contentHashError: null, contentHash });
|
||||
this.setState({ contentHashError: null, contentHashOwner, contentHash });
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const { api } = window.parity;
|
||||
const api = window.parent.secureApi;
|
||||
|
||||
export {
|
||||
api
|
||||
|
||||
@@ -18,7 +18,7 @@ import * as abis from '../../contracts/abi';
|
||||
import { api } from './parity';
|
||||
|
||||
export function attachInterface () {
|
||||
return api.ethcore
|
||||
return api.parity
|
||||
.registryAddress()
|
||||
.then((registryAddress) => {
|
||||
console.log(`the registry was found at ${registryAddress}`);
|
||||
@@ -28,26 +28,26 @@ export function attachInterface () {
|
||||
return Promise
|
||||
.all([
|
||||
registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A']),
|
||||
api.eth.accounts(),
|
||||
null // api.personal.accountsInfo()
|
||||
api.parity.accounts()
|
||||
]);
|
||||
})
|
||||
.then(([address, addresses, accountsInfo]) => {
|
||||
accountsInfo = accountsInfo || {};
|
||||
.then(([address, accountsInfo]) => {
|
||||
console.log(`githubhint was found at ${address}`);
|
||||
|
||||
const contract = api.newContract(abis.githubhint, address);
|
||||
const accounts = addresses.reduce((obj, address) => {
|
||||
const info = accountsInfo[address] || {};
|
||||
const accounts = Object
|
||||
.keys(accountsInfo)
|
||||
.filter((address) => accountsInfo[address].uuid)
|
||||
.reduce((obj, address) => {
|
||||
const account = accountsInfo[address];
|
||||
|
||||
return Object.assign(obj, {
|
||||
[address]: {
|
||||
address,
|
||||
name: info.name,
|
||||
uuid: info.uuid
|
||||
}
|
||||
});
|
||||
}, {});
|
||||
return Object.assign(obj, {
|
||||
[address]: {
|
||||
address,
|
||||
name: account.name
|
||||
}
|
||||
});
|
||||
}, {});
|
||||
const fromAddress = Object.keys(accounts)[0];
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="/parity-logo-black-no-text.png" type="image/png">
|
||||
<title>Token Registry</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -36,6 +36,8 @@ export default class Accounts extends Component {
|
||||
render () {
|
||||
const { all, selected } = this.props;
|
||||
|
||||
const origin = { horizontal: 'right', vertical: 'top' };
|
||||
|
||||
const accountsButton = (
|
||||
<IconButton className={ styles.button }>
|
||||
{ selected
|
||||
@@ -49,7 +51,9 @@ export default class Accounts extends Component {
|
||||
value={ selected ? this.renderAccount(selected) : null }
|
||||
onChange={ this.onAccountSelect }
|
||||
iconButtonElement={ accountsButton }
|
||||
animated={ false }
|
||||
|
||||
anchorOrigin={ origin }
|
||||
targetOrigin={ origin }
|
||||
>
|
||||
{ Object.values(all).map(this.renderAccount) }
|
||||
</IconMenu>
|
||||
|
||||
@@ -37,3 +37,27 @@
|
||||
font-size: 80%;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin: 1em;
|
||||
|
||||
* {
|
||||
font-size: 1.3rem !important;
|
||||
}
|
||||
|
||||
> * {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
background: #f80;
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
left: 0;
|
||||
opacity: 1;
|
||||
padding: 1.5em;
|
||||
position: fixed;
|
||||
right: 50%;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import getMuiTheme from 'material-ui/styles/getMuiTheme';
|
||||
@@ -21,6 +20,7 @@ import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
|
||||
const muiTheme = getMuiTheme(lightBaseTheme);
|
||||
|
||||
import CircularProgress from 'material-ui/CircularProgress';
|
||||
import { Card, CardText } from 'material-ui/Card';
|
||||
import styles from './application.css';
|
||||
import Accounts from '../Accounts';
|
||||
import Events from '../Events';
|
||||
@@ -35,6 +35,7 @@ export default class Application extends Component {
|
||||
muiTheme: PropTypes.object.isRequired,
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
getChildContext () {
|
||||
return { muiTheme, api: window.parity.api };
|
||||
}
|
||||
@@ -52,18 +53,19 @@ export default class Application extends Component {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { api } = window.parity;
|
||||
const {
|
||||
actions,
|
||||
accounts, contacts,
|
||||
contract, fee,
|
||||
lookup,
|
||||
events,
|
||||
names,
|
||||
records
|
||||
events
|
||||
} = this.props;
|
||||
let warning = null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ warning }
|
||||
<div className={ styles.header }>
|
||||
<h1>RΞgistry</h1>
|
||||
<Accounts { ...accounts } actions={ actions.accounts } />
|
||||
@@ -71,12 +73,11 @@ export default class Application extends Component {
|
||||
{ contract && fee ? (
|
||||
<div>
|
||||
<Lookup { ...lookup } accounts={ accounts.all } contacts={ contacts } actions={ actions.lookup } />
|
||||
<Names { ...names } fee={ fee } actions={ actions.names } />
|
||||
<Records { ...records } actions={ actions.records } />
|
||||
{ this.renderActions() }
|
||||
<Events { ...events } accounts={ accounts.all } contacts={ contacts } actions={ actions.events } />
|
||||
<p className={ styles.address }>
|
||||
The Registry is provided by the contract at <code>{ contract.address }.</code>
|
||||
</p>
|
||||
<div className={ styles.warning }>
|
||||
WARNING: The name registry is experimental. Please ensure that you understand the risks, benefits & consequences of registering a name before doing so. A non-refundable fee of { api.util.fromWei(fee).toFormat(3) }<small>ETH</small> is required for all registrations.
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<CircularProgress size={ 60 } />
|
||||
@@ -85,4 +86,34 @@ export default class Application extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderActions () {
|
||||
const {
|
||||
actions,
|
||||
accounts,
|
||||
fee,
|
||||
names,
|
||||
records
|
||||
} = this.props;
|
||||
|
||||
const hasAccount = !!accounts.selected;
|
||||
|
||||
if (!hasAccount) {
|
||||
return (
|
||||
<Card className={ styles.actions }>
|
||||
<CardText>
|
||||
Please select a valid account in order
|
||||
to execute actions.
|
||||
</CardText>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Names { ...names } fee={ fee } actions={ actions.names } />
|
||||
<Records { ...records } actions={ actions.records } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -93,6 +93,37 @@ export default class Events extends Component {
|
||||
|
||||
render () {
|
||||
const { subscriptions, pending, accounts, contacts } = this.props;
|
||||
|
||||
const eventsObject = this.props.events
|
||||
.filter((e) => eventTypes[e.type])
|
||||
.reduce((eventsObject, event) => {
|
||||
const txHash = event.transaction;
|
||||
|
||||
if (
|
||||
(eventsObject[txHash] && eventsObject[txHash].state === 'pending') ||
|
||||
!eventsObject[txHash]
|
||||
) {
|
||||
eventsObject[txHash] = event;
|
||||
}
|
||||
|
||||
return eventsObject;
|
||||
}, {});
|
||||
|
||||
const events = Object
|
||||
.values(eventsObject)
|
||||
.sort((evA, evB) => {
|
||||
if (evA.state === 'pending') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (evB.state === 'pending') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return evB.timestamp - evA.timestamp;
|
||||
})
|
||||
.map((e) => eventTypes[e.type](e, accounts, contacts));
|
||||
|
||||
return (
|
||||
<Card className={ styles.events }>
|
||||
<CardHeader title='Event Log' />
|
||||
@@ -122,11 +153,7 @@ export default class Events extends Component {
|
||||
<CardText>
|
||||
<table className={ styles.eventsList }>
|
||||
<tbody>
|
||||
{
|
||||
this.props.events
|
||||
.filter((e) => eventTypes[e.type])
|
||||
.map((e) => eventTypes[e.type](e, accounts, contacts))
|
||||
}
|
||||
{ events }
|
||||
</tbody>
|
||||
</table>
|
||||
</CardText>
|
||||
|
||||
@@ -29,12 +29,19 @@ import styles from './names.css';
|
||||
const useSignerText = (<p>Use the <a href='/#/signer' className={ styles.link } target='_blank'>Signer</a> to authenticate the following changes.</p>);
|
||||
|
||||
const renderNames = (names) => {
|
||||
const out = [];
|
||||
for (let name of names) {
|
||||
out.push((<code>{ name }</code>), ', ');
|
||||
}
|
||||
out.pop();
|
||||
return out;
|
||||
const values = Object.values(names);
|
||||
|
||||
return values
|
||||
.map((name, index) => (
|
||||
<span key={ index }>
|
||||
<code>{ name }</code>
|
||||
{
|
||||
index < values.length - 1
|
||||
? (<span>, </span>)
|
||||
: null
|
||||
}
|
||||
</span>
|
||||
));
|
||||
};
|
||||
|
||||
const renderQueue = (queue) => {
|
||||
@@ -70,7 +77,6 @@ export default class Names extends Component {
|
||||
static propTypes = {
|
||||
actions: PropTypes.object.isRequired,
|
||||
fee: PropTypes.object.isRequired,
|
||||
hasAccount: PropTypes.bool.isRequired,
|
||||
pending: PropTypes.bool.isRequired,
|
||||
queue: PropTypes.array.isRequired
|
||||
}
|
||||
@@ -82,20 +88,18 @@ export default class Names extends Component {
|
||||
|
||||
render () {
|
||||
const { action, name } = this.state;
|
||||
const { fee, hasAccount, pending, queue } = this.props;
|
||||
const { fee, pending, queue } = this.props;
|
||||
|
||||
return (
|
||||
<Card className={ styles.names }>
|
||||
<CardHeader title={ 'Manage Names' } />
|
||||
<CardText>
|
||||
{ !hasAccount
|
||||
? (<p className={ styles.noSpacing }>Please select an account first.</p>)
|
||||
: (action === 'reserve'
|
||||
? (<p className={ styles.noSpacing }>
|
||||
The fee to reserve a name is <code>{ fromWei(fee).toFixed(3) }</code>ETH.
|
||||
</p>)
|
||||
: (<p className={ styles.noSpacing }>To drop a name, you have to be the owner.</p>)
|
||||
)
|
||||
{ (action === 'reserve'
|
||||
? (<p className={ styles.noSpacing }>
|
||||
The fee to reserve a name is <code>{ fromWei(fee).toFixed(3) }</code>ETH.
|
||||
</p>)
|
||||
: (<p className={ styles.noSpacing }>To drop a name, you have to be the owner.</p>)
|
||||
)
|
||||
}
|
||||
<TextField
|
||||
hintText='name'
|
||||
@@ -103,7 +107,7 @@ export default class Names extends Component {
|
||||
onChange={ this.onNameChange }
|
||||
/>
|
||||
<DropDownMenu
|
||||
disabled={ !hasAccount || pending }
|
||||
disabled={ pending }
|
||||
value={ action }
|
||||
onChange={ this.onActionChange }
|
||||
>
|
||||
@@ -111,7 +115,7 @@ export default class Names extends Component {
|
||||
<MenuItem value='drop' primaryText='drop this name' />
|
||||
</DropDownMenu>
|
||||
<RaisedButton
|
||||
disabled={ !hasAccount || pending }
|
||||
disabled={ pending }
|
||||
className={ styles.spacing }
|
||||
label={ action === 'reserve' ? 'Reserve' : 'Drop' }
|
||||
primary
|
||||
|
||||
@@ -15,16 +15,11 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const initialState = {
|
||||
hasAccount: false,
|
||||
pending: false,
|
||||
queue: []
|
||||
};
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
if (action.type === 'accounts select') {
|
||||
return { ...state, hasAccount: !!action.address };
|
||||
}
|
||||
|
||||
if (action.type === 'names reserve start') {
|
||||
return { ...state, pending: true };
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ export default class Records extends Component {
|
||||
|
||||
static propTypes = {
|
||||
actions: PropTypes.object.isRequired,
|
||||
hasAccount: PropTypes.bool.isRequired,
|
||||
pending: PropTypes.bool.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
@@ -21,7 +20,7 @@ export default class Records extends Component {
|
||||
state = { name: '', type: 'A', value: '' };
|
||||
|
||||
render () {
|
||||
const { hasAccount, pending } = this.props;
|
||||
const { pending } = this.props;
|
||||
const name = this.state.name || this.props.name;
|
||||
const type = this.state.type || this.props.type;
|
||||
const value = this.state.value || this.props.value;
|
||||
@@ -30,10 +29,10 @@ export default class Records extends Component {
|
||||
<Card className={ styles.records }>
|
||||
<CardHeader title={ 'Manage Entries of a Name' } />
|
||||
<CardText>
|
||||
{ !hasAccount
|
||||
? (<p className={ styles.noSpacing }>Please select an account first.</p>)
|
||||
: (<p className={ styles.noSpacing }>You can only modify entries of names that you previously registered.</p>)
|
||||
}
|
||||
<p className={ styles.noSpacing }>
|
||||
You can only modify entries of names that you previously registered.
|
||||
</p>
|
||||
|
||||
<TextField
|
||||
className={ styles.spacing }
|
||||
hintText='name'
|
||||
@@ -48,7 +47,7 @@ export default class Records extends Component {
|
||||
onChange={ this.onValueChange }
|
||||
/>
|
||||
<RaisedButton
|
||||
disabled={ !hasAccount || pending }
|
||||
disabled={ pending }
|
||||
className={ styles.spacing }
|
||||
label='Save'
|
||||
primary
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
const initialState = {
|
||||
hasAccount: false,
|
||||
pending: false,
|
||||
name: '', type: '', value: ''
|
||||
};
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
if (action.type === 'accounts select') {
|
||||
return { ...state, hasAccount: !!action.address };
|
||||
}
|
||||
|
||||
if (action.type === 'records update start') {
|
||||
return {
|
||||
...state,
|
||||
@@ -17,7 +12,7 @@ export default (state = initialState, action) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === 'records update error' && action.type === 'records update success') {
|
||||
if (action.type === 'records update error' || action.type === 'records update success') {
|
||||
return {
|
||||
...state,
|
||||
pending: false,
|
||||
|
||||
@@ -29,7 +29,7 @@ export { addresses, accounts, lookup, events, names, records };
|
||||
export const setContract = (contract) => ({ type: 'set contract', contract });
|
||||
|
||||
export const fetchContract = () => (dispatch) =>
|
||||
api.ethcore.registryAddress()
|
||||
api.parity.registryAddress()
|
||||
.then((address) => {
|
||||
const contract = api.newContract(registryAbi, address);
|
||||
dispatch(setContract(contract));
|
||||
|
||||
@@ -19,15 +19,17 @@ import { api } from '../parity';
|
||||
export const set = (addresses) => ({ type: 'addresses set', addresses });
|
||||
|
||||
export const fetch = () => (dispatch) => {
|
||||
return Promise
|
||||
.all([
|
||||
api.eth.accounts(),
|
||||
null // api.personal.accountsInfo()
|
||||
])
|
||||
.then(([ accounts, data ]) => {
|
||||
const addresses = accounts.map((address) => {
|
||||
return { address, isAccount: true };
|
||||
});
|
||||
return api.parity
|
||||
.accounts()
|
||||
.then((accountsInfo) => {
|
||||
const addresses = Object
|
||||
.keys(accountsInfo)
|
||||
.filter((address) => accountsInfo[address] && !accountsInfo[address].meta.deleted)
|
||||
.map((address) => ({
|
||||
...accountsInfo[address],
|
||||
address,
|
||||
isAccount: !!accountsInfo[address].uuid
|
||||
}));
|
||||
dispatch(set(addresses));
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user