on master help sign in
flows: add flow for accepting star lockup transfer
4c65a29fang
src/lib/constants.js+2
@@ -34,6 +34,8 @@ const GAS_LIMITS = {
34 //
34 //
35 GIFT_PLANET: 450000, //NOTE low sample size, //NOTE also update in gas tank
35 GIFT_PLANET: 450000, //NOTE low sample size, //NOTE also update in gas tank
36 //
36 //
37+ TRANSFER_LOCKUP: 200000, //NOTE no samples
38+ //
37 SEND_ETH: 21000,
39 SEND_ETH: 21000,
38 //
40 //
39 DEFAULT: 550000,
41 DEFAULT: 550000,
src/lib/flowCommand.js+16−1
@@ -1,4 +1,5 @@
1import { useMemo } from 'react';
1import { useMemo } from 'react';
2+import { isValidAddress } from './wallet';
2
3
3// looks into the GET parameters and attempts to construct a "flow" object,
4// looks into the GET parameters and attempts to construct a "flow" object,
4// which would indicate bridge is to be used for one specific flow.
5// which would indicate bridge is to be used for one specific flow.
@@ -9,9 +10,13 @@ import { useMemo } from 'react';
9// & ship = ~ship // what planet to send as the invite
10// & ship = ~ship // what planet to send as the invite
10// & email = ex@amp.le // email to send the invite code to
11// & email = ex@amp.le // email to send the invite code to
11//
12//
13+// ? kind = takeLockup
14+// & lock = linear | conditional // which lockup contract, defaults to linear
15+// & from = 0x1234abcd // ethereum address to accept transfer from
16+//
12
17
13const COMMANDS = {
18const COMMANDS = {
14− //
19+ TAKE_LOCKUP: 'takeLockup',
15};
20};
16
21
17const useFlowCommand = () => {
22const useFlowCommand = () => {
@@ -29,6 +34,16 @@ const useFlowCommand = () => {
29
34
30 if (typeof flow === 'object') {
35 if (typeof flow === 'object') {
31 switch (flow.kind) {
36 switch (flow.kind) {
37+ case COMMANDS.TAKE_LOCKUP:
38+ flow.lock = flow.lock || 'linear';
39+ if (
40+ (flow.lock !== 'linear' && flow.lock !== 'conditional') ||
41+ !isValidAddress(flow.from)
42+ ) {
43+ flow = null;
44+ }
45+ break;
46+ //
32 default:
47 default:
33 console.log('unrecognized kind of flow:', flow.kind);
48 console.log('unrecognized kind of flow:', flow.kind);
34 // eslint-disable-next-line no-fallthrough
49 // eslint-disable-next-line no-fallthrough
src/lib/routeNames.js+1
@@ -23,6 +23,7 @@ export const ROUTE_NAMES = {
23 PARTY_SET_POOL_SIZE: Symbol('PARTY_SET_POOL_SIZE'),
23 PARTY_SET_POOL_SIZE: Symbol('PARTY_SET_POOL_SIZE'),
24 INVITE_COHORT: Symbol('INVITE_COHORT'),
24 INVITE_COHORT: Symbol('INVITE_COHORT'),
25 STAR_RELEASE: Symbol('STAR_RELEASE'),
25 STAR_RELEASE: Symbol('STAR_RELEASE'),
26+ ACCEPT_LOCKUP: Symbol('ACCEPT_LOCKUP'),
26 URBIT_OS: Symbol('URBIT_OS'),
27 URBIT_OS: Symbol('URBIT_OS'),
27 URBIT_ID: Symbol('URBIT_ID'),
28 URBIT_ID: Symbol('URBIT_ID'),
28 RESIDENTS: Symbol('RESIDENTS'),
29 RESIDENTS: Symbol('RESIDENTS'),
src/lib/router.js+2
@@ -5,6 +5,7 @@ import Senate from 'views/Senate';
5import CreateGalaxy from 'views/CreateGalaxy';
5import CreateGalaxy from 'views/CreateGalaxy';
6import IssueChild from 'views/IssueChild';
6import IssueChild from 'views/IssueChild';
7import StarRelease from 'views/Release';
7import StarRelease from 'views/Release';
8+import AcceptLockup from 'views/AcceptLockup';
8
9
9import Point from 'views/Point';
10import Point from 'views/Point';
10import Points from 'views/Points';
11import Points from 'views/Points';
@@ -38,6 +39,7 @@ export const ROUTES = {
38 [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
39 [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
39 [ROUTE_NAMES.INVITE_COHORT]: InviteCohort,
40 [ROUTE_NAMES.INVITE_COHORT]: InviteCohort,
40 [ROUTE_NAMES.STAR_RELEASE]: StarRelease,
41 [ROUTE_NAMES.STAR_RELEASE]: StarRelease,
42+ [ROUTE_NAMES.ACCEPT_LOCKUP]: AcceptLockup,
41 [ROUTE_NAMES.URBIT_OS]: UrbitOS,
43 [ROUTE_NAMES.URBIT_OS]: UrbitOS,
42 [ROUTE_NAMES.URBIT_ID]: UrbitID,
44 [ROUTE_NAMES.URBIT_ID]: UrbitID,
43 [ROUTE_NAMES.RESIDENTS]: Residents,
45 [ROUTE_NAMES.RESIDENTS]: Residents,
src/views/AcceptLockup.jsnew file+156
@@ -0,0 +1,156 @@
1+import React, { useCallback, useMemo } from 'react';
2+import cn from 'classnames';
3+import { Grid, Text } from 'indigo-react';
4+import * as azimuth from 'azimuth-js';
5+import { memoize } from 'lodash';
6+
7+import { useNetwork } from 'store/network';
8+import { useWallet } from 'store/wallet';
9+import { useHistory } from 'store/history';
10+
11+import * as need from 'lib/need';
12+import useEthereumTransaction from 'lib/useEthereumTransaction';
13+import { GAS_LIMITS } from 'lib/constants';
14+import { useLocalRouter } from 'lib/LocalRouter';
15+import { useFlowCommand } from 'lib/flowCommand';
16+import { isValidAddress, eqAddr } from 'lib/wallet';
17+
18+import ViewHeader from 'components/ViewHeader';
19+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
20+import View from 'components/View';
21+
22+import BridgeForm from 'form/BridgeForm';
23+import { AddressInput } from 'form/Inputs';
24+import { composeValidator } from 'form/validators';
25+
26+function useAcceptLockup() {
27+ const { contracts } = useNetwork();
28+ const { push, names } = useHistory();
29+
30+ const flow = useFlowCommand();
31+
32+ const _contracts = need.contracts(contracts);
33+
34+ const transaction = useEthereumTransaction(
35+ useCallback(
36+ from => {
37+ if (flow.lock === 'conditional') {
38+ return azimuth.conditionalSR.transferCommitment(_contracts, from);
39+ } else {
40+ return azimuth.linearSR.transferBatch(_contracts, from);
41+ }
42+ },
43+ [_contracts, flow]
44+ ),
45+ useCallback(() => push(names.STAR_RELEASE), [push, names]),
46+ GAS_LIMITS.TRANSFER_LOCKUP
47+ );
48+
49+ return {
50+ ...transaction,
51+ };
52+}
53+
54+export default function AcceptLockup() {
55+ const { pop } = useLocalRouter();
56+ const { contracts } = useNetwork();
57+ const { wallet } = useWallet();
58+ const flow = useFlowCommand();
59+
60+ const _contracts = need.contracts(contracts);
61+ const _address = need.addressFromWallet(wallet);
62+
63+ const {
64+ completed,
65+ bind,
66+ inputsLocked,
67+ construct,
68+ unconstruct,
69+ } = useAcceptLockup();
70+
71+ const initialValues = useMemo(() => {
72+ if (!flow.from) return {};
73+ return { from: flow.from };
74+ }, [flow]);
75+
76+ const getApproved = useMemo(() => {
77+ return memoize(async from => {
78+ console.log('------ looking up');
79+ const lok = await (flow.lock === 'conditional'
80+ ? azimuth.conditionalSR.getCommitment(_contracts, from)
81+ : azimuth.linearSR.getBatch(_contracts, from));
82+ return lok.approvedTransferTo;
83+ });
84+ }, [flow, _contracts]);
85+
86+ const validate = useMemo(
87+ () =>
88+ composeValidator({
89+ from: async from => {
90+ if (!isValidAddress(from)) {
91+ return 'This is not a valid Ethereum address.';
92+ }
93+ const approved = await getApproved(from);
94+ if (!eqAddr(_address, approved)) {
95+ return 'Not permitted to take lockup from this address.';
96+ }
97+ },
98+ }),
99+ [_address, getApproved]
100+ );
101+
102+ const onValues = useCallback(
103+ ({ valid, values, form }) => {
104+ if (valid) {
105+ construct(values.from);
106+ } else {
107+ unconstruct();
108+ }
109+ },
110+ [construct, unconstruct]
111+ );
112+
113+ return (
114+ <View pop={pop} inset>
115+ <Grid>
116+ <Grid.Item full as={ViewHeader}>
117+ Accept Star Lockup
118+ </Grid.Item>
119+
120+ <Grid.Item
121+ full
122+ as={Text}
123+ className={cn('f5 wrap', {
124+ green3: completed,
125+ })}>
126+ {completed
127+ ? `You have accepted the star lockup.`
128+ : `Accept the incoming transfer of a star lockup.`}
129+ </Grid.Item>
130+
131+ <BridgeForm
132+ validate={validate}
133+ initialValues={initialValues}
134+ onValues={onValues}>
135+ {() => (
136+ <Grid.Item
137+ full
138+ as={AddressInput}
139+ name="from"
140+ label="Previous owner of the star lockup"
141+ disabled={inputsLocked}
142+ className="mv4"
143+ />
144+ )}
145+ </BridgeForm>
146+
147+ <Grid.Item
148+ full
149+ as={InlineEthereumTransaction}
150+ {...bind}
151+ onReturn={() => pop()}
152+ />
153+ </Grid>
154+ </View>
155+ );
156+}
src/views/Login.js+7
@@ -61,6 +61,10 @@ export default function Login() {
61 push(names.POINTS);
61 push(names.POINTS);
62 } else {
62 } else {
63 switch (flow.kind) {
63 switch (flow.kind) {
64+ case COMMANDS.TAKE_LOCKUP:
65+ push(names.ACCEPT_LOCKUP);
66+ break;
67+ //
64 default:
68 default:
65 throw new Error('unimplemented flow ' + flow.kind);
69 throw new Error('unimplemented flow ' + flow.kind);
66 }
70 }
@@ -69,6 +73,9 @@ export default function Login() {
69
73
70 const flowDescription = command => {
74 const flowDescription = command => {
71 switch (command.kind) {
75 switch (command.kind) {
76+ case COMMANDS.TAKE_LOCKUP:
77+ //TODO kind of want "sign in as 0xabc.." here...
78+ return <>To accept a star lockup batch, please sign in.</>;
72 default:
79 default:
73 return <>Flow: {command.kind}</>;
80 return <>Flow: {command.kind}</>;
74 }
81 }