@@ -0,0 +1,259 @@
1+//NOTE repurposed from /views/Activate/PassportTransfer,
2+// with a lot of logic from /lib/useInviter
4+//TODO display "you may now _close this window_" cta after calling a flow cb
5+//TODO make sure we also call failure cb if we get closed prematurely
7+import React, { useState, useCallback } from 'react';
8+import { Grid, Text, ErrorText, H4 } from 'indigo-react';
9+import cn from 'classnames';
10+import ob from 'urbit-ob';
11+import { toBN } from 'web3-utils';
13+import { useNetwork } from 'store/network';
14+import { useWallet } from 'store/wallet';
16+import * as wg from 'lib/walletgen';
17+import * as tank from 'lib/tank';
18+import { GAS_LIMITS } from 'lib/constants';
21+ sendSignedTransaction,
22+ waitForTransactionConfirm,
25+import useGasPrice from 'lib/useGasPrice';
26+import useLifecycle from 'lib/useLifecycle';
27+import useBlockWindowClose from 'lib/useBlockWindowClose';
28+import { useFlowCommand } from 'lib/flowCommand';
29+import { hasReceived, sendMail } from 'lib/inviteMail';
30+import { safeToWei } from 'lib/lib';
32+import LoadingBar from 'components/LoadingBar';
33+import { RestartButton } from 'components/Buttons';
34+import NeedFundsNotice from 'components/NeedFundsNotice';
36+import * as az from 'azimuth-js';
38+const labelForProgress = progress => {
40+ return 'Checking sanity...';
41+ } else if (progress <= 0.2) {
42+ return 'Generating invite...';
43+ } else if (progress <= 0.3) {
44+ return 'Building transaction...';
45+ } else if (progress <= 0.4) {
46+ return 'Signing transaction...';
47+ } else if (progress <= 0.5) {
48+ return 'Funding transaction...';
49+ } else if (progress <= 0.7) {
50+ return 'Sending transaction...';
51+ } else if (progress <= 0.9) {
52+ return 'Sending invite email...';
58+export default function JustInvite({ className, resetActivateRouter }) {
59+ const { wallet, walletType, walletHdPath, authToken } = useWallet();
60+ const flow = useFlowCommand();
61+ const { web3, contracts, networkType } = useNetwork();
62+ const { gasPrice } = useGasPrice();
63+ const [generalError, setGeneralError] = useState();
64+ const [progress, setProgress] = useState(0);
65+ const [needFunds, setNeedFunds] = useState(null);
67+ useBlockWindowClose();
72+ // return setProcesss(state);
73+ // case 'askFunding':
74+ // return setNeedFunds(value);
75+ // case 'gotFunding':
76+ // return setNeedFunds(false);
78+ // console.error(`Unknown update: ${type}`);
82+ const sendInvite = useCallback(async () => {
83+ setGeneralError(false);
86+ const _contracts = contracts.getOrElse(null);
87+ const _web3 = web3.getOrElse(null);
88+ const _wallet = wallet.getOrElse(null);
89+ const _authToken = authToken.getOrElse(null);
90+ if (!_contracts || !_web3 || !_wallet || !_authToken) {
91+ // not using need because we want a custom error
92+ throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
95+ const _as = ob.patp2dec(flow.as);
96+ const _ship = ob.patp2dec(flow.ship);
97+ const _email = flow.email;
99+ console.log('xx checking email');
100+ // check if email not invited before
101+ if (await hasReceived(_email)) {
103+ `This email (${_email}) has already received an invite.`
108+ console.log('xx checking owner', _wallet.address, _as);
109+ // check if logged in with flow.as's owner
110+ //TODO we could move this check to the login screen
111+ if (!(await az.azimuth.isOwner(_contracts, _as, _wallet.address))) {
112+ throw new Error(`You are not logged in as ${flow.as}'s owner.'`);
116+ // generate invite code + address
117+ const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
123+ // generate invite tx
124+ const inviteTx = az.delegatedSending.sendPoint(
133+ const signedTx = await signTransaction({
138+ chainId: await _web3.eth.net.getId(),
139+ nonce: await _web3.eth.getTransactionCount(_wallet.address),
141+ gasPrice: gasPrice.toFixed(), // expects string gwei
142+ gasLimit: GAS_LIMITS.GIFT_PLANET.toFixed(),
144+ const rawTx = hexify(signedTx.serialize());
148+ const totalCost = toBN(GAS_LIMITS.GIFT_PLANET).mul(toBN(gasPrice));
149+ const tankWasUsed = await tank.ensureFundsFor(
154+ safeToWei(totalCost, 'gwei'),
156+ (address, minBalance, balance) =>
157+ setNeedFunds({ address, minBalance, balance }),
158+ () => setNeedFunds(undefined)
162+ // send and await transaction
163+ const txHash = await sendSignedTransaction(_web3, signedTx, tankWasUsed);
164+ await waitForTransactionConfirm(_web3, txHash);
167+ // send invite email
168+ //NOTE we could chuck a "pls join group-name" message in here
169+ await sendMail(_email, ticket, flow.as, '', rawTx);
174+ // some generic error
175+ setGeneralError(error);
176+ flow.failure(error);
190+ useLifecycle(() => {
194+ const goToRestart = useCallback(() => {
198+ const renderAdditionalInfo = () => {
200+ console.log(generalError);
202+ typeof generalError === 'string'
204+ : generalError.message || 'Something went wrong!';
207+ <Grid.Item full className="mt8">
208+ <ErrorText>{msg}</ErrorText>
215+ onClick={goToRestart}>
224+ <Grid.Item full as={NeedFundsNotice} className="mt8" {...needFunds} />
228+ // if (progress < 1.0) {
230+ // <Grid.Item full as={WarningBox} className="mt8">
231+ // Never give your Master Ticket to anyone
240+ <Grid className={cn(className, 'auto-rows-min')}>
241+ <Grid.Item full as={H4}>
242+ {labelForProgress(progress)}
245+ <Grid.Item full as={Grid} className="mt3" gap={3}>
246+ <Grid.Item full as={LoadingBar} progress={progress} />
248+ <Text className="f5 green4">
249+ This process can take up to 5 minutes to complete. Don't close
250+ this page until the process is complete.
255+ {renderAdditionalInfo()}