on a64a26c help sign in
bridge/src/views/SetProxy.js
373 lines 9.1 kB history raw
1import Maybe from 'folktale/maybe'
2import React from 'react'
3import * as azimuth from 'azimuth-js'
4import { Row, Col, H1, H3, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
5import { AddressInput, Form, Warning } from '../components/Base'
6import * as ob from 'urbit-ob'
8import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
9import { BRIDGE_ERROR } from '../lib/error'
11import StatelessTransaction from '../components/StatelessTransaction'
13import { ROUTE_NAMES } from '../lib/router'
15import {
16 sendSignedTransaction,
17 fromWei,
18 } from '../lib/txn'
20import {
21 isValidAddress,
22 addressFromSecp256k1Public,
23} from '../lib/wallet'
25const SetManagementProxy = (props) =>
26 <SetProxy
27 { ...props }
28 proxyType={ PROXY_TYPE.MANAGEMENT_PROXY }
29 />
31const SetSpawnProxy = (props) =>
32 <SetProxy
33 { ...props }
34 proxyType={ PROXY_TYPE.SPAWN_PROXY }
35 />
37const SetTransferProxy = (props) =>
38 <SetProxy
39 { ...props }
40 proxyType={ PROXY_TYPE.TRANSFER_PROXY }
41 />
43class SetProxy extends React.Component {
44 constructor(props) {
45 super(props)
47 const issuingPoint = props.pointCursor.matchWith({
48 Just: (shp) => shp.value,
49 Nothing: () => {
50 throw BRIDGE_ERROR.MISSING_POINT
51 }
52 })
54 this.state = {
55 proxyAddress: '',
56 txn: Maybe.Nothing(),
57 txError: Maybe.Nothing(),
58 issuingPoint: issuingPoint,
59 userApproval: false,
60 nonce: '',
61 gasPrice: '5',
62 chainId: '',
63 gasLimit: '600000',
64 stx: Maybe.Nothing(),
65 }
67 this.handleAddressInput = this.handleAddressInput.bind(this)
68 // Transaction
69 this.handleCreateUnsignedTxn = this.handleCreateUnsignedTxn.bind(this)
70 this.handleSubmit = this.handleSubmit.bind(this)
71 this.handleSetUserApproval = this.handleSetUserApproval.bind(this)
72 this.handleSetTxn = this.handleSetTxn.bind(this)
73 this.handleSetStx = this.handleSetStx.bind(this)
74 this.handleSetNonce = this.handleSetNonce.bind(this)
75 this.handleSetChainId = this.handleSetChainId.bind(this)
76 this.handleSetGasPrice = this.handleSetGasPrice.bind(this)
77 this.handleSetGasLimit = this.handleSetGasLimit.bind(this)
78 }
80 componentDidMount() {
81 const { props } = this
83 const addr = props.wallet.matchWith({
84 Just: wal => addressFromSecp256k1Public(wal.value.publicKey),
85 Nothing: () => {
86 throw BRIDGE_ERROR.MISSING_WALLET
87 }
88 })
90 props.web3.matchWith({
91 Nothing: () => {},
92 Just: (w3) => {
93 const validWeb3 = w3.value
95 const getTxMetadata = [
96 validWeb3.eth.getTransactionCount(addr),
97 validWeb3.eth.net.getId(),
98 validWeb3.eth.getGasPrice()
99 ];
101 Promise.all(getTxMetadata).then(r => {
102 const txMetadata = {
103 nonce: r[0],
104 chainId: r[1],
105 gasPrice: fromWei(r[2], 'gwei'),
106 };
108 this.setState({...txMetadata})
110 })
111 }
112 });
114 }
118 handleAddressInput(proxyAddress) {
119 this.setState({ proxyAddress })
120 this.handleClearTxn()
121 }
124 handleCreateUnsignedTxn() {
125 const txn = this.createUnsignedTxn()
126 this.setState({ txn })
127 }
130 handleSetUserApproval(){
131 const {state} = this
132 this.setState({ userApproval: !state.userApproval })
133 }
137 handleSetTxn(txn){
138 this.setState({ txn })
139 }
143 handleSetStx(stx){
144 this.setState({
145 stx,
146 userApproval: false,
147 })
148 }
152 handleSetNonce(nonce){
153 this.setState({ nonce })
154 this.handleClearStx()
155 }
159 handleSetChainId(chainId){
160 this.setState({ chainId })
161 this.handleClearStx()
162 }
166 handleSetGasPrice(gasPrice){
167 this.setState({ gasPrice })
168 this.handleClearStx()
169 }
173 handleSetGasLimit(gasLimit){
174 this.setState({ gasLimit })
175 this.handleClearStx()
176 }
180 handleClearStx() {
181 this.setState({
182 userApproval: false,
183 stx: Maybe.Nothing(),
184 })
185 }
189 handleClearTxn() {
190 this.setState({
191 userApproval: false,
192 txn: Maybe.Nothing(),
193 stx: Maybe.Nothing(),
194 })
195 }
199 handleClearTransaction() {
200 this.setState({
201 userApproval: false,
202 txn: Maybe.Nothing(),
203 stx: Maybe.Nothing(),
204 })
205 }
209 handleSubmit(){
210 const { props, state } = this
211 sendSignedTransaction(props.web3.value, state.stx)
212 .then(sent => {
213 props.setTxnCursor(sent)
214 props.popRoute()
215 props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION)
216 })
217 .catch(err => {
218 // Note that value.value is due to wrapped Maybe.Just + Result.Error
219 this.setState({ txError: Maybe.Just(err.value.value) })
220 })
221 }
224 createUnsignedTxn(proxyAddress) {
225 const { state, props } = this
227 const validContracts = props.contracts.matchWith({
228 Just: (cs) => cs.value,
229 Nothing: () => {
230 throw BRIDGE_ERROR.MISSING_CONTRACTS
231 }
232 })
234 const validPoint = props.pointCursor.matchWith({
235 Just: (shp) => shp.value,
236 Nothing: () => {
237 throw BRIDGE_ERROR.MISSING_POINT
238 }
239 })
241 const txArgs = [
242 validContracts,
243 validPoint,
244 state.proxyAddress
245 ]
247 if (props.proxyType === PROXY_TYPE.TRANSFER_PROXY) {
248 return Maybe.Just(azimuth.ecliptic.setTransferProxy(...txArgs))
249 }
251 if (props.proxyType === PROXY_TYPE.SPAWN_PROXY) {
252 return Maybe.Just(azimuth.ecliptic.setSpawnProxy(...txArgs))
253 }
255 if (props.proxyType === PROXY_TYPE.MANAGEMENT_PROXY) {
256 return Maybe.Just(azimuth.ecliptic.setManagementProxy(...txArgs))
257 }
259 return Maybe.Nothing()
261 }
263 render() {
265 const { props, state } = this
267 const renderedProxyType = renderProxyType(props.proxyType)
269 const validAddress = isValidAddress(state.proxyAddress)
271 const canGenerate = validAddress === true
273 const canSign = !Maybe.Nothing.hasInstance(state.txn)
274 const canApprove = !Maybe.Nothing.hasInstance(state.stx)
275 const canSend = !Maybe.Nothing.hasInstance(state.stx) && state.userApproval === true
278 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
281 return (
282 <Row>
283 <Col>
284 <H1>
285 { `Set ${ucFirst(renderedProxyType)} Proxy For ` }
286 <code>{ `${ob.patp(state.issuingPoint)}` }</code>
287 </H1>
289 <P>
290 {
291 'Please provide an Ethereum address to act as the ' +
292 `${renderedProxyType} proxy. You can also use Wallet ` +
293 'Generator to generate a keypair.'
294 }
295 </P>
296 <Form>
297 <AddressInput
298 className='mono mt-8'
299 prop-size='lg'
300 prop-format='innerLabel'
301 placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
302 value={ state.proxyAddress }
303 onChange={ v => this.handleAddressInput(v) }>
304 <InnerLabel>{ 'Proxy Address' }</InnerLabel>
305 <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
306 </AddressInput>
308 <Anchor
309 className={'mt-1'}
310 prop-size={'sm'}
311 disabled={!isValidAddress(state.proxyAddress)}
312 target={'_blank'}
313 href={`https://etherscan.io/address/${state.proxyAddress}`}>
314 {'View on Etherscan ↗'}
315 </Anchor>
317 <StatelessTransaction
318 // Upper scope
319 web3={props.web3}
320 contracts={props.contracts}
321 wallet={props.wallet}
322 walletType={props.walletType}
323 walletHdPath={props.walletHdPath}
324 // Tx
325 txn={state.txn}
326 stx={state.stx}
327 // Tx details
328 nonce={state.nonce}
329 gasPrice={state.gasPrice}
330 chainId={state.chainId}
331 gasLimit={state.gasLimit}
332 // Checks
333 userApproval={state.userApproval}
334 canGenerate={ canGenerate }
335 canSign={ canSign }
336 canApprove={ canApprove }
337 canSend={ canSend }
338 // Methods
339 createUnsignedTxn={this.handleCreateUnsignedTxn}
340 setUserApproval={this.handleSetUserApproval}
341 setTxn={this.handleSetTxn}
342 setStx={this.handleSetStx}
343 setNonce={this.handleSetNonce}
344 setChainId={this.handleSetChainId}
345 setGasPrice={this.handleSetGasPrice}
346 setGasLimit={this.handleSetGasLimit}
347 handleSubmit={this.handleSubmit} />
349 {
350 Maybe.Nothing.hasInstance(state.txError)
351 ? ''
352 : <Warning className={'mt-8'}>
353 <H3 style={{marginTop: 0, paddingTop: 0}}>
354 {
355 'There was an error sending your transaction.'
356 }
357 </H3>
358 { state.txError.value }
359 </Warning>
360 }
362 </Form>
363 </Col>
364 </Row>
365 )
366 }
369export {
370 SetManagementProxy,
371 SetSpawnProxy,
372 SetTransferProxy