Toy vanity address generator (#31)
* Basic vanity address generator * Node operations moved to cli completely * Cleanups * cli progress
This commit is contained in:
parent
45937acddd
commit
4cec0ec5ea
@ -10,6 +10,7 @@
|
||||
"build:apps": "cd packages/apps && react-scripts build",
|
||||
"check": "eslint packages && flow check",
|
||||
"deploy:ghpages": "gh-pages -d packages/apps/build",
|
||||
"generator": "node packages/app-vanitygen/build/generator/cli.js",
|
||||
"start": "cd packages/apps && react-scripts start",
|
||||
"test": "echo 'no tests executed'"
|
||||
},
|
||||
|
||||
@ -50,6 +50,8 @@ function addressFromSeed (seed: string): string {
|
||||
}
|
||||
|
||||
class Creator extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
|
||||
@ -30,6 +30,8 @@ type State = {
|
||||
}
|
||||
|
||||
class Editor extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
|
||||
15
packages/app-vanitygen/LICENSE
Normal file
15
packages/app-vanitygen/LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
ISC License (ISC)
|
||||
|
||||
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
1
packages/app-vanitygen/README.md
Normal file
1
packages/app-vanitygen/README.md
Normal file
@ -0,0 +1 @@
|
||||
# @polkadot/app-vanitygen
|
||||
18
packages/app-vanitygen/package.json
Normal file
18
packages/app-vanitygen/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@polkadot/app-vanitygen",
|
||||
"version": "0.2.1",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/polkadot-js/apps.git",
|
||||
"author": "Jaco Greeff <jacogr@gmail.com>",
|
||||
"license": "ISC",
|
||||
"scripts": {
|
||||
"build": "polkadot-dev-build-babel",
|
||||
"check": "stylelint 'src/**/*.css' && eslint src && flow check",
|
||||
"test": "jest --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/ui-react-app": "^0.2.1",
|
||||
"chalk": "^2.4.1",
|
||||
"yargs": "10.1.2"
|
||||
}
|
||||
}
|
||||
51
packages/app-vanitygen/src/Match.js
Normal file
51
packages/app-vanitygen/src/Match.js
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { BareProps } from '@polkadot/ui-react-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
|
||||
type Props = BareProps & {
|
||||
address: string;
|
||||
count: number;
|
||||
offset: number;
|
||||
onRemove: (address: string) => void,
|
||||
seed: Uint8Array;
|
||||
}
|
||||
|
||||
export default function Match ({ address, className, count, offset, onRemove, seed, style }: Props): React$Node {
|
||||
const _onRemove = (): void =>
|
||||
onRemove(address);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['vanity--Match', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='vanity--Match-item'>
|
||||
<IdentityIcon
|
||||
className='vanity--Match-icon'
|
||||
size={48}
|
||||
value={address}
|
||||
/>
|
||||
<div className='vanity--Match-addr'>
|
||||
<span className='no'>{address.slice(0, offset)}</span><span className='yes'>{address.slice(offset, count + offset)}</span><span className='no'>{address.slice(count + offset)}</span>
|
||||
</div>
|
||||
<div className='vanity--Match-seed'>
|
||||
{u8aToHex(seed)}
|
||||
</div>
|
||||
<Button
|
||||
className='vanity--Match-remove'
|
||||
icon='close'
|
||||
negative
|
||||
onClick={_onRemove}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
47
packages/app-vanitygen/src/generator/calculate.js
Normal file
47
packages/app-vanitygen/src/generator/calculate.js
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Generator$Calculation, Generator$Options } from './types';
|
||||
|
||||
module.exports = function calculate (test: Array<string>, _address: string, { atOffset = -1, withCase = false }: Generator$Options): Generator$Calculation {
|
||||
const address = withCase
|
||||
? _address
|
||||
: _address.toLowerCase();
|
||||
|
||||
const calculateAt = (atOffset: number): Generator$Calculation => {
|
||||
return {
|
||||
count: test.reduce((count, c, index) => {
|
||||
if (index === count) {
|
||||
count += (c === '?' || c === address.charAt(index + atOffset)) ? 1 : 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}, 0),
|
||||
offset: atOffset
|
||||
};
|
||||
};
|
||||
|
||||
if (atOffset > 0) {
|
||||
return calculateAt(atOffset);
|
||||
}
|
||||
|
||||
const maxOffset = address.length - test.length - 1;
|
||||
let bestCount = 0;
|
||||
let bestOffset = 1;
|
||||
|
||||
for (let index = 1; index < maxOffset; index++) {
|
||||
const { count, offset } = calculateAt(index);
|
||||
|
||||
if (count > bestCount) {
|
||||
bestCount = count;
|
||||
bestOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
count: bestCount,
|
||||
offset: bestOffset
|
||||
};
|
||||
};
|
||||
65
packages/app-vanitygen/src/generator/cli.js
Normal file
65
packages/app-vanitygen/src/generator/cli.js
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env node
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
const yargs = require('yargs');
|
||||
const chalk = require('chalk');
|
||||
const u8aToHex = require('@polkadot/util/u8a/toHex');
|
||||
|
||||
const generator = require('./index.js');
|
||||
|
||||
const { match } = yargs
|
||||
.option('match', {
|
||||
alias: 'm',
|
||||
default: 'EEEEE'
|
||||
})
|
||||
.argv;
|
||||
|
||||
const INDICATORS = ['|', '/', '-', '\\'];
|
||||
|
||||
const options = {
|
||||
match,
|
||||
runs: 100
|
||||
};
|
||||
const startAt = Date.now();
|
||||
let best = { count: -1 };
|
||||
let total = 0;
|
||||
let indicator = -1;
|
||||
|
||||
function showProgress () {
|
||||
const elapsed = Date.now() - startAt;
|
||||
|
||||
indicator++;
|
||||
|
||||
if (indicator === INDICATORS.length) {
|
||||
indicator = 0;
|
||||
}
|
||||
|
||||
process.stdout.write(`\r[${INDICATORS[indicator]}] ${total} keys in ${(elapsed / 1000).toFixed(2)}s, ${(elapsed / total).toFixed(3)}ms/key`);
|
||||
}
|
||||
|
||||
function showBest () {
|
||||
const { address, count, offset, seed } = best;
|
||||
|
||||
console.log(`\r::: ${address.slice(0, offset)}${chalk.cyan(address.slice(offset, count + offset))}${address.slice(count + offset)} <= ${u8aToHex(seed)} (count=${count}, offset=${offset})`);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const nextBest = generator(options).found.reduce((best, match) => {
|
||||
if ((match.count > best.count) || ((match.count === best.count) && (match.offset < best.offset))) {
|
||||
return match;
|
||||
}
|
||||
|
||||
return best;
|
||||
}, best);
|
||||
|
||||
total += options.runs;
|
||||
|
||||
if (nextBest.address !== best.address) {
|
||||
best = nextBest;
|
||||
showBest();
|
||||
}
|
||||
|
||||
showProgress();
|
||||
}
|
||||
27
packages/app-vanitygen/src/generator/generate.js
Normal file
27
packages/app-vanitygen/src/generator/generate.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Generator$Match, Generator$Options } from './types';
|
||||
|
||||
const keypairFromSeed = require('@polkadot/util-crypto/nacl/keypair/fromSeed');
|
||||
const randomBytes = require('@polkadot/util-crypto/random/asU8a');
|
||||
const addressEncode = require('@polkadot/util-keyring/address/encode');
|
||||
|
||||
const calculate = require('./calculate');
|
||||
|
||||
module.exports = function generator (test: Array<string>, options: Generator$Options): Generator$Match {
|
||||
const seed = randomBytes();
|
||||
const address = addressEncode(
|
||||
keypairFromSeed(seed).publicKey
|
||||
);
|
||||
const { count, offset } = calculate(test, address, options);
|
||||
|
||||
return {
|
||||
address,
|
||||
count,
|
||||
offset,
|
||||
seed
|
||||
};
|
||||
};
|
||||
26
packages/app-vanitygen/src/generator/index.js
Normal file
26
packages/app-vanitygen/src/generator/index.js
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Generator$Matches, Generator$Result, Generator$Options } from './types';
|
||||
|
||||
const generate = require('./generate');
|
||||
|
||||
module.exports = function generator (options: Generator$Options): Generator$Result {
|
||||
const { match, runs = 10, withCase = false } = options;
|
||||
const test = withCase
|
||||
? match.split('')
|
||||
: match.toLowerCase().split('');
|
||||
const startAt = Date.now();
|
||||
const found: Generator$Matches = [];
|
||||
|
||||
while (found.length !== runs) {
|
||||
found.push(generate(test, options));
|
||||
}
|
||||
|
||||
return {
|
||||
elapsed: Date.now() - startAt,
|
||||
found
|
||||
};
|
||||
};
|
||||
32
packages/app-vanitygen/src/generator/sort.js
Normal file
32
packages/app-vanitygen/src/generator/sort.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Generator$Match } from './types';
|
||||
|
||||
function numberSort (a: number, b: number): number {
|
||||
if (a > b) {
|
||||
return -1;
|
||||
} else if (a < b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module.exports = function sort (a: Generator$Match, b: Generator$Match): number {
|
||||
const countResult = numberSort(a.count, b.count);
|
||||
|
||||
if (countResult !== 0) {
|
||||
return countResult;
|
||||
}
|
||||
|
||||
const positionResult = numberSort(b.offset, a.offset);
|
||||
|
||||
if (positionResult !== 0) {
|
||||
return positionResult;
|
||||
}
|
||||
|
||||
return a.address.localeCompare(b.address);
|
||||
};
|
||||
28
packages/app-vanitygen/src/generator/types.js
Normal file
28
packages/app-vanitygen/src/generator/types.js
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
export type Generator$Calculation = {
|
||||
count: number,
|
||||
offset: number
|
||||
};
|
||||
|
||||
export type Generator$Match = Generator$Calculation & {
|
||||
address: string,
|
||||
seed: Uint8Array
|
||||
};
|
||||
|
||||
export type Generator$Matches = Array<Generator$Match>;
|
||||
|
||||
export type Generator$Options = {
|
||||
atOffset?: number,
|
||||
match: string,
|
||||
runs?: number,
|
||||
withCase?: boolean
|
||||
};
|
||||
|
||||
export type Generator$Result = {
|
||||
elapsed: number,
|
||||
found: Generator$Matches
|
||||
}
|
||||
65
packages/app-vanitygen/src/index.css
Normal file
65
packages/app-vanitygen/src/index.css
Normal file
@ -0,0 +1,65 @@
|
||||
/* Copyright 2017-2018 Jaco Greeff
|
||||
/* This software may be modified and distributed under the terms
|
||||
/* of the ISC license. See the LICENSE file for details. */
|
||||
|
||||
.vanity--App {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.vanity--App-matches {
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
.vanity--App-stats {
|
||||
padding: 1em 0 0 0;
|
||||
opacity: 0.45;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vanity--Match {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.vanity--Match:hover {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.vanity--Match-item {
|
||||
display: inline-block;
|
||||
font-family: monospace;
|
||||
margin: 0.5em auto;
|
||||
padding: 0.5em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.vanity--Match-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
.vanity--Match-addr,
|
||||
.vanity--Match-seed {
|
||||
padding: 0 5rem;
|
||||
}
|
||||
|
||||
.vanity--Match-addr {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.vanity--Match-addr .no {
|
||||
}
|
||||
|
||||
.vanity--Match-addr .yes {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.vanity--Match-remove {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.vanity--Match-seed {
|
||||
opacity: 0.45;
|
||||
}
|
||||
237
packages/app-vanitygen/src/index.js
Normal file
237
packages/app-vanitygen/src/index.js
Normal file
@ -0,0 +1,237 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { I18nProps } from '@polkadot/ui-react-app/types';
|
||||
import type { Generator$Matches, Generator$Result } from './generator/types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'semantic-ui-react/dist/es/elements/Button';
|
||||
import Input from 'semantic-ui-react/dist/es/elements/Input';
|
||||
import Label from 'semantic-ui-react/dist/es/elements/Label';
|
||||
|
||||
import Match from './Match';
|
||||
import generator from './generator';
|
||||
import generatorSort from './generator/sort';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
|
||||
type State = {
|
||||
elapsed: number,
|
||||
isMatchValid: boolean,
|
||||
isRunning: boolean,
|
||||
keyCount: 0,
|
||||
keyTime: 0,
|
||||
match: string,
|
||||
matches: Generator$Matches,
|
||||
startAt: number
|
||||
}
|
||||
|
||||
const DEFAULT_MATCH = 'S?me';
|
||||
const MATCH_REGEX = /[1-9A-Za-z?]*$/;
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
results: Array<Generator$Result>;
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.results = [];
|
||||
this.state = {
|
||||
elapsed: 0,
|
||||
isMatchValid: true,
|
||||
isRunning: false,
|
||||
keyCount: 0,
|
||||
keyTime: 0,
|
||||
match: DEFAULT_MATCH,
|
||||
matches: [],
|
||||
startAt: 0
|
||||
};
|
||||
}
|
||||
|
||||
render (): React$Node {
|
||||
const { className, style, t } = this.props;
|
||||
const { elapsed, isMatchValid, isRunning, match, matches, keyCount } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['vanity--App', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
<div className='ui--form'>
|
||||
<div className='medium'>
|
||||
<Label>{t('vanity.matching', {
|
||||
defaultValue: 'generate address containing characters'
|
||||
})}</Label>
|
||||
<Input
|
||||
disabled={isRunning}
|
||||
error={!isMatchValid}
|
||||
onChange={this.onChangeMatch}
|
||||
value={match}
|
||||
/>
|
||||
</div>
|
||||
<div className='small'>
|
||||
<Label>{t('vanity.case', {
|
||||
defaultValue: 'case sensitivity'
|
||||
})}</Label>
|
||||
<div className='ui dropdown selection disabled'>
|
||||
{t('vanity.case.off', {
|
||||
defaultValue: 'No'
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className='small'>
|
||||
<Label>{t('vanity.offset', {
|
||||
defaultValue: 'exact offset'
|
||||
})}</Label>
|
||||
<div className='ui dropdown selection disabled'>
|
||||
{t('vanity.offset.off', {
|
||||
defaultValue: 'No'
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='ui--form-buttons'>
|
||||
<Button
|
||||
disabled={!isMatchValid}
|
||||
negative={isRunning}
|
||||
primary
|
||||
onClick={this.toggleStart}
|
||||
>
|
||||
{
|
||||
isRunning
|
||||
? t('vanity.stop', { defaultValue: 'Stop' })
|
||||
: t('vanity.start', { defaultValue: 'Start' })
|
||||
}
|
||||
</Button>
|
||||
</div>
|
||||
{
|
||||
keyCount
|
||||
? (
|
||||
<div className='vanity--App-stats'>
|
||||
{t('vanity.stats', {
|
||||
defaultValue: 'Generated {{count}} keys in {{elapsed}}s ({{avg}}ms/key)',
|
||||
replace: {
|
||||
avg: (elapsed / keyCount).toFixed(3),
|
||||
count: keyCount,
|
||||
elapsed: (elapsed / 1000).toFixed(2)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div className='vanity--App-matches'>
|
||||
{matches.map((match) => (
|
||||
<Match
|
||||
{...match}
|
||||
key={match.address}
|
||||
onRemove={this.onRemove}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
checkMatches (): void {
|
||||
const results = this.results;
|
||||
|
||||
this.results = [];
|
||||
|
||||
if (results.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
(prevState: State) => {
|
||||
let newKeyCount = prevState.keyCount;
|
||||
let newKeyTime = prevState.keyTime;
|
||||
|
||||
const matches = results
|
||||
.reduce((result, { elapsed, found }) => {
|
||||
newKeyCount += found.length;
|
||||
newKeyTime += elapsed;
|
||||
|
||||
return result.concat(found);
|
||||
}, prevState.matches)
|
||||
.sort(generatorSort)
|
||||
.slice(0, 25);
|
||||
const elapsed = Date.now() - prevState.startAt;
|
||||
|
||||
return {
|
||||
elapsed,
|
||||
matches,
|
||||
keyCount: newKeyCount,
|
||||
keyTime: newKeyTime
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
executeGeneration = (): void => {
|
||||
if (!this.state.isRunning) {
|
||||
this.checkMatches();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.results.length === 5) {
|
||||
this.checkMatches();
|
||||
}
|
||||
|
||||
this.results.push(
|
||||
generator({
|
||||
match: this.state.match,
|
||||
runs: 10
|
||||
})
|
||||
);
|
||||
|
||||
this.executeGeneration();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
onChangeMatch = (event: SyntheticEvent<*>, { value }): void => {
|
||||
const isMatchValid = MATCH_REGEX.test(value) && value.length < 31;
|
||||
|
||||
this.setState({
|
||||
isMatchValid,
|
||||
match: value
|
||||
});
|
||||
}
|
||||
|
||||
onRemove = (address: string): void => {
|
||||
this.setState(
|
||||
(prevState: State) => ({
|
||||
matches: prevState.matches.filter((item) =>
|
||||
item.address !== address
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
toggleStart = (): void => {
|
||||
this.setState(
|
||||
(prevState: State) => {
|
||||
const { isRunning, keyCount, keyTime, startAt } = prevState;
|
||||
|
||||
return {
|
||||
isRunning: !isRunning,
|
||||
keyCount: !isRunning ? 0 : keyCount,
|
||||
keyTime: !isRunning ? 0 : keyTime,
|
||||
startAt: !isRunning ? Date.now() : startAt
|
||||
};
|
||||
},
|
||||
this.executeGeneration
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(App);
|
||||
8
packages/app-vanitygen/src/translate.js
Normal file
8
packages/app-vanitygen/src/translate.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import { translate } from 'react-i18next';
|
||||
|
||||
export default translate(['vanitygen', 'ui']);
|
||||
@ -29,7 +29,7 @@ function SideBar ({ className, style, t }: Props): React$Node {
|
||||
vertical
|
||||
>
|
||||
{
|
||||
routing.routes.map((route) => (
|
||||
routing.routes.map((route, index) => (
|
||||
route
|
||||
? (
|
||||
<Item
|
||||
@ -39,7 +39,12 @@ function SideBar ({ className, style, t }: Props): React$Node {
|
||||
{...route}
|
||||
/>
|
||||
)
|
||||
: <Divider hidden />
|
||||
: (
|
||||
<Divider
|
||||
hidden
|
||||
key={index}
|
||||
/>
|
||||
)
|
||||
))
|
||||
}
|
||||
<Divider hidden />
|
||||
|
||||
@ -12,11 +12,11 @@ import extrinsics from './extrinsics';
|
||||
// import home from './home';
|
||||
// import settings from './settings';
|
||||
import storage from './storage';
|
||||
// import vanitygen from './vanitygen';
|
||||
import vanitygen from './vanitygen';
|
||||
|
||||
export default ({
|
||||
default: 'explorer',
|
||||
routes: [].concat(
|
||||
explorer, extrinsics, storage, null, accounts
|
||||
explorer, extrinsics, storage, null, accounts, vanitygen
|
||||
)
|
||||
}: Routing);
|
||||
|
||||
21
packages/apps/src/routing/vanitygen.js
Normal file
21
packages/apps/src/routing/vanitygen.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2017-2018 Jaco Greeff
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
// @flow
|
||||
|
||||
import type { Routes } from '../types';
|
||||
|
||||
import Vanity from '@polkadot/app-vanitygen/src';
|
||||
|
||||
export default ([
|
||||
{
|
||||
component: Vanity,
|
||||
i18n: {
|
||||
defaultValue: 'Vanity'
|
||||
},
|
||||
icon: 'hand lizard',
|
||||
isExact: false,
|
||||
isHidden: false,
|
||||
name: 'vanitygen'
|
||||
}
|
||||
]: Routes);
|
||||
42
yarn.lock
42
yarn.lock
@ -2426,6 +2426,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
character-entities-html4@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.1.tgz#359a2a4a0f7e29d3dc2ac99bdbe21ee39438ea50"
|
||||
@ -10789,6 +10797,23 @@ yargs-parser@^9.0.2:
|
||||
dependencies:
|
||||
camelcase "^4.1.0"
|
||||
|
||||
yargs@10.1.2, yargs@^10.0.3:
|
||||
version "10.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5"
|
||||
dependencies:
|
||||
cliui "^4.0.0"
|
||||
decamelize "^1.1.1"
|
||||
find-up "^2.1.0"
|
||||
get-caller-file "^1.0.1"
|
||||
os-locale "^2.0.0"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^2.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^8.1.0"
|
||||
|
||||
yargs@6.6.0:
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
|
||||
@ -10807,23 +10832,6 @@ yargs@6.6.0:
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^4.2.0"
|
||||
|
||||
yargs@^10.0.3:
|
||||
version "10.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5"
|
||||
dependencies:
|
||||
cliui "^4.0.0"
|
||||
decamelize "^1.1.1"
|
||||
find-up "^2.1.0"
|
||||
get-caller-file "^1.0.1"
|
||||
os-locale "^2.0.0"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^2.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^8.1.0"
|
||||
|
||||
yargs@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user