Use webpack instead of gulp + browserify

This commit is contained in:
Boris Kubiak 2018-01-27 12:25:32 +01:00
parent 075e445fef
commit 87d740c51e
22 changed files with 5129 additions and 1992 deletions

6
.babelrc Normal file
View file

@ -0,0 +1,6 @@
{
"presets": [
["env", { "modules": false }],
"stage-3"
]
}

6
.gitignore vendored
View file

@ -1,6 +1,4 @@
node_modules
js/bundle.js
css/stylesheet.css
dist
#js/vanity.js
#css/stylesheet.sass
.idea

View file

@ -18,13 +18,17 @@ Install dependencies
```sh
npm i
npm i -g gulp
```
Run the watcher to compile CSS/JS while you code
Run the dev-sever while you code
```sh
npm run dev
```
Build the project
```sh
gulp watch # or npm run-script watch
npm run build
```
The Travis CI bot is in charge of building and deploying Vanity-ETH to Github pages.

File diff suppressed because one or more lines are too long

6
dist/vue.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,70 +0,0 @@
const gulp = require('gulp');
const pump = require('pump');
const browserify = require('browserify');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const source = require('vinyl-source-stream');
const sass = require('gulp-sass');
const autoprefixer = require('gulp-autoprefixer');
const cleanCSS = require('gulp-clean-css');
const replace = require('gulp-replace');
// Browserify
gulp.task('browserify', cb => {
pump([
browserify('js/vanity.js', {standalone: 'vanity'}).bundle(),
source('bundle.js'),
gulp.dest('js')
], cb);
});
// SASS
gulp.task('sass', cb => {
pump([
gulp.src('css/stylesheet.sass'),
sass().on('error', sass.logError),
gulp.dest('css')
], cb);
});
// Build javascript
gulp.task('build-js', ['browserify'], cb => {
pump([
gulp.src(['js/index.js', 'js/bundle.js']),
babel({presets: ['env'], plugins: ['@babel/plugin-transform-object-assign']}),
uglify(),
gulp.dest('js')
], cb);
});
// Build CSS
gulp.task('build-css', ['sass'], cb => {
pump([
gulp.src('css/stylesheet.css'),
autoprefixer({
browsers: ['last 2 versions'],
remove: false,
cascade: false
}),
cleanCSS({compatibility: 'ie8'}),
gulp.dest('css')
], cb);
});
// Build app
gulp.task('build', ['build-js', 'build-css'], cb => {
pump([
gulp.src('.gitignore'),
replace('js/bundle.js', ''),
replace('css/stylesheet.css', ''),
replace('#', ''),
gulp.dest('./')
], cb);
});
// Watch changes and compile on the fly
gulp.task('watch', () => {
gulp.watch(['js/vanity.js'], ['browserify']);
gulp.watch(['css/stylesheet.sass'], ['sass']);
});

View file

