on master help sign in
Merge pull request #96 from urbit/stateless-transaction
bf5e9bbChristopher Johnson
src/bridge/components/StatelessTransaction.js+397−215
@@ -1,244 +1,426 @@
1−import { Nothing } from 'folktale/maybe'
1+import { Nothing, Just } from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3
3
4import { Code, H3 } from './Base'
4import { Code, H3 } from './Base'
5import { Button } from './Base'
5import { Button } from './Base'
6import { CheckboxButton, Input, InnerLabel } from './Base'
6import { CheckboxButton, Input, InnerLabel } from './Base'
7+import { Warning } from '../components/Base'
7
8
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'
9
12
13+import {
14+ sendSignedTransaction,
15+ fromWei,
16+ renderSignedTx,
17+ signTransaction
18+ } from '../lib/txn'
19+
10−const StatelessTransaction = props => {
20+class StatelessTransaction extends React.Component {
11− const { web3, gasPrice, gasLimit, nonce, chainId } = props
21+
12− const { setNonce, setChainId, setGasLimit, setGasPrice, showGasDetails, toggleGasDetails } = props
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+
13− const { txn, stx, createUnsignedTxn } = props
37+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
14− const { canSign, canGenerate, canApprove } = props
38+ this.submit = this.submit.bind(this)
15− const { setUserApproval, userApproval } = props
39+ this.setUserApproval = this.setUserApproval.bind(this)
16− const { canSend, handleSubmit } = props
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+ }
17
175
18 // TODO: Investigate
176 // TODO: Investigate
19− // setState doesn't seem to work in SetProxy/handleSubmit;
177+ // setState doesn't seem to work in SetProxy/submit;
20 // - TypeError: Cannot read property 'updater' of undefined
178 // - TypeError: Cannot read property 'updater' of undefined
21 // so just modifying the DOM manually here. (https://imgur.com/a/i0Qsyq1)
179 // so just modifying the DOM manually here. (https://imgur.com/a/i0Qsyq1)
22
180
23− const handleRangeChange = (e) => {
24− setGasPrice(e.target.value);
25− }
26−
27− const sendTxn = (e) => {
181+ sendTxn(e) {
28 e.target.setAttribute("disabled", true);
182 e.target.setAttribute("disabled", true);
29 let spinner = e.target.querySelectorAll('.btn-spinner')[0];
183 let spinner = e.target.querySelectorAll('.btn-spinner')[0];
30 spinner.classList.remove('hide');
184 spinner.classList.remove('hide');
31− handleSubmit()
185+ this.submit()
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 =>
58− <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− })
186+ }
67
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+
68− const gasPriceRangeDialogue = (
236+ const gasPriceRangeDialogue = (
69− <React.Fragment>
237+ <React.Fragment>
70− <div className="mt-12 flex space-between align-baseline">
238+ <div className="mt-12 flex space-between align-baseline">
71− <div>
239+ <div>
72− <span>Gas Price:</span>
240+ <span>Gas Price:</span>
73− <span className="ml-4 text-700 text-sm">{gasPrice} gwei</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>
74 </div>
247 </div>
248+
249+ <input
75− <div className="text-sm">
250+ className="mt-4"
76− <span>Max transaction fee: </span>
251+ type="range"
77− <span className="text-700">{(gasPrice * gasLimit) / 1000000000} eth</span>
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>
78 </div>
262 </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>
263 </React.Fragment>
160− })
264+ )
161−
265+
162− const signTxnButton =
266+ const toggleGasDetailsDialogue = (
163− <Button
267+ <span className="text-link" onClick={toggleGasDetails}>Gas Details</span>
164− disabled={ !canSign }
268+ )
165− className={ 'mt-8' }
269+
166− prop-size={ 'lg wide' }
270+ const gasPriceDialogue =
167− prop-color={ signerButtonColor }
271+ <Input
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' }>
272+ className={ 'mono mt-4' }
178− { 'Signed Transaction' }
273+ prop-size={ 'md' }
179− </H3>
274+ prop-format={ 'innerLabel' }
180− <Code>
275+ value={ gasPrice }
181− { JSON.stringify(renderSignedTx(tx.value), null, 2) }
276+ onChange={ setGasPrice }
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>
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− >
277+ >
205− <span className="relative">
278+ <InnerLabel>
206− <span className="btn-spinner hide"></span>
279+ { 'Gas Price (gwei)' }
207− { 'Send Transaction' }
280+ </InnerLabel>
208− </span>
281+ </Input>
209− </Button>
210−
211− const sendDialogue = web3.matchWith({
212− Nothing: _ => '',
213− Just: _ =>
214− <React.Fragment>
215− { confirmButton }
216− { sendTxnButton }
217− </React.Fragment>
218− })
219
282
220− return (
283+ const gasLimitDialogue =
221− <React.Fragment>
284+ <Input
222− { generateTxnButton }
285+ className={ 'mono mt-4' }
223− { unsignedTxnDisplay }
286+ prop-size={ 'md' }
287+ prop-format={ 'innerLabel' }
288+ value={ gasLimit }
289+ onChange={ setGasLimit }>
290+ <InnerLabel>
291+ { 'Gas Limit' }
292+ </InnerLabel>
293+ </Input>
224
294
225− { gasPriceRangeDialogue }
295+ const nonceDialogue =
226− { toggleGasDetailsDialogue }
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>
227
307
228− { showGasDetails &&
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+ >
229 <div>
361 <div>
230− { gasPriceDialogue }
362+ { `I approve this transaction and wish to send.` }
231− { gasLimitDialogue }
232 </div>
363 </div>
233− }
364+ </CheckboxButton>
234− { onlineParamsDialogue }
235
365
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>
237
378
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+
238− { signedTxnDisplay }
417+ { signedTxnDisplay }
239− { sendDialogue }
418+ { sendDialogue }
419+
420+ { txnErrorDialogue }
240− </React.Fragment>
421+ </React.Fragment>
241− )
422+ )
423+ }
242}
424}
243
425
244export default StatelessTransaction
426export default StatelessTransaction
src/bridge/style/extend.css+5
@@ -466,6 +466,11 @@ code {
466 cursor: pointer;
466 cursor: pointer;
467}
467}
468
468
469+.text-link {
470+ cursor: pointer;
471+ text-decoration: underline;
472+}
473+
469.keyfile {
474.keyfile {
470 white-space: pre-wrap;
475 white-space: pre-wrap;
471 word-break: break-word;
476 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'
2import React from 'react'
2import React from 'react'
3−import { Row, Col, H1, H3, P, Warning } from '../components/Base'
3+import { Row, Col, H1, P } from '../components/Base'
4import { InnerLabel, AddressInput, ShowBlockie, Anchor } from '../components/Base'
4import { InnerLabel, AddressInput, ShowBlockie, Anchor } from '../components/Base'
5import * as azimuth from 'azimuth-js'
5import * as azimuth from 'azimuth-js'
6import * as ob from 'urbit-ob'
6import * as ob from 'urbit-ob'
@@ -8,8 +8,6 @@ import * as ob from 'urbit-ob'
8import StatelessTransaction from '../components/StatelessTransaction'
8import StatelessTransaction from '../components/StatelessTransaction'
9import { BRIDGE_ERROR } from '../lib/error'
9import { BRIDGE_ERROR } from '../lib/error'
10import { NETWORK_NAMES } from '../lib/network'
10import { NETWORK_NAMES } from '../lib/network'
11−import { ROUTE_NAMES } from '../lib/router'
12−import { sendSignedTransaction, fromWei } from '../lib/txn'
13import { isValidAddress, addressFromSecp256k1Public } from '../lib/wallet'
11import { isValidAddress, addressFromSecp256k1Public } from '../lib/wallet'
14
12
15class AcceptTransfer extends React.Component {
13class AcceptTransfer extends React.Component {
@@ -32,144 +30,17 @@ class AcceptTransfer extends React.Component {
32
30
33 this.state = {
31 this.state = {
34 receivingAddress: receivingAddress,
32 receivingAddress: receivingAddress,
35− txn: Nothing(),
36− txError: Nothing(),
37 incomingPoint: incomingPoint,
33 incomingPoint: incomingPoint,
38− userApproval: false,
39− nonce: '',
40− gasPrice: '5',
41− showGasDetails: false,
42− chainId: '',
43− gasLimit: '600000',
44− stx: Nothing(),
45 }
34 }
46
35
47 this.handleAddressInput = this.handleAddressInput.bind(this)
36 this.handleAddressInput = this.handleAddressInput.bind(this)
48− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
37+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
49− this.handleSubmit = this.handleSubmit.bind(this)
38+ this.statelessRef = React.createRef();
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− });
99 }
39 }
100
40
101 handleAddressInput(proxyAddress) {
41 handleAddressInput(proxyAddress) {
102 this.setState({ proxyAddress })
42 this.setState({ proxyAddress })
103− this.handleClearTxn()
43+ this.statelessRef.current.clearTxn()
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− })
173 }
44 }
174
45
175 createUnsignedTxn() {
46 createUnsignedTxn() {
@@ -200,15 +71,9 @@ class AcceptTransfer extends React.Component {
200 }
71 }
201
72
202 render() {
73 render() {
203−
204 const { state, props } = this
74 const { state, props } = this
205 const validAddress = isValidAddress(state.receivingAddress)
75 const validAddress = isValidAddress(state.receivingAddress)
206 const canGenerate = validAddress === true
76 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
212
77
213 const esvisible =
78 const esvisible =
214 props.networkType === NETWORK_NAMES.ROPSTEN ||
79 props.networkType === NETWORK_NAMES.ROPSTEN ||
@@ -260,46 +125,13 @@ class AcceptTransfer extends React.Component {
260 walletType={props.walletType}
125 walletType={props.walletType}
261 walletHdPath={props.walletHdPath}
126 walletHdPath={props.walletHdPath}
262 networkType={props.networkType}
127 networkType={props.networkType}
263− // Tx
128+ setTxnHashCursor={props.setTxnHashCursor}
264− txn={state.txn}
129+ popRoute={props.popRoute}
265− stx={state.stx}
130+ pushRoute={props.pushRoute}
266− // Tx details
131+ // Other
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}
275 canGenerate={ canGenerate }
132 canGenerate={ canGenerate }
276− canSign={ canSign }
277− canApprove={ canApprove }
278− canSend={ canSend }
279− // Methods
280− createUnsignedTxn={this.handleCreateUnsignedTxn}
133+ createUnsignedTxn={this.createUnsignedTxn}
281− setUserApproval={this.handleSetUserApproval}
134+ ref={ this.statelessRef } />
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−
303 </Col>
135 </Col>
304 </Row>
136 </Row>
305 )
137 )
src/bridge/views/CancelTransfer.js+7−197
@@ -1,23 +1,12 @@
1import Maybe from 'folktale/maybe'
1import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3−import { Row, Col, H1, P, Warning, H3 } from '../components/Base'
3+import { Row, Col, H1, P } from '../components/Base'
4import * as azimuth from 'azimuth-js'
4import * as azimuth from 'azimuth-js'
5import * as ob from 'urbit-ob'
5import * as ob from 'urbit-ob'
6
6
7import StatelessTransaction from '../components/StatelessTransaction'
7import StatelessTransaction from '../components/StatelessTransaction'
8import { BRIDGE_ERROR } from '../lib/error'
8import { BRIDGE_ERROR } from '../lib/error'
9import { ETH_ZERO_ADDR } from '../lib/wallet'
9import { 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−
21
10
22class CancelTransfer extends React.Component {
11class CancelTransfer extends React.Component {
23 constructor(props) {
12 constructor(props) {
@@ -32,150 +21,10 @@ class CancelTransfer extends React.Component {
32
21
33 this.state = {
22 this.state = {
34 proxyAddress: '',
23 proxyAddress: '',
35− txn: Maybe.Nothing(),
36− txError: Maybe.Nothing(),
37 pointInTransfer: pointInTransfer,
24 pointInTransfer: pointInTransfer,
38− userApproval: false,
39− nonce: '',
40− gasPrice: '5',
41− showGasDetails: false,
42− chainId: '',
43− gasLimit: '600000',
44− stx: Maybe.Nothing(),
45 }
25 }
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− }
58
26
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()
27+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
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− })
179 }
28 }
180
29
181 createUnsignedTxn() {
30 createUnsignedTxn() {
@@ -204,7 +53,6 @@ class CancelTransfer extends React.Component {
204 return Maybe.Just(txn)
53 return Maybe.Just(txn)
205 }
54 }
206
55
207−
208 render() {
56 render() {
209 const { props, state } = this
57 const { props, state } = this
210
58
@@ -217,10 +65,6 @@ class CancelTransfer extends React.Component {
217 // const canGenerate = validAddress === true
65 // const canGenerate = validAddress === true
218
66
219 const canGenerate = true
67 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−
224
68
225 return (
69 return (
226 <Row>
70 <Row>
@@ -242,46 +86,12 @@ class CancelTransfer extends React.Component {
242 walletType={props.walletType}
86 walletType={props.walletType}
243 walletHdPath={props.walletHdPath}
87 walletHdPath={props.walletHdPath}
244 networkType={props.networkType}
88 networkType={props.networkType}
245− // Tx
89+ setTxnHashCursor={props.setTxnHashCursor}
246− txn={state.txn}
90+ popRoute={props.popRoute}
247− stx={state.stx}
91+ pushRoute={props.pushRoute}
248− // Tx details
92+ // Other
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}
257 canGenerate={ canGenerate }
93 canGenerate={ canGenerate }
258− canSign={ canSign }
259− canApprove={ canApprove }
260− canSend={ canSend }
261− // Methods
262− createUnsignedTxn={this.handleCreateUnsignedTxn}
94+ createUnsignedTxn={this.createUnsignedTxn} />
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−
285 </Col>
95 </Col>
286 </Row>
96 </Row>
287 )
97 )
src/bridge/views/CreateGalaxy.js+18−96
@@ -9,16 +9,8 @@ import { InnerLabel, GalaxyInput, AddressInput, ValidatedSigil, ShowBlockie } fr
9import StatelessTransaction from '../components/StatelessTransaction'
9import StatelessTransaction from '../components/StatelessTransaction'
10
10
11import { BRIDGE_ERROR } from '../lib/error'
11import { BRIDGE_ERROR } from '../lib/error'
12−
13import { NETWORK_NAMES } from '../lib/network'
12import { NETWORK_NAMES } from '../lib/network'
14−
15−import {
16− sendSignedTransaction,
17− getTxnInfo,
18− canDecodePatp
13+import { canDecodePatp } from '../lib/txn'
19−} from '../lib/txn'
20−
21−import { ROUTE_NAMES } from '../lib/router'
22
14
23import {
15import {
24 addressFromSecp256k1Public,
16 addressFromSecp256k1Public,
@@ -61,54 +53,23 @@ class CreateGalaxy extends React.Component {
61 galaxyOwner: galaxyOwner,
53 galaxyOwner: galaxyOwner,
62 galaxyName: '',
54 galaxyName: '',
63 isAvailable: null,
55 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(),
73 }
56 }
74
57
75 this.handleAddressInput = this.handleAddressInput.bind(this)
58 this.handleAddressInput = this.handleAddressInput.bind(this)
76 this.handleGalaxyNameInput = this.handleGalaxyNameInput.bind(this)
59 this.handleGalaxyNameInput = this.handleGalaxyNameInput.bind(this)
77 this.confirmAvailability = this.confirmAvailability.bind(this)
60 this.confirmAvailability = this.confirmAvailability.bind(this)
78− this.handleSubmit = this.handleSubmit.bind(this)
61+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
79− this.toggleGasDetails = this.toggleGasDetails.bind(this)
62+ this.statelessRef = React.createRef();
80− }
81−
82− toggleGasDetails() {
83− this.setState({
84− showGasDetails: !this.state.showGasDetails
85− })
86 }
63 }
87
64
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−
104 handleGalaxyNameInput = (galaxyName) => {
65 handleGalaxyNameInput = (galaxyName) => {
105 this.setState({ galaxyName, isAvailable: null })
66 this.setState({ galaxyName, isAvailable: null })
106− this.clearTransaction()
67+ this.statelessRef.current.clearTxn()
107 }
68 }
108
69
109 handleAddressInput = (galaxyOwner) => {
70 handleAddressInput = (galaxyOwner) => {
110 this.setState({ galaxyOwner })
71 this.setState({ galaxyOwner })
111− this.clearTransaction()
72+ this.statelessRef.current.clearTxn()
112 }
73 }
113
74
114 createUnsignedTxn = () => {
75 createUnsignedTxn = () => {
@@ -133,18 +94,7 @@ class CreateGalaxy extends React.Component {
133 state.galaxyOwner
94 state.galaxyOwner
134 )
95 )
135
96
136− this.setState({ txn: Maybe.Just(txn) })
97+ return 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− })
148 }
98 }
149
99
150 confirmAvailability = async () => {
100 confirmAvailability = async () => {
@@ -174,21 +124,16 @@ class CreateGalaxy extends React.Component {
174 this.setState({ isAvailable: available })
124 this.setState({ isAvailable: available })
175 }
125 }
176
126
177−
178− clearTransaction = () => {
179− this.setState({
180− txApproval: false,
181− txn: Maybe.Nothing(),
182− stx: Maybe.Nothing(),
183− })
184− }
185−
186 render() {
127 render() {
187 const { props, state } = this
128 const { props, state } = this
188
129
189 const validAddress = isValidAddress(state.galaxyOwner)
130 const validAddress = isValidAddress(state.galaxyOwner)
190 const validGalaxy = isValidGalaxy(state.galaxyName)
131 const validGalaxy = isValidGalaxy(state.galaxyName)
191
132
133+ const canGenerate = validAddress === true &&
134+ validGalaxy === true &&
135+ state.isAvailable === true
136+
192 const esvisible =
137 const esvisible =
193 props.networkType === NETWORK_NAMES.ROPSTEN ||
138 props.networkType === NETWORK_NAMES.ROPSTEN ||
194 props.networkType === NETWORK_NAMES.MAINNET
139 props.networkType === NETWORK_NAMES.MAINNET
@@ -259,43 +204,20 @@ class CreateGalaxy extends React.Component {
259 </Button>
204 </Button>
260
205
261 <StatelessTransaction
206 <StatelessTransaction
262− address={state.galaxyOwner}
207+ // Upper scope
263− shipName={state.galaxyName}
264− isAvailable={state.isAvailable}
265 web3={props.web3}
208 web3={props.web3}
266 contracts={props.contracts}
209 contracts={props.contracts}
267 wallet={props.wallet}
210 wallet={props.wallet}
268 walletType={props.walletType}
211 walletType={props.walletType}
269 walletHdPath={props.walletHdPath}
212 walletHdPath={props.walletHdPath}
270 networkType={props.networkType}
213 networkType={props.networkType}
271− // Tx
214+ setTxnHashCursor={props.setTxnHashCursor}
272− txApproval={state.txApproval}
215+ popRoute={props.popRoute}
273− txn={state.txn}
216+ pushRoute={props.pushRoute}
274− stx={state.stx}
217+ // Other
275− nonce={state.nonce}
218+ ref={this.statelessRef}
276− gasPrice={state.gasPrice}
277− showGasDetails={state.showGasDetails}
278− toggleGasDetails={this.toggleGasDetails}
279− chainId={state.chainId}
280− gasLimit={state.gasLimit}
281− canGenerate={
219+ canGenerate={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()}
220+ 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} />
299
221
300 </Col>
222 </Col>
301 </Row>
223 </Row>
src/bridge/views/IssueChild.js+14−225
@@ -3,23 +3,21 @@ import React from 'react'
3import { azimuth, ecliptic } from 'azimuth-js'
3import { azimuth, ecliptic } from 'azimuth-js'
4import * as ob from 'urbit-ob'
4import * as ob from 'urbit-ob'
5
5
6−import { Row, Col, H1, H3, P, Anchor, Warning } from '../components/Base'
6+import { Row, Col, H1, P, Anchor } from '../components/Base'
7import { Button, ShowBlockie, ValidatedSigil } from '../components/Base'
7import { Button, ShowBlockie, ValidatedSigil } from '../components/Base'
8import { PointInput, AddressInput, InnerLabel } from '../components/Base'
8import { PointInput, AddressInput, InnerLabel } from '../components/Base'
9
9
10import StatelessTransaction from '../components/StatelessTransaction'
10import StatelessTransaction from '../components/StatelessTransaction'
11
11
12−import { ROUTE_NAMES } from '../lib/router'
13import { NETWORK_NAMES } from '../lib/network'
12import { NETWORK_NAMES } from '../lib/network'
14import { BRIDGE_ERROR } from '../lib/error'
13import { BRIDGE_ERROR } from '../lib/error'
15import { getSpawnCandidate } from '../lib/child'
14import { getSpawnCandidate } from '../lib/child'
16−import { canDecodePatp, sendSignedTransaction, fromWei } from '../lib/txn'
15+import { canDecodePatp } from '../lib/txn'
17
16
18import {
17import {
19 ETH_ZERO_ADDR,
18 ETH_ZERO_ADDR,
20 isValidAddress,
19 isValidAddress,
21− eqAddr,
20+ eqAddr
22− addressFromSecp256k1Public
23} from '../lib/wallet'
21} from '../lib/wallet'
24
22
25class IssueChild extends React.Component {
23class IssueChild extends React.Component {
@@ -43,100 +41,35 @@ class IssueChild extends React.Component {
43 ]
41 ]
44
42
45 this.state = {
43 this.state = {
46− txError: Nothing(),
47 receivingAddress: '',
44 receivingAddress: '',
48 issuingPoint: issuingPoint,
45 issuingPoint: issuingPoint,
49 desiredPoint: '',
46 desiredPoint: '',
50 isAvailable: Nothing(), // use Nothing to allow attempt when offline
47 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(),
59 suggestions: suggestions,
48 suggestions: suggestions,
60 }
49 }
61
50
62 this.handlePointInput = this.handlePointInput.bind(this)
51 this.handlePointInput = this.handlePointInput.bind(this)
63 this.handleAddressInput = this.handleAddressInput.bind(this)
52 this.handleAddressInput = this.handleAddressInput.bind(this)
64 this.handleConfirmAvailability = this.handleConfirmAvailability.bind(this)
53 this.handleConfirmAvailability = this.handleConfirmAvailability.bind(this)
65− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
54+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
66− this.handleSubmit = this.handleSubmit.bind(this)
55+ this.statelessRef = React.createRef();
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)
75 }
56 }
76
57
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−
121 handleAddressInput = (receivingAddress) => {
58 handleAddressInput = (receivingAddress) => {
122 this.setState({ receivingAddress })
59 this.setState({ receivingAddress })
123− this.handleClearTxn()
60+ this.statelessRef.current.clearTxn()
124 }
61 }
125
62
126−
127−
128 handlePointInput(desiredPoint) {
63 handlePointInput(desiredPoint) {
129 if (desiredPoint.length < 15) {
64 if (desiredPoint.length < 15) {
130 this.setState({
65 this.setState({
131 desiredPoint,
66 desiredPoint,
132 isAvailable: Nothing()
67 isAvailable: Nothing()
133 })
68 })
134− this.handleClearTxn()
69+ this.statelessRef.current.clearTxn()
135 }
70 }
136 }
71 }
137
72
138−
139−
140 handleConfirmAvailability() {
73 handleConfirmAvailability() {
141 this.confirmPointAvailability().then(r => {
74 this.confirmPointAvailability().then(r => {
142 this.setState({
75 this.setState({
@@ -146,106 +79,6 @@ class IssueChild extends React.Component {
146
79
147 }
80 }
148
81
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−
249 async confirmPointAvailability() {
82 async confirmPointAvailability() {
250 const { contracts } = this.props
83 const { contracts } = this.props
251 const { desiredPoint } = this.state
84 const { desiredPoint } = this.state
@@ -307,8 +140,6 @@ class IssueChild extends React.Component {
307 return Just(txn)
140 return Just(txn)
308 }
141 }
309
142
310−
311−
312 buttonTriState() {
143 buttonTriState() {
313 const a = this.state.isAvailable
144 const a = this.state.isAvailable
314 if (Nothing.hasInstance(a)) return 'blue'
145 if (Nothing.hasInstance(a)) return 'blue'
@@ -316,8 +147,6 @@ class IssueChild extends React.Component {
316 if (a.value === true) return 'green'
147 if (a.value === true) return 'green'
317 }
148 }
318
149
319−
320−
321 buttonTriStateText() {
150 buttonTriStateText() {
322 const a = this.state.isAvailable
151 const a = this.state.isAvailable
323 if (Nothing.hasInstance(a)) return 'Confirm Availablility'
152 if (Nothing.hasInstance(a)) return 'Confirm Availablility'
@@ -343,7 +172,6 @@ class IssueChild extends React.Component {
343 return vpatp && vchild
172 return vpatp && vchild
344 }
173 }
345
174
346−
347 render() {
175 render() {
348 const { props, state } = this
176 const { props, state } = this
349
177
@@ -362,12 +190,6 @@ class IssueChild extends React.Component {
362 }
190 }
363 })
191 })
364
192
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−
371 const esvisible =
193 const esvisible =
372 props.networkType === NETWORK_NAMES.ROPSTEN ||
194 props.networkType === NETWORK_NAMES.ROPSTEN ||
373 props.networkType === NETWORK_NAMES.MAINNET
195 props.networkType === NETWORK_NAMES.MAINNET
@@ -480,46 +302,13 @@ class IssueChild extends React.Component {
480 walletType={props.walletType}
302 walletType={props.walletType}
481 walletHdPath={props.walletHdPath}
303 walletHdPath={props.walletHdPath}
482 networkType={props.networkType}
304 networkType={props.networkType}
483− // Tx
305+ setTxnHashCursor={props.setTxnHashCursor}
484− txn={state.txn}
306+ popRoute={props.popRoute}
485− stx={state.stx}
307+ pushRoute={props.pushRoute}
486− // Tx details
308+ // Other
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 }
309+ canGenerate={canGenerate}
496− canSign={ canSign }
497− canApprove={ canApprove }
498− canSend={ canSend }
499− // Methods
500− createUnsignedTxn={this.handleCreateUnsignedTxn}
310+ createUnsignedTxn={this.createUnsignedTxn}
501− setUserApproval={this.handleSetUserApproval}
311+ ref={this.statelessRef} />
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−
523 </Col>
312 </Col>
524 </Row>
313 </Row>
525 )
314 )
src/bridge/views/Point/Actions.js−1
@@ -30,7 +30,6 @@ const Actions = (props) => {
30 eqAddr(details.value.owner, addr) && details.value.active
30 eqAddr(details.value.owner, addr) && details.value.active
31 })
31 })
32
32
33−
34 const canSetManagementProxy = pointDetails.matchWith({
33 const canSetManagementProxy = pointDetails.matchWith({
35 Nothing: _ => false,
34 Nothing: _ => false,
36 Just: details =>
35 Just: details =>
src/bridge/views/SetKeys.js+9−223
@@ -2,22 +2,14 @@ import React from 'react'
2import Maybe from 'folktale/maybe'
2import Maybe from 'folktale/maybe'
3import * as azimuth from 'azimuth-js'
3import * as azimuth from 'azimuth-js'
4import * as ob from 'urbit-ob'
4import * as ob from 'urbit-ob'
5−import { Row, Col, H1, P, Warning, H3 } from '../components/Base'
5+import { Row, Col, H1, P, Warning } from '../components/Base'
6
6
7import StatelessTransaction from '../components/StatelessTransaction'
7import StatelessTransaction from '../components/StatelessTransaction'
8import { BRIDGE_ERROR } from '../lib/error'
8import { BRIDGE_ERROR } from '../lib/error'
9−import { ROUTE_NAMES } from '../lib/router'
10−import { attemptSeedDerivation, genKey } from '../lib/keys'
9+import { attemptSeedDerivation } from '../lib/keys'
11−
12−import saveAs from 'file-saver'
13
10
14import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
11import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
15
12
16−import {
17− sendSignedTransaction,
18− fromWei,
19− } from '../lib/txn'
20−
21import {
13import {
22 addressFromSecp256k1Public,
14 addressFromSecp256k1Public,
23 addHexPrefix
15 addHexPrefix
@@ -42,36 +34,12 @@ class SetKeys extends React.Component {
42 point: point,
34 point: point,
43 cryptoSuiteVersion: 1,
35 cryptoSuiteVersion: 1,
44 continuity: false,
36 continuity: false,
45− txn: Maybe.Nothing(),
46− txError: Maybe.Nothing(),
47 isManagementSeed: false,
37 isManagementSeed: false,
48− userApproval: false,
49− nonce: '',
50− gasPrice: '5',
51− showGasDetails: false,
52− chainId: '',
53− gasLimit: '600000',
54− stx: Maybe.Nothing(),
55 }
38 }
56
39
57 this.handleNetworkSeedInput = this.handleNetworkSeedInput.bind(this)
40 this.handleNetworkSeedInput = this.handleNetworkSeedInput.bind(this)
58 // Transaction
41 // Transaction
59− this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
42+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
60− this.handleSubmit = this.handleSubmit.bind(this)
61− this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
62− this.handleSetTxn = this.handleSetTxn.bind(this)
63− this.handleSetStx = this.handleSetStx.bind(this)
64− this.handleSetNonce = this.handleSetNonce.bind(this)
65− this.handleSetChainId = this.handleSetChainId.bind(this)
66− this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
67− this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
68− this.toggleGasDetails = this.toggleGasDetails.bind(this)
69− }
70−
71− toggleGasDetails() {
72− this.setState({
73− showGasDetails: !this.state.showGasDetails
74− })
75 }
43 }
76
44
77 componentDidMount() {
45 componentDidMount() {
@@ -87,30 +55,6 @@ class SetKeys extends React.Component {
87 })
55 })
88
56
89 this.determineManagementSeed(props.contracts.value, addr)
57 this.determineManagementSeed(props.contracts.value, addr)
90−
91− props.web3.matchWith({
92− Nothing: () => {},
93− Just: (w3) => {
94− const validWeb3 = w3.value
95−
96− const getTxMetadata = [
97− validWeb3.eth.getTransactionCount(addr),
98− validWeb3.eth.net.getId(),
99− validWeb3.eth.getGasPrice()
100− ];
101−
102− Promise.all(getTxMetadata).then(r => {
103− const txMetadata = {
104− nonce: r[0],
105− chainId: r[1],
106− gasPrice: fromWei(r[2], 'gwei'),
107− };
108−
109− this.setState({...txMetadata})
110−
111− })
112− }
113− });
114 }
58 }
115
59
116 async determineManagementSeed(ctrcs, addr) {
60 async determineManagementSeed(ctrcs, addr) {
@@ -154,126 +98,6 @@ class SetKeys extends React.Component {
154 this.setState({ networkSeed })
98 this.setState({ networkSeed })
155 }
99 }
156
100
157− handleCreateUnsignedTxn() {
158− const txn = this.createUnsignedTxn()
159− this.setState({ txn })
160− }
161−
162−
163− handleSetUserApproval(){
164− const {state} = this
165− this.setState({ userApproval: !state.userApproval })
166− }
167−
168−
169−
170− handleSetTxn(txn){
171− this.setState({ txn })
172− }
173−
174−
175−
176− handleSetStx(stx){
177− this.setState({
178− stx,
179− userApproval: false,
180− })
181− }
182−
183−
184−
185− handleSetNonce(nonce){
186− this.setState({ nonce })
187− this.handleClearStx()
188− }
189−
190−
191−
192− handleSetChainId(chainId){
193− this.setState({ chainId })
194− this.handleClearStx()
195− }
196−
197−
198−
199− handleSetGasPrice(gasPrice){
200− this.setState({ gasPrice })
201− this.handleClearStx()
202− }
203−
204−
205−
206− handleSetGasLimit(gasLimit){
207− this.setState({ gasLimit })
208− this.handleClearStx()
209− }
210−
211−
212−
213− handleClearStx() {
214− this.setState({
215− userApproval: false,
216− stx: Maybe.Nothing(),
217− })
218− }
219−
220−
221−
222−
223− handleClearTxn() {
224− this.setState({
225− userApproval: false,
226− txn: Maybe.Nothing(),
227− stx: Maybe.Nothing(),
228− })
229− }
230−
231−
232−
233− handleClearTransaction() {
234− this.setState({
235− userApproval: false,
236− txn: Maybe.Nothing(),
237− stx: Maybe.Nothing(),
238− })
239− }
240−
241− downloadKeyfile(networkSeed) {
242− const { pointCache } = this.props
243− const { pointCursor } = this.props
244−
245− const point = pointCursor.matchWith({
246− Just: (pt) => pt.value,
247− Nothing: () => {
248− throw BRIDGE_ERROR.MISSING_POINT
249− }
250− })
251−
252− const pointDetails =
253− point in pointCache
254− ? pointCache[point]
255− : (() => { throw BRIDGE_ERROR.MISSING_POINT })()
256−
257− const revision = parseInt(pointDetails.keyRevisionNumber)
258− const keyfile = genKey(networkSeed, point, revision)
259− let blob = new Blob([keyfile], {type:"text/plain;charset=utf-8"});
260− saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`)
261− }
262−
263− handleSubmit(){
264− const { props, state } = this
265− sendSignedTransaction(props.web3.value, state.stx)
266− .then(sent => {
267− props.setNetworkSeedCache(this.state.networkSeed)
268− props.setTxnHashCursor(sent)
269− props.popRoute()
270− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, {promptKeyfile: true})
271− })
272− .catch(err => {
273− this.setState({ txError: err.map(val => val.merge()) })
274− })
275− }
276−
277 createUnsignedTxn() {
101 createUnsignedTxn() {
278 const { state, props } = this
102 const { state, props } = this
279
103
@@ -322,13 +146,6 @@ class SetKeys extends React.Component {
322 state.networkSeed.length === 64
146 state.networkSeed.length === 64
323 && state.networkSeed.length === 64
147 && state.networkSeed.length === 64
324
148
325− const canSign = Maybe.Just.hasInstance(state.txn)
326− const canApprove = Maybe.Just.hasInstance(state.stx)
327−
328− const canSend =
329− Maybe.Just.hasInstance(state.stx)
330− && state.userApproval === true
331−
332 const pointDetails =
149 const pointDetails =
333 state.point in props.pointCache
150 state.point in props.pointCache
334 ? props.pointCache[state.point]
151 ? props.pointCache[state.point]
@@ -376,45 +193,14 @@ class SetKeys extends React.Component {
376 walletType={props.walletType}
193 walletType={props.walletType}
377 walletHdPath={props.walletHdPath}
194 walletHdPath={props.walletHdPath}
378 networkType={props.networkType}
195 networkType={props.networkType}
196+ setTxnHashCursor={props.setTxnHashCursor}
197+ popRoute={props.popRoute}
198+ pushRoute={props.pushRoute}
379 // Tx
199 // Tx
380− txn={state.txn}
381− stx={state.stx}
382− // Tx details
383− nonce={state.nonce}
384− gasPrice={state.gasPrice}
385− chainId={state.chainId}
386− gasLimit={state.gasLimit}
387− showGasDetails={state.showGasDetails}
388− toggleGasDetails={this.toggleGasDetails}
389− // Checks
390− userApproval={state.userApproval}
391 canGenerate={ canGenerate }
200 canGenerate={ canGenerate }
392− canSign={ canSign }
393− canApprove={ canApprove }
394− canSend={ canSend }
395− // Methods
396− createUnsignedTxn={this.handleCreateUnsignedTxn}
201+ createUnsignedTxn={this.createUnsignedTxn}
397− setUserApproval={this.handleSetUserApproval}
202+ networkSeed={ state.networkSeed }
398− setTxn={this.handleSetTxn}
203+ setNetworkSeedCache={ props.setNetworkSeedCache } />
399− setStx={this.handleSetStx}
400− setNonce={this.handleSetNonce}
401− setChainId={this.handleSetChainId}
402− setGasPrice={this.handleSetGasPrice}
403− setGasLimit={this.handleSetGasLimit}
404− handleSubmit={this.handleSubmit} />
405−
406− {
407− Maybe.Nothing.hasInstance(state.txError)
408− ? ''
409− : <Warning className={'mt-8'}>
410− <H3 style={{marginTop: 0, paddingTop: 0}}>
411− {
412− 'There was an error sending your transaction.'
413− }
414− </H3>
415− { state.txError.value }
416− </Warning>
417− }
418
204
419 </Col>
205 </Col>
420 </Row>
206 </Row>
src/bridge/views/SetProxy.js+13−224
@@ -1,8 +1,8 @@
1import Maybe from 'folktale/maybe'
1import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3import * as azimuth from 'azimuth-js'
3import * as azimuth from 'azimuth-js'
4−import { Row, Col, H1, H3, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
4+import { Row, Col, H1, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
5−import { AddressInput, Warning } from '../components/Base'
5+import { AddressInput } from '../components/Base'
6import * as ob from 'urbit-ob'
6import * as ob from 'urbit-ob'
7
7
8import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
8import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
@@ -12,17 +12,7 @@ import { NETWORK_NAMES } from '../lib/network'
12
12
13import StatelessTransaction from '../components/StatelessTransaction'
13import StatelessTransaction from '../components/StatelessTransaction'
14
14
15−import { ROUTE_NAMES } from '../lib/router'
16−
17−import {
18− sendSignedTransaction,
19− fromWei,
20− } from '../lib/txn'
21−
22−import {
23− isValidAddress,
15+import { isValidAddress } from '../lib/wallet'
24− addressFromSecp256k1Public,
25−} from '../lib/wallet'
26
16
27const SetManagementProxy = (props) =>
17const SetManagementProxy = (props) =>
28 <SetProxy
18 <SetProxy
@@ -55,181 +45,20 @@ class SetProxy extends React.Component {
55
45
56 this.state = {
46 this.state = {
57 proxyAddress: '',
47 proxyAddress: '',
58− txn: Maybe.Nothing(),
59− txError: Maybe.Nothing(),
60 issuingPoint: issuingPoint,
48 issuingPoint: issuingPoint,
61− showGasDetails: false,
62− userApproval: false,
63− nonce: '',
64− gasPrice: '5',
65− chainId: '',
66− gasLimit: '600000',
67− stx: Maybe.Nothing(),
68 }
49 }
69
50
51+ this.statelessRef = React.createRef();
70 this.handleAddressInput = this.handleAddressInput.bind(this)
52 this.handleAddressInput = this.handleAddressInput.bind(this)
71− // Transaction
53+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
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− })
88 }
54 }
89
55
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−
128 handleAddressInput(proxyAddress) {
56 handleAddressInput(proxyAddress) {
129 this.setState({ proxyAddress })
57 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−
161
58
162− handleSetNonce(nonce){
59+ this.statelessRef.current.clearTxn()
163− this.setState({ nonce })
164− this.handleClearStx()
165 }
60 }
166
61
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−
233 createUnsignedTxn(proxyAddress) {
62 createUnsignedTxn(proxyAddress) {
234 const { state, props } = this
63 const { state, props } = this
235
64
@@ -270,19 +99,12 @@ class SetProxy extends React.Component {
270 }
99 }
271
100
272 render() {
101 render() {
273−
274 const { props, state } = this
102 const { props, state } = this
275
103
276 const renderedProxyType = renderProxyType(props.proxyType)
104 const renderedProxyType = renderProxyType(props.proxyType)
277−
278 const validAddress = isValidAddress(state.proxyAddress)
105 const validAddress = isValidAddress(state.proxyAddress)
279−
280 const canGenerate = validAddress === true
106 const canGenerate = validAddress === true
281
107
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−
286 const esvisible =
108 const esvisible =
287 props.networkType === NETWORK_NAMES.ROPSTEN ||
109 props.networkType === NETWORK_NAMES.ROPSTEN ||
288 props.networkType === NETWORK_NAMES.MAINNET
110 props.networkType === NETWORK_NAMES.MAINNET
@@ -339,46 +161,13 @@ class SetProxy extends React.Component {
339 walletType={props.walletType}
161 walletType={props.walletType}
340 walletHdPath={props.walletHdPath}
162 walletHdPath={props.walletHdPath}
341 networkType={props.networkType}
163 networkType={props.networkType}
342− // Tx
164+ setTxnHashCursor={props.setTxnHashCursor}
343− txn={state.txn}
165+ popRoute={props.popRoute}
344− stx={state.stx}
166+ pushRoute={props.pushRoute}
345− // Tx details
167+ // Other
346− nonce={state.nonce}
168+ ref={this.statelessRef}
347− gasPrice={state.gasPrice}
169+ createUnsignedTxn={this.createUnsignedTxn}
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 }
170+ 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−
382 </Col>
171 </Col>
383 </Row>
172 </Row>
384 )
173 )
src/bridge/views/Transfer.js+12−222
@@ -4,22 +4,15 @@ import * as azimuth from 'azimuth-js'
4import * as ob from 'urbit-ob'
4import * as ob from 'urbit-ob'
5
5
6import { Row, Col, H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/Base'
6import { Row, Col, H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/Base'
7−import { AddressInput, Warning, H3 } from '../components/Base'
7+import { AddressInput } from '../components/Base'
8import StatelessTransaction from '../components/StatelessTransaction'
8import StatelessTransaction from '../components/StatelessTransaction'
9
9
10import { BRIDGE_ERROR } from '../lib/error'
10import { BRIDGE_ERROR } from '../lib/error'
11import { NETWORK_NAMES } from '../lib/network'
11import { NETWORK_NAMES } from '../lib/network'
12−import { ROUTE_NAMES } from '../lib/router'
13
12
14import {
13import {
15− sendSignedTransaction,
16− fromWei,
17− } from '../lib/txn'
18−
19− import {
20− isValidAddress,
14+ isValidAddress
21− addressFromSecp256k1Public
22− } from '../lib/wallet'
15+} from '../lib/wallet'
23
16
24class Transfer extends React.Component {
17class Transfer extends React.Component {
25 constructor(props) {
18 constructor(props) {
@@ -33,191 +26,26 @@ class Transfer extends React.Component {
33 })
26 })
34
27
35 this.state = {
28 this.state = {
36− txError: Maybe.Nothing(),
37 receivingAddress: '',
29 receivingAddress: '',
38 issuingPoint: issuingPoint,
30 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(),
47 }
31 }
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− });
101
32
33+ this.handleAddressInput = this.handleAddressInput.bind(this)
34+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
35+ this.statelessRef = React.createRef();
102 }
36 }
103
37
104−
105−
106 handleAddressInput(receivingAddress) {
38 handleAddressInput(receivingAddress) {
107 this.setState({ receivingAddress })
39 this.setState({ receivingAddress })
108− this.handleClearTxn()
40+ this.statelessRef.current.clearTxn()
109 }
41 }
110
42
111−
112−
113 handleConfirmAvailability() {
43 handleConfirmAvailability() {
114 this.confirmPointAvailability().then(r => {
44 this.confirmPointAvailability().then(r => {
115 this.setState({ isAvailable: r })
45 this.setState({ isAvailable: r })
116 })
46 })
117 }
47 }
118
48
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−
221 createUnsignedTxn() {
49 createUnsignedTxn() {
222 const { state, props } = this
50 const { state, props } = this
223
51
@@ -244,19 +72,12 @@ class Transfer extends React.Component {
244 return Maybe.Just(txn)
72 return Maybe.Just(txn)
245 }
73 }
246
74
247−
248−
249 render() {
75 render() {
250 const { props, state } = this
76 const { props, state } = this
251
77
252 const validAddress = isValidAddress(state.receivingAddress)
78 const validAddress = isValidAddress(state.receivingAddress)
253−
254 const canGenerate = validAddress === true
79 const canGenerate = validAddress === true
255
80
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−
260 const esvisible =
81 const esvisible =
261 props.networkType === NETWORK_NAMES.ROPSTEN ||
82 props.networkType === NETWORK_NAMES.ROPSTEN ||
262 props.networkType === NETWORK_NAMES.MAINNET
83 props.networkType === NETWORK_NAMES.MAINNET
@@ -310,46 +131,15 @@ class Transfer extends React.Component {
310 walletType={props.walletType}
131 walletType={props.walletType}
311 walletHdPath={props.walletHdPath}
132 walletHdPath={props.walletHdPath}
312 networkType={props.networkType}
133 networkType={props.networkType}
313− // Tx
134+ setTxnHashCursor={props.setTxnHashCursor}
314− txn={state.txn}
135+ popRoute={props.popRoute}
315− stx={state.stx}
136+ pushRoute={props.pushRoute}
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}
323 // Checks
137 // Checks
324 userApproval={state.userApproval}
138 userApproval={state.userApproval}
325 canGenerate={ canGenerate }
139 canGenerate={ canGenerate }
326− canSign={ canSign }
327− canApprove={ canApprove }
328− canSend={ canSend }
329 // Methods
140 // Methods
330− createUnsignedTxn={this.handleCreateUnsignedTxn}
141+ createUnsignedTxn={this.createUnsignedTxn}
331− setUserApproval={this.handleSetUserApproval}
142+ ref={this.statelessRef} />
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−
353 </Col>
143 </Col>
354 </Row>
144 </Row>
355 )
145 )
src/common/components/AppNavigation.js+5−5
@@ -23,17 +23,17 @@ class AppNavigation extends React.Component {
23 let style = {
23 let style = {
24 position: "fixed",
24 position: "fixed",
25 bottom: "10px",
25 bottom: "10px",
26− right: "10px"
26+ right: "10px",
27+ "text-decoration": "underline",
28+ cursor: "pointer"
27 }
29 }
28
30
29 return (
31 return (
30− <a
32+ <span
31− href="javascript:void(0)"
32− type="button"
33 style={style}
33 style={style}
34 onClick={this.navigate}>
34 onClick={this.navigate}>
35 {buttonText}
35 {buttonText}
36− </a>
36+ </span>
37 )
37 )
38 }
38 }
39}
39}