@@ -0,0 +1,170 @@
1+import React, { useCallback, useEffect, useMemo } from 'react';
2+import cn from 'classnames';
3+import { Grid, Flex } from 'indigo-react';
4+import { conditionalSR, linearSR } from 'azimuth-js';
6+import * as need from 'lib/need';
7+import useEthereumTransaction from 'lib/useEthereumTransaction';
8+import { getLockupKind } from 'lib/starRelease';
9+import { eqAddr, isZeroAddress } from 'lib/wallet';
11+import { useNetwork } from 'store/network';
12+import { useWallet } from 'store/wallet';
13+import { useHistory } from 'store/history';
14+import { useStarReleaseCache } from 'store/starRelease';
16+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
17+import CopiableWithTooltip from 'components/CopiableWithTooltip';
18+import CopiableAddress from 'components/CopiableAddress';
19+import NoticeBox from 'components/NoticeBox';
21+import { AddressInput } from 'form/Inputs';
22+import BridgeForm from 'form/BridgeForm';
23+import FormError from 'form/FormError';
24+import { composeValidator, buildAddressValidator } from 'form/validators';
25+import { isValidAddress } from 'ethereumjs-util';
27+function useTransferLockup(kind) {
28+ const { contracts } = useNetwork();
30+ return useEthereumTransaction(
33+ const _contracts = need.contracts(contracts);
34+ if (kind === 'conditional') {
35+ return conditionalSR.approveCommitmentTransfer(_contracts, to);
37+ return linearSR.approveBatchTransfer(_contracts, to);
46+export 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();
55+ syncStarReleaseDetails();
56+ }, [syncStarReleaseDetails]);
58+ const kind = useMemo(() => {
59+ return starReleaseDetails.map(a => a.kind).getOrElse('') === 'conditional'
62+ }, [starReleaseDetails]);
64+ const approvedFor = useMemo(() => {
66+ if (kind === 'conditional') {
67+ approved = starReleaseDetails.map(a => a.conditional.approvedTransferTo);
69+ approved = starReleaseDetails.map(a => a.linear.approvedTransferTo);
71+ approved = approved.getOrElse('0x0');
72+ if (isZeroAddress(approved) || eqAddr(approved, address)) {
77+ }, [starReleaseDetails, kind, address]);
85+ } = useTransferLockup(kind);
87+ const onValues = useCallback(
88+ async ({ valid, values }) => {
90+ construct(values.to);
95+ [construct, unconstruct]
98+ const validate = useMemo(
102+ if (!isValidAddress(to)) {
103+ return 'This is not a valid Ethereum address.';
105+ const theirs = await getLockupKind(need.contracts(contracts), to);
106+ if (theirs === kind || theirs === 'both') {
107+ return `Target address already owns a ${kind} lockup.`;
115+ <Grid gap={completed ? 0 : 6} className={cn('mt4', className)} full>
116+ <BridgeForm onValues={onValues} validate={validate}>
117+ {({ values, form }) => (
121+ <Grid.Item full className="f5">
122+ Allow the specified address to take over your {kind} lockup.
124+ After they accept the transfer, you will no longer be able to{' '}
125+ withdraw from this lockup.
128+ <Grid.Item full as={NoticeBox}>
129+ Transfer currently approved for{' '}
130+ <CopiableAddress>{approvedFor}</CopiableAddress>
133+ <Grid.Item full className="f5" cols={[1, 13]} as={Flex} col>
137+ label="Approve transfer to"
138+ disabled={inputsLocked}
144+ <Grid.Item full as={FormError} />
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:
151+ <Grid.Item full as="code" className="f5 mb4 mono wrap">
152+ https://bridge.urbit.org/?kind=takeLockup&lock={kind}
160+ as={InlineEthereumTransaction}
162+ label="Approve Transfer"
163+ onReturn={() => pop()}