@ -7,138 +7,11 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="google-site-verification" content="AP7ip-eAPj3abUpEkg2_RJsN3o-kUWd83TwIrxfyDCQ" />
<title>Vanity ETH</title>
<link rel="icon" type="image/png" href="images/favicon.png" />
<!--CSS-->
<link rel="stylesheet" href="dist/bootstrap.min.css"/>
<link href="css/stylesheet.css" rel="stylesheet" type="text/css" media="all"/>
<link rel="icon" type="image/png" href="src/assets/images/favicon.png" />
<link rel="stylesheet" href="dist/style.css">
</head>
<body>
<div class="container" id="app">
<div class="header text-center">
<h1>VANITY-ETH</h1>
<p>Vanity ETH address generator</p>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel description">
<p>
Vanity-ETH is a browser-based tool to generate vanity Ethereum addresses.
</p>
<h4>Usage</h4>
<p>
Enter the prefix of your choice below, then click 'generate' to start.<br>
Ethereum addresses are hexadecimal, which means your prefix can only contain numbers and letters
from A to F.<br>
You can increase the number of threads allocated to address generation to be faster, or decrease it
if you computer struggles.<br>
</p>
<h4>How it works</h4>
<p>
You browser is going to generate a ton of random addresses until one of them starts with your prefix.<br>
Everything is computed by your browser, so you should notice a better speed on a powerful
computer.<br>
</p>
<h4>Security</h4>
<p>
As explained above, everything is computed in your browser. Nothing ever leaves your machine, or
even your browser tab.<br>
You can download the latest build of Vantiy-ETH from
<a href="https://github.com/bokub/vanity-eth/wiki/download-Vanity-ETH">Github</a> and use it
completely offline.<br>
Vanity-ETH uses a cryptographically secure pseudorandom number generator (CSPRNG) to generate
Ethereum addresses.
</p>
</div>
</div>
</div>
<div v-if="error" class="row" v-cloak>
<div class="col-md-12">
<div class="panel error">
<p v-if="error === 'local_workers_forbidden'">
Your browser disallows multi-thread computation when run from a local file.<br>
Please use the online version at <a href="https://git.io/veth">git.io/veth</a>, or use a different browser.
</p>
<p v-else-if="error === 'workers_unsupported'">
Your browser does not support multi-thread computation.<br>
Please use a different browser.
</p>
<p v-else v-html="error.replace('\n', '<br>')"></p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel form">
<form :class="{error: inputError}">
<div class="error-text">Numbers and letters from A to F only</div>
<input type="text" placeholder="Prefix" v-model="input.prefix" :disabled="running">
<div class="check">
<label class="checkbox">
<input type="checkbox" name="checkbox" checked="" v-model="input.checksum" :disabled="running">
<i class="left"> </i>
Case-sensitive
</label>
</div>
<div class="threads">
<input type="button" class="square-btn" value="-" @click="threads--" :disabled="running || threads <= 1">
<input type="button" class="square-btn arrow" value="+" @click="threads++" :disabled="running">
<h4 v-text="threads"></h4>
<span v-cloak>threads</span>
<span v-if="threads === cores" v-cloak>(recommended)</span>
</div>
<div class="row">
<div class="col-lg-6 col-sm-12">
<input type="button" value="Generate" @click="startGen" :disabled="running || inputError || error">
</div>
<div class="col-lg-6 col-sm-12">
<input type="button" value="Stop" @click="stopGen" :disabled="!running">
</div>
</div>
</form>
</div>
</div>
<div class="col-md-6">
<div class="panel statistics">
<div>Difficulty: <span class="output" v-text="formatNum(difficulty)">1</span></div>
<div>Generated: <span class="output" v-text="formatNum(count) + (count === 1 ? ' address' : ' addresses')">0 addresses</span></div>
<div>50% probability: <span class="output" v-text="probability50">0 addresses</span></div>
<div>Speed: <span class="output" v-text="speed + ' addr/s'">0 addr/s</span></div>
<div>Status: <span class="output" v-text="status">Waiting</span></div>
<!--Probability:-->
<div class="probability">
<div class="probability-bar" :style="'width:' + probability + '%'"></div>
</div>
<div class="percentage">
<h5 v-text="probability + '%'">0%</h5>
<div>Probability</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel result">
<div>Address: <span class="output" v-text="result.address"></span></div>
<div>Private key: <span class="output" v-text="result.privateKey"></span></div>
</div>
</div>
</div>
</div>
<!--Github corner-->
<a href="https://github.com/bokub/vanity-eth" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>
</svg>
</a>
<!--JS-->
<script src="dist/vue.min.js"></script>
<script src="js/index.js" type="text/javascript"></script>
<div id="app"></div>
<script src="dist/index.js"></script>
</body>
</html>

View file

