on master help sign in
- Merged from master
commit729b8fd998926c66f8bf839445e67355464c0d17 authorChris Johnson <chris@cjohnson.io> dateMon 8 Apr 2019, 20:49 (8 years ago) parent9633832bf5e9bb
18 files changed+679−1662
package-lock.json+45−16 src/Root.js−1 src/bridge/components/StatelessTransaction.js+398−216 src/bridge/lib/wallet.js+2−2 src/bridge/style/extend.css+5 src/bridge/views/AcceptTransfer.js+11−179 src/bridge/views/CancelTransfer.js+7−197 src/bridge/views/CreateGalaxy.js+18−96 src/bridge/views/GenKeyfile.js+1−3 src/bridge/views/IssueChild.js+14−225 src/bridge/views/Mnemonic.js+53−11 src/bridge/views/Point/Actions.js−7 src/bridge/views/SetKeys.js+9−226 src/bridge/views/SetProxy.js+13−224 src/bridge/views/Shards.js+44−15 src/bridge/views/Ticket.js+42−17 src/bridge/views/Transfer.js+12−222 src/common/components/AppNavigation.js+5−5
package-lock.json+45−16
@@ -1,6 +1,6 @@
11{
22 "name": "bridge",
3− "version": "1.1.0",
3+ "version": "1.2.1",
44 "lockfileVersion": 1,
55 "requires": true,
66 "dependencies": {
@@ -18700,24 +18700,27 @@
1870018700 "integrity": "sha512-zbfndGg//cMW4aytXuF4dkBuUu/SUBA94U2zkmDoRwcplS9hCCn/S3VDp4A7CXw5tVxeCm9F6zdfHzWzei6nXg=="
1870118701 },
1870218702 "trezor-connect": {
18703− "version": "6.0.3",
18704− "resolved": "https://registry.npmjs.org/trezor-connect/-/trezor-connect-6.0.3.tgz",
18705− "integrity": "sha512-QqgnulN9uQstVjxYTF9+gcNk01kBUQA4xXy2abyPOgxh8Wio9iiSGftQa12KkFfWLZKkZD1sL03KvQ2ievL05g==",
18703+ "version": "7.0.2",
18704+ "resolved": "https://registry.npmjs.org/trezor-connect/-/trezor-connect-7.0.2.tgz",
18705+ "integrity": "sha512-KAFOqxEHHaFvrG8NGLFlM/QxHcwIa3gwfXcgTjCYM0g0zRpwIQBwe35AKsjAQO5yiTJQGa0Cu5MZufGJRGYjjw==",
1870618706 "requires": {
18707− "babel-runtime": "^6.26.0",
18708− "events": "^1.1.1",
18709− "whatwg-fetch": "^2.0.4"
18707+ "@babel/runtime": "^7.3.1",
18708+ "events": "^3.0.0",
18709+ "whatwg-fetch": "^3.0.0"
1871018710 },
1871118711 "dependencies": {
18712− "events": {
18713− "version": "1.1.1",
18714− "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
18715− "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
18712+ "@babel/runtime": {
18713+ "version": "7.4.3",
18714+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz",
18715+ "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==",
18716+ "requires": {
18717+ "regenerator-runtime": "^0.13.2"
18718+ }
1871618719 },
18717− "whatwg-fetch": {
18718− "version": "2.0.4",
18719− "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
18720− "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
18720+ "regenerator-runtime": {
18721+ "version": "0.13.2",
18722+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
18723+ "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
1872118724 }
1872218725 }
1872318726 },
@@ -18791,6 +18794,14 @@
1879118794 "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
1879218795 "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
1879318796 },
18797+ "typedarray-to-buffer": {
18798+ "version": "3.1.5",
18799+ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
18800+ "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
18801+ "requires": {
18802+ "is-typedarray": "^1.0.0"
18803+ }
18804+ },
1879418805 "typeforce": {
1879518806 "version": "1.18.0",
1879618807 "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
@@ -19599,7 +19610,20 @@
1959919610 "integrity": "sha1-j93qQuGbvyUh7IeVRkV6Yjqdye8=",
1960019611 "requires": {
1960119612 "underscore": "1.8.3",
19602− "web3-core-helpers": "1.0.0-beta.33"
19613+ "web3-core-helpers": "1.0.0-beta.33",
19614+ "websocket": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible"
19615+ },
19616+ "dependencies": {
19617+ "websocket": {
19618+ "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
19619+ "from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
19620+ "requires": {
19621+ "debug": "^2.2.0",
19622+ "nan": "^2.3.3",
19623+ "typedarray-to-buffer": "^3.1.2",
19624+ "yaeti": "^0.0.6"
19625+ }
19626+ }
1960319627 }
1960419628 },
1960519629 "web3-shh": {
@@ -20700,6 +20724,11 @@
2070020724 "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
2070120725 "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
2070220726 },
20727+ "yaeti": {
20728+ "version": "0.0.6",
20729+ "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
20730+ "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
20731+ },
2070320732 "yallist": {
2070420733 "version": "2.1.2",
2070520734 "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
src/Root.js−1
@@ -1,7 +1,6 @@
11import 'babel-polyfill' // required for @ledgerhq/hw-transport-u2f
22
33import React from 'react'
4−import ReactDOM from 'react-dom'
54import _ from 'lodash'
65
76import Bridge from './bridge/Bridge'
src/bridge/components/StatelessTransaction.js+398−216
@@ -1,244 +1,426 @@
1−import { Nothing } from 'folktale/maybe'
1+import { Nothing, Just } from 'folktale/maybe'
22import React from 'react'
33
44import { Code, H3 } from './Base'
55import { Button } from './Base'
66import { CheckboxButton, Input, InnerLabel } from './Base'
7+import { Warning } from '../components/Base'
78
8−import { renderSignedTx, signTransaction } from '../lib/txn'
9+import { addressFromSecp256k1Public } from '../lib/wallet'
10+import { BRIDGE_ERROR } from '../lib/error'
11+import { ROUTE_NAMES } from '../lib/router'
912
10−const StatelessTransaction = props => {
11− const { web3, gasPrice, gasLimit, nonce, chainId } = props
12− const { setNonce, setChainId, setGasLimit, setGasPrice, showGasDetails, toggleGasDetails } = props
13− const { txn, stx, createUnsignedTxn } = props
14− const { canSign, canGenerate, canApprove } = props
15− const { setUserApproval, userApproval } = props
16− const { canSend, handleSubmit } = props
13+import {
14+ sendSignedTransaction,
15+ fromWei,
16+ renderSignedTx,
17+ signTransaction
18+ } from '../lib/txn'
19+
20+class StatelessTransaction extends React.Component {
21+
22+ constructor(props) {
23+ super(props)
24+
25+ this.state = {
26+ gasPrice: '5',
27+ gasLimit: '600000',
28+ showGasDetails: false,
29+ userApproval: false,
30+ chainId: '',
31+ nonce: '',
32+ stx: Nothing(),
33+ txn: Nothing(),
34+ txError: Nothing(),
35+ }
36+
37+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
38+ this.submit = this.submit.bind(this)
39+ this.setUserApproval = this.setUserApproval.bind(this)
40+ this.setTxn = this.setTxn.bind(this)
41+ this.setStx = this.setStx.bind(this)
42+ this.sendTxn = this.sendTxn.bind(this)
43+ this.setNonce = this.setNonce.bind(this)
44+ this.setChainId = this.setChainId.bind(this)
45+ this.setGasPrice = this.setGasPrice.bind(this)
46+ this.setGasLimit = this.setGasLimit.bind(this)
47+ this.rangeChange = this.rangeChange.bind(this)
48+ this.toggleGasDetails = this.toggleGasDetails.bind(this)
49+ }
50+
51+ componentDidMount() {
52+ const { props } = this
53+
54+ const addr = props.wallet.matchWith({
55+ Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
56+ Nothing: () => {
57+ throw BRIDGE_ERROR.MISSING_WALLET
58+ }
59+ })
60+
61+ props.web3.matchWith({
62+ Nothing: () => {},
63+ Just: (w3) => {
64+ const validWeb3 = w3.value
65+
66+ const getTxMetadata = [
67+ validWeb3.eth.getTransactionCount(addr),
68+ validWeb3.eth.net.getId(),
69+ validWeb3.eth.getGasPrice()
70+ ];
71+
72+ Promise.all(getTxMetadata).then(r => {
73+ const txMetadata = {
74+ nonce: r[0],
75+ chainId: r[1],
76+ gasPrice: fromWei(r[2], 'gwei'),
77+ };
78+
79+ this.setState({...txMetadata})
80+
81+ })
82+ }
83+ });
84+ }
85+
86+ submit(){
87+ const { props, state } = this
88+ sendSignedTransaction(props.web3.value, state.stx)
89+ .then(sent => {
90+ props.setTxnHashCursor(sent)
91+ props.popRoute()
92+
93+ if (props.networkSeed) {
94+ props.setNetworkSeedCache(props.networkSeed)
95+ props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, {promptKeyfile: true})
96+ } else {
97+ props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
98+ }
99+ })
100+ .catch(err => {
101+ if (err.map) {
102+ this.setState({ txError: err.map(val => val.merge()) })
103+ } else {
104+ this.setState({ txError: err })
105+ }
106+ })
107+ }
108+
109+ setUserApproval(){
110+ const {state} = this
111+ this.setState({ userApproval: !state.userApproval })
112+ }
113+
114+ toggleGasDetails() {
115+ this.setState({
116+ showGasDetails: !this.state.showGasDetails
117+ })
118+ }
119+
120+ setStx(stx){
121+ this.setState({
122+ stx,
123+ userApproval: false,
124+ })
125+ }
126+
127+ setTxn(txn){
128+ this.setState({ txn })
129+ }
130+
131+ createUnsignedTxn() {
132+ const txn = this.props.createUnsignedTxn()
133+
134+ this.setState({ txn })
135+ }
136+
137+ clearTxn() {
138+ this.setState({
139+ userApproval: false,
140+ txn: Nothing(),
141+ stx: Nothing(),
142+ })
143+ }
144+
145+ setNonce(nonce){
146+ this.setState({ nonce })
147+ this.clearStx()
148+ }
149+
150+ setChainId(chainId){
151+ this.setState({ chainId })
152+ this.clearStx()
153+ }
154+
155+ setGasPrice(gasPrice) {
156+ this.setState({ gasPrice })
157+ this.clearStx()
158+ }
159+
160+ rangeChange(e) {
161+ this.setGasPrice(e.target.value);
162+ }
163+
164+ setGasLimit(gasLimit){
165+ this.setState({ gasLimit })
166+ this.clearStx()
167+ }
168+
169+ clearStx() {
170+ this.setState({
171+ userApproval: false,
172+ stx: Nothing(),
173+ })
174+ }
17175
18176 // TODO: Investigate
19− // setState doesn't seem to work in SetProxy/handleSubmit;
177+ // setState doesn't seem to work in SetProxy/submit;
20178 // - TypeError: Cannot read property 'updater' of undefined
21179 // so just modifying the DOM manually here. (https://imgur.com/a/i0Qsyq1)
22180
23− const handleRangeChange = (e) => {
24− setGasPrice(e.target.value);
25− }
26−
27− const sendTxn = (e) => {
181+ sendTxn(e) {
28182 e.target.setAttribute("disabled", true);
29183 let spinner = e.target.querySelectorAll('.btn-spinner')[0];
30184 spinner.classList.remove('hide');
31− handleSubmit()
32− }
33−
34− const generateButtonColor =
35− Nothing.hasInstance(txn)
36− ? 'blue'
37− : 'green'
38−
39− const signerButtonColor =
40− Nothing.hasInstance(stx)
41− ? 'blue'
42− : 'green'
43−
44− const generateTxnButton =
45− <Button
46− className={ 'mt-8' }
47− disabled={ !canGenerate }
48− prop-color={ generateButtonColor }
49− prop-size={ 'lg wide' }
50− onClick={ createUnsignedTxn }
51− >
52− { 'Generate Transaction' }
53− </Button>
54−
55− const unsignedTxnDisplay = txn.matchWith({
56− Nothing: _ => '',
57− Just: tx =>
185+ this.submit()
186+ }
187+
188+ render() {
189+ const { web3, canGenerate } = this.props
190+ const { gasPrice, gasLimit, nonce, chainId,
191+ txn, stx, userApproval, showGasDetails } = this.state
192+
193+ const { setNonce, setChainId, setGasLimit, setGasPrice, toggleGasDetails,
194+ setUserApproval, sendTxn, createUnsignedTxn } = this
195+
196+ const { state } = this
197+
198+ const canSign = Just.hasInstance(txn)
199+ const canApprove = Just.hasInstance(stx)
200+ const canSend = Just.hasInstance(stx) && userApproval === true
201+
202+ const generateButtonColor =
203+ Nothing.hasInstance(txn)
204+ ? 'blue'
205+ : 'green'
206+
207+ const signerButtonColor =
208+ Nothing.hasInstance(stx)
209+ ? 'blue'
210+ : 'green'
211+
212+ const generateTxnButton =
213+ <Button
214+ className={ 'mt-8' }
215+ disabled={ !canGenerate }
216+ prop-color={ generateButtonColor }
217+ prop-size={ 'lg wide' }
218+ onClick={ createUnsignedTxn }
219+ >
220+ { 'Generate Transaction' }
221+ </Button>
222+
223+ const unsignedTxnDisplay = txn.matchWith({
224+ Nothing: _ => '',
225+ Just: tx =>
226+ <React.Fragment>
227+ <H3 className={ 'mt-8' }>
228+ { 'Unsigned Transaction' }
229+ </H3>
230+ <Code>
231+ { JSON.stringify(tx.value, null, 2) }
232+ </Code>
233+ </React.Fragment>
234+ })
235+
236+ const gasPriceRangeDialogue = (
58237 <React.Fragment>
59− <H3 className={ 'mt-8' }>
60− { 'Unsigned Transaction' }
61− </H3>
62− <Code>
63− { JSON.stringify(tx.value, null, 2) }
64− </Code>
65− </React.Fragment>
66− })
67−
68− const gasPriceRangeDialogue = (
69− <React.Fragment>
70− <div className="mt-12 flex space-between align-baseline">
71− <div>
72− <span>Gas Price:</span>
73− <span className="ml-4 text-700 text-sm">{gasPrice} gwei</span>
238+ <div className="mt-12 flex space-between align-baseline">
239+ <div>
240+ <span>Gas Price:</span>
241+ <span className="ml-4 text-700 text-sm">{gasPrice} gwei</span>
242+ </div>
243+ <div className="text-sm">
244+ <span>Max transaction fee: </span>
245+ <span className="text-700">{(gasPrice * gasLimit) / 1000000000} eth</span>
246+ </div>
74247 </div>
75− <div className="text-sm">
76− <span>Max transaction fee: </span>
77− <span className="text-700">{(gasPrice * gasLimit) / 1000000000} eth</span>
248+
249+ <input
250+ className="mt-4"
251+ type="range"
252+ min="2"
253+ max="20"
254+ list="gweiVals"
255+ value={gasPrice}
256+ onChange={this.rangeChange}
257+ />
258+
259+ <div className="flex space-between text-sm mb-8">
260+ <div>Cheap</div>
261+ <div>Fast</div>
78262 </div>
79− </div>
80−
81− <input
82− className="mt-4"
83− type="range"
84− min="2"
85− max="20"
86− list="gweiVals"
87− value={gasPrice}
88− onChange={handleRangeChange}
89− />
90−
91− <div className="flex space-between text-sm mb-8">
92− <div>Cheap</div>
93− <div>Fast</div>
94− </div>
95− </React.Fragment>
96− )
97−
98− const toggleGasDetailsDialogue = (
99− <a href="javascript:void(0)" onClick={toggleGasDetails}>Gas Details</a>
100− )
101−
102− const gasPriceDialogue =
103− <Input
104− className={ 'mono mt-4' }
105− prop-size={ 'md' }
106− prop-format={ 'innerLabel' }
107− value={ gasPrice }
108− onChange={ setGasPrice }
109− >
110− <InnerLabel>
111− { 'Gas Price (gwei)' }
112− </InnerLabel>
113− </Input>
114−
115− const gasLimitDialogue =
116− <Input
117− className={ 'mono mt-4' }
118− prop-size={ 'md' }
119− prop-format={ 'innerLabel' }
120− value={ gasLimit }
121− onChange={ setGasLimit }>
122− <InnerLabel>
123− { 'Gas Limit' }
124− </InnerLabel>
125− </Input>
126−
127− const nonceDialogue =
128− <Input
129− className={ 'mono mt-4' }
130− prop-size={ 'md' }
131− prop-format={ 'innerLabel' }
132− value={ nonce }
133− onChange={ setNonce }
134− >
135− <InnerLabel>
136− { 'Nonce' }
137− </InnerLabel>
138− </Input>
139−
140− const chainDialogue =
141− <Input
142− className={ 'mono mt-4 mb-8' }
143− prop-size={ 'md' }
144− prop-format={ 'innerLabel' }
145− value={ chainId }
146− onChange={ setChainId }
147− >
148− <InnerLabel>
149− { 'Chain ID' }
150− </InnerLabel>
151− </Input>
152−
153− const onlineParamsDialogue = web3.matchWith({
154− Just: _ => <div />,
155− Nothing: _ =>
156− <React.Fragment>
157− { nonceDialogue }
158− { chainDialogue }
159− </React.Fragment>
160− })
161−
162− const signTxnButton =
163− <Button
164− disabled={ !canSign }
165− className={ 'mt-8' }
166− prop-size={ 'lg wide' }
167− prop-color={ signerButtonColor }
168− onClick={ () => signTransaction(props) }
169− >
170− { 'Sign Transaction' }
171− </Button>
172−
173− const signedTxnDisplay = stx.matchWith({
174− Nothing: _ => '',
175− Just: tx =>
176− <React.Fragment>
177− <H3 className={ 'mt-8' }>
178− { 'Signed Transaction' }
179− </H3>
180− <Code>
181− { JSON.stringify(renderSignedTx(tx.value), null, 2) }
182− </Code>
183− </React.Fragment>
184− })
185−
186− const confirmButton =
187− <CheckboxButton
188− className={ 'mt-8' }
189− disabled={ !canApprove }
190− onClick={ setUserApproval }
191− state={ userApproval }
192− >
193− <div onClick={ setUserApproval }>
194− { `I approve this transaction and wish to send.` }
195− </div>
196− </CheckboxButton>
197−
198− const sendTxnButton =
199− <Button
200− prop-size={ 'xl wide' }
201− className={ 'mt-8' }
202− disabled={ !canSend }
203− onClick={ sendTxn }
204− >
205− <span className="relative">
206− <span className="btn-spinner hide"></span>
207− { 'Send Transaction' }
208− </span>
209− </Button>
210−
211− const sendDialogue = web3.matchWith({
212− Nothing: _ => '',
213− Just: _ =>
214− <React.Fragment>
215− { confirmButton }
216− { sendTxnButton }
217263 </React.Fragment>
218− })
264+ )
265+
266+ const toggleGasDetailsDialogue = (
267+ <span className="text-link" onClick={toggleGasDetails}>Gas Details</span>
268+ )
269+
270+ const gasPriceDialogue =
271+ <Input
272+ className={ 'mono mt-4' }
273+ prop-size={ 'md' }
274+ prop-format={ 'innerLabel' }
275+ value={ gasPrice }
276+ onChange={ setGasPrice }
277+ >
278+ <InnerLabel>
279+ { 'Gas Price (gwei)' }
280+ </InnerLabel>
281+ </Input>
219282
220− return (
221− <React.Fragment>
222− { generateTxnButton }
223− { unsignedTxnDisplay }
283+ const gasLimitDialogue =
284+ <Input
285+ className={ 'mono mt-4' }
286+ prop-size={ 'md' }
287+ prop-format={ 'innerLabel' }
288+ value={ gasLimit }
289+ onChange={ setGasLimit }>
290+ <InnerLabel>
291+ { 'Gas Limit' }
292+ </InnerLabel>
293+ </Input>
224294
225− { gasPriceRangeDialogue }
226− { toggleGasDetailsDialogue }
295+ const nonceDialogue =
296+ <Input
297+ className={ 'mono mt-4' }
298+ prop-size={ 'md' }
299+ prop-format={ 'innerLabel' }
300+ value={ nonce }
301+ onChange={ setNonce }
302+ >
303+ <InnerLabel>
304+ { 'Nonce' }
305+ </InnerLabel>
306+ </Input>
227307
228− { showGasDetails &&
229− <div>
230− { gasPriceDialogue }
231− { gasLimitDialogue }
308+ const chainDialogue =
309+ <Input
310+ className={ 'mono mt-4 mb-8' }
311+ prop-size={ 'md' }
312+ prop-format={ 'innerLabel' }
313+ value={ chainId }
314+ onChange={ setChainId }
315+ >
316+ <InnerLabel>
317+ { 'Chain ID' }
318+ </InnerLabel>
319+ </Input>
320+
321+ const onlineParamsDialogue = web3.matchWith({
322+ Just: _ => <div />,
323+ Nothing: _ =>
324+ <React.Fragment>
325+ { nonceDialogue }
326+ { chainDialogue }
327+ </React.Fragment>
328+ })
329+
330+ const signTxnButton =
331+ <Button
332+ disabled={ !canSign }
333+ className={ 'mt-8' }
334+ prop-size={ 'lg wide' }
335+ prop-color={ signerButtonColor }
336+ onClick={ () => signTransaction({...this.props, ...this.state, setStx: this.setStx}) }
337+ >
338+ { 'Sign Transaction' }
339+ </Button>
340+
341+ const signedTxnDisplay = stx.matchWith({
342+ Nothing: _ => '',
343+ Just: tx =>
344+ <React.Fragment>
345+ <H3 className={ 'mt-8' }>
346+ { 'Signed Transaction' }
347+ </H3>
348+ <Code>
349+ { JSON.stringify(renderSignedTx(tx.value), null, 2) }
350+ </Code>
351+ </React.Fragment>
352+ })
353+
354+ const confirmButton =
355+ <CheckboxButton
356+ className={ 'mt-8' }
357+ disabled={ !canApprove }
358+ onClick={ setUserApproval }
359+ state={ userApproval }
360+ >
361+ <div onClick={ setUserApproval }>
362+ { `I approve this transaction and wish to send.` }
232363 </div>
233− }
234− { onlineParamsDialogue }
364+ </CheckboxButton>
235365
236− { signTxnButton }
366+ const sendTxnButton =
367+ <Button
368+ prop-size={ 'xl wide' }
369+ className={ 'mt-8' }
370+ disabled={ !canSend }
371+ onClick={ sendTxn }
372+ >
373+ <span className="relative">
374+ <span className="btn-spinner hide"></span>
375+ { 'Send Transaction' }
376+ </span>
377+ </Button>
237378
238− { signedTxnDisplay }
239− { sendDialogue }
240− </React.Fragment>
241− )
379+ const sendDialogue = web3.matchWith({
380+ Nothing: _ => '',
381+ Just: _ =>
382+ <React.Fragment>
383+ { confirmButton }
384+ { sendTxnButton }
385+ </React.Fragment>
386+ })
387+
388+ const txnErrorDialogue = Nothing.hasInstance(state.txError)
389+ ? ''
390+ : <Warning className={'mt-8'}>
391+ <H3 style={{marginTop: 0, paddingTop: 0}}>
392+ {
393+ 'There was an error sending your transaction.'
394+ }
395+ </H3>
396+ { state.txError.value }
397+ </Warning>
398+
399+ return (
400+ <React.Fragment>
401+ { generateTxnButton }
402+ { unsignedTxnDisplay }
403+
404+ { gasPriceRangeDialogue }
405+ { toggleGasDetailsDialogue }
406+
407+ { showGasDetails &&
408+ <div>
409+ { gasPriceDialogue }
410+ { gasLimitDialogue }
411+ </div>
412+ }
413+ { onlineParamsDialogue }
414+
415+ { signTxnButton }
416+
417+ { signedTxnDisplay }
418+ { sendDialogue }
419+
420+ { txnErrorDialogue }
421+ </React.Fragment>
422+ )
423+ }
242424}
243425
244426export default StatelessTransaction
src/bridge/lib/wallet.js+2−2
@@ -86,10 +86,10 @@ const toChecksumAddress = (address) => {
8686const eqAddr = (addr0, addr1) =>
8787 toChecksumAddress(addr0) === toChecksumAddress(addr1)
8888
89−const walletFromMnemonic = (mnemonic, hdpath) => {
89+const walletFromMnemonic = (mnemonic, hdpath, passphrase) => {
9090 const seed =
9191 bip39.validateMnemonic(mnemonic)
92− ? Maybe.Just(bip39.mnemonicToSeed(mnemonic))
92+ ? Maybe.Just(bip39.mnemonicToSeed(mnemonic, passphrase))
9393 : Maybe.Nothing()
9494
9595 const toWallet = (sd, path) => {
src/bridge/style/extend.css+5
@@ -466,6 +466,11 @@ code {
466466 cursor: pointer;
467467}
468468
469+.text-link {
470+ cursor: pointer;
471+ text-decoration: underline;
472+}
473+
469474.keyfile {
470475 white-space: pre-wrap;
471476 word-break: break-word;
src/bridge/views/AcceptTransfer.js+11−179
@@ -1,6 +1,6 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just } from 'folktale/maybe'
22import React from 'react'
3−import { Row, Col, H1, H3, P, Warning } from '../components/Base'
3+import { Row, Col, H1, P } from '../components/Base'
44import { InnerLabel, AddressInput, ShowBlockie, Anchor } from '../components/Base'
55import * as azimuth from 'azimuth-js'
66import * as ob from 'urbit-ob'
@@ -8,8 +8,6 @@ import * as ob from 'urbit-ob'
88import StatelessTransaction from '../components/StatelessTransaction'
99import { BRIDGE_ERROR } from '../lib/error'
1010import { NETWORK_NAMES } from '../lib/network'
11−import { ROUTE_NAMES } from '../lib/router'
12−import { sendSignedTransaction, fromWei } from '../lib/txn'
1311import { isValidAddress, addressFromSecp256k1Public } from '../lib/wallet'
1412
1513class AcceptTransfer extends React.Component {
@@ -32,144 +30,17 @@ class AcceptTransfer extends React.Component {
3230
3331 this.state = {
3432 receivingAddress: receivingAddress,
35− txn: Nothing(),
36− txError: Nothing(),
3733 incomingPoint: incomingPoint,
38− userApproval: false,
39− nonce: '',
40− gasPrice: '5',
41− showGasDetails: false,
42− chainId: '',
43− gasLimit: '600000',
44− stx: Nothing(),
4534 }
4635
4736 this.handleAddressInput = this.handleAddressInput.bind(this)
48− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
49− this.handleSubmit = this.handleSubmit.bind(this)
50− this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
51− this.handleSetTxn = this.handleSetTxn.bind(this)
52− this.handleSetStx = this.handleSetStx.bind(this)
53− this.handleSetNonce = this.handleSetNonce.bind(this)
54− this.handleSetChainId = this.handleSetChainId.bind(this)
55− this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
56− this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
57− this.toggleGasDetails = this.toggleGasDetails.bind(this)
58− }
59−
60− toggleGasDetails() {
61− this.setState({
62− showGasDetails: !this.state.showGasDetails
63− })
64− }
65−
66− componentDidMount() {
67− const { props } = this
68−
69− const addr = props.wallet.matchWith({
70− Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
71− Nothing: () => {
72− throw BRIDGE_ERROR.MISSING_WALLET
73− }
74− })
75−
76− props.web3.matchWith({
77− Nothing: () => {},
78− Just: (w3) => {
79− const validWeb3 = w3.value
80−
81− const getTxMetadata = [
82− validWeb3.eth.getTransactionCount(addr),
83− validWeb3.eth.net.getId(),
84− validWeb3.eth.getGasPrice()
85− ];
86−
87− Promise.all(getTxMetadata).then(r => {
88− const txMetadata = {
89− nonce: r[0],
90− chainId: r[1],
91− gasPrice: fromWei(r[2], 'gwei'),
92− };
93−
94− this.setState({...txMetadata})
95−
96− })
97− }
98− });
37+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
38+ this.statelessRef = React.createRef();
9939 }
10040
10141 handleAddressInput(proxyAddress) {
10242 this.setState({ proxyAddress })
103− this.handleClearTxn()
104− }
105−
106− handleCreateUnsignedTxn() {
107− const txn = this.createUnsignedTxn()
108− this.setState({ txn })
109− }
110−
111− handleSetUserApproval(){
112− const {state} = this
113− this.setState({ userApproval: !state.userApproval })
114− }
115−
116− handleSetTxn(txn){
117− this.setState({ txn })
118− }
119−
120− handleSetStx(stx){
121− this.setState({
122− stx,
123− userApproval: false,
124− })
125− }
126−
127− handleSetNonce(nonce){
128− this.setState({ nonce })
129− this.handleClearStx()
130− }
131−
132− handleSetChainId(chainId){
133− this.setState({ chainId })
134− this.handleClearStx()
135− }
136−
137− handleSetGasPrice(gasPrice){
138− this.setState({ gasPrice })
139− this.handleClearStx()
140− }
141−
142− handleSetGasLimit(gasLimit){
143− this.setState({ gasLimit })
144− this.handleClearStx()
145− }
146−
147− handleClearStx() {
148− this.setState({
149− userApproval: false,
150− stx: Nothing(),
151− })
152− }
153−
154− handleClearTxn() {
155− this.setState({
156− userApproval: false,
157− txn: Nothing(),
158− stx: Nothing(),
159− })
160− }
161−
162− handleSubmit() {
163− const { props, state } = this
164− sendSignedTransaction(props.web3.value, state.stx)
165− .then(sent => {
166− props.setTxnHashCursor(sent)
167− props.popRoute()
168− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
169− })
170− .catch(err => {
171− this.setState({ txError: err.map(val => val.merge()) })
172− })
43+ this.statelessRef.current.clearTxn()
17344 }
17445
17546 createUnsignedTxn() {
@@ -200,15 +71,9 @@ class AcceptTransfer extends React.Component {
20071 }
20172
20273 render() {
203−
20474 const { state, props } = this
20575 const validAddress = isValidAddress(state.receivingAddress)
20676 const canGenerate = validAddress === true
207− const canSign = Just.hasInstance(state.txn)
208− const canApprove = Just.hasInstance(state.stx)
209− const canSend =
210− Just.hasInstance(state.stx)
211− && state.userApproval === true
21277
21378 const esvisible =
21479 props.networkType === NETWORK_NAMES.ROPSTEN ||
@@ -260,46 +125,13 @@ class AcceptTransfer extends React.Component {
260125 walletType={props.walletType}
261126 walletHdPath={props.walletHdPath}
262127 networkType={props.networkType}
263− // Tx
264− txn={state.txn}
265− stx={state.stx}
266− // Tx details
267− nonce={state.nonce}
268− gasPrice={state.gasPrice}
269− chainId={state.chainId}
270− gasLimit={state.gasLimit}
271− showGasDetails={state.showGasDetails}
272− toggleGasDetails={this.toggleGasDetails}
273− // Checks
274− userApproval={state.userApproval}
128+ setTxnHashCursor={props.setTxnHashCursor}
129+ popRoute={props.popRoute}
130+ pushRoute={props.pushRoute}
131+ // Other
275132 canGenerate={ canGenerate }
276− canSign={ canSign }
277− canApprove={ canApprove }
278− canSend={ canSend }
279− // Methods
280− createUnsignedTxn={this.handleCreateUnsignedTxn}
281− setUserApproval={this.handleSetUserApproval}
282− setTxn={this.handleSetTxn}
283− setStx={this.handleSetStx}
284− setNonce={this.handleSetNonce}
285− setChainId={this.handleSetChainId}
286− setGasPrice={this.handleSetGasPrice}
287− setGasLimit={this.handleSetGasLimit}
288− handleSubmit={this.handleSubmit} />
289−
290− {
291− Nothing.hasInstance(state.txError)
292− ? ''
293− : <Warning className={'mt-8'}>
294− <H3 style={{marginTop: 0, paddingTop: 0}}>
295− {
296− 'There was an error sending your transaction.'
297− }
298− </H3>
299− { state.txError.value }
300− </Warning>
301− }
302−
133+ createUnsignedTxn={this.createUnsignedTxn}
134+ ref={ this.statelessRef } />
303135 </Col>
304136 </Row>
305137 )
src/bridge/views/CancelTransfer.js+7−197
@@ -1,23 +1,12 @@
11import Maybe from 'folktale/maybe'
22import React from 'react'
3−import { Row, Col, H1, P, Warning, H3 } from '../components/Base'
3+import { Row, Col, H1, P } from '../components/Base'
44import * as azimuth from 'azimuth-js'
55import * as ob from 'urbit-ob'
66
77import StatelessTransaction from '../components/StatelessTransaction'
88import { BRIDGE_ERROR } from '../lib/error'
99import { ETH_ZERO_ADDR } from '../lib/wallet'
10−import { ROUTE_NAMES } from '../lib/router'
11−
12−import {
13− sendSignedTransaction,
14− fromWei,
15− } from '../lib/txn'
16−
17−import {
18− addressFromSecp256k1Public,
19−} from '../lib/wallet'
20−
2110
2211class CancelTransfer extends React.Component {
2312 constructor(props) {
@@ -32,150 +21,10 @@ class CancelTransfer extends React.Component {
3221
3322 this.state = {
3423 proxyAddress: '',
35− txn: Maybe.Nothing(),
36− txError: Maybe.Nothing(),
3724 pointInTransfer: pointInTransfer,
38− userApproval: false,
39− nonce: '',
40− gasPrice: '5',
41− showGasDetails: false,
42− chainId: '',
43− gasLimit: '600000',
44− stx: Maybe.Nothing(),
4525 }
46− // Transaction
47− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
48− this.handleSubmit = this.handleSubmit.bind(this)
49− this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
50− this.handleSetTxn = this.handleSetTxn.bind(this)
51− this.handleSetStx = this.handleSetStx.bind(this)
52− this.handleSetNonce = this.handleSetNonce.bind(this)
53− this.handleSetChainId = this.handleSetChainId.bind(this)
54− this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
55− this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
56− this.toggleGasDetails = this.toggleGasDetails.bind(this)
57− }
5826
59− toggleGasDetails() {
60− this.setState({
61− showGasDetails: !this.state.showGasDetails
62− })
63− }
64−
65− componentDidMount() {
66− const { props } = this
67−
68− const addr = props.wallet.matchWith({
69− Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
70− Nothing: () => {
71− throw BRIDGE_ERROR.MISSING_WALLET
72− }
73− })
74−
75− props.web3.matchWith({
76− Nothing: () => {},
77− Just: (w3) => {
78− const validWeb3 = w3.value
79−
80− const getTxMetadata = [
81− validWeb3.eth.getTransactionCount(addr),
82− validWeb3.eth.net.getId(),
83− validWeb3.eth.getGasPrice()
84− ];
85−
86− Promise.all(getTxMetadata).then(r => {
87− const txMetadata = {
88− nonce: r[0],
89− chainId: r[1],
90− gasPrice: fromWei(r[2], 'gwei'),
91− };
92−
93− this.setState({...txMetadata})
94−
95− })
96− }
97− });
98−
99− }
100−
101−
102−
103− handleCreateUnsignedTxn() {
104− const txn = this.createUnsignedTxn()
105− this.setState({ txn })
106− }
107−
108−
109−
110− handleSetUserApproval(){
111− const {state} = this
112− this.setState({ userApproval: !state.userApproval })
113− }
114−
115−
116−
117− handleSetTxn(txn){
118− this.setState({ txn })
119− }
120−
121−
122−
123− handleSetStx(stx){
124− this.setState({
125− stx,
126− userApproval: false,
127− })
128− }
129−
130−
131−
132− handleSetNonce(nonce){
133− this.setState({ nonce })
134− this.handleClearStx()
135− }
136−
137−
138−
139− handleSetChainId(chainId){
140− this.setState({ chainId })
141− this.handleClearStx()
142− }
143−
144−
145−
146− handleSetGasPrice(gasPrice){
147− this.setState({ gasPrice })
148− this.handleClearStx()
149− }
150−
151−
152−
153− handleSetGasLimit(gasLimit){
154− this.setState({ gasLimit })
155− this.handleClearStx()
156− }
157−
158−
159−
160− handleClearStx() {
161− this.setState({
162− userApproval: false,
163− stx: Maybe.Nothing(),
164− })
165− }
166−
167−
168− handleSubmit(){
169− const { props, state } = this
170− sendSignedTransaction(props.web3.value, state.stx)
171− .then(sent => {
172− props.setTxnHashCursor(sent)
173− props.popRoute()
174− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
175− })
176− .catch(err => {
177− this.setState({ txError: err.map(val => val.merge()) })
178− })
27+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
17928 }
18029
18130 createUnsignedTxn() {
@@ -204,7 +53,6 @@ class CancelTransfer extends React.Component {
20453 return Maybe.Just(txn)
20554 }
20655
207−
20856 render() {
20957 const { props, state } = this
21058
@@ -217,10 +65,6 @@ class CancelTransfer extends React.Component {
21765 // const canGenerate = validAddress === true
21866
21967 const canGenerate = true
220− const canSign = Maybe.Just.hasInstance(state.txn)
221− const canApprove = Maybe.Just.hasInstance(state.stx)
222− const canSend = Maybe.Just.hasInstance(state.stx) && state.userApproval === true
223−
22468
22569 return (
22670 <Row>
@@ -242,46 +86,12 @@ class CancelTransfer extends React.Component {
24286 walletType={props.walletType}
24387 walletHdPath={props.walletHdPath}
24488 networkType={props.networkType}
245− // Tx
246− txn={state.txn}
247− stx={state.stx}
248− // Tx details
249− nonce={state.nonce}
250− gasPrice={state.gasPrice}
251− chainId={state.chainId}
252− gasLimit={state.gasLimit}
253− showGasDetails={state.showGasDetails}
254− toggleGasDetails={this.toggleGasDetails}
255− // Checks
256− userApproval={state.userApproval}
89+ setTxnHashCursor={props.setTxnHashCursor}
90+ popRoute={props.popRoute}
91+ pushRoute={props.pushRoute}
92+ // Other
25793 canGenerate={ canGenerate }
258− canSign={ canSign }
259− canApprove={ canApprove }
260− canSend={ canSend }
261− // Methods
262− createUnsignedTxn={this.handleCreateUnsignedTxn}
263− setUserApproval={this.handleSetUserApproval}
264− setTxn={this.handleSetTxn}
265− setStx={this.handleSetStx}
266− setNonce={this.handleSetNonce}
267− setChainId={this.handleSetChainId}
268− setGasPrice={this.handleSetGasPrice}
269− setGasLimit={this.handleSetGasLimit}
270− handleSubmit={this.handleSubmit} />
271−
272− {
273− Maybe.Nothing.hasInstance(state.txError)
274− ? ''
275− : <Warning className={'mt-8'}>
276− <H3 style={{marginTop: 0, paddingTop: 0}}>
277− {
278− 'There was an error sending your transaction.'
279− }
280− </H3>
281− { state.txError.value }
282− </Warning>
283− }
284−
94+ createUnsignedTxn={this.createUnsignedTxn} />
28595 </Col>
28696 </Row>
28797 )
src/bridge/views/CreateGalaxy.js+18−96
@@ -9,16 +9,8 @@ import { InnerLabel, GalaxyInput, AddressInput, ValidatedSigil, ShowBlockie } fr
99import StatelessTransaction from '../components/StatelessTransaction'
1010
1111import { BRIDGE_ERROR } from '../lib/error'
12−
1312import { NETWORK_NAMES } from '../lib/network'
14−
15−import {
16− sendSignedTransaction,
17− getTxnInfo,
18− canDecodePatp
19−} from '../lib/txn'
20−
21−import { ROUTE_NAMES } from '../lib/router'
13+import { canDecodePatp } from '../lib/txn'
2214
2315import {
2416 addressFromSecp256k1Public,
@@ -61,54 +53,23 @@ class CreateGalaxy extends React.Component {
6153 galaxyOwner: galaxyOwner,
6254 galaxyName: '',
6355 isAvailable: null,
64− txApproval: false,
65− nonce: '',
66− gasPrice: '5',
67− showGasDetails: false,
68− chainId: '',
69− txInfo: '',
70− gasLimit: '600000',
71− txn: Maybe.Nothing(),
72− stx: Maybe.Nothing(),
7356 }
7457
7558 this.handleAddressInput = this.handleAddressInput.bind(this)
7659 this.handleGalaxyNameInput = this.handleGalaxyNameInput.bind(this)
7760 this.confirmAvailability = this.confirmAvailability.bind(this)
78− this.handleSubmit = this.handleSubmit.bind(this)
79− this.toggleGasDetails = this.toggleGasDetails.bind(this)
80− }
81−
82− toggleGasDetails() {
83− this.setState({
84− showGasDetails: !this.state.showGasDetails
85− })
61+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
62+ this.statelessRef = React.createRef();
8663 }
8764
88− componentDidMount() {
89− const { props } = this
90− const addr = props.wallet.matchWith({
91− Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
92− Nothing: () => {
93− throw BRIDGE_ERROR.MISSING_WALLET
94− }
95− })
96−
97− // getTxnInfo(props.web3.value, addr).then(txInfo => this.setState(txInfo))
98− props.web3.map(w3 => getTxnInfo(w3, addr)
99− .then(txInfo => this.setState(txInfo))
100− )
101− }
102−
103−
10465 handleGalaxyNameInput = (galaxyName) => {
10566 this.setState({ galaxyName, isAvailable: null })
106− this.clearTransaction()
67+ this.statelessRef.current.clearTxn()
10768 }
10869
10970 handleAddressInput = (galaxyOwner) => {
11071 this.setState({ galaxyOwner })
111− this.clearTransaction()
72+ this.statelessRef.current.clearTxn()
11273 }
11374
11475 createUnsignedTxn = () => {
@@ -133,18 +94,7 @@ class CreateGalaxy extends React.Component {
13394 state.galaxyOwner
13495 )
13596
136− this.setState({ txn: Maybe.Just(txn) })
137− }
138−
139−
140− handleSubmit = () => {
141− const { props, state } = this
142− sendSignedTransaction(props.web3.value, state.stx)
143− .then(sent => {
144− props.setTxnHashCursor(sent)
145− props.popRoute()
146− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
147− })
97+ return Maybe.Just(txn)
14898 }
14999
150100 confirmAvailability = async () => {
@@ -174,21 +124,16 @@ class CreateGalaxy extends React.Component {
174124 this.setState({ isAvailable: available })
175125 }
176126
177−
178− clearTransaction = () => {
179− this.setState({
180− txApproval: false,
181− txn: Maybe.Nothing(),
182− stx: Maybe.Nothing(),
183− })
184− }
185−
186127 render() {
187128 const { props, state } = this
188129
189130 const validAddress = isValidAddress(state.galaxyOwner)
190131 const validGalaxy = isValidGalaxy(state.galaxyName)
191132
133+ const canGenerate = validAddress === true &&
134+ validGalaxy === true &&
135+ state.isAvailable === true
136+
192137 const esvisible =
193138 props.networkType === NETWORK_NAMES.ROPSTEN ||
194139 props.networkType === NETWORK_NAMES.MAINNET
@@ -259,43 +204,20 @@ class CreateGalaxy extends React.Component {
259204 </Button>
260205
261206 <StatelessTransaction
262− address={state.galaxyOwner}
263− shipName={state.galaxyName}
264− isAvailable={state.isAvailable}
207+ // Upper scope
265208 web3={props.web3}
266209 contracts={props.contracts}
267210 wallet={props.wallet}
268211 walletType={props.walletType}
269212 walletHdPath={props.walletHdPath}
270213 networkType={props.networkType}
271− // Tx
272− txApproval={state.txApproval}
273− txn={state.txn}
274− stx={state.stx}
275− nonce={state.nonce}
276− gasPrice={state.gasPrice}
277− showGasDetails={state.showGasDetails}
278− toggleGasDetails={this.toggleGasDetails}
279− chainId={state.chainId}
280− gasLimit={state.gasLimit}
281− canGenerate={
282− validAddress === true &&
283− validGalaxy === true &&
284− state.isAvailable === true
285− }
286− canSign={Maybe.Just.hasInstance(state.txn)}
287− canSend={Maybe.Just.hasInstance(state.stx)}
288− // Methods
289− createUnsignedTxn={() => this.createUnsignedTxn()}
290− clearTransaction={() => this.clearTransaction()}
291− setApproval={() => this.setState({txApproval: !state.txApproval})}
292− setTxn={txn => this.setState({txn})}
293− setStx={stx => this.setState({stx})}
294− setNonce={nonce => this.setState({nonce})}
295− setChainId={chainId => this.setState({chainId})}
296− setGasPrice={gasPrice => this.setState({gasPrice})}
297− setGasLimit={gasLimit => this.setState({gasLimit})}
298− handleSubmit={this.handleSubmit} />
214+ setTxnHashCursor={props.setTxnHashCursor}
215+ popRoute={props.popRoute}
216+ pushRoute={props.pushRoute}
217+ // Other
218+ ref={this.statelessRef}
219+ canGenerate={canGenerate}
220+ createUnsignedTxn={this.createUnsignedTxn} />
299221
300222 </Col>
301223 </Row>
src/bridge/views/GenKeyfile.js+1−3
@@ -2,9 +2,7 @@ import React from 'react'
22import Maybe from 'folktale/maybe'
33
44import { Button } from '../components/Base'
5−import { RequiredInput, InnerLabel } from '../components/Base'
65import { Row, Col, H1, P } from '../components/Base'
7−import { Code } from '../components/Base'
86
97import * as ob from 'urbit-ob'
108import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
@@ -96,7 +94,7 @@ class GenKeyfile extends React.Component {
9694 }
9795
9896 render() {
99− const { point, pointDetails, revision } = this.getPointDetails();
97+ const { point, revision } = this.getPointDetails();
10098 const { keyfile, loaded } = this.state
10199
102100 return (
src/bridge/views/IssueChild.js+14−225
@@ -3,23 +3,21 @@ import React from 'react'
33import { azimuth, ecliptic } from 'azimuth-js'
44import * as ob from 'urbit-ob'
55
6−import { Row, Col, H1, H3, P, Anchor, Warning } from '../components/Base'
6+import { Row, Col, H1, P, Anchor } from '../components/Base'
77import { Button, ShowBlockie, ValidatedSigil } from '../components/Base'
88import { PointInput, AddressInput, InnerLabel } from '../components/Base'
99
1010import StatelessTransaction from '../components/StatelessTransaction'
1111
12−import { ROUTE_NAMES } from '../lib/router'
1312import { NETWORK_NAMES } from '../lib/network'
1413import { BRIDGE_ERROR } from '../lib/error'
1514import { getSpawnCandidate } from '../lib/child'
16−import { canDecodePatp, sendSignedTransaction, fromWei } from '../lib/txn'
15+import { canDecodePatp } from '../lib/txn'
1716
1817import {
1918 ETH_ZERO_ADDR,
2019 isValidAddress,
21− eqAddr,
22− addressFromSecp256k1Public
20+ eqAddr
2321} from '../lib/wallet'
2422
2523class IssueChild extends React.Component {
@@ -43,100 +41,35 @@ class IssueChild extends React.Component {
4341 ]
4442
4543 this.state = {
46− txError: Nothing(),
4744 receivingAddress: '',
4845 issuingPoint: issuingPoint,
4946 desiredPoint: '',
5047 isAvailable: Nothing(), // use Nothing to allow attempt when offline
51− userApproval: false,
52− nonce: '',
53− gasPrice: '5',
54− showGasDetails: false,
55− chainId: '',
56− gasLimit: '600000',
57− txn: Nothing(),
58− stx: Nothing(),
5948 suggestions: suggestions,
6049 }
6150
6251 this.handlePointInput = this.handlePointInput.bind(this)
6352 this.handleAddressInput = this.handleAddressInput.bind(this)
6453 this.handleConfirmAvailability = this.handleConfirmAvailability.bind(this)
65− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
66− this.handleSubmit = this.handleSubmit.bind(this)
67− this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
68− this.handleSetTxn = this.handleSetTxn.bind(this)
69− this.handleSetStx = this.handleSetStx.bind(this)
70− this.handleSetNonce = this.handleSetNonce.bind(this)
71− this.handleSetChainId = this.handleSetChainId.bind(this)
72− this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
73− this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
74− this.toggleGasDetails = this.toggleGasDetails.bind(this)
54+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
55+ this.statelessRef = React.createRef();
7556 }
7657
77− toggleGasDetails() {
78− this.setState({
79− showGasDetails: !this.state.showGasDetails
80− })
81− }
82−
83− componentDidMount() {
84− const { props } = this
85−
86− const addr = props.wallet.matchWith({
87− Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
88− Nothing: () => {
89− throw BRIDGE_ERROR.MISSING_WALLET
90− }
91− })
92−
93− props.web3.matchWith({
94− Nothing: () => {},
95− Just: (w3) => {
96− const validWeb3 = w3.value
97−
98− const getTxMetadata = [
99− validWeb3.eth.getTransactionCount(addr),
100− validWeb3.eth.net.getId(),
101− validWeb3.eth.getGasPrice()
102− ]
103−
104− Promise.all(getTxMetadata).then(r => {
105− const txMetadata = {
106− nonce: r[0],
107− chainId: r[1],
108− gasPrice: fromWei(r[2], 'gwei'),
109− }
110−
111− this.setState({...txMetadata})
112−
113− })
114− }
115− })
116−
117− }
118−
119−
120−
12158 handleAddressInput = (receivingAddress) => {
12259 this.setState({ receivingAddress })
123− this.handleClearTxn()
60+ this.statelessRef.current.clearTxn()
12461 }
12562
126−
127−
12863 handlePointInput(desiredPoint) {
12964 if (desiredPoint.length < 15) {
13065 this.setState({
13166 desiredPoint,
13267 isAvailable: Nothing()
13368 })
134− this.handleClearTxn()
69+ this.statelessRef.current.clearTxn()
13570 }
13671 }
13772
138−
139−
14073 handleConfirmAvailability() {
14174 this.confirmPointAvailability().then(r => {
14275 this.setState({
@@ -146,106 +79,6 @@ class IssueChild extends React.Component {
14679
14780 }
14881
149−
150−
151− handleCreateUnsignedTxn() {
152− const txn = this.createUnsignedTxn()
153− this.setState({ txn })
154− }
155−
156−
157− handleSetUserApproval(){
158− const {state} = this
159− this.setState({ userApproval: !state.userApproval })
160− }
161−
162−
163−
164− handleSetTxn(txn){
165− this.setState({ txn })
166− }
167−
168−
169−
170− handleSetStx(stx){
171− this.setState({
172− stx,
173− userApproval: false,
174− })
175− }
176−
177−
178−
179− handleSetNonce(nonce){
180− this.setState({ nonce })
181− this.handleClearStx()
182− }
183−
184−
185−
186− handleSetChainId(chainId){
187− this.setState({ chainId })
188− this.handleClearStx()
189− }
190−
191−
192−
193− handleSetGasPrice(gasPrice){
194− this.setState({ gasPrice })
195− this.handleClearStx()
196− }
197−
198−
199−
200− handleSetGasLimit(gasLimit){
201− this.setState({ gasLimit })
202− this.handleClearStx()
203− }
204−
205−
206−
207− handleClearStx() {
208− this.setState({
209− userApproval: false,
210− stx: Nothing(),
211− })
212− }
213−
214−
215−
216− handleClearTxn() {
217− this.setState({
218− userApproval: false,
219− txn: Nothing(),
220− stx: Nothing(),
221− })
222− }
223−
224−
225−
226− handleClearTransaction() {
227− this.setState({
228− userApproval: false,
229− txn: Nothing(),
230− stx: Nothing(),
231− })
232− }
233−
234−
235−
236− handleSubmit(){
237− const { props, state } = this
238− sendSignedTransaction(props.web3.value, state.stx)
239− .then(sent => {
240− props.setTxnHashCursor(sent)
241− props.popRoute()
242− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
243− })
244− .catch(err => {
245− this.setState({ txError: err.map(val => val.merge()) })
246− })
247− }
248−
24982 async confirmPointAvailability() {
25083 const { contracts } = this.props
25184 const { desiredPoint } = this.state
@@ -307,8 +140,6 @@ class IssueChild extends React.Component {
307140 return Just(txn)
308141 }
309142
310−
311−
312143 buttonTriState() {
313144 const a = this.state.isAvailable
314145 if (Nothing.hasInstance(a)) return 'blue'
@@ -316,8 +147,6 @@ class IssueChild extends React.Component {
316147 if (a.value === true) return 'green'
317148 }
318149
319−
320−
321150 buttonTriStateText() {
322151 const a = this.state.isAvailable
323152 if (Nothing.hasInstance(a)) return 'Confirm Availablility'
@@ -343,7 +172,6 @@ class IssueChild extends React.Component {
343172 return vpatp && vchild
344173 }
345174
346−
347175 render() {
348176 const { props, state } = this
349177
@@ -362,12 +190,6 @@ class IssueChild extends React.Component {
362190 }
363191 })
364192
365− const canSign = Just.hasInstance(state.txn)
366− const canApprove = Just.hasInstance(state.stx)
367− const canSend =
368− Just.hasInstance(state.stx)
369− && state.userApproval === true
370−
371193 const esvisible =
372194 props.networkType === NETWORK_NAMES.ROPSTEN ||
373195 props.networkType === NETWORK_NAMES.MAINNET
@@ -480,46 +302,13 @@ class IssueChild extends React.Component {
480302 walletType={props.walletType}
481303 walletHdPath={props.walletHdPath}
482304 networkType={props.networkType}
483− // Tx
484− txn={state.txn}
485− stx={state.stx}
486− // Tx details
487− nonce={state.nonce}
488− gasPrice={state.gasPrice}
489− showGasDetails={state.showGasDetails}
490− toggleGasDetails={this.toggleGasDetails}
491− chainId={state.chainId}
492− gasLimit={state.gasLimit}
493− // Checks
494− userApproval={state.userApproval}
495− canGenerate={ canGenerate }
496− canSign={ canSign }
497− canApprove={ canApprove }
498− canSend={ canSend }
499− // Methods
500− createUnsignedTxn={this.handleCreateUnsignedTxn}
501− setUserApproval={this.handleSetUserApproval}
502− setTxn={this.handleSetTxn}
503− setStx={this.handleSetStx}
504− setNonce={this.handleSetNonce}
505− setChainId={this.handleSetChainId}
506− setGasPrice={this.handleSetGasPrice}
507− setGasLimit={this.handleSetGasLimit}
508− handleSubmit={this.handleSubmit} />
509−
510− {
511− Nothing.hasInstance(state.txError)
512− ? ''
513− : <Warning className={'mt-8'}>
514− <H3 style={{marginTop: 0, paddingTop: 0}}>
515− {
516− 'There was an error sending your transaction.'
517− }
518− </H3>
519− { state.txError.value }
520− </Warning>
521− }
522−
305+ setTxnHashCursor={props.setTxnHashCursor}
306+ popRoute={props.popRoute}
307+ pushRoute={props.pushRoute}
308+ // Other
309+ canGenerate={canGenerate}
310+ createUnsignedTxn={this.createUnsignedTxn}
311+ ref={this.statelessRef} />
523312 </Col>
524313 </Row>
525314 )
src/bridge/views/Mnemonic.js+53−11
@@ -1,8 +1,13 @@
11import * as bip39 from 'bip39'
2−import Maybe from 'folktale/maybe'
2+import { Just, Nothing } from 'folktale/maybe'
33import React from 'react'
44import { Button } from '../components/Base'
5−import { Input, MnemonicInput, InnerLabel, InputCaption } from '../components/Base'
5+import {
6+ Input,
7+ MnemonicInput,
8+ InnerLabel,
9+ InputCaption
10+ } from '../components/Base'
611import { Row, Col, H1 } from '../components/Base'
712
813import { ROUTE_NAMES } from '../lib/router'
@@ -16,28 +21,47 @@ class Mnemonic extends React.Component {
1621
1722 this.state = {
1823 mnemonic: '',
24+ passphrase: '',
1925 hdpath: DEFAULT_HD_PATH,
2026 }
2127
2228 this.handleMnemonicInput = this.handleMnemonicInput.bind(this)
29+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
2330 this.handleHdPathInput = this.handleHdPathInput.bind(this)
2431 }
2532
2633 componentDidMount() {
27− const { mnemonic, hdpath } = this.state
28− this.attemptWalletDerivation(mnemonic, hdpath)
34+ const { mnemonic, hdpath, passphrase } = this.state
35+ this.attemptWalletDerivation(mnemonic, hdpath, passphrase)
2936
3037 this.setState({
3138 exampleMnemonic: bip39.generateMnemonic(),
3239 })
3340 }
3441
42+ handlePassphraseInput(passphrase) {
43+ this.setState((state, _) => {
44+ const mnemonic = state.mnemonic
45+ const hdpath = state.hdpath
46+ this.attemptWalletDerivation(
47+ mnemonic,
48+ hdpath === '' ? DEFAULT_HD_PATH : hdpath,
49+ passphrase
50+ )
51+ return {
52+ passphrase
53+ }
54+ })
55+ }
56+
3557 handleMnemonicInput(mnemonic) {
3658 this.setState((state, _) => {
3759 const hdpath = state.hdpath
60+ const passphrase = state.passphrase
3861 this.attemptWalletDerivation(
3962 mnemonic,
40− hdpath === '' ? DEFAULT_HD_PATH : hdpath
63+ hdpath === '' ? DEFAULT_HD_PATH : hdpath,
64+ passphrase
4165 )
4266 return {
4367 mnemonic
@@ -48,9 +72,11 @@ class Mnemonic extends React.Component {
4872 handleHdPathInput(hdpath) {
4973 this.setState((state, _) => {
5074 const mnemonic = state.mnemonic
75+ const passphrase = state.passphrase
5176 this.attemptWalletDerivation(
5277 mnemonic,
53− hdpath === '' ? DEFAULT_HD_PATH : hdpath
78+ hdpath === '' ? DEFAULT_HD_PATH : hdpath,
79+ passphrase
5480 )
5581 return {
5682 hdpath
@@ -58,11 +84,11 @@ class Mnemonic extends React.Component {
5884 })
5985 }
6086
61− attemptWalletDerivation(mnemonic, hdpath) {
87+ attemptWalletDerivation(mnemonic, hdpath, passphrase) {
6288 const { setWallet, setAuthMnemonic, setWalletHdPath } = this.props
63− const wallet = walletFromMnemonic(mnemonic, hdpath)
89+ const wallet = walletFromMnemonic(mnemonic, hdpath, passphrase)
6490 setWallet(wallet)
65− setAuthMnemonic(Maybe.Just(mnemonic))
91+ setAuthMnemonic(Just(mnemonic))
6692 setWalletHdPath(hdpath)
6793 }
6894
@@ -71,7 +97,8 @@ class Mnemonic extends React.Component {
7197 const {
7298 mnemonic,
7399 hdpath,
74− exampleMnemonic
100+ exampleMnemonic,
101+ passphrase
75102 } = this.state
76103
77104 return (
@@ -96,6 +123,21 @@ class Mnemonic extends React.Component {
96123 <InnerLabel>{'Mnemonic'}</InnerLabel>
97124 </MnemonicInput>
98125
126+ <InputCaption>
127+ {`If your wallet requires a passphrase, you may enter it below.`}
128+ </InputCaption>
129+
130+ <Input
131+ className='pt-8'
132+ prop-size='md'
133+ prop-format='innerLabel'
134+ name='passphrase'
135+ type='password'
136+ value={ passphrase }
137+ autocomplete='off'
138+ onChange={ this.handlePassphraseInput }>
139+ <InnerLabel>{'Passphrase'}</InnerLabel>
140+ </Input>
99141
100142 <InputCaption>
101143 {`If you'd like to use a custom derivation path, you may enter it below.`}
@@ -114,7 +156,7 @@ class Mnemonic extends React.Component {
114156
115157 <Button
116158 className={'mt-10'}
117− disabled={ Maybe.Nothing.hasInstance(wallet) }
159+ disabled={ Nothing.hasInstance(wallet) }
118160 onClick={ () => {
119161 popRoute()
120162 pushRoute(ROUTE_NAMES.SHIPS)
src/bridge/views/Point/Actions.js−7
@@ -24,19 +24,12 @@ const Actions = (props) => {
2424 }
2525 })
2626
27− const canSetTransferProxy = pointDetails.matchWith({
28− // also should check is addr is operator
29− Nothing: _ => false,
30− Just: details => eqAddr(details.value.owner, addr)
31− })
32−
3327 const canSetSpawnProxy = pointDetails.matchWith({
3428 Nothing: _ => false,
3529 Just: details =>
3630 eqAddr(details.value.owner, addr) && details.value.active
3731 })
3832
39−
4033 const canSetManagementProxy = pointDetails.matchWith({
4134 Nothing: _ => false,
4235 Just: details =>
src/bridge/views/SetKeys.js+9−226
@@ -2,25 +2,14 @@ import React from 'react'
22import Maybe from 'folktale/maybe'
33import * as azimuth from 'azimuth-js'
44import * as ob from 'urbit-ob'
5−import { Row, Col, H1, P, Warning, H3 } from '../components/Base'
6−import { RequiredInput, InnerLabel } from '../components/Base'
5+import { Row, Col, H1, P, Warning } from '../components/Base'
76
87import StatelessTransaction from '../components/StatelessTransaction'
98import { BRIDGE_ERROR } from '../lib/error'
10−import { ROUTE_NAMES } from '../lib/router'
11−import { attemptSeedDerivation, genKey } from '../lib/keys'
12−
13−import saveAs from 'file-saver'
14−
15−import { WALLET_NAMES } from '../lib/wallet'
9+import { attemptSeedDerivation } from '../lib/keys'
1610
1711import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
1812
19−import {
20− sendSignedTransaction,
21− fromWei,
22− } from '../lib/txn'
23−
2413import {
2514 addressFromSecp256k1Public,
2615 addHexPrefix
@@ -45,36 +34,12 @@ class SetKeys extends React.Component {
4534 point: point,
4635 cryptoSuiteVersion: 1,
4736 continuity: false,
48− txn: Maybe.Nothing(),
49− txError: Maybe.Nothing(),
5037 isManagementSeed: false,
51− userApproval: false,
52− nonce: '',
53− gasPrice: '5',
54− showGasDetails: false,
55− chainId: '',
56− gasLimit: '600000',
57− stx: Maybe.Nothing(),
5838 }
5939
6040 this.handleNetworkSeedInput = this.handleNetworkSeedInput.bind(this)
6141 // Transaction
62− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
63− this.handleSubmit = this.handleSubmit.bind(this)
64− this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
65− this.handleSetTxn = this.handleSetTxn.bind(this)
66− this.handleSetStx = this.handleSetStx.bind(this)
67− this.handleSetNonce = this.handleSetNonce.bind(this)
68− this.handleSetChainId = this.handleSetChainId.bind(this)
69− this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
70− this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
71− this.toggleGasDetails = this.toggleGasDetails.bind(this)
72− }
73−
74− toggleGasDetails() {
75− this.setState({
76− showGasDetails: !this.state.showGasDetails
77− })
42+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
7843 }
7944
8045 componentDidMount() {
@@ -90,30 +55,6 @@ class SetKeys extends React.Component {
9055 })
9156
9257 this.determineManagementSeed(props.contracts.value, addr)
93−
94− props.web3.matchWith({
95− Nothing: () => {},
96− Just: (w3) => {
97− const validWeb3 = w3.value
98−
99− const getTxMetadata = [
100− validWeb3.eth.getTransactionCount(addr),
101− validWeb3.eth.net.getId(),
102− validWeb3.eth.getGasPrice()
103− ];
104−
105− Promise.all(getTxMetadata).then(r => {
106− const txMetadata = {
107− nonce: r[0],
108− chainId: r[1],
109− gasPrice: fromWei(r[2], 'gwei'),
110− };
111−
112− this.setState({...txMetadata})
113−
114− })
115− }
116− });
11758 }
11859
11960 async determineManagementSeed(ctrcs, addr) {
@@ -157,126 +98,6 @@ class SetKeys extends React.Component {
15798 this.setState({ networkSeed })
15899 }
159100
160− handleCreateUnsignedTxn() {
161− const txn = this.createUnsignedTxn()
162− this.setState({ txn })
163− }
164−
165−
166− handleSetUserApproval(){
167− const {state} = this
168− this.setState({ userApproval: !state.userApproval })
169− }
170−
171−
172−
173− handleSetTxn(txn){
174− this.setState({ txn })
175− }
176−
177−
178−
179− handleSetStx(stx){
180− this.setState({
181− stx,
182− userApproval: false,
183− })
184− }
185−
186−
187−
188− handleSetNonce(nonce){
189− this.setState({ nonce })
190− this.handleClearStx()
191− }
192−
193−
194−
195− handleSetChainId(chainId){
196− this.setState({ chainId })
197− this.handleClearStx()
198− }
199−
200−
201−
202− handleSetGasPrice(gasPrice){
203− this.setState({ gasPrice })
204− this.handleClearStx()
205− }
206−
207−
208−
209− handleSetGasLimit(gasLimit){
210− this.setState({ gasLimit })
211− this.handleClearStx()
212− }
213−
214−
215−
216− handleClearStx() {
217− this.setState({
218− userApproval: false,
219− stx: Maybe.Nothing(),
220− })
221− }
222−
223−
224−
225−
226− handleClearTxn() {
227− this.setState({
228− userApproval: false,
229− txn: Maybe.Nothing(),
230− stx: Maybe.Nothing(),
231− })
232− }
233−
234−
235−
236− handleClearTransaction() {
237− this.setState({
238− userApproval: false,
239− txn: Maybe.Nothing(),
240− stx: Maybe.Nothing(),
241− })
242− }
243−
244− downloadKeyfile(networkSeed) {
245− const { pointCache } = this.props
246− const { pointCursor } = this.props
247−
248− const point = pointCursor.matchWith({
249− Just: (pt) => pt.value,
250− Nothing: () => {
251− throw BRIDGE_ERROR.MISSING_POINT
252− }
253− })
254−
255− const pointDetails =
256− point in pointCache
257− ? pointCache[point]
258− : (() => { throw BRIDGE_ERROR.MISSING_POINT })()
259−
260− const revision = parseInt(pointDetails.keyRevisionNumber)
261− const keyfile = genKey(networkSeed, point, revision)
262− let blob = new Blob([keyfile], {type:"text/plain;charset=utf-8"});
263− saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`)
264− }
265−
266− handleSubmit(){
267− const { props, state } = this
268− sendSignedTransaction(props.web3.value, state.stx)
269− .then(sent => {
270− props.setNetworkSeedCache(this.state.networkSeed)
271− props.setTxnHashCursor(sent)
272− props.popRoute()
273− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, {promptKeyfile: true})
274− })
275− .catch(err => {
276− this.setState({ txError: err.map(val => val.merge()) })
277− })
278− }
279−
280101 createUnsignedTxn() {
281102 const { state, props } = this
282103
@@ -325,13 +146,6 @@ class SetKeys extends React.Component {
325146 state.networkSeed.length === 64
326147 && state.networkSeed.length === 64
327148
328− const canSign = Maybe.Just.hasInstance(state.txn)
329− const canApprove = Maybe.Just.hasInstance(state.stx)
330−
331− const canSend =
332− Maybe.Just.hasInstance(state.stx)
333− && state.userApproval === true
334−
335149 const pointDetails =
336150 state.point in props.pointCache
337151 ? props.pointCache[state.point]
@@ -379,45 +193,14 @@ class SetKeys extends React.Component {
379193 walletType={props.walletType}
380194 walletHdPath={props.walletHdPath}
381195 networkType={props.networkType}
196+ setTxnHashCursor={props.setTxnHashCursor}
197+ popRoute={props.popRoute}
198+ pushRoute={props.pushRoute}
382199 // Tx
383− txn={state.txn}
384− stx={state.stx}
385− // Tx details
386− nonce={state.nonce}
387− gasPrice={state.gasPrice}
388− chainId={state.chainId}
389− gasLimit={state.gasLimit}
390− showGasDetails={state.showGasDetails}
391− toggleGasDetails={this.toggleGasDetails}
392− // Checks
393− userApproval={state.userApproval}
394200 canGenerate={ canGenerate }
395− canSign={ canSign }
396− canApprove={ canApprove }
397− canSend={ canSend }
398− // Methods
399− createUnsignedTxn={this.handleCreateUnsignedTxn}
400− setUserApproval={this.handleSetUserApproval}
401− setTxn={this.handleSetTxn}
402− setStx={this.handleSetStx}
403− setNonce={this.handleSetNonce}
404− setChainId={this.handleSetChainId}
405− setGasPrice={this.handleSetGasPrice}
406− setGasLimit={this.handleSetGasLimit}
407− handleSubmit={this.handleSubmit} />
408−
409− {
410− Maybe.Nothing.hasInstance(state.txError)
411− ? ''
412− : <Warning className={'mt-8'}>
413− <H3 style={{marginTop: 0, paddingTop: 0}}>
414− {
415− 'There was an error sending your transaction.'
416− }
417− </H3>
418− { state.txError.value }
419− </Warning>
420− }
201+ createUnsignedTxn={this.createUnsignedTxn}
202+ networkSeed={ state.networkSeed }
203+ setNetworkSeedCache={ props.setNetworkSeedCache } />
421204
422205 </Col>
423206 </Row>
src/bridge/views/SetProxy.js+13−224
@@ -1,8 +1,8 @@
11import Maybe from 'folktale/maybe'
22import React from 'react'
33import * as azimuth from 'azimuth-js'
4−import { Row, Col, H1, H3, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
5−import { AddressInput, Warning } from '../components/Base'
4+import { Row, Col, H1, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
5+import { AddressInput } from '../components/Base'
66import * as ob from 'urbit-ob'
77
88import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
@@ -12,17 +12,7 @@ import { NETWORK_NAMES } from '../lib/network'
1212
1313import StatelessTransaction from '../components/StatelessTransaction'
1414
15−import { ROUTE_NAMES } from '../lib/router'
16−
17−import {
18− sendSignedTransaction,
19− fromWei,
20− } from '../lib/txn'
21−
22−import {
23− isValidAddress,
24− addressFromSecp256k1Public,
25−} from '../lib/wallet'
15+import { isValidAddress } from '../lib/wallet'
2616
2717const SetManagementProxy = (props) =>
2818 <SetProxy
@@ -55,181 +45,20 @@ class SetProxy extends React.Component {
5545
5646 this.state = {
5747 proxyAddress: '',
58− txn: Maybe.Nothing(),
59− txError: Maybe.Nothing(),
6048 issuingPoint: issuingPoint,
61− showGasDetails: false,
62− userApproval: false,
63− nonce: '',
64− gasPrice: '5',
65− chainId: '',
66− gasLimit: '600000',
67− stx: Maybe.Nothing(),
6849 }
6950
51+ this.statelessRef = React.createRef();
7052 this.handleAddressInput = this.handleAddressInput.bind(this)
71− // Transaction
72− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
73− this.handleSubmit = this.handleSubmit.bind(this)
74− this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
75− this.handleSetTxn = this.handleSetTxn.bind(this)
76− this.handleSetStx = this.handleSetStx.bind(this)
77− this.handleSetNonce = this.handleSetNonce.bind(this)
78− this.handleSetChainId = this.handleSetChainId.bind(this)
79− this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
80− this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
81− this.toggleGasDetails = this.toggleGasDetails.bind(this)
82− }
83−
84− toggleGasDetails() {
85− this.setState({
86− showGasDetails: !this.state.showGasDetails
87− })
53+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
8854 }
8955
90− componentDidMount() {
91− const { props } = this
92−
93− const addr = props.wallet.matchWith({
94− Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
95− Nothing: () => {
96− throw BRIDGE_ERROR.MISSING_WALLET
97− }
98− })
99−
100− props.web3.matchWith({
101− Nothing: () => {},
102− Just: (w3) => {
103− const validWeb3 = w3.value
104−
105− const getTxMetadata = [
106− validWeb3.eth.getTransactionCount(addr),
107− validWeb3.eth.net.getId(),
108− validWeb3.eth.getGasPrice()
109− ];
110−
111− Promise.all(getTxMetadata).then(r => {
112− const txMetadata = {
113− nonce: r[0],
114− chainId: r[1],
115− gasPrice: fromWei(r[2], 'gwei'),
116− };
117−
118− this.setState({...txMetadata})
119−
120− })
121− }
122− });
123−
124− }
125−
126−
127−
12856 handleAddressInput(proxyAddress) {
12957 this.setState({ proxyAddress })
130− this.handleClearTxn()
131− }
132−
133−
134− handleCreateUnsignedTxn() {
135− const txn = this.createUnsignedTxn()
136− this.setState({ txn })
137− }
138−
139−
140− handleSetUserApproval(){
141− const {state} = this
142− this.setState({ userApproval: !state.userApproval })
143− }
144−
145−
146−
147− handleSetTxn(txn){
148− this.setState({ txn })
149− }
150−
151−
152−
153− handleSetStx(stx){
154− this.setState({
155− stx,
156− userApproval: false,
157− })
158− }
159−
160−
16158
162− handleSetNonce(nonce){
163− this.setState({ nonce })
164− this.handleClearStx()
59+ this.statelessRef.current.clearTxn()
16560 }
16661
167−
168−
169− handleSetChainId(chainId){
170− this.setState({ chainId })
171− this.handleClearStx()
172− }
173−
174−
175−
176− handleSetGasPrice(gasPrice){
177− this.setState({ gasPrice })
178− this.handleClearStx()
179− }
180−
181−
182−
183− handleSetGasLimit(gasLimit){
184− this.setState({ gasLimit })
185− this.handleClearStx()
186− }
187−
188−
189−
190− handleClearStx() {
191− this.setState({
192− userApproval: false,
193− stx: Maybe.Nothing(),
194− })
195− }
196−
197−
198−
199− handleClearTxn() {
200− this.setState({
201− userApproval: false,
202− txn: Maybe.Nothing(),
203− stx: Maybe.Nothing(),
204− })
205− }
206−
207−
208−
209− handleClearTransaction() {
210− this.setState({
211− userApproval: false,
212− txn: Maybe.Nothing(),
213− stx: Maybe.Nothing(),
214− })
215− }
216−
217−
218−
219− handleSubmit(){
220− const { props, state } = this
221− sendSignedTransaction(props.web3.value, state.stx)
222− .then(sent => {
223− props.setTxnHashCursor(sent)
224− props.popRoute()
225− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
226− })
227− .catch(err => {
228− this.setState({ txError: err.map(val => val.merge()) })
229− })
230− }
231−
232−
23362 createUnsignedTxn(proxyAddress) {
23463 const { state, props } = this
23564
@@ -270,19 +99,12 @@ class SetProxy extends React.Component {
27099 }
271100
272101 render() {
273−
274102 const { props, state } = this
275103
276104 const renderedProxyType = renderProxyType(props.proxyType)
277−
278105 const validAddress = isValidAddress(state.proxyAddress)
279−
280106 const canGenerate = validAddress === true
281107
282− const canSign = Maybe.Just.hasInstance(state.txn)
283− const canApprove = Maybe.Just.hasInstance(state.stx)
284− const canSend = Maybe.Just.hasInstance(state.stx) && state.userApproval === true
285−
286108 const esvisible =
287109 props.networkType === NETWORK_NAMES.ROPSTEN ||
288110 props.networkType === NETWORK_NAMES.MAINNET
@@ -339,46 +161,13 @@ class SetProxy extends React.Component {
339161 walletType={props.walletType}
340162 walletHdPath={props.walletHdPath}
341163 networkType={props.networkType}
342− // Tx
343− txn={state.txn}
344− stx={state.stx}
345− // Tx details
346− nonce={state.nonce}
347− gasPrice={state.gasPrice}
348− chainId={state.chainId}
349− gasLimit={state.gasLimit}
350− showGasDetails={state.showGasDetails}
351− toggleGasDetails={this.toggleGasDetails}
352− // Checks
353− userApproval={state.userApproval}
354− canGenerate={ canGenerate }
355− canSign={ canSign }
356− canApprove={ canApprove }
357− canSend={ canSend }
358− // Methods
359− createUnsignedTxn={this.handleCreateUnsignedTxn}
360− setUserApproval={this.handleSetUserApproval}
361− setTxn={this.handleSetTxn}
362− setStx={this.handleSetStx}
363− setNonce={this.handleSetNonce}
364− setChainId={this.handleSetChainId}
365− setGasPrice={this.handleSetGasPrice}
366− setGasLimit={this.handleSetGasLimit}
367− handleSubmit={this.handleSubmit} />
368−
369− {
370− Maybe.Nothing.hasInstance(state.txError)
371− ? ''
372− : <Warning className={'mt-8'}>
373− <H3 style={{marginTop: 0, paddingTop: 0}}>
374− {
375− 'There was an error sending your transaction.'
376− }
377− </H3>
378− { state.txError.value }
379− </Warning>
380− }
381−
164+ setTxnHashCursor={props.setTxnHashCursor}
165+ popRoute={props.popRoute}
166+ pushRoute={props.pushRoute}
167+ // Other
168+ ref={this.statelessRef}
169+ createUnsignedTxn={this.createUnsignedTxn}
170+ canGenerate={ canGenerate } />
382171 </Col>
383172 </Row>
384173 )
src/bridge/views/Shards.js+44−15
@@ -1,7 +1,14 @@
1−import Maybe from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe'
22import React from 'react'
33import { Button } from '../components/Base'
4−import { InnerLabel, ValidatedSigil, PointInput, ShardInput } from '../components/Base'
4+import {
5+ InnerLabel,
6+ ValidatedSigil,
7+ PointInput,
8+ ShardInput,
9+ InputCaption,
10+ Input
11+ } from '../components/Base'
512import { Row, Col, H1, P } from '../components/Base'
613import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
714import * as ob from 'urbit-ob'
@@ -33,12 +40,14 @@ class Shards extends React.Component {
3340 shard1: '',
3441 shard2: '',
3542 shard3: '',
43+ passphrase: '',
3644 pointName: ''
3745 }
3846
3947 this.pointPlaceholder = placeholder(4)
4048 this.ticketPlaceholder = placeholder(16)
4149
50+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
4251 this.handleShardInput = this.handleShardInput.bind(this)
4352 this.handlePointNameInput = this.handlePointNameInput.bind(this)
4453 }
@@ -53,19 +62,17 @@ class Shards extends React.Component {
5362 }
5463 }
5564
65+ handlePassphraseInput(passphrase) {
66+ this.setState({ passphrase })
67+ }
68+
5669 handlePointNameInput(pointName) {
5770 if (pointName.length < 15) {
5871 this.setState({ pointName })
5972 }
6073 }
6174
62− // buttonTriState = (wallet) => {
63− // if (wallet.isNothing()) return 'blue'
64− // if (wallet === false) return 'yellow'
65− // if (Maybe.Nothing.hasInstance(wallet)) return 'green'
66− // }
67−
68− async walletFromShards(shard1, shard2, shard3, pointName) {
75+ async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
6976 const { setWallet, setUrbitWallet } = this.props
7077
7178 const s1 = shard1 === '' ? undefined : shard1
@@ -82,18 +89,19 @@ class Shards extends React.Component {
8289 if (ticket !== undefined) {
8390 const urbitWallet = await kg.generateWallet({
8491 ticket: ticket,
85− ship: ob.patp2dec(pointName)
92+ ship: ob.patp2dec(pointName),
93+ passphrase: passphrase
8694 })
8795 const mnemonic = urbitWallet.ownership.seed
88− const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH)
96+ const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH, passphrase)
8997 setWallet(wallet)
90− setUrbitWallet(Maybe.Just(urbitWallet))
98+ setUrbitWallet(Just(urbitWallet))
9199 }
92100 }
93101
94102 render() {
95103 const { popRoute, pushRoute, wallet } = this.props
96− const { shard1, shard2, shard3, pointName } = this.state
104+ const { shard1, shard2, shard3, pointName, passphrase } = this.state
97105
98106 const phPoint = this.pointPlaceholder
99107 const phTick = this.ticketPlaceholder
@@ -169,13 +177,34 @@ class Shards extends React.Component {
169177 <InnerLabel>{ 'Shard 3' }</InnerLabel>
170178 </ShardInput>
171179
180+ <InputCaption>
181+ { 'If your wallet requires a passphrase, you may enter it below.' }
182+ </InputCaption>
183+
184+ <Input
185+ className='pt-8'
186+ prop-size='md'
187+ prop-format='innerLabel'
188+ name='passphrase'
189+ type='password'
190+ value={ passphrase }
191+ autocomplete='off'
192+ onChange={ this.handlePassphraseInput }>
193+ <InnerLabel>{'Passphrase'}</InnerLabel>
194+ </Input>
195+
172196 <Button
173197 disabled={ !ready }
174198 className={'mt-8'}
175199 prop-size={'lg wide'}
176200 // prop-color={this.buttonTriState(wallet)}
177201 onClick={() =>
178− this.walletFromShards(shard1, shard2, shard3, pointName)
202+ this.walletFromShards(
203+ shard1,
204+ shard2,
205+ shard3,
206+ pointName,
207+ passphrase)
179208 }>
180209 {'Unlock Wallet →'}
181210 </Button>
@@ -183,7 +212,7 @@ class Shards extends React.Component {
183212 <Button
184213 className={'mt-4'}
185214 prop-size={'xl wide'}
186− disabled={ Maybe.Nothing.hasInstance(wallet) }
215+ disabled={ Nothing.hasInstance(wallet) }
187216 onClick={ () => {
188217 popRoute()
189218 pushRoute(ROUTE_NAMES.SHIPS)
src/bridge/views/Ticket.js+42−17
@@ -1,7 +1,14 @@
1−import Maybe from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe'
22import React from 'react'
33import { Button } from '../components/Base'
4−import { InnerLabel, ValidatedSigil, PointInput, TicketInput } from '../components/Base'
4+import {
5+ InnerLabel,
6+ ValidatedSigil,
7+ PointInput,
8+ TicketInput,
9+ Input,
10+ InputCaption
11+ } from '../components/Base'
512import { Row, Col, H1, P } from '../components/Base'
613import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
714import * as ob from 'urbit-ob'
@@ -26,6 +33,7 @@ class Ticket extends React.Component {
2633 this.state = {
2734 ticket: '',
2835 pointName: '',
36+ passphrase: '',
2937 isUnlocking: false
3038 }
3139
@@ -33,6 +41,7 @@ class Ticket extends React.Component {
3341 this.ticketPlaceholder = placeholder(8)
3442
3543 this.handleTicketInput = this.handleTicketInput.bind(this)
44+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
3645 this.handlePointNameInput = this.handlePointNameInput.bind(this)
3746 }
3847
@@ -40,19 +49,17 @@ class Ticket extends React.Component {
4049 this.setState({ ticket })
4150 }
4251
52+ handlePassphraseInput(passphrase) {
53+ this.setState({ passphrase })
54+ }
55+
4356 handlePointNameInput(pointName) {
4457 if (pointName.length < 15) {
4558 this.setState({ pointName })
4659 }
4760 }
4861
49− // buttonTriState = (wallet) => {
50− // if (wallet.isNothing()) return 'blue'
51− // if (wallet === false) return 'yellow'
52− // if (Maybe.Nothing.hasInstance(wallet)) return 'green'
53− // }
54−
55− async walletFromTicket(ticket, pointName) {
62+ async walletFromTicket(ticket, pointName, passphrase) {
5663 const { setWallet, setUrbitWallet } = this.props
5764
5865 this.setState({
@@ -61,12 +68,13 @@ class Ticket extends React.Component {
6168
6269 const urbitWallet = await kg.generateWallet({
6370 ticket: ticket,
64− ship: ob.patp2dec(pointName)
71+ ship: ob.patp2dec(pointName),
72+ passphrase: passphrase
6573 })
6674 const mnemonic = urbitWallet.ownership.seed
67− const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH)
75+ const wallet = walletFromMnemonic(mnemonic, DEFAULT_HD_PATH, passphrase)
6876 setWallet(wallet)
69− setUrbitWallet(Maybe.Just(urbitWallet))
77+ setUrbitWallet(Just(urbitWallet))
7078
7179 this.setState({
7280 isUnlocking: false
@@ -75,7 +83,7 @@ class Ticket extends React.Component {
7583
7684 render() {
7785 const { popRoute, pushRoute, wallet } = this.props
78− const { ticket, pointName } = this.state
86+ const { ticket, pointName, passphrase } = this.state
7987
8088 const phPoint = this.pointPlaceholder
8189 const phTick = this.ticketPlaceholder
@@ -119,12 +127,29 @@ class Ticket extends React.Component {
119127 <InnerLabel>{ 'Ticket' }</InnerLabel>
120128 </TicketInput>
121129
130+ <InputCaption>
131+ { 'If your wallet requires a passphrase, you may enter it below.' }
132+ </InputCaption>
133+
134+ <Input
135+ className='pt-8'
136+ prop-size='md'
137+ prop-format='innerLabel'
138+ name='passphrase'
139+ type='password'
140+ value={ passphrase }
141+ autocomplete='off'
142+ onChange={ this.handlePassphraseInput }>
143+ <InnerLabel>{'Passphrase'}</InnerLabel>
144+ </Input>
145+
122146 <Button
123147 className={'mt-8'}
124148 prop-size={'lg wide'}
125− disabled={this.state.isUnlocking || Maybe.Just.hasInstance(wallet)}
126− // prop-color={this.buttonTriState(wallet)}
127− onClick={() => this.walletFromTicket(ticket, pointName)}>
149+ disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
150+ onClick={
151+ () => this.walletFromTicket(ticket, pointName, passphrase)
152+ }>
128153
129154 <span className="relative">
130155 {this.state.isUnlocking &&
@@ -137,7 +162,7 @@ class Ticket extends React.Component {
137162 <Button
138163 className={'mt-4'}
139164 prop-size={'xl wide'}
140− disabled={ Maybe.Nothing.hasInstance(wallet) }
165+ disabled={ Nothing.hasInstance(wallet) }
141166 onClick={ () => {
142167 popRoute()
143168 pushRoute(ROUTE_NAMES.SHIPS)
src/bridge/views/Transfer.js+12−222
@@ -4,22 +4,15 @@ import * as azimuth from 'azimuth-js'
44import * as ob from 'urbit-ob'
55
66import { Row, Col, H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/Base'
7−import { AddressInput, Warning, H3 } from '../components/Base'
7+import { AddressInput } from '../components/Base'
88import StatelessTransaction from '../components/StatelessTransaction'
99
1010import { BRIDGE_ERROR } from '../lib/error'
1111import { NETWORK_NAMES } from '../lib/network'
12−import { ROUTE_NAMES } from '../lib/router'
1312
1413import {
15− sendSignedTransaction,
16− fromWei,
17− } from '../lib/txn'
18−
19− import {
20− isValidAddress,
21− addressFromSecp256k1Public
22− } from '../lib/wallet'
14+ isValidAddress
15+} from '../lib/wallet'
2316
2417class Transfer extends React.Component {
2518 constructor(props) {
@@ -33,191 +26,26 @@ class Transfer extends React.Component {
3326 })
3427
3528 this.state = {
36− txError: Maybe.Nothing(),
3729 receivingAddress: '',
3830 issuingPoint: issuingPoint,
39− userApproval: false,
40− nonce: '',
41− gasPrice: '5',
42− showGasDetails: false,
43− chainId: '',
44− gasLimit: '600000',
45− txn: Maybe.Nothing(),
46− stx: Maybe.Nothing(),
4731 }
48− this.handleAddressInput = this.handleAddressInput.bind(this)
49− // Transaction
50− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
51− this.handleSubmit = this.handleSubmit.bind(this)
52− this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
53− this.handleSetTxn = this.handleSetTxn.bind(this)
54− this.handleSetStx = this.handleSetStx.bind(this)
55− this.handleSetNonce = this.handleSetNonce.bind(this)
56− this.handleSetChainId = this.handleSetChainId.bind(this)
57− this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
58− this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
59− this.toggleGasDetails = this.toggleGasDetails.bind(this)
60− }
61−
62− toggleGasDetails() {
63− this.setState({
64− showGasDetails: !this.state.showGasDetails
65− })
66− }
67−
68− componentDidMount() {
69− const { props } = this
70−
71− const addr = props.wallet.matchWith({
72− Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
73− Nothing: () => {
74− throw BRIDGE_ERROR.MISSING_WALLET
75− }
76− })
77−
78− props.web3.matchWith({
79− Nothing: () => {},
80− Just: (w3) => {
81− const validWeb3 = w3.value
82−
83− const getTxMetadata = [
84− validWeb3.eth.getTransactionCount(addr),
85− validWeb3.eth.net.getId(),
86− validWeb3.eth.getGasPrice()
87− ];
88−
89− Promise.all(getTxMetadata).then(r => {
90− const txMetadata = {
91− nonce: r[0],
92− chainId: r[1],
93− gasPrice: fromWei(r[2], 'gwei'),
94− };
95−
96− this.setState({...txMetadata})
97−
98− })
99− }
100− });
10132
33+ this.handleAddressInput = this.handleAddressInput.bind(this)
34+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
35+ this.statelessRef = React.createRef();
10236 }
10337
104−
105−
10638 handleAddressInput(receivingAddress) {
10739 this.setState({ receivingAddress })
108− this.handleClearTxn()
40+ this.statelessRef.current.clearTxn()
10941 }
11042
111−
112−
11343 handleConfirmAvailability() {
11444 this.confirmPointAvailability().then(r => {
11545 this.setState({ isAvailable: r })
11646 })
11747 }
11848
119−
120−
121− handleCreateUnsignedTxn() {
122− const txn = this.createUnsignedTxn()
123− this.setState({ txn })
124− }
125−
126−
127− handleSetUserApproval(){
128− const {state} = this
129− this.setState({ userApproval: !state.userApproval })
130− }
131−
132−
133−
134− handleSetTxn(txn){
135− this.setState({ txn })
136− }
137−
138−
139−
140− handleSetStx(stx){
141− this.setState({
142− stx,
143− userApproval: false,
144− })
145− }
146−
147−
148−
149− handleSetNonce(nonce){
150− this.setState({ nonce })
151− this.handleClearStx()
152− }
153−
154−
155−
156− handleSetChainId(chainId){
157− this.setState({ chainId })
158− this.handleClearStx()
159− }
160−
161−
162−
163− handleSetGasPrice(gasPrice){
164− this.setState({ gasPrice })
165− this.handleClearStx()
166− }
167−
168−
169−
170− handleSetGasLimit(gasLimit){
171− this.setState({ gasLimit })
172− this.handleClearStx()
173− }
174−
175−
176−
177− handleClearStx() {
178− this.setState({
179− userApproval: false,
180− stx: Maybe.Nothing(),
181− })
182− }
183−
184−
185−
186− handleClearTxn() {
187− this.setState({
188− userApproval: false,
189− txn: Maybe.Nothing(),
190− stx: Maybe.Nothing(),
191− })
192− }
193−
194−
195−
196− handleClearTransaction() {
197− this.setState({
198− userApproval: false,
199− txn: Maybe.Nothing(),
200− stx: Maybe.Nothing(),
201− })
202− }
203−
204−
205−
206− handleSubmit(){
207− const { props, state } = this
208− sendSignedTransaction(props.web3.value, state.stx)
209− .then(sent => {
210− props.setTxnHashCursor(sent)
211− props.popRoute()
212− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
213− })
214− .catch(err => {
215− this.setState({ txError: err.map(val => val.merge()) })
216− })
217− }
218−
219−
220−
22149 createUnsignedTxn() {
22250 const { state, props } = this
22351
@@ -244,19 +72,12 @@ class Transfer extends React.Component {
24472 return Maybe.Just(txn)
24573 }
24674
247−
248−
24975 render() {
25076 const { props, state } = this
25177
25278 const validAddress = isValidAddress(state.receivingAddress)
253−
25479 const canGenerate = validAddress === true
25580
256− const canSign = Maybe.Just.hasInstance(state.txn)
257− const canApprove = Maybe.Just.hasInstance(state.stx)
258− const canSend = Maybe.Just.hasInstance(state.stx) && state.userApproval === true
259−
26081 const esvisible =
26182 props.networkType === NETWORK_NAMES.ROPSTEN ||
26283 props.networkType === NETWORK_NAMES.MAINNET
@@ -310,46 +131,15 @@ class Transfer extends React.Component {
310131 walletType={props.walletType}
311132 walletHdPath={props.walletHdPath}
312133 networkType={props.networkType}
313− // Tx
314− txn={state.txn}
315− stx={state.stx}
316− // Tx details
317− nonce={state.nonce}
318− gasPrice={state.gasPrice}
319− chainId={state.chainId}
320− gasLimit={state.gasLimit}
321− showGasDetails={state.showGasDetails}
322− toggleGasDetails={this.toggleGasDetails}
134+ setTxnHashCursor={props.setTxnHashCursor}
135+ popRoute={props.popRoute}
136+ pushRoute={props.pushRoute}
323137 // Checks
324138 userApproval={state.userApproval}
325139 canGenerate={ canGenerate }
326− canSign={ canSign }
327− canApprove={ canApprove }
328− canSend={ canSend }
329140 // Methods
330− createUnsignedTxn={this.handleCreateUnsignedTxn}
331− setUserApproval={this.handleSetUserApproval}
332− setTxn={this.handleSetTxn}
333− setStx={this.handleSetStx}
334− setNonce={this.handleSetNonce}
335− setChainId={this.handleSetChainId}
336− setGasPrice={this.handleSetGasPrice}
337− setGasLimit={this.handleSetGasLimit}
338− handleSubmit={this.handleSubmit} />
339−
340− {
341− Maybe.Nothing.hasInstance(state.txError)
342− ? ''
343− : <Warning className={'mt-8'}>
344− <H3 style={{marginTop: 0, paddingTop: 0}}>
345− {
346− 'There was an error sending your transaction.'
347− }
348− </H3>
349− { state.txError.value }
350− </Warning>
351− }
352−
141+ createUnsignedTxn={this.createUnsignedTxn}
142+ ref={this.statelessRef} />
353143 </Col>
354144 </Row>
355145 )
src/common/components/AppNavigation.js+5−5
@@ -23,17 +23,17 @@ class AppNavigation extends React.Component {
2323 let style = {
2424 position: "fixed",
2525 bottom: "10px",
26− right: "10px"
26+ right: "10px",
27+ "text-decoration": "underline",
28+ cursor: "pointer"
2729 }
2830
2931 return (
30− <a
31− href="javascript:void(0)"
32− type="button"
32+ <span
3333 style={style}
3434 onClick={this.navigate}>
3535 {buttonText}
36− </a>
36+ </span>
3737 )
3838 }
3939}