1import { SpawnedPoints, UnspawnedPoints } from '@urbit/roller-api';
2import { POINT_DOMINIONS } from 'lib/constants';
3import { deriveNetworkSeedFromUrbitWallet } from 'lib/keys';
4import * as need from 'lib/need';
5import * as azimuth from 'azimuth-js';
6import { Invite, Invites } from 'lib/types/Invite';
7import useRoller from 'lib/useRoller';
8import { useWalletConnect } from 'lib/useWalletConnect';
9import { showNotification } from 'lib/utils/notifications';
15} from 'lib/utils/roller';
16import { useCallback, useRef } from 'react';
17import { useNetwork } from 'store/network';
18import { usePointCursor } from 'store/pointCursor';
19import { useRollerStore } from 'store/rollerStore';
24} from 'store/storage/roller';
25import { useWallet } from 'store/wallet';
26import { usePointCache } from 'store/pointCache';
27import create from 'zustand';
28import { generateCsvLine } from 'lib/utils/invite';
29import Point from 'lib/types/Point';
31const inviteTemplate = (
43export type InviteGeneratingStatus =
50 generatingNum: number;
51 status: InviteGeneratingStatus;
54interface InviteStore {
57 [point: number]: InviteJob;
59 addInvite: (point: number, invite: Invite) => void;
60 setInvites: (points: number, invites: Invite[]) => void;
61 updateJob: (point: number, updates: Partial<InviteJob>) => void;
64export const useInviteStore = create<InviteStore>((set, get) => ({
67 addInvite: (point: number, invite: Invite) => {
69 const newInvites: Invites = {};
70 const invites = state.invites[point];
72 if (!invites.find(inv => inv.planet === invite.planet)) {
73 newInvites[point] = [...invites, invite];
75 return { invites: Object.assign(state.invites, newInvites) };
78 setInvites: (point: number, invites: Invite[]) =>
80 const newInvites: Invites = {};
81 newInvites[point] = invites;
82 return { invites: Object.assign(state.invites, newInvites) };
84 updateJob: (point: number, updates: Partial<InviteJob>) => {
85 const jobs = get().inviteJobs;
86 const job = jobs[point] || { status: 'initial', generatingNum: 0 };
87 const newJob = Object.assign({}, job, updates);
90 set({ inviteJobs: jobs });
94export function useInvites() {
95 const { wallet, authToken, walletType }: any = useWallet();
96 const { connector } = useWalletConnect();
97 const { pointCursor }: any = usePointCursor();
98 const { web3, contracts }: any = useNetwork();
99 const allPoints: any = usePointCache();
100 const getDetails = allPoints?.getDetails;
101 const { api, ls, performL2Reticket } = useRoller();
102 const { point, pointList, setPendingTransactions } = useRollerStore();
109 } = useInviteStore();
111 const generateInviteInfo = async (planet: number, _authToken: string) => {
112 const { ticket, inviteWallet } = await generateInviteWallet(
117 return inviteTemplate(planet, ticket, inviteWallet.ownership.keys.address);
120 const getInvites = useCallback(async () => {
121 const inviteJob = inviteJobs[point.value];
122 if (inviteJob?.status === 'generating') {
127 updateJob(point.value, { status: 'generating' });
129 const curPoint: number = Number(need.point(pointCursor));
130 const _authToken = authToken.getOrElse(null);
131 const _contracts = contracts.getOrElse(null);
133 if (_authToken && _contracts) {
134 const spawnedPoints = point.isL2Spawn
135 ? await api.getSpawned(curPoint)
138 const newPending = await api.getPendingByShip(curPoint);
139 setPendingTransactions(newPending);
140 const pendingSpawns = getPendingSpawns(newPending);
142 const availablePoints = await azimuth.azimuth.getUnspawnedChildren(
147 const outgoingPoints = pointList
149 ({ value, isPlanet }) => isPlanet && availablePoints.includes(value)
151 .map(({ value }) => value);
153 const invitePlanets = spawnedPoints
155 outgoingPoints.filter((p: number) => !spawnedPoints.includes(p))
157 .filter((p: number) => !pendingSpawns.has(p))
160 setInvites(curPoint, []);
162 const newInvites: Invite[] = [];
163 // Iterate over all of the stored invites, generating wallet info as necessary
164 const storedInvites = getStoredInvites(ls);
166 for (let i = 0; i < invitePlanets.length; i++) {
167 updateJob(point.value, { generatingNum: i + 1 });
168 const planet = invitePlanets[i];
169 const storedInvite = storedInvites[planet];
171 storedInvite || (await generateInviteInfo(planet, _authToken));
173 // TODO: check if the invite point's owner still matches the deterministic wallet address
174 const planetInfo = await api.getPoint(planet);
175 setStoredInvite(ls, invite);
178 invite.owner.toLowerCase() ===
179 planetInfo.ownership?.owner?.address &&
180 planetInfo.dominion === POINT_DOMINIONS.L2
182 addInvite(curPoint, invite);
183 newInvites.push(invite);
187 setStoredInvite(ls, invite);
191 updateJob(point.value, { status: 'finished', generatingNum: 0 });
195 console.warn('ERROR GETTING INVITES', error);
196 updateJob(point.value, { status: 'errored', generatingNum: 0 });
208 setPendingTransactions,
212 const generateInviteCodes = useCallback(
215 planets: SpawnedPoints | UnspawnedPoints,
216 spawn: boolean = true
218 const _contracts = contracts.getOrElse(null);
219 const _web3 = web3.getOrElse(null);
220 const _wallet = wallet.getOrElse(null);
221 const _authToken = authToken.getOrElse(null);
223 if (!_contracts || !_web3 || !_wallet || !_authToken) {
224 // not using need because we want a custom error
225 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
228 // TODO: remove when UI disables management proxy from spawning invites
229 // if (Just.hasInstance(authMnemonic)) {
231 // "Auth key Error: A management mnemonic can't create invite codes"
235 const invites: Invite[] = [];
237 const proxy = point.getSpawnProxy();
239 if (proxy === undefined)
240 throw new Error("Error: Address doesn't match proxy");
242 const nonce = await api.getNonce({
247 for (let i = 0; i < planets.length; i++) {
248 updateJob(point.value, { generatingNum: i + 1 });
249 const planet = planets[i];
250 const nonceInc = i + nonce;
252 const { ticket, inviteWallet } = await generateInviteWallet(
258 await performL2Reticket({
260 to: inviteWallet.ownership.keys.address,
261 manager: inviteWallet.management.keys.address,
263 toWallet: inviteWallet,
266 await submitL2Transaction({
268 address: _wallet.address,
273 pointToSpawn: planet,
280 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
284 await submitL2Transaction({
291 type: 'configureKeys',
297 await registerProxyAddress(
304 inviteWallet.management.keys.address,
310 await submitL2Transaction({
315 type: 'transferPoint',
317 address: inviteWallet.ownership.keys.address,
328 owner: inviteWallet.ownership.keys.address,
332 setStoredInvites(ls, invites);
333 console.log('getting invites from code generation');
336 `Your invite${invites.length > 1 ? 's have' : ' has'} been generated!`
357 const generateCsv = useCallback(() => {
358 const pointInvites = invites[point.value];
361 throw new Error('There was an error creating the CSV');
364 return pointInvites.reduce(
365 (csvData, { ticket, planet }, ind) =>
366 (csvData += generateCsvLine(ind, ticket, planet)),
367 'Number,Planet,Invite URL,Point,Ticket\n'
369 }, [invites, point]);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list