@ -1,180 +0,0 @@
/* eslint-env browser */
/* global Vue:false */
/**
* Check if a string is valid hexadecimal
* @param hex
* @returns {boolean}
*/
const isValidHex = function (hex) {
return hex.length ? /^[0-9A-F]+$/g.test(hex.toUpperCase()) : true;
};
const computeDifficulty = function (pattern, isChecksum) {
const ret = Math.pow(16, pattern.length);
return isChecksum ? (ret * Math.pow(2, pattern.replace(/[^a-f]/gi, '').length)) : ret;
};
const computeProbability = function (difficulty, attempts) {
return 1 - Math.pow(1 - (1 / difficulty), attempts);
};
new Vue({
el: '#app',
data: {
count: 0,
firstTick: null,
running: false,
speed: 0,
status: 'Waiting',
workers: [],
threads: 4,
cores: 0,
result: {
address: '',
privateKey: ''
},
input: {
prefix: '',
checksum: true
},
error: false
},
computed: {
inputError: function () {
return !isValidHex(this.input.prefix);
},
difficulty: function () {
return this.inputError ? 'N/A' : computeDifficulty(this.input.prefix, this.input.checksum);
},
probability50: function () {
return this.inputError ? 'N/A' : this.formatNum(Math.floor(Math.log(0.5) / Math.log(1 - (1 / this.difficulty)))) + ' addresses';
},
probability: function () {
return Math.round(10000 * computeProbability(this.difficulty, this.count)) / 100;
}
},
watch: {
threads: function () {
if (!this.running) {
this.initWorkers();
}
}
},
methods: {
incrementCounter: function (incr) {
this.count += incr;
this.speed = incr > 0 ? Math.floor(1000 * this.count / (performance.now() - this.firstTick)) : 0;
},
displayResult: function (result) {
this.incrementCounter(result.attempts);
this.result.address = result.address;
this.result.privateKey = result.privKey;
this.status = 'Address found';
},
clearResult: function () {
this.result.address = '';
this.result.privateKey = '';
this.incrementCounter(-this.count);
},
/**
* Create missing workers, remove the unwanted ones.
*/
initWorkers: function () {
const self = this;
if (this.workers.length === this.threads) {
return;
}
// Remove unwanted workers
if (this.workers.length > this.threads) {
for (let w = this.threads - 1; w < this.workers.length; w++) {
this.workers[w].terminate();
}
this.workers = this.workers.slice(0, this.threads);
return;
}
// Create workers
for (let w = this.workers.length; w < this.threads; w++) {
try {
this.workers[w] = new Worker('js/bundle.js');
this.workers[w].onmessage = event => self.parseWorkerMessage(event.data);
} catch (err) {
this.error = 'local_workers_forbidden';
break;
}
}
},
parseWorkerMessage: function (wallet) {
if (wallet.error) {
this.stopGen();
this.error = wallet.error;
this.status = 'Error';
return;
}
if (wallet.address) {
this.stopGen();
return this.displayResult(wallet);
}
this.incrementCounter(wallet.attempts);
},
startGen: function () {
if (!window.Worker) {
this.error = 'workers_unsupported';
return;
}
this.clearResult();
this.running = true;
for (let w = 0; w < this.workers.length; w++) {
this.workers[w].postMessage(this.input);
}
this.status = 'Running';
this.firstTick = performance.now();
},
stopGen: function () {
this.running = false;
this.status = 'Stopped';
for (let i = 0; i < this.workers.length; i++) {
this.workers[i].terminate();
}
this.workers = [];
this.initWorkers();
},
countCores: function () {
// Estimate number of cores on machine
let cores = 0;
try {
cores = parseInt(navigator.hardwareConcurrency, 10);
} catch (err) {
console.error(err);
}
if (cores) {
this.cores = cores;
this.threads = this.cores;
}
},
formatNum: function (num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
}
},
created: function () {
this.countCores();
this.initWorkers();
}
});

