on master help sign in
Merge pull request #948 from urbit/t/931-reticketing-migrated-spawn-proxy-star
7945ab7tomholford
package-lock.json+3−3
@@ -7775,9 +7775,9 @@
7775 }
7775 }
7776 },
7776 },
7777 "@urbit/roller-api": {
7777 "@urbit/roller-api": {
7778− "version": "0.1.14",
7778+ "version": "0.1.15",
7779− "resolved": "https://registry.npmjs.org/@urbit/roller-api/-/roller-api-0.1.14.tgz",
7779+ "resolved": "https://registry.npmjs.org/@urbit/roller-api/-/roller-api-0.1.15.tgz",
7780− "integrity": "sha512-8I26tia2n6+7IbSl7pVhW7PSaDVFH+QA4Y4RJvgnYpdQDTpry4cXFynvsiTtlU5p97csBd+hXdNub7JDon4LKg==",
7780+ "integrity": "sha512-pP+XtpKOlSdx25+/lHPRtCihLSlVnOXLUk6igA2tCFMdo0cgrH1fCREoT3GlqkwhJQBq+VY08gnyD7Kn58i/Ug==",
7781 "requires": {
7781 "requires": {
7782 "@open-rpc/client-js": "1.7.1",
7782 "@open-rpc/client-js": "1.7.1",
7783 "@open-rpc/meta-schema": "1.14.2",
7783 "@open-rpc/meta-schema": "1.14.2",
package.json+1−1
@@ -32,7 +32,7 @@
32 "@types/styled-components": "^5.1.10",
32 "@types/styled-components": "^5.1.10",
33 "@types/styled-system": "^5.1.11",
33 "@types/styled-system": "^5.1.11",
34 "@types/styled-system__css": "^5.0.15",
34 "@types/styled-system__css": "^5.0.15",
35− "@urbit/roller-api": "^0.1.14",
35+ "@urbit/roller-api": "^0.1.15",
36 "@walletconnect/client": "^1.7.1",
36 "@walletconnect/client": "^1.7.1",
37 "@walletconnect/qrcode-modal": "^1.7.1",
37 "@walletconnect/qrcode-modal": "^1.7.1",
38 "@welldone-software/why-did-you-render": "^3.2.3",
38 "@welldone-software/why-did-you-render": "^3.2.3",
src/lib/constants.ts+4
@@ -219,6 +219,9 @@ const PASSPORT_TOOLTIP =
219const LOCKUP_TOOLTIP =
219const LOCKUP_TOOLTIP =
220 'Stars in a lockup schedule can be withdrawn after a set time duration. The duration is predetermined by the issuer';
220 'Stars in a lockup schedule can be withdrawn after a set time duration. The duration is predetermined by the issuer';
221
221
222+// the initial network key revision is always 1
223+const INITIAL_NETWORK_KEY_REVISION = 1;
224+
222export {
225export {
223 CHECK_BLOCK_EVERY_MS,
226 CHECK_BLOCK_EVERY_MS,
224 DEFAULT_GAS_PRICE_GWEI,
227 DEFAULT_GAS_PRICE_GWEI,
@@ -265,4 +268,5 @@ export {
265 MASTER_TICKET_TOOLTIP,
268 MASTER_TICKET_TOOLTIP,
266 PASSPORT_TOOLTIP,
269 PASSPORT_TOOLTIP,
267 LOCKUP_TOOLTIP,
270 LOCKUP_TOOLTIP,
271+ INITIAL_NETWORK_KEY_REVISION,
268};
272};
src/lib/reticket.js+14−17
@@ -14,13 +14,10 @@ import {
14 sendSignedTransaction,
14 sendSignedTransaction,
15} from './txn';
15} from './txn';
16import getSuggestedGasPrice from './getSuggestedGasPrice';
16import getSuggestedGasPrice from './getSuggestedGasPrice';
17−import { GAS_LIMITS } from './constants';
17+import { GAS_LIMITS, INITIAL_NETWORK_KEY_REVISION } from './constants';
18import { toBN } from 'web3-utils';
18import { toBN } from 'web3-utils';
19import { safeToWei } from './lib';
19import { safeToWei } from './lib';
20−import { isPlanet } from './utils/point';
20+import { isGalaxy, isPlanet } from './utils/point';
21−
22−// the initial network key revision is always 1
23−const INITIAL_NETWORK_KEY_REVISION = 1;
24
21
25export const TRANSACTION_PROGRESS = {
22export const TRANSACTION_PROGRESS = {
26 GENERATING: 0.01,
23 GENERATING: 0.01,
@@ -45,6 +42,7 @@ export async function reticketPointBetweenWallets({
45 nextRevision = INITIAL_NETWORK_KEY_REVISION,
42 nextRevision = INITIAL_NETWORK_KEY_REVISION,
46 txnSigner,
43 txnSigner,
47 txnSender,
44 txnSender,
45+ doSetSpawnProxy = true,
48}) {
46}) {
49 const askForFunding = (address, minBalance, balance) =>
47 const askForFunding = (address, minBalance, balance) =>
50 onUpdate({
48 onUpdate({
@@ -109,8 +107,9 @@ export async function reticketPointBetweenWallets({
109
107
110 // set spawn & voting proxies situationally
108 // set spawn & voting proxies situationally
111
109
110+ // skip this step if reticketing an L1 point with L2-migrated spawn proxy
112 let txs = [transferTmpTx, keysTx, managementTx];
111 let txs = [transferTmpTx, keysTx, managementTx];
113− if (!isPlanet(point)) {
112+ if (!isPlanet(point) && doSetSpawnProxy) {
114 const spawnTx = azimuth.ecliptic.setSpawnProxy(
113 const spawnTx = azimuth.ecliptic.setSpawnProxy(
115 contracts,
114 contracts,
116 point,
115 point,
@@ -118,18 +117,16 @@ export async function reticketPointBetweenWallets({
118 );
117 );
119 spawnTx.gas = GAS_LIMITS.SET_PROXY;
118 spawnTx.gas = GAS_LIMITS.SET_PROXY;
120 txs.push(spawnTx);
119 txs.push(spawnTx);
120+ }
121
121
122− if (
122+ if (isGalaxy(point)) {
123− azimuth.azimuth.getPointSize(point) === azimuth.azimuth.PointSize.Galaxy
124− ) {
125− const votingTx = azimuth.ecliptic.setVotingProxy(
123+ const votingTx = azimuth.ecliptic.setVotingProxy(
126− contracts,
124+ contracts,
127− point,
125+ point,
128− toWallet.voting.keys.address
126+ toWallet.voting.keys.address
129− );
127+ );
130− votingTx.gas = GAS_LIMITS.SET_PROXY;
128+ votingTx.gas = GAS_LIMITS.SET_PROXY;
131− txs.push(votingTx);
129+ txs.push(votingTx);
132− }
133 }
130 }
134
131
135 // transfer configured point to user's new wallet
132 // transfer configured point to user's new wallet
src/lib/types/L2Transaction.ts+13
@@ -40,10 +40,23 @@ export interface TransactionData {
40 method: Function;
40 method: Function;
41}
41}
42
42
43+export interface ReticketProgressCallback {
44+ ({
45+ type,
46+ state,
47+ value,
48+ }: {
49+ type: string;
50+ state: number;
51+ value?: string;
52+ }): void;
53+}
54+
43export interface ReticketParams {
55export interface ReticketParams {
44 point: Ship;
56 point: Ship;
45 to: EthAddress;
57 to: EthAddress;
46 manager: EthAddress;
58 manager: EthAddress;
47 fromWallet: BridgeWallet;
59 fromWallet: BridgeWallet;
48 toWallet: UrbitWallet;
60 toWallet: UrbitWallet;
61+ onUpdate?: ReticketProgressCallback;
49}
62}
src/lib/useReticketL2Spawn.tsnew file+134
@@ -0,0 +1,134 @@
1+import { EthAddress, Ship } from '@urbit/roller-api';
2+import { useCallback } from 'react';
3+import Web3 from 'web3';
4+import { ITxData } from '@walletconnect/types';
5+import { INITIAL_NETWORK_KEY_REVISION } from './constants';
6+import { reticketPointBetweenWallets } from './reticket';
7+import BridgeWallet from './types/BridgeWallet';
8+import { ReticketProgressCallback } from './types/L2Transaction';
9+import useRoller from './useRoller';
10+import { registerProxyAddress } from './utils/roller';
11+import { useWallet } from 'store/wallet';
12+import { useWalletConnect } from './useWalletConnect';
13+
14+interface ReticketL2SpawnParams {
15+ fromWallet: BridgeWallet;
16+ fromWalletType: symbol;
17+ fromWalletHdPath: string;
18+ toWallet: UrbitWallet;
19+ point: Ship;
20+ web3: Web3;
21+ contracts: any; // azimuth contracts
22+ networkType: symbol;
23+ onUpdate?: ReticketProgressCallback;
24+ transferEth?: boolean;
25+ nextRevision: number;
26+ to: EthAddress;
27+ txnSigner?: ({
28+ from,
29+ to,
30+ gas,
31+ gasPrice,
32+ value,
33+ data,
34+ nonce,
35+ }: ITxData) => Promise<string>;
36+ txnSender?: ({
37+ from,
38+ to,
39+ gas,
40+ gasPrice,
41+ value,
42+ data,
43+ nonce,
44+ }: ITxData) => Promise<string>;
45+}
46+
47+/**
48+ * This hook is used to reticket points that are still on L1 but have the
49+ * spawn set to L2. It first performs 3-4 L1 transactions (set keys, set
50+ * management / voting proxy, and transfer).
51+ *
52+ * It then sets the spawn proxy using an L2 transaction.
53+ *
54+ * Under the hood, it wraps two existing functions:
55+ * `reticketPointBetweenWallets` for L1, and `registerProxyAddress` for L2.
56+ *
57+ */
58+export const useReticketL2Spawn = () => {
59+ const { api } = useRoller();
60+ const { walletType }: any = useWallet();
61+ const { connector } = useWalletConnect();
62+
63+ const performL2SpawnReticket = useCallback(
64+ async ({
65+ fromWallet,
66+ fromWalletType,
67+ fromWalletHdPath,
68+ toWallet,
69+ point,
70+ web3,
71+ contracts,
72+ networkType,
73+ onUpdate,
74+ transferEth = false,
75+ nextRevision = INITIAL_NETWORK_KEY_REVISION,
76+ to,
77+ txnSigner,
78+ txnSender,
79+ }: ReticketL2SpawnParams) => {
80+ // call reticketPointBetweenWallets, with isL2Spawn set to true; this will
81+ // skip setting the L1 spawn proxy
82+ await reticketPointBetweenWallets({
83+ fromWallet,
84+ fromWalletType,
85+ fromWalletHdPath,
86+ toWallet,
87+ point,
88+ web3,
89+ contracts,
90+ networkType,
91+ onUpdate,
92+ transferEth,
93+ nextRevision,
94+ txnSigner,
95+ txnSender,
96+ doSetSpawnProxy: false,
97+ });
98+
99+ // instead, set spawn proxy on L2
100+ // In most TX sending cases, we would use the fromWallet to sign and send
101+ // the TX. However, because we are transferring this point during the
102+ // reticket, we will instead sign the final set spawn proxy TX with the
103+ // receiving wallet's private key.
104+
105+ // `registerProxyAddress` expects a toWallet object shaped like a
106+ // BridgeWallet with `address` and `privateKey` properties.
107+ const toWalletStub = {
108+ address: toWallet.ownership.keys.address,
109+ privateKey: Buffer.from(toWallet.ownership.keys.private, 'hex'),
110+ };
111+ const proxy = 'own';
112+ const nonce = await api.getNonce({ ship: point, proxy });
113+
114+ await registerProxyAddress(
115+ api,
116+ toWalletStub,
117+ point,
118+ proxy,
119+ 'spawn',
120+ nonce,
121+ toWallet?.spawn?.keys?.address || to,
122+ walletType,
123+ web3,
124+ connector,
125+ true
126+ );
127+ },
128+ [api, connector, walletType]
129+ );
130+
131+ return {
132+ performL2SpawnReticket,
133+ };
134+};
src/lib/useRoller.ts+13
@@ -51,6 +51,7 @@ import { ddmmmYYYY } from './utils/date';
51import { showNotification } from './utils/notifications';
51import { showNotification } from './utils/notifications';
52import { useWalletConnect } from './useWalletConnect';
52import { useWalletConnect } from './useWalletConnect';
53import { PendingL1Txn } from './types/PendingL1Transaction';
53import { PendingL1Txn } from './types/PendingL1Transaction';
54+import { TRANSACTION_PROGRESS } from './reticket';
54
55
55const ONE_SECOND = 1000;
56const ONE_SECOND = 1000;
56
57
@@ -700,15 +701,20 @@ export default function useRoller() {
700 manager,
701 manager,
701 fromWallet,
702 fromWallet,
702 toWallet,
703 toWallet,
704+ onUpdate,
703 }: ReticketParams) => {
705 }: ReticketParams) => {
704 const azimuthPoint = await api.getPoint(point);
706 const azimuthPoint = await api.getPoint(point);
705 const proxy = 'own';
707 const proxy = 'own';
706 const _web3 = web3.getOrElse(null);
708 const _web3 = web3.getOrElse(null);
707 let nonce = await api.getNonce({ ship: point, proxy });
709 let nonce = await api.getNonce({ ship: point, proxy });
710+ const progress = onUpdate
711+ ? (state: number) => onUpdate({ type: 'progress', state })
712+ : () => {};
708
713
709 let requests = [];
714 let requests = [];
710
715
711 // 1. Update networking keys
716 // 1. Update networking keys
717+ progress(TRANSACTION_PROGRESS.GENERATING);
712 const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10);
718 const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10);
713 const nextRevision = networkRevision + 1;
719 const nextRevision = networkRevision + 1;
714 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
720 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
@@ -731,6 +737,7 @@ export default function useRoller() {
731 requests.push(configureKeysRequest);
737 requests.push(configureKeysRequest);
732
738
733 // 2. Set Management Proxy
739 // 2. Set Management Proxy
740+ progress(TRANSACTION_PROGRESS.SIGNING);
734 const registerMgmtRequest = await registerProxyAddress(
741 const registerMgmtRequest = await registerProxyAddress(
735 api,
742 api,
736 fromWallet,
743 fromWallet,
@@ -747,6 +754,7 @@ export default function useRoller() {
747 requests.push(registerMgmtRequest);
754 requests.push(registerMgmtRequest);
748
755
749 // 3. Set Spawn Proxy
756 // 3. Set Spawn Proxy
757+ progress(TRANSACTION_PROGRESS.FUNDING);
750 if (!isPlanet(Number(point))) {
758 if (!isPlanet(Number(point))) {
751 const registerSpawnRequest = await registerProxyAddress(
759 const registerSpawnRequest = await registerProxyAddress(
752 api,
760 api,
@@ -763,7 +771,9 @@ export default function useRoller() {
763 nonce = nonce + 1;
771 nonce = nonce + 1;
764 requests.push(registerSpawnRequest);
772 requests.push(registerSpawnRequest);
765 }
773 }
774+
766 // 4. Transfer point
775 // 4. Transfer point
776+ progress(TRANSACTION_PROGRESS.TRANSFERRING);
767 const transferTxRequest = await submitL2Transaction({
777 const transferTxRequest = await submitL2Transaction({
768 api,
778 api,
769 wallet: fromWallet,
779 wallet: fromWallet,
@@ -778,7 +788,10 @@ export default function useRoller() {
778 });
788 });
779 requests.push(transferTxRequest);
789 requests.push(transferTxRequest);
780
790
791+ progress(TRANSACTION_PROGRESS.CLEANING);
781 const hashes = await Promise.all(requests);
792 const hashes = await Promise.all(requests);
793+
794+ progress(TRANSACTION_PROGRESS.DONE);
782 return hashes;
795 return hashes;
783 };
796 };
784
797
src/lib/utils/roller.ts+23−11
@@ -93,7 +93,8 @@ export const configureKeys = async (
93 walletType: symbol,
93 walletType: symbol,
94 web3: any,
94 web3: any,
95 connector: WalletConnect | null,
95 connector: WalletConnect | null,
96− breach?: boolean
96+ breach?: boolean,
97+ force: boolean = false
97) => {
98) => {
98 const from = {
99 const from = {
99 ship: point, //ship to configure keys
100 ship: point, //ship to configure keys
@@ -130,7 +131,7 @@ export const configureKeys = async (
130 web3,
131 web3,
131 connector
132 connector
132 );
133 );
133− return api.configureKeys(sig, from, wallet.address, data);
134+ return api.configureKeys(sig, force, from, wallet.address, data);
134};
135};
135
136
136export const transferPointRequest = async (
137export const transferPointRequest = async (
@@ -143,7 +144,8 @@ export const transferPointRequest = async (
143 walletType: symbol,
144 walletType: symbol,
144 web3: any,
145 web3: any,
145 connector: WalletConnect | null,
146 connector: WalletConnect | null,
146− reset?: boolean
147+ reset?: boolean,
148+ force: boolean = false
147) => {
149) => {
148 const from = {
150 const from = {
149 ship: pointToTransfer,
151 ship: pointToTransfer,
@@ -167,7 +169,7 @@ export const transferPointRequest = async (
167 connector
169 connector
168 );
170 );
169
171
170− return api.transferPoint(sig, from, wallet.address, data);
172+ return api.transferPoint(sig, force, from, wallet.address, data);
171};
173};
172
174
173const proxyType = (proxy: Proxy) => {
175const proxyType = (proxy: Proxy) => {
@@ -189,15 +191,16 @@ const setProxy = async (
189 sig: Signature,
191 sig: Signature,
190 from: From,
192 from: From,
191 address: EthAddress,
193 address: EthAddress,
192− data: AddressParams
194+ data: AddressParams,
195+ force: boolean = false
193) => {
196) => {
194 switch (proxyAddressType) {
197 switch (proxyAddressType) {
195 case 'manage':
198 case 'manage':
196− return await api.setManagementProxy(sig, from, address, data);
199+ return await api.setManagementProxy(sig, force, from, address, data);
197 case 'spawn':
200 case 'spawn':
198− return await api.setSpawnProxy(sig, from, address, data);
201+ return await api.setSpawnProxy(sig, force, from, address, data);
199 case 'transfer':
202 case 'transfer':
200− return await api.setTransferProxy(sig, from, address, data);
203+ return await api.setTransferProxy(sig, force, from, address, data);
201 default:
204 default:
202 throw new Error(`Unknown proxyType ${proxyAddressType}`);
205 throw new Error(`Unknown proxyType ${proxyAddressType}`);
203 }
206 }
@@ -205,7 +208,7 @@ const setProxy = async (
205
208
206export const registerProxyAddress = async (
209export const registerProxyAddress = async (
207 api: RollerRPCAPI,
210 api: RollerRPCAPI,
208− wallet: any,
211+ wallet: any, // BridgeWallet
209 _point: Ship,
212 _point: Ship,
210 proxy: string,
213 proxy: string,
211 proxyAddressType: string,
214 proxyAddressType: string,
@@ -213,7 +216,8 @@ export const registerProxyAddress = async (
213 address: string,
216 address: string,
214 walletType: symbol,
217 walletType: symbol,
215 web3: any,
218 web3: any,
216− connector: WalletConnect | null
219+ connector: WalletConnect | null,
220+ force?: boolean
217) => {
221) => {
218 const from = {
222 const from = {
219 ship: _point,
223 ship: _point,
@@ -233,7 +237,15 @@ export const registerProxyAddress = async (
233 connector
237 connector
234 );
238 );
235
239
240+ return setProxy(
241+ api,
236− return setProxy(api, proxyAddressType, sig, from, wallet.address, data);
242+ proxyAddressType,
243+ sig,
244+ from,
245+ wallet.address,
246+ data,
247+ force
248+ );
237};
249};
238
250
239const getDataAndMethod = (args: L2TransactionArgs) => {
251const getDataAndMethod = (args: L2TransactionArgs) => {
src/views/UrbitID/ResetKeys/ResetExecute.js → src/views/UrbitID/ResetKeys/ResetExecute.tsxrenamed+140−77
@@ -7,9 +7,10 @@ import {
7 TRANSACTION_PROGRESS,
7 TRANSACTION_PROGRESS,
8} from 'lib/reticket';
8} from 'lib/reticket';
9import * as need from 'lib/need';
9import * as need from 'lib/need';
10−import { WALLET_TYPES } from 'lib/constants';
10+import { POINT_DOMINIONS, WALLET_TYPES } from 'lib/constants';
11import { convertToInt } from 'lib/convertToInt';
11import { convertToInt } from 'lib/convertToInt';
12import useBlockWindowClose from 'lib/useBlockWindowClose';
12import useBlockWindowClose from 'lib/useBlockWindowClose';
13+import { useReticketL2Spawn } from 'lib/useReticketL2Spawn';
13
14
14import { useNetwork } from 'store/network';
15import { useNetwork } from 'store/network';
15import { useWallet } from 'store/wallet';
16import { useWallet } from 'store/wallet';
@@ -25,8 +26,9 @@ import { RestartButton, ForwardButton } from 'components/Buttons';
25import WarningBox from 'components/WarningBox';
26import WarningBox from 'components/WarningBox';
26import LoadingBar from 'components/LoadingBar';
27import LoadingBar from 'components/LoadingBar';
27import NeedFundsNotice from 'components/NeedFundsNotice';
28import NeedFundsNotice from 'components/NeedFundsNotice';
29+import { ReticketProgressCallback } from 'lib/types/L2Transaction';
28
30
29−const labelForProgress = progress => {
31+const labelForProgress = (progress: number) => {
30 if (progress <= 0) {
32 if (progress <= 0) {
31 return 'Starting...';
33 return 'Starting...';
32 } else if (progress <= TRANSACTION_PROGRESS.GENERATING) {
34 } else if (progress <= TRANSACTION_PROGRESS.GENERATING) {
@@ -44,9 +46,19 @@ const labelForProgress = progress => {
44 }
46 }
45};
47};
46
48
49+interface ResetExecuteProps {
50+ // a Maybe<UrbitWallet>
51+ newWallet: any;
52+ // a useState setter, accepts Maybe<UrbitWallet>
53+ setNewWallet: (args: any) => {};
54+}
55+
47−export default function ResetExecute({ newWallet, setNewWallet }) {
56+export default function ResetExecute({
57+ newWallet,
58+ setNewWallet,
59+}: ResetExecuteProps) {
48− const { popTo, names, reset } = useHistory();
60+ const { popTo, names, reset }: any = useHistory();
49− const { web3, contracts, networkType } = useNetwork();
61+ const { web3, contracts, networkType }: any = useNetwork();
50 const {
62 const {
51 wallet,
63 wallet,
52 setWalletType,
64 setWalletType,
@@ -54,94 +66,34 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
54 setUrbitWallet,
66 setUrbitWallet,
55 walletType,
67 walletType,
56 walletHdPath,
68 walletHdPath,
57− } = useWallet();
69+ }: any = useWallet();
58− const { performL2Reticket } = useRoller();
70+ const { api, performL2Reticket } = useRoller();
59 const {
71 const {
60 point: { isL2 },
72 point: { isL2 },
61 } = useRollerStore();
73 } = useRollerStore();
62− const { pointCursor } = usePointCursor();
74+ const { pointCursor }: any = usePointCursor();
63− const { getDetails } = usePointCache();
75+ const { getDetails }: any = usePointCache();
64 const {
76 const {
65 signTransaction: wcSign,
77 signTransaction: wcSign,
66 sendTransaction: wcSend,
78 sendTransaction: wcSend,
67 connector,
79 connector,
68 } = useWalletConnect();
80 } = useWalletConnect();
81+ const { performL2SpawnReticket } = useReticketL2Spawn();
69
82
70− const [generalError, setGeneralError] = useState();
83+ const [generalError, setGeneralError] = useState<Error | undefined>();
71 const [progress, setProgress] = useState(0);
84 const [progress, setProgress] = useState(0);
72− const [needFunds, setNeedFunds] = useState();
85+ const [needFunds, setNeedFunds] = useState<string | boolean | undefined>();
73 const isDone = progress >= 1.0;
86 const isDone = progress >= 1.0;
87+ const point = need.point(pointCursor);
74
88
75 useBlockWindowClose();
89 useBlockWindowClose();
76
90
77− // start reticketing transactions on mount
78− useEffect(() => {
79− (async () => {
80− // due to react shenanigans we may need to wait for the connector
81− if (
82− walletType === WALLET_TYPES.WALLET_CONNECT &&
83− (!connector || !connector.connected)
84− ) {
85− setGeneralError(new Error('Awaiting WalletConnect connection...'));
86− return;
87− }
88− setGeneralError();
89−
90− const point = need.point(pointCursor);
91− const details = need.details(getDetails(point));
92− const networkRevision = convertToInt(details.keyRevisionNumber, 10);
93−
94− // see also comment in useEthereumTransaction
95− const txnSigner =
96− walletType === WALLET_TYPES.WALLET_CONNECT ? wcSign : undefined;
97− const txnSender =
98− walletType === WALLET_TYPES.WALLET_CONNECT ? wcSend : undefined;
99−
100− try {
101− if (isL2) {
102− console.log(newWallet.value);
103− // FIXME: the useEffect is called twice, which is fine since the duplicate
104− // L2 txs will be discarded
105− //
106− await performL2Reticket({
107− point,
108− to: newWallet.value.wallet.ownership.keys.address,
109− manager: newWallet.value.wallet.management.keys.address,
110− toWallet: newWallet.value.wallet,
111− fromWallet: need.wallet(wallet),
112− });
113− } else
114− await reticketPointBetweenWallets({
115− fromWallet: need.wallet(wallet),
116− fromWalletType: walletType,
117− fromWalletHdPath: walletHdPath,
118− toWallet: newWallet.value.wallet,
119− point: point,
120− web3: need.web3(web3),
121− contracts: need.contracts(contracts),
122− networkType,
123− onUpdate: handleUpdate,
124− nextRevision: networkRevision + 1,
125− txnSigner,
126− txnSender,
127− });
128− } catch (err) {
129− console.error(err);
130− setGeneralError(err);
131− }
132− })();
133− //NOTE this was using useLifecycle previously (which is useEffect without
134− // dependencies) and worked fine, but in the wc case we might need to
135− // wait for the connector to get set due to react state shenanigans.
136− // eslint-disable-next-line react-hooks/exhaustive-deps
137− }, [connector]);
138−
139 const goToRestart = useCallback(() => {
91 const goToRestart = useCallback(() => {
140 reset();
92 reset();
141 setNewWallet(Nothing());
93 setNewWallet(Nothing());
142 }, [reset, setNewWallet]);
94 }, [reset, setNewWallet]);
143
95
144− const loginAndGoHome = useCallback(() => {
96+ const loginAndGoHome = useCallback(async () => {
145 // set wallet state
97 // set wallet state
146 resetWallet();
98 resetWallet();
147 setWalletType(WALLET_TYPES.TICKET);
99 setWalletType(WALLET_TYPES.TICKET);
@@ -149,9 +101,16 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
149 // (implicit) pointCursor stays the same
101 // (implicit) pointCursor stays the same
150 // redirect to point
102 // redirect to point
151 popTo(names.POINT);
103 popTo(names.POINT);
152− }, [resetWallet, setWalletType, setUrbitWallet, newWallet, popTo, names]);
104+ }, [
105+ resetWallet,
106+ setWalletType,
107+ setUrbitWallet,
108+ newWallet.value.wallet,
109+ popTo,
110+ names.POINT,
111+ ]);
153
112
154− const handleUpdate = useCallback(
113+ const handleUpdate: ReticketProgressCallback = useCallback(
155 ({ type, state, value }) => {
114 ({ type, state, value }) => {
156 switch (type) {
115 switch (type) {
157 case 'progress':
116 case 'progress':
@@ -167,6 +126,108 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
167 [setProgress, setNeedFunds]
126 [setProgress, setNeedFunds]
168 );
127 );
169
128
129+ const performReticket = useCallback(async () => {
130+ // due to react shenanigans we may need to wait for the connector
131+ if (
132+ walletType === WALLET_TYPES.WALLET_CONNECT &&
133+ (!connector || !connector.connected)
134+ ) {
135+ setGeneralError(new Error('Awaiting WalletConnect connection...'));
136+ return;
137+ }
138+
139+ setGeneralError(undefined);
140+
141+ const l2point = await api.getPoint(point);
142+ const details = need.details(getDetails(point));
143+ const networkRevision = convertToInt(details.keyRevisionNumber, 10);
144+
145+ // see also comment in useEthereumTransaction
146+ const txnSigner =
147+ walletType === WALLET_TYPES.WALLET_CONNECT ? wcSign : undefined;
148+ const txnSender =
149+ walletType === WALLET_TYPES.WALLET_CONNECT ? wcSend : undefined;
150+
151+ try {
152+ // if L1 point with migrated spawn proxy (dominion = 'spawn')
153+ if (l2point.dominion === POINT_DOMINIONS.SPAWN) {
154+ await performL2SpawnReticket({
155+ fromWallet: need.wallet(wallet),
156+ fromWalletType: walletType,
157+ fromWalletHdPath: walletHdPath,
158+ toWallet: newWallet.value.wallet,
159+ to: newWallet.value.wallet.ownership.keys.address,
160+ point: point,
161+ web3: need.web3(web3),
162+ contracts: need.contracts(contracts),
163+ networkType,
164+ onUpdate: handleUpdate,
165+ nextRevision: networkRevision + 1,
166+ txnSigner,
167+ txnSender,
168+ });
169+ // If L2 point
170+ } else if (isL2) {
171+ await performL2Reticket({
172+ point,
173+ to: newWallet.value.wallet.ownership.keys.address,
174+ manager: newWallet.value.wallet.management.keys.address,
175+ toWallet: newWallet.value.wallet,
176+ fromWallet: need.wallet(wallet),
177+ onUpdate: handleUpdate,
178+ });
179+ // Fallback to L1 point flow
180+ } else {
181+ await reticketPointBetweenWallets({
182+ fromWallet: need.wallet(wallet),
183+ fromWalletType: walletType,
184+ fromWalletHdPath: walletHdPath,
185+ toWallet: newWallet.value.wallet,
186+ point: point,
187+ web3: need.web3(web3),
188+ contracts: need.contracts(contracts),
189+ networkType,
190+ onUpdate: handleUpdate,
191+ nextRevision: networkRevision + 1,
192+ txnSigner,
193+ txnSender,
194+ });
195+ }
196+ } catch (err) {
197+ console.error(err);
198+ setGeneralError(err);
199+ }
200+ }, [
201+ api,
202+ connector,
203+ contracts,
204+ getDetails,
205+ handleUpdate,
206+ isL2,
207+ networkType,
208+ newWallet.value.wallet,
209+ performL2Reticket,
210+ performL2SpawnReticket,
211+ point,
212+ wallet,
213+ walletHdPath,
214+ walletType,
215+ wcSend,
216+ wcSign,
217+ web3,
218+ ]);
219+
220+ useEffect(() => {
221+ if (!connector) {
222+ return;
223+ }
224+
225+ performReticket();
226+ // We want to perform the reticket only once, after the WalletConnect
227+ // connector has finished instantiating and setting up a websocket
228+ // eslint-disable-next-line react-hooks/exhaustive-deps
229+ }, [connector]);
230+
170 const renderAdditionalInfo = () => {
231 const renderAdditionalInfo = () => {
171 if (generalError) {
232 if (generalError) {
172 console.log(generalError);
233 console.log(generalError);
@@ -174,7 +235,9 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
174 return (
235 return (
175 <>
236 <>
176 <Grid.Item full className="mt4">
237 <Grid.Item full className="mt4">
238+ <ErrorText className={''}>
177− <ErrorText>{generalError.message.toString()}</ErrorText>
239+ {generalError.message.toString()}
240+ </ErrorText>
178 </Grid.Item>
241 </Grid.Item>
179 <Grid.Item
242 <Grid.Item
180 full
243 full