@@ -0,0 +1,203 @@
1+import * as need from 'lib/need';
2+import saveAs from 'file-saver';
3+import ob from 'urbit-ob';
4+import { Just, Nothing } from 'folktale/maybe';
6+import { useCallback, useEffect, useMemo, useState } from 'react';
7+import { usePointCache } from 'store/pointCache';
8+import { usePointCursor } from 'store/pointCursor';
9+import { useWallet } from 'store/wallet';
10+import { deriveNetworkKeys } from 'urbit-key-generation';
11+import { convertToInt } from './convertToInt';
13+ attemptNetworkSeedDerivation,
17+import { generateCode } from './networkCode';
18+import { L1Point } from './types/L1Point';
19+import useCurrentPermissions from './useCurrentPermissions';
20+import { stripSigPrefix } from 'form/formatters';
22+interface useMultikeyFileGeneratorArgs {
30+ * - seed: derived from wallet, mnemonic, or token (if posisble)
31+ * - point: the current pointCursor
33+export default function useMultikeyFileGenerator({
37+}: useMultikeyFileGeneratorArgs) {
38+ const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
39+ const { pointCursor }: any = usePointCursor();
40+ const { syncDetails, getDetails }: any = usePointCache();
41+ const { isOwner, isManagementProxy } = useCurrentPermissions();
43+ const [notice, setNotice] = useState('Deriving networking keys...');
44+ const [downloaded, setDownloaded] = useState(false);
45+ const [generating, setGenerating] = useState(true);
46+ const [keyfile, setKeyfile] = useState<boolean | string>(false);
47+ const [code, setCode] = useState(false);
49+ const _point = useMemo(() => point || need.point(pointCursor), [
53+ const details = getDetails(_point);
54+ const _details: L1Point | null = useMemo(
57+ Just: d => need.details(d),
63+ const currentRevision: number = useMemo(
66+ Just: ({ value }) => convertToInt(value.keyRevisionNumber, 10),
71+ const nextRevision = useMemo(() => currentRevision + 1, [currentRevision]);
73+ const pairFromRevision = useCallback(
74+ async (revision: number) => {
79+ const networkSeed = seed
81+ : await attemptNetworkSeedDerivation({
82+ urbitWallet: seedWallet || urbitWallet,
91+ if (Nothing.hasInstance(networkSeed)) {
92+ setGenerating(false);
94+ 'Custom or nondeterministic networking keys cannot be re-downloaded.'
96+ console.log(`seed is nondeterminable for revision ${revision}`);
100+ const _networkSeed = networkSeed.value;
102+ return deriveNetworkKeys(_networkSeed);
116+ const hasNetworkKeys = currentRevision > 0;
118+ (isOwner || isManagementProxy) && hasNetworkKeys && !!keyfile;
120+ const generate = useCallback(async () => {
121+ if (!hasNetworkKeys) {
122+ setGenerating(false);
123+ setNotice('Network keys not yet set.');
125+ `no networking keys available for revision ${currentRevision}`
130+ const currentPair = await pairFromRevision(currentRevision);
132+ if (!currentPair || !keysMatchChain(currentPair, _details || {})) {
133+ setGenerating(false);
134+ setNotice('Derived networking keys do not match on-chain details.');
135+ console.log(`keys do not match details for revision ${currentRevision}`);
139+ const nextPair = await pairFromRevision(nextRevision);
142+ setCode(generateCode(currentPair));
144+ compileMultiKey(_point, [
146+ revision: currentRevision,
150+ revision: nextRevision,
155+ setGenerating(false);
165+ const filename = useMemo(() => {
166+ return `${stripSigPrefix(ob.patp(_point))}-${currentRevision}.key`;
167+ }, [_point, currentRevision]);
169+ const download = useCallback(() => {
170+ if (typeof keyfile !== 'string') {
175+ new Blob([keyfile], {
176+ type: 'text/plain;charset=utf-8',
180+ setDownloaded(true);
181+ }, [filename, keyfile]);
184+ syncDetails(_point);
185+ }, [_point, syncDetails]);
202+ return { ...output, bind: output };