6211
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,28 +3,39 @@
"description": "Online ETH vanity address generator",
"version": "1.0.0",
"dependencies": {
"@babel/plugin-transform-object-assign": "^7.0.0-beta.37",
"babel-preset-env": "^1.6.1",
"browserify": "^14.5.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-3": "^6.24.1",
"bootstrap": "^4.0.0",
"cross-env": "^5.0.5",
"css-loader": "^0.28.7",
"ethereumjs-util": "^5.1.2",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^4.0.0",
"gulp-babel": "^7.0.0",
"gulp-clean-css": "^3.9.0",
"gulp-replace": "^0.6.1",
"gulp-sass": "^3.1.0",
"gulp-uglify": "^3.0.0",
"pump": "^2.0.0",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.6",
"node-sass": "^4.5.3",
"randombytes": "^2.0.6",
"vinyl-source-stream": "^1.1.0",
"sass-loader": "^6.0.6",
"url-loader": "^0.6.2",
"vue": "^2.5.11",
"vue-loader": "^13.0.5",
"vue-template-compiler": "^2.4.4",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1",
"worker-loader": "^1.1.0",
"xo": "^0.18.2"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"license": "ISC",
"main": "index.js",
"scripts": {
"build": "node ./node_modules/gulp/bin/gulp.js build",
"test": "node ./node_modules/xo/cli.js",
"watch": "node ./node_modules/gulp/bin/gulp.js watch"
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},
"xo": {
"rules": {

198
src/App.vue Normal file
View file

@ -0,0 +1,198 @@
<template src="./templates/App.html"></template>
<script>
import Worker from './js/vanity.js';
import Corner from './vue/Corner';
export default {
components: {
corner: Corner
},
data: function () {
return {
count: 0,
firstTick: null,
running: false,
speed: 0,
status: 'Waiting',
workers: [],
threads: 4,
cores: 0,
result: {
address: '',
privateKey: ''
},
input: {
prefix: '',
checksum: true
},
error: false
}
},
computed: {
inputError: function () {
return !isValidHex(this.input.prefix);
},
difficulty: function () {
return this.inputError ? 'N/A' : computeDifficulty(this.input.prefix, this.input.checksum);
},
probability50: function () {
return this.inputError ? 'N/A' : this.formatNum(Math.floor(Math.log(0.5) / Math.log(1 - (1 / this.difficulty)))) + ' addresses';
},
probability: function () {
return Math.round(10000 * computeProbability(this.difficulty, this.count)) / 100;
}
},
watch: {
threads: function () {
if (!this.running) {
this.initWorkers();
}
}
},
methods: {
incrementCounter: function (incr) {
this.count += incr;
this.speed = incr > 0 ? Math.floor(1000 * this.count / (performance.now() - this.firstTick)) : 0;
},
displayResult: function (result) {
this.incrementCounter(result.attempts);
this.result.address = result.address;
this.result.privateKey = result.privKey;
this.status = 'Address found';
},
clearResult: function () {
this.result.address = '';
this.result.privateKey = '';
this.incrementCounter(-this.count);
},
/**
* Create missing workers, remove the unwanted ones.
*/
initWorkers: function () {
const self = this;
if (this.workers.length === this.threads) {
return;
}
// Remove unwanted workers
if (this.workers.length > this.threads) {
for (let w = this.threads - 1; w < this.workers.length; w++) {
this.workers[w].terminate();
}
this.workers = this.workers.slice(0, this.threads);
return;
}
// Create workers
for (let w = this.workers.length; w < this.threads; w++) {
try {
this.workers[w] = new Worker();
this.workers[w].onmessage = event => self.parseWorkerMessage(event.data);
} catch (err) {
this.error = 'local_workers_forbidden';
break;
}
}
},
parseWorkerMessage: function (wallet) {
if (wallet.error) {
this.stopGen();
this.error = wallet.error;
this.status = 'Error';
return;
}
if (wallet.address) {
this.stopGen();
return this.displayResult(wallet);
}
this.incrementCounter(wallet.attempts);
},
startGen: function () {
if (!window.Worker) {
this.error = 'workers_unsupported';
return;
}
this.clearResult();
this.running = true;
for (let w = 0; w < this.workers.length; w++) {
this.workers[w].postMessage(this.input);
}
this.status = 'Running';
this.firstTick = performance.now();
},
stopGen: function () {
this.running = false;
this.status = 'Stopped';
for (let i = 0; i < this.workers.length; i++) {
this.workers[i].terminate();
}
this.workers = [];
this.initWorkers();
},
countCores: function () {
// Estimate number of cores on machine
let cores = 0;
try {
cores = parseInt(navigator.hardwareConcurrency, 10);
} catch (err) {
console.error(err);
}
if (cores) {
this.cores = cores;
this.threads = this.cores;
}
},
formatNum: function (num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
}
},
created: function () {
this.countCores();
this.initWorkers();
}
}
const isValidHex = function (hex) {
return hex.length ? /^[0-9A-F]+$/g.test(hex.toUpperCase()) : true;
};
const computeDifficulty = function (pattern, isChecksum) {
const ret = Math.pow(16, pattern.length);
return isChecksum ? (ret * Math.pow(2, pattern.replace(/[^a-f]/gi, '').length)) : ret;
};
const computeProbability = function (difficulty, attempts) {
return 1 - Math.pow(1 - (1 / difficulty), attempts);
};
</script>
<style lang="sass">
// Bootstrap - Required
@import "~bootstrap/scss/functions"
@import "~bootstrap/scss/variables"
@import "~bootstrap/scss/mixins"
// Bootstrap - Optional
@import "~bootstrap/scss/reboot"
@import "~bootstrap/scss/grid"
// Custom style
@import "css/stylesheet.sass"
</style>

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -17,6 +17,7 @@ body
h1, h2, h3, h4, h5, h6, p, label
margin: 0
font-weight: normal
[v-cloak]
display: none
@ -32,6 +33,7 @@ a:hover
margin-bottom: 8em
color: $white-text
font-family: 'Montserrat', sans-serif
text-align: center
h1
font-size: 3em
@ -39,7 +41,7 @@ a:hover
border: 4px solid $white-text
width: 33%
margin: 0 auto
padding: 10px
padding: 1px
p
font-size: 1.5em
@ -59,10 +61,10 @@ a:hover
color: $white-text
text-decoration: underline
.description p
.description
p
margin: 15px 0 20px
color: $grey-text
.form
input
&[type="text"]
@ -136,7 +138,7 @@ a:hover
input
+ i:after
content: ''
background: url("../images/tick-mark.png") no-repeat 1px 2px
background: url("./assets/images/tick-mark.png") no-repeat 1px 2px
top: 4px
left: 2px
width: 15px
@ -209,21 +211,21 @@ a:hover
font-family: 'Lato'
font-style: normal
font-weight: 400
src: local('Lato Regular'), local('Lato-Regular'), url(../fonts/lato-regular.woff2) format('woff2')
src: local('Lato Regular'), local('Lato-Regular'), url(./assets/fonts/lato-regular.woff2) format('woff2')
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215
@font-face
font-family: 'Montserrat'
font-style: normal
font-weight: 400
src: local('Montserrat Regular'), local('Montserrat-Regular'), url(../fonts/montserrat.woff2) format('woff2')
src: local('Montserrat Regular'), local('Montserrat-Regular'), url(./assets/fonts/montserrat.woff2) format('woff2')
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215
@font-face
font-family: 'Montserrat'
font-style: normal
font-weight: 700
src: local('Montserrat Bold'), local('Montserrat-Bold'), url(../fonts/montserrat-bold.woff2) format('woff2')
src: local('Montserrat Bold'), local('Montserrat-Bold'), url(./assets/fonts/montserrat-bold.woff2) format('woff2')
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215
/*-- Responsive design --
@ -290,8 +292,6 @@ a:hover
&:hover .octo-arm
animation: octocat-wave 560ms ease-in-out
svg
fill: #fff
color: #151513
position: absolute
top: 0
border: 0

View file

@ -75,7 +75,7 @@ onmessage = function (event) {
try {
getVanityWallet(input.prefix, input.checksum, message => postMessage(message));
} catch (err) {
postMessage({error: err.toString()});
self.postMessage({error: err.toString()});
}
};

7
src/main.js Normal file
View file

@ -0,0 +1,7 @@
import Vue from 'vue';
import App from './App.vue';
new Vue({
el: '#app',
render: h => h(App)
});

124
src/templates/App.html Normal file
View file

@ -0,0 +1,124 @@
<div>
<div class="container">
<div class="header">
<h1>VANITY-ETH</h1>
<p>Vanity ETH address generator</p>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel description">
<p>
Vanity-ETH is a browser-based tool to generate vanity Ethereum addresses.
</p>
<h2>Usage</h2>
<p>
Enter the prefix of your choice below, then click 'generate' to start.<br>
Ethereum addresses are hexadecimal, which means your prefix can only contain numbers and letters
from A to F.<br>
You can increase the number of threads allocated to address generation to be faster, or decrease
it
if you computer struggles.<br>
</p>
<h2>How it works</h2>
<p>
You browser is going to generate a ton of random addresses until one of them starts with your
prefix.<br>
Everything is computed by your browser, so you should notice a better speed on a powerful
computer.<br>
</p>
<h2>Security</h2>
<p>
As explained above, everything is computed in your browser. Nothing ever leaves your machine, or
even your browser tab.<br>
You can download the latest build of Vantiy-ETH from
<a href="https://github.com/bokub/vanity-eth/wiki/download-Vanity-ETH">Github</a> and use it
completely offline.<br>
Vanity-ETH uses a cryptographically secure pseudorandom number generator (CSPRNG) to generate
Ethereum addresses.
</p>
</div>
</div>
</div>
<div v-if="error" class="row" v-cloak>
<div class="col-md-12">
<div class="panel error">
<p v-if="error === 'local_workers_forbidden'">
Your browser disallows multi-thread computation when run from a local file.<br>
Please use the online version at <a href="https://git.io/veth">git.io/veth</a>, or use a
different
browser.
</p>
<p v-else-if="error === 'workers_unsupported'">
Your browser does not support multi-thread computation.<br>
Please use a different browser.
</p>
<p v-else v-html="error.replace('\n', '<br>')"></p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel form">
<form :class="{error: inputError}">
<div class="error-text">Numbers and letters from A to F only</div>
<input type="text" placeholder="Prefix" v-model="input.prefix" :disabled="running">
<div class="check">
<label class="checkbox">
<input type="checkbox" name="checkbox" checked="" v-model="input.checksum"
:disabled="running">
<i class="left"> </i>
Case-sensitive
</label>
</div>
<div class="threads">
<input type="button" class="square-btn" value="-" @click="threads--"
:disabled="running || threads <= 1">
<input type="button" class="square-btn arrow" value="+" @click="threads++"
:disabled="running">
<h4 v-text="threads"></h4>
<span v-cloak>threads</span>
<span v-if="threads === cores" v-cloak>(recommended)</span>
</div>
<div class="row">
<div class="col-lg-6 col-sm-12">
<input type="button" value="Generate" @click="startGen"
:disabled="running || inputError || error">
</div>
<div class="col-lg-6 col-sm-12">
<input type="button" value="Stop" @click="stopGen" :disabled="!running">
</div>
</div>
</form>
</div>
</div>
<div class="col-md-6">
<div class="panel statistics">
<div>Difficulty: <span class="output" v-text="formatNum(difficulty)">1</span></div>
<div>Generated: <span class="output"
v-text="formatNum(count) + (count === 1 ? ' address' : ' addresses')">0 addresses</span>
</div>
<div>50% probability: <span class="output" v-text="probability50">0 addresses</span></div>
<div>Speed: <span class="output" v-text="speed + ' addr/s'">0 addr/s</span></div>
<div>Status: <span class="output" v-text="status">Waiting</span></div>
<!--Probability:-->
<div class="probability">
<div class="probability-bar" :style="'width:' + probability + '%'"></div>
</div>
<div class="percentage">
<h4 v-text="probability + '%'">0%</h4>
<div>Probability</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel result">
<div>Address: <span class="output" v-text="result.address"></span></div>
<div>Private key: <span class="output" v-text="result.privateKey"></span></div>
</div>
</div>
</div>
</div>
<corner></corner>
</div>

16
src/vue/Corner.vue Normal file
View file

@ -0,0 +1,16 @@
<template>
<!--Github corner-->
<a href="https://github.com/bokub/vanity-eth" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="#fff"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="#000" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="#000" class="octo-body"></path>
</svg>
</a>
</template>
<script>
export default {}
</script>

79
webpack.config.js Normal file
View file

@ -0,0 +1,79 @@
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: {
index: './src/main.js'
},
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: '[name].js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
extractCSS: true,
}
},
{
test: /vanity\.js$/,
loader: 'worker-loader',
exclude: /node_modules/,
options: {
inline: true,
name: '[name].[ext]?[hash]'
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|woff2)/,
exclude: /node_modules/,
loader: 'url-loader',
}
]
},
plugins: [
new ExtractTextPlugin("style.css")
],
resolve: {
alias: {
vue$: 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
};
if (process.env.NODE_ENV === 'production') {
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {NODE_ENV: '"production"'}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: false,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
]);
}