on 8286dd7 help sign in
bridge/src/bridge/Bridge.js
362 lines 11 kB history raw
1import { Just, Nothing } from 'folktale/maybe'
2import * as lodash from 'lodash'
3import * as azimuth from 'azimuth-js'
4import { Stack } from 'immutable'
5import { CONTRACT_ADDRESSES } from './lib/contracts'
6import React from 'react'
8import Web3 from 'web3'
9import Footer from './components/Footer'
10import Header from './components/Header'
11import { Container, Row, Col } from './components/Base'
13import { ROUTE_NAMES, router } from './lib/router'
14import { NETWORK_NAMES } from './lib/network'
15import { WALLET_NAMES, DEFAULT_HD_PATH, walletFromMnemonic } from './lib/wallet'
16import { BRIDGE_ERROR } from './lib/error'
18const initWeb3 = (networkType) => {
19 if (networkType === NETWORK_NAMES.MAINNET) {
20 const endpoint =
21 `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`
23 const provider = new Web3.providers.HttpProvider(endpoint)
24 const web3 = new Web3(provider)
25 const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET)
26 return {web3: web3, contracts: contracts}
27 } else if (networkType === NETWORK_NAMES.LOCAL) {
28 const protocol =
29 process.env.NODE_ENV === 'development'
30 ? 'ws'
31 : 'wss'
32 const endpoint = `${protocol}://localhost:8545`
33 const provider = new Web3.providers.WebsocketProvider(endpoint)
34 const web3 = new Web3(provider)
35 const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
36 return {web3: web3, contracts: contracts}
37 }
40class Bridge extends React.Component {
42 constructor(props) {
43 super(props)
45 const networkType =
46 process.env.NODE_ENV === 'development'
47 ? NETWORK_NAMES.LOCAL
48 : NETWORK_NAMES.MAINNET
50 // Sidestepping full network selection to allow for point viewing,
51 // but still respecting the networkType
52 const { web3, contracts } = initWeb3(networkType)
54 // NB (jtobin):
55 //
56 // Note that the 'wallet' prop has type depending on the 'walletType' prop.
57 // These could be bound together in a single structure (so that
58 // 'walletType' acted more explicitly as a data constructor of sorts) but
59 // it doesn't necessarily help us much, since we branch on 'walletType'
60 // before setting 'wallet'.
61 //
62 // Wallets are always wrapped in Maybe. For most authentication
63 // mechanisms, Maybe wraps a BIP32 HD wallet; for Ethereum private keys,
64 // JSON keystore files, and Metamask authentication, it wraps an
65 // 'EthereumWallet'.
67 this.state = {
68 // routes
69 routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
70 routeData: {},
71 // network
72 networkType: networkType,
73 web3: Just(web3),
74 contracts: Just(contracts),
75 // wallet
76 walletType: WALLET_NAMES.MNEMONIC,
77 wallet: Nothing(),
78 walletHdPath: DEFAULT_HD_PATH,
79 // urbit wallet-related
80 urbitWallet: Nothing(),
81 authMnemonic: Nothing(),
82 networkSeedCache: null,
83 // point
84 pointCursor: Nothing(),
85 pointCache: {},
86 // txn
87 txnHashCursor: Nothing(),
88 txnConfirmations: {}
89 }
91 this.pushRoute = this.pushRoute.bind(this)
92 this.popRoute = this.popRoute.bind(this)
93 this.skipRoute = this.skipRoute.bind(this)
94 this.setNetworkType = this.setNetworkType.bind(this)
95 this.setNetwork = this.setNetwork.bind(this)
96 this.setWalletType = this.setWalletType.bind(this)
97 this.setWallet = this.setWallet.bind(this)
98 this.setWalletHdPath = this.setWalletHdPath.bind(this)
99 this.setUrbitWallet = this.setUrbitWallet.bind(this)
100 this.setAuthMnemonic = this.setAuthMnemonic.bind(this)
101 this.setPointCursor = this.setPointCursor.bind(this)
102 this.setTxnHashCursor = this.setTxnHashCursor.bind(this)
103 this.setTxnConfirmations = this.setTxnConfirmations.bind(this)
104 this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this)
105 this.addToPointCache = this.addToPointCache.bind(this)
106 }
108 componentDidMount() {
109 window.history.pushState(null, null, null);
111 window.onpopstate = (e) => {
112 window.history.pushState(null, null, null);
113 this.popRoute();
114 }
116 // NB (jtobin):
117 //
118 // If running in development, the following can be tweaked to get you set
119 // up with a desirable initial state.
121 // if (process.env.NODE_ENV === 'development') {
122 //
123 // const socket = 'ws://localhost:8545'
124 // const provider = new Web3.providers.WebsocketProvider(socket)
125 // const web3 = new Web3(provider)
126 // const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
127 //
128 // const mnemonic = process.env.REACT_APP_DEV_MNEMONIC
129 // const hdpath = process.env.REACT_APP_HD_PATH
130 //
131 // this.setState({
132 // routeCrumbs: Stack([
133 // // ROUTE_NAMES.CREATE_GALAXY,
134 // // ROUTE_NAMES.SHIP,
135 // ROUTE_NAMES.SHIPS,
136 // // ROUTE_NAMES.MNEMONIC,
137 // ROUTE_NAMES.WALLET,
138 // ROUTE_NAMES.NETWORK,
139 // ROUTE_NAMES.LANDING
140 // ]),
141 // networkType: NETWORK_NAMES.LOCAL,
142 // pointCursor: Just(0),
143 // web3: Just(web3),
144 // contracts: Just(contracts),
145 // walletType: WALLET_NAMES.MNEMONIC,
146 // wallet: walletFromMnemonic(mnemonic, hdpath),
147 // urbitWallet: Nothing(),
148 // authMnemonic: Just('benefit crew supreme gesture quantum web media hazard theory mercy wing kitten')
149 // })
150 // }
153 pushRoute(symbol, routeData) {
154 if (lodash.includes(ROUTE_NAMES, symbol)) {
155 this.setState((state, _) => ({
156 routeCrumbs: state.routeCrumbs.push(symbol),
157 routeData: routeData
158 }));
160 // Scroll to top of page with each route transition.
161 window.scrollTo(0, 0);
163 } else {
164 throw BRIDGE_ERROR.INVALID_ROUTE
165 }
166 }
168 skipRoute(len) {
169 this.setState((state, _) => ({
170 routeCrumbs: state.routeCrumbs.skip(len)
171 }))
172 }
174 popRoute() {
175 this.setState((state, _) => ({
176 routeCrumbs: state.routeCrumbs.shift()
177 }))
178 }
180 setNetworkType(symbol) {
181 if (lodash.includes(NETWORK_NAMES, symbol)) {
182 this.setState({
183 networkType: symbol
184 })
185 } else {
186 throw BRIDGE_ERROR.INVALID_NETWORK_TYPE
187 }
188 }
190 setNetworkSeedCache(networkSeed, revision) {
191 this.setState({
192 networkSeedCache: networkSeed,
193 networkRevisionCache: revision
194 })
195 }
197 setNetwork(web3, contracts) {
198 this.setState({
199 web3: web3,
200 contracts: contracts
201 })
202 }
204 setWalletType(symbol) {
205 if (lodash.includes(WALLET_NAMES, symbol)) {
206 this.setState({
207 walletType: symbol
208 })
209 } else {
210 throw BRIDGE_ERROR.INVALID_WALLET_TYPE
211 }
212 }
214 setWallet(wallet) {
215 this.setState({ wallet })
216 }
218 setWalletHdPath(walletHdPath) {
219 this.setState({ walletHdPath })
220 }
222 // also sets wallet to ownership address
223 setUrbitWallet(urbitWallet) {
224 let wallet = this.state.wallet;
225 if (Just.hasInstance(urbitWallet)) {
226 const mnemonic = urbitWallet.value.ownership.seed;
227 wallet = walletFromMnemonic(
228 mnemonic,
229 DEFAULT_HD_PATH,
230 urbitWallet.value.meta.passphrase
231 );
232 }
233 this.setState({ urbitWallet, wallet })
234 }
236 setAuthMnemonic(authMnemonic) {
237 this.setState({ authMnemonic })
238 }
240 setPointCursor(pointCursor) {
241 this.setState({ pointCursor })
242 }
244 addToPointCache(entry) {
245 this.setState((state, _) => ({
246 pointCache: Object.assign(state.pointCache, entry)
247 }))
248 }
250 setTxnHashCursor(txnHashCursor) {
251 this.setState({ txnHashCursor })
252 }
254 setTxnConfirmations(txnHash, txnConfirmations) {
255 this.setState((prevState, _) => {
256 return prevState.routeCrumbs.peek() === ROUTE_NAMES.SENT_TRANSACTION
257 ? {txnConfirmations: { ...prevState.txnConfirmations,
258 [txnHash]: txnConfirmations}}
259 : null
260 })
261 }
263 render() {
264 const {
265 routeCrumbs,
266 routeData,
267 networkType,
268 walletType,
269 walletHdPath,
270 wallet,
271 urbitWallet,
272 authMnemonic,
273 networkSeedCache,
274 networkRevisionCache,
275 pointCursor,
276 pointCache,
277 txnHashCursor,
278 txnConfirmations,
279 web3,
280 contracts
281 } = this.state
283 const View = router(routeCrumbs.peek())
285 // For the invite acceptance flow, widen the screen to use the full
286 // container, and hide the breadcrumbs
287 const colClass = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
288 ? 'col-md-12'
289 : 'col-md-offset-1 col-md-10'
291 const colStyle = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
292 ? {}
293 : {maxWidth: '620px'}
295 const showCrumbs = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
296 ? false
297 : true
299 return (
300 <Container>
301 <Row>
302 <Col className={colClass} style={colStyle}>
303 <Header
304 routeCrumbs={ routeCrumbs }
305 skipRoute={ this.skipRoute }
306 networkType={ networkType }
307 wallet={ wallet }
308 showCrumbs={ showCrumbs }
309 pointCursor={ pointCursor } />
311 <Row className={ 'row wrapper' }>
313 <View
314 // router
315 pushRoute={ this.pushRoute }
316 popRoute={ this.popRoute }
317 routeData={ routeData }
318 // network
319 setNetworkType={ this.setNetworkType }
320 setNetwork={ this.setNetwork }
321 networkType={ networkType }
322 web3={ web3 }
323 contracts={ contracts }
324 // wallet
325 setWalletType={ this.setWalletType }
326 setWalletHdPath={ this.setWalletHdPath }
327 setWallet={ this.setWallet }
328 walletType={ walletType }
329 walletHdPath={ walletHdPath }
330 wallet={ wallet }
331 // urbit wallet
332 urbitWallet={ urbitWallet }
333 setUrbitWallet={ this.setUrbitWallet }
334 authMnemonic={ authMnemonic }
335 setAuthMnemonic={ this.setAuthMnemonic }
336 // point
337 setPointCursor={ this.setPointCursor }
338 addToPointCache={ this.addToPointCache }
339 pointCursor={ pointCursor }
340 pointCache={ pointCache }
341 networkSeedCache= { networkSeedCache }
342 networkRevisionCache={ networkRevisionCache }
343 setNetworkSeedCache= { this.setNetworkSeedCache }
344 // txn
345 setTxnHashCursor={ this.setTxnHashCursor }
346 txnHashCursor={ txnHashCursor }
347 setTxnConfirmations={ this.setTxnConfirmations }
348 txnConfirmations={ txnConfirmations } />
350 <div className={'push'} />
352 </Row>
354 <Footer />
355 </Col>
356 </Row>
357 </Container>
358 )
359 }
362export default Bridge