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