on master help sign in
flows: add flow for accepting star lockup transfer

This lets the previous owner give you a Bridge link to accept the transfer of a star lockup batch. ie bridge.urbit.org/?kind=takeLockup&lock=linear&from=0xabc..def

commit4c65a29641c08afd3c2f5a16a67c501958b13921 authorfang <git@fang.io> dateFri 26 Mar 2021, 15:30 (6 years ago) parente692a23
6 files changed+184−1
src/lib/constants.js+2 src/lib/flowCommand.js+16−1 src/lib/routeNames.js+1 src/lib/router.js+2 src/views/AcceptLockup.js+156 src/views/Login.js+7
src/lib/constants.js+2
@@ -34,6 +34,8 @@ const GAS_LIMITS = {
3434 //
3535 GIFT_PLANET: 450000, //NOTE low sample size, //NOTE also update in gas tank
3636 //
37+ TRANSFER_LOCKUP: 200000, //NOTE no samples
38+ //
3739 SEND_ETH: 21000,
3840 //
3941 DEFAULT: 550000,
src/lib/flowCommand.js+16−1
@@ -1,4 +1,5 @@
11import { useMemo } from 'react';
2+import { isValidAddress } from './wallet';
23
34// looks into the GET parameters and attempts to construct a "flow" object,
45// which would indicate bridge is to be used for one specific flow.
@@ -9,9 +10,13 @@ import { useMemo } from 'react';
910// & ship = ~ship // what planet to send as the invite
1011// & email = ex@amp.le // email to send the invite code to
1112//
13+// ? kind = takeLockup
14+// & lock = linear | conditional // which lockup contract, defaults to linear
15+// & from = 0x1234abcd // ethereum address to accept transfer from
16+//
1217
1318const COMMANDS = {
14− //
19+ TAKE_LOCKUP: 'takeLockup',
1520};
1621
1722const useFlowCommand = () => {
@@ -29,6 +34,16 @@ const useFlowCommand = () => {
2934
3035 if (typeof flow === 'object') {
3136 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+ //
3247 default:
3348 console.log('unrecognized kind of flow:', flow.kind);
3449 // eslint-disable-next-line no-fallthrough
src/lib/routeNames.js+1
@@ -23,6 +23,7 @@ export const ROUTE_NAMES = {
2323 PARTY_SET_POOL_SIZE: Symbol('PARTY_SET_POOL_SIZE'),
2424 INVITE_COHORT: Symbol('INVITE_COHORT'),
2525 STAR_RELEASE: Symbol('STAR_RELEASE'),
26+ ACCEPT_LOCKUP: Symbol('ACCEPT_LOCKUP'),
2627 URBIT_OS: Symbol('URBIT_OS'),
2728 URBIT_ID: Symbol('URBIT_ID'),
2829 RESIDENTS: Symbol('RESIDENTS'),
src/lib/router.js+2
@@ -5,6 +5,7 @@ import Senate from 'views/Senate';
55import CreateGalaxy from 'views/CreateGalaxy';
66import IssueChild from 'views/IssueChild';
77import StarRelease from 'views/Release';
8+import AcceptLockup from 'views/AcceptLockup';
89
910import Point from 'views/Point';
1011import Points from 'views/Points';
@@ -38,6 +39,7 @@ export const ROUTES = {
3839 [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
3940 [ROUTE_NAMES.INVITE_COHORT]: InviteCohort,
4041 [ROUTE_NAMES.STAR_RELEASE]: StarRelease,
42+ [ROUTE_NAMES.ACCEPT_LOCKUP]: AcceptLockup,
4143 [ROUTE_NAMES.URBIT_OS]: UrbitOS,
4244 [ROUTE_NAMES.URBIT_ID]: UrbitID,
4345 [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() {
6161 push(names.POINTS);
6262 } else {
6363 switch (flow.kind) {
64+ case COMMANDS.TAKE_LOCKUP:
65+ push(names.ACCEPT_LOCKUP);
66+ break;
67+ //
6468 default:
6569 throw new Error('unimplemented flow ' + flow.kind);
6670 }
@@ -69,6 +73,9 @@ export default function Login() {
6973
7074 const flowDescription = command => {
7175 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.</>;
7279 default:
7380 return <>Flow: {command.kind}</>;
7481 }