@@ -0,0 +1,233 @@
1+import React, { useCallback, useMemo } from 'react';
2+import cn from 'classnames';
3+import { Grid, Text, Flex, ToggleInput } from 'indigo-react';
4+import * as azimuth from 'azimuth-js';
5+
6+import { useNetwork } from 'store/network';
7+import { usePointCache } from 'store/pointCache';
8+import { usePointCursor } from 'store/pointCursor';
9+
10+import {
11+ PROXY_TYPE,
12+ proxyTypeToHuman,
13+ proxyTypeToHumanDescription,
14+} from 'lib/proxy';
15+import * as need from 'lib/need';
16+import { useLocalRouter } from 'lib/LocalRouter';
17+import { ETH_ZERO_ADDR, eqAddr, isZeroAddress } from 'lib/wallet';
18+import capitalize from 'lib/capitalize';
19+import useEthereumTransaction from 'lib/useEthereumTransaction';
20+
21+import ViewHeader from 'components/ViewHeader';
22+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
23+import { GAS_LIMITS } from 'lib/constants';
24+import { AddressInput } from 'form/Inputs';
25+import {
26+ composeValidator,
27+ buildCheckboxValidator,
28+ buildAddressValidator,
29+} from 'form/validators';
30+import BridgeForm from 'form/BridgeForm';
31+import FormError from 'form/FormError';
32+import CopiableAddress from 'components/CopiableAddress';
33+
34+const proxyFromDetails = (details, contracts, proxyType) => {
35+ switch (proxyType) {
36+ case PROXY_TYPE.MANAGEMENT:
37+ return details.managementProxy;
38+ case PROXY_TYPE.SPAWN:
39+ return details.spawnProxy;
40+ case PROXY_TYPE.TRANSFER:
41+ return details.transferProxy;
42+ case PROXY_TYPE.VOTING:
43+ if (eqAddr(details.votingProxy, contracts.delegatedSending.address)) {
44+ return `${details.votingProxy} (invites contract)`;
45+ } else {
46+ return details.votingProxy;
47+ }
48+ default:
49+ throw new Error(`Unknown proxyType: ${proxyType}`);
50+ }
51+};
52+
53+function useSetProxy(proxyType) {
54+ const { contracts } = useNetwork();
55+ const { pointCursor } = usePointCursor();
56+ const { syncDetails } = usePointCache();
57+
58+ const _contracts = need.contracts(contracts);
59+ const _point = need.point(pointCursor);
60+
61+ const { construct, ...rest } = useEthereumTransaction(
62+ useCallback(
63+ address => {
64+ const txArgs = [_contracts, _point, address];
65+
66+ switch (proxyType) {
67+ case PROXY_TYPE.MANAGEMENT:
68+ return azimuth.ecliptic.setManagementProxy(...txArgs);
69+ case PROXY_TYPE.SPAWN:
70+ return azimuth.ecliptic.setSpawnProxy(...txArgs);
71+ case PROXY_TYPE.TRANSFER:
72+ return azimuth.ecliptic.setTransferProxy(...txArgs);
73+ case PROXY_TYPE.VOTING:
74+ return azimuth.ecliptic.setVotingProxy(...txArgs);
75+ default:
76+ throw new Error(`Unknown proxyType ${proxyType}`);
77+ }
78+ },
79+ [_contracts, _point, proxyType]
80+ ),
81+ useCallback(() => syncDetails(_point), [_point, syncDetails]),
82+ GAS_LIMITS.SET_PROXY
83+ );
84+
85+ // force-unset
86+ const unset = useCallback(() => {
87+ construct(ETH_ZERO_ADDR);
88+ }, [construct]);
89+
90+ return {
91+ construct,
92+ unset,
93+ ...rest,
94+ };
95+}
96+
97+export default function SetProxy() {
98+ const { data, pop } = useLocalRouter();
99+ const { getDetails } = usePointCache();
100+ const { pointCursor } = usePointCursor();
101+ const { contracts } = useNetwork();
102+
103+ const _point = need.point(pointCursor);
104+ const _details = need.details(getDetails(_point));
105+ const _contracts = need.contracts(contracts);
106+
107+ const properProxyType = capitalize(proxyTypeToHuman(data.proxyType));
108+
109+ const {
110+ construct,
111+ unconstruct,
112+ unset,
113+ inputsLocked,
114+ completed,
115+ bind,
116+ } = useSetProxy(data.proxyType);
117+
118+ const validateForm = useCallback((values, errors) => {
119+ if (!values.unset && errors.address) {
120+ return errors;
121+ }
122+ }, []);
123+
124+ const validate = useMemo(
125+ () =>
126+ composeValidator(
127+ {
128+ unset: buildCheckboxValidator(),
129+ address: buildAddressValidator(),
130+ },
131+ validateForm
132+ ),
133+ [validateForm]
134+ );
135+
136+ const onValues = useCallback(
137+ ({ valid, values, form }) => {
138+ if (valid) {
139+ if (values.unset) {
140+ unset();
141+ form.change('address', '');
142+ } else {
143+ construct(values.address);
144+ }
145+ } else {
146+ unconstruct();
147+ }
148+ },
149+ [construct, unconstruct, unset]
150+ );
151+
152+ const initialValues = useMemo(() => ({ unset: false }), []);
153+
154+ const proxyAddress = proxyFromDetails(_details, _contracts, data.proxyType);
155+ const isProxySet = !isZeroAddress(proxyAddress);
156+
157+ const proxyAddressLabel = useMemo(() => `Current ${properProxyType} Key`, [
158+ properProxyType,
159+ ]);
160+
161+ const header = useMemo(() => {
162+ const pfix = completed ? 'New' : 'Edit';
163+ return `${pfix} ${properProxyType} Key`;
164+ }, [completed, properProxyType]);
165+
166+ return (
167+ <Grid>
168+ <Grid.Item full className="f5 mb2">
169+ {header}
170+ </Grid.Item>
171+
172+ {!completed && (
173+ <>
174+ <Grid.Item full as={Text} className="mb4 f5 gray4">
175+ {proxyTypeToHumanDescription(data.proxyType)}
176+ </Grid.Item>
177+ <Grid.Item
178+ full
179+ as={Text}
180+ className={cn('f6', {
181+ green3: completed,
182+ })}>
183+ Current
184+ </Grid.Item>
185+ </>
186+ )}
187+
188+ <BridgeForm
189+ validate={validate}
190+ onValues={onValues}
191+ initialValues={initialValues}>
192+ {({ handleSubmit, values }) => (
193+ <>
194+ <Grid.Item full as={Flex} row justify="between" align="center">
195+ <Flex.Item
196+ flex
197+ as={Text}
198+ className={cn('mono mv3 gray4', {
199+ black: !completed && isProxySet,
200+ gray4: !completed && !isProxySet,
201+ green3: completed,
202+ })}>
203+ {isProxySet ? proxyAddress : 'Unset'}
204+ </Flex.Item>
205+ </Grid.Item>
206+
207+ {completed ? (
208+ <Grid.Item full className="mb4" />
209+ ) : (
210+ <Grid.Item
211+ full
212+ as={AddressInput}
213+ className="mv4"
214+ name="address"
215+ label={`New ${properProxyType} Address`}
216+ disabled={inputsLocked || values.unset}
217+ />
218+ )}
219+
220+ <Grid.Item full as={FormError} />
221+
222+ <Grid.Item
223+ full
224+ as={InlineEthereumTransaction}
225+ {...bind}
226+ onReturn={() => pop()}
227+ />
228+ </>
229+ )}
230+ </BridgeForm>
231+ </Grid>
232+ );
233+}