on 4c8c607 help sign in
bridge/src/views/Release/Transfer.js
170 lines 5.4 kB history raw
1import React, { useCallback, useEffect, useMemo } from 'react';
2import cn from 'classnames';
3import { Grid, Flex } from 'indigo-react';
4import { conditionalSR, linearSR } from 'azimuth-js';
6import * as need from 'lib/need';
7import useEthereumTransaction from 'lib/useEthereumTransaction';
8import { getLockupKind } from 'lib/starRelease';
9import { eqAddr, isZeroAddress } from 'lib/wallet';
11import { useNetwork } from 'store/network';
12import { useWallet } from 'store/wallet';
13import { useHistory } from 'store/history';
14import { useStarReleaseCache } from 'store/starRelease';
16import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
17import CopiableWithTooltip from 'components/CopiableWithTooltip';
18import CopiableAddress from 'components/CopiableAddress';
19import NoticeBox from 'components/NoticeBox';
21import { AddressInput } from 'form/Inputs';
22import BridgeForm from 'form/BridgeForm';
23import FormError from 'form/FormError';
24import { composeValidator, buildAddressValidator } from 'form/validators';
25import { isValidAddress } from 'ethereumjs-util';
27function useTransferLockup(kind) {
28 const { contracts } = useNetwork();
30 return useEthereumTransaction(
31 useCallback(
32 to => {
33 const _contracts = need.contracts(contracts);
34 if (kind === 'conditional') {
35 return conditionalSR.approveCommitmentTransfer(_contracts, to);
36 } else {
37 return linearSR.approveBatchTransfer(_contracts, to);
38 }
39 },
40 [kind, contracts]
41 ),
42 () => {}
43 );
46export default function Transfer({ className, goActive }) {
47 const { wallet } = useWallet();
48 const { contracts } = useNetwork();
49 const { pop } = useHistory();
50 const address = need.addressFromWallet(wallet);
52 const { syncStarReleaseDetails, starReleaseDetails } = useStarReleaseCache();
54 useEffect(() => {
55 syncStarReleaseDetails();
56 }, [syncStarReleaseDetails]);
58 const kind = useMemo(() => {
59 return starReleaseDetails.map(a => a.kind).getOrElse('') === 'conditional'
60 ? 'conditional'
61 : 'linear';
62 }, [starReleaseDetails]);
64 const approvedFor = useMemo(() => {
65 let approved;
66 if (kind === 'conditional') {
67 approved = starReleaseDetails.map(a => a.conditional.approvedTransferTo);
68 } else {
69 approved = starReleaseDetails.map(a => a.linear.approvedTransferTo);
70 }
71 approved = approved.getOrElse('0x0');
72 if (isZeroAddress(approved) || eqAddr(approved, address)) {
73 return false;
74 } else {
75 return approved;
76 }
77 }, [starReleaseDetails, kind, address]);
79 const {
80 bind,
81 construct,
82 unconstruct,
83 inputsLocked,
84 completed,
85 } = useTransferLockup(kind);
87 const onValues = useCallback(
88 async ({ valid, values }) => {
89 if (valid) {
90 construct(values.to);
91 } else {
92 unconstruct();
93 }
94 },
95 [construct, unconstruct]
96 );
98 const validate = useMemo(
99 () =>
100 composeValidator({
101 to: async to => {
102 if (!isValidAddress(to)) {
103 return 'This is not a valid Ethereum address.';
104 }
105 const theirs = await getLockupKind(need.contracts(contracts), to);
106 if (theirs === kind || theirs === 'both') {
107 return `Target address already owns a ${kind} lockup.`;
108 }
109 },
110 }),
111 [kind, contracts]
112 );
114 return (
115 <Grid gap={completed ? 0 : 6} className={cn('mt4', className)} full>
116 <BridgeForm onValues={onValues} validate={validate}>
117 {({ values, form }) => (
118 <>
119 {!completed && (
120 <>
121 <Grid.Item full className="f5">
122 Allow the specified address to take over your {kind} lockup.
123 <br />
124 After they accept the transfer, you will no longer be able to{' '}
125 withdraw from this lockup.
126 </Grid.Item>
127 {approvedFor && (
128 <Grid.Item full as={NoticeBox}>
129 Transfer currently approved for{' '}
130 <CopiableAddress>{approvedFor}</CopiableAddress>
131 </Grid.Item>
132 )}
133 <Grid.Item full className="f5" cols={[1, 13]} as={Flex} col>
134 <Flex.Item
135 name="to"
136 as={AddressInput}
137 label="Approve transfer to"
138 disabled={inputsLocked}
139 />
140 </Grid.Item>
141 </>
142 )}
144 <Grid.Item full as={FormError} />
145 {completed && (
146 <>
147 <Grid.Item full className="pv4">
148 <CopiableAddress>{values.to}</CopiableAddress> is now allowed{' '}
149 to take ownership of this lockup. They can do so by visiting:
150 </Grid.Item>
151 <Grid.Item full as="code" className="f5 mb4 mono wrap">
152 https://bridge.urbit.org/?kind=takeLockup&lock={kind}
153 &from={address}
154 </Grid.Item>
155 <Grid.Divider />
156 </>
157 )}
158 <Grid.Item
159 full
160 as={InlineEthereumTransaction}
161 {...bind}
162 label="Approve Transfer"
163 onReturn={() => pop()}
164 />
165 </>
166 )}
167 </BridgeForm>
168 </Grid>
169 );