+ - 0:00:00
Notes for current slide
Notes for next slide

Ethereum Web Wallet DIY

 

 

Francesco Canessa

@makevoid

 

Lead Software Developer

Applied Blockchain

 

Rise London - Blockchain Week '18 - Development Workshop

1 / 42

Ethereum Web Wallet

 

  1. Setup
  1. Generate a Private Key

  2. Derive an Ethereum address

  3. Get the address balance

  4. Sign transansaction

  5. Broadcast transaction

2 / 42

Setup

 

Requirement:

Node installed 7.6+

 

Upgrade or use github:yortus/asyncawait

https://github.com/yortus/asyncawait#6-quick-start

 

Alternative: Runkit (https://runkit.com)

 

3 / 42

Setup

 

$ node -v
v7.6+ (at least)
v8 is good (aim for v8.9.x)

 

9 should be ok as well

4 / 42

Setup

 

Check Async-Await support

https://runkit.com/makevoid/check-async-support

 

if you don't have native support, you can use github:yortus/asyncawait

5 / 42

Hello Node

 

 

 

console.log("hello world")

 

6 / 42

Hello Bitcore-Lib

 

 

npm init -fy
npm install --save bitcore-lib

 

7 / 42

Generate a Private Key

 

const bitcore = require('bitcore-lib')
const PrivateKey = bitcore.PrivateKey
const privateKey = new PrivateKey()
console.log(privateKey.toString())

 

 

https://runkit.com/makevoid/bitcore-lib-privatekey

 

8 / 42

Derive the PublicKey

 

const bitcore = require('bitcore-lib')
const PrivateKey = bitcore.PrivateKey
const privateKey = new PrivateKey("711fd1eeecec8bb8c912129466504de109a17e764060c7e5e5b56d3ea0a8aa3b")
const publicKey = privateKey.toPublicKey()
console.log(publicKey.toString())

 

 

https://runkit.com/makevoid/bitcore-lib-address

9 / 42

Generate an address (bitcoin)

 

const bitcore = require('bitcore-lib')
const PrivateKey = bitcore.PrivateKey
const privateKey = new PrivateKey()
const publicKey = privateKey.toPublicKey()
console.log(publicKey.toString())
const address = publicKey.toAddress().toString()
console.log(address)

 

https://runkit.com/makevoid/bitcore-lib-address

10 / 42

Wallet Mockup

11 / 42

Setup Web3

 

const Web3 = require('web3')
const web3 = new Web3("https://mainnet.infura.io")

 

 

 

http://web3js.readthedocs.io/en/1.0/web3.html

 

12 / 42

Setup Web3

 

npm init -fy
npm i --save web3-eth-accounts
npm i --save isomorphic-fetch
npm i --save isomorphic-form-data

 

Optional:

npm i --save-dev lerna
npm i --save ethereum/web3.js#1.0

 

13 / 42

Setup Web3

 

 

 

const Web3 = require('web3')
const web3 = new Web3("https://kovan.infura.io")

 

14 / 42

Setup Web3

 

 

const Web3 = require('web3')
const provUrl = "https://mainnet.infura.io"
const provider = new Web3.providers.HttpProvider(provUrl)
const web3 = new Web3(provider)

 

15 / 42

Setup Web3 (accounts)

 

 

 

const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()

 

16 / 42

Derive an ethereum address

 

const bitcore = require('bitcore-lib')
const PrivateKey = bitcore.PrivateKey
const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()
const privateKey = new PrivateKey()
const key = `0x${privateKey.toString()}`
const account = accounts.privateKeyToAccount(key)
console.log(account.address)

 

https://runkit.com/makevoid/bitcore-lib-web3-accounts

 

17 / 42

Generate an ethereum "account"

 

const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()
const account = accounts.create()
console.log(account.address)

 

 

https://runkit.com/makevoid/web3-eth-accounts-address

18 / 42

Load the private key (Node) 1/2

 

const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()
const account = accounts.create()
console.log(account.privateKey)

 

 

Extra step: save key into private-key.txt

 

https://runkit.com/makevoid/web3-eth-accounts-private-key

19 / 42

Load the private key (Node) 2/2

 

const fs = require('fs')
const readFileSync = fs.readFileSync
const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()
const key = readFileSync("private-key.txt")
const account = accounts.privateKeyToAccount(key)
console.log(account.address)
20 / 42

Load the same key (Node)

 

const fs = require('fs')
const readFileSync = fs.readFileSync
const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()
const key = readFileSync("private-key.txt")
const account = accounts.privateKeyToAccount(key)
console.log(account.address)

 

run this again - notice that it will return the same address! :D

21 / 42

Receive Ethers

 

https://duckduckgo.com

 

!qr 0x1234...

 

 

 

 

 

tip for future UI - handy QR npm package:

https://www.npmjs.com/package/davidshimjs-qrcodejs

22 / 42

Check transaction on block explorer

 

 

https://etherscan.io/address/0x1234...

 

 

https://kovan.etherscan.io/...

(for testnet)

23 / 42

Kovan Faucet

 

 

https://gitter.im/kovan-testnet/faucet

24 / 42

Check address balance

require('isomorphic-fetch')
const getBalance = async (address) => {
const balanceUrl = `https://api.etherscan.io/api?module=account&action=balance&address=${address}&tag=latest`
let resp = await fetch(balanceUrl)
resp = await resp.json()
return Number(resp['result'] || 0)
}
(async () => {
const balance = await getBalance("0x738d145faabb1e00cf5a017588a9c0f998318012")
console.log(balance)
})()

https://runkit.com/makevoid/etherscan-getbalance

Etherscan API: https://etherscan.io/apis#accounts

25 / 42

Create Transaction - 1/2

const createTx = async ({recipient, account, value}) => {
const txData = {
value: value,
to: recipient,
gas: 21000,
gasPrice: 5000000000, // 5 gwei
from: account.address,
// nonce: 1,
}
const tx = await account.signTransaction(txData)
const txRaw = tx.rawTransaction
console.log("TX RAW", txRaw)
return txRaw
}
26 / 42

Create Transaction - 2/2

(async () => {
const fs = require('fs')
const readFileSync = fs.readFileSync
const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()
const key = readFileSync("private-key.txt")
const account = accounts.privateKeyToAccount(key)
createTx({
account: account,
recipient: "0xD9dDF72Ef671261Cb2266B9D924c5980C5186699",
value: 100000000000000, // 100 szabo
})
})()

https://ethgasstation.info - https://converter.murkin.me

27 / 42

For reference:

const createTx = async ({recipient, account, value}) => {
const txData = {
value: value,
to: recipient,
gas: 21000,
gasPrice: 5000000000, // 5 gwei
from: account.address,
// nonce: 1,
}
const tx = await account.signTransaction(txData)
const txRaw = tx.rawTransaction
console.log("TX RAW", txRaw)
return txRaw
}
(async () => {
const fs = require('fs')
const readFileSync = fs.readFileSync
const Accounts = require('web3-eth-accounts')
const accounts = new Accounts()
const key = readFileSync("private-key.txt")
const account = accounts.privateKeyToAccount(key)
createTx({
account: account,
recipient: "0xD9dDF72Ef671261Cb2266B9D924c5980C5186699",
value: 100000000000000, // 100 szabo
})
})()
28 / 42

Broadcast Transaction

 

 

 

https://etherscan.io/pushTx

(manual)

29 / 42

Broadcast Transaction

programmatically

require('isomorphic-fetch')
require('isomorphic-form-data')
const broadcastTransaction = async (rawTx) => {
const broadcastUrl = "https://api.etherscan.io/api"
// ...
}
30 / 42

Broadcast Transaction

const broadcastTransaction = async (rawTx) => {
const broadcastUrl = "https://api.etherscan.io/api"
const data = new FormData()
data.append('module', 'proxy')
data.append('action', 'eth_sendRawTransaction')
data.append('hex', rawTx)
data.append('apikey', '3DQFQQZ51G4M18SW8RDKHIMERD79GYTVEA') // please use your own api key
let resp = await fetch(broadcastUrl, {
method: "post",
body: data,
})
resp = await resp.json()
c.log("broadcast Tx:", resp)
return resp
}
31 / 42

Broadcast Transaction

// ...
(async () => {
const rawTx = "0xf86a0285012a05f20082520894d9ddf72ef671261cb2266b9d924c5980c5186699865af3107a40008026a0824d7b8b937437feff4ab206a556d599a70ce150ecc6ceeee7174bb5c679e0cea0643e49ae4bb8aae422245b6837997cff4a03714088043afc83deabf03043f253"
await broadcastTransaction(rawTx)
})()
32 / 42

For reference:

require('isomorphic-fetch')
require('isomorphic-form-data')
const broadcastTransaction = async (rawTx) => {
const broadcastUrl = "https://api.etherscan.io/api"
const data = new FormData()
data.append('module', 'proxy')
data.append('action', 'eth_sendRawTransaction')
data.append('hex', rawTx)
data.append('apikey', '3DQFQQZ51G4M18SW8RDKHIMERD79GYTVEA') // please use your own api key
let resp = await fetch(broadcastUrl, {
method: "post",
body: data,
})
resp = await resp.json()
c.log("broadcast Tx:", resp)
return resp
}
(async () => {
const rawTx = "0xf86a0285012a05f20082520894d9ddf72ef671261cb2266b9d924c5980c5186699865af3107a40008026a0824d7b8b937437feff4ab206a556d599a70ce150ecc6ceeee7174bb5c679e0cea0643e49ae4bb8aae422245b6837997cff4a03714088043afc83deabf03043f253"
await broadcastTransaction(rawTx)
})()
33 / 42

Wallet Mockup

34 / 42

Tools

 

- Browserify

- Babel

 

npm i -g browserify babelify
35 / 42

Tools - Browserify

 

RUN:

browserify js/index.js > js/dist/bundle.js

 

ADD:

<script src="js/dist/bundle.js" charset="utf-8"></script>
36 / 42

Tools - Browserify (bonus)

 

npm i -g watchify

 

RUN:

watchify js/index.js -o js/dist/bundle.js
37 / 42

Tools - Babel

 

.babelrc

 

{
"presets": ["env"]
}
38 / 42

Have fun!

 

 

 

 

 

 

 

 

@makevoid

Applied Blockchain

39 / 42

 

 

 

 

Few minutes left!

40 / 42

 

 

 

 

Time's up!

41 / 42

Thanks for attending!

 

 

 

 

 

 

 

 

@makevoid

Applied Blockchain

42 / 42

Ethereum Web Wallet

 

  1. Setup
  1. Generate a Private Key

  2. Derive an Ethereum address

  3. Get the address balance

  4. Sign transansaction

  5. Broadcast transaction

2 / 42
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow