on f8149eb help sign in
bridge/src/lib/utils/roller.ts
361 lines 8.9 kB history raw
1import { Just, Nothing } from 'folktale/maybe';
2import { randomHex } from 'web3-utils';
3import WalletConnect from '@walletconnect/client';
4import RollerRPCAPI, {
5 Proxy,
6 Signature,
7 From,
8 EthAddress,
9 AddressParams,
10 Ship,
11 SpawnParams,
12 L2Data,
13 PendingTransaction,
14} from '@urbit/roller-api';
16import { Invite } from 'lib/types/Invite';
17import { signTransactionHash } from 'lib/authToken';
18import { deriveNetworkKeys, CRYPTO_SUITE_VERSION } from 'lib/keys';
19import { addHexPrefix } from 'lib/utils/address';
20import { makeDeterministicTicket, generateWallet } from 'lib/walletgen';
21import { WALLET_TYPES } from 'lib/constants';
22import { L2TransactionArgs, TransactionData } from 'lib/types/L2Transaction';
24export const SECOND = 1000;
25export const MINUTE = SECOND * 60;
26export const HOUR = MINUTE * 60;
28export const padZero = (amount: number) =>
29 `${amount < 10 && amount >= 0 ? '0' : ''}${amount}`;
31export const hasPoint = (point: number) => (invite: Invite) =>
32 invite.planet === point;
34export const getTimeToNextBatch = (nextBatch: number, now: number) => {
35 const toNext = Math.max(nextBatch - now, 0);
36 const hours = Math.floor(toNext / HOUR);
37 const minutes = Math.floor((toNext - hours * HOUR) / MINUTE);
38 const seconds = Math.floor(
39 (toNext - hours * HOUR - minutes * MINUTE) / SECOND
40 );
42 return `${padZero(hours)}h ${padZero(minutes)}m ${padZero(seconds)}s`;
43};
45export const isL2 = (dom?: string) => dom === 'l2';
47export const isL2Spawn = (dom?: string) => dom === 'l2' || dom === 'spawn';
49export const generateInviteWallet = async (point: number, seed: string) => {
50 const ticket = makeDeterministicTicket(point, seed);
51 const inviteWallet = await generateWallet(point, ticket, true);
53 return { ticket, inviteWallet };
54};
56export const generateHashAndSign = async (
57 api: RollerRPCAPI,
58 wallet: any,
59 nonce: number,
60 from: From,
61 type: string,
62 data: any,
63 walletType: symbol,
64 web3: any,
65 connector: WalletConnect | null
66) => {
67 if (walletType === WALLET_TYPES.METAMASK) {
68 const hash = await api.prepareForSigning(nonce, from, type, data);
69 const sig = await web3.eth.personal.sign(hash, wallet.address, '');
70 return sig;
71 } else if (walletType === WALLET_TYPES.WALLET_CONNECT) {
72 if (!connector || !connector.connected) {
73 throw new Error('Awaiting WalletConnect connection...');
74 }
76 const hash = await api.prepareForSigning(nonce, from, type, data);
77 const sig = await connector.signPersonalMessage([hash, wallet.address]);
78 return sig;
79 } else {
80 const hash = await api.getUnsignedTx(nonce, from, type, data);
81 const sig = await signTransactionHash(hash, wallet.privateKey);
82 return sig;
83 }
84};
86export const configureKeys = async (
87 api: RollerRPCAPI,
88 wallet: any,
89 point: Ship,
90 proxy: string,
91 nonce: number,
92 networkSeed: any,
93 walletType: symbol,
94 web3: any,
95 connector: WalletConnect | null,
96 breach?: boolean
97) => {
98 const from = {
99 ship: point, //ship to configure keys
100 proxy,
101 };
103 // TODO: do something here?
104 if (Nothing.hasInstance(networkSeed)) {
105 console.log("Network key Error: couldn't derive network keys");
106 // throw new Error("Network key Error: couldn't derive network keys");
107 }
109 const seed = Just.hasInstance(networkSeed)
110 ? networkSeed.value
111 : randomHex(32);
113 const pair = deriveNetworkKeys(seed);
115 const data = {
116 encrypt: addHexPrefix(pair.crypt.public),
117 auth: addHexPrefix(pair.auth.public),
118 cryptoSuite: String(CRYPTO_SUITE_VERSION),
119 breach: breach || false,
120 };
122 const sig = await generateHashAndSign(
123 api,
124 wallet,
125 nonce,
126 from,
127 'configureKeys',
128 data,
129 walletType,
130 web3,
131 connector
132 );
133 return api.configureKeys(sig, from, wallet.address, data);
134};
136export const transferPointRequest = async (
137 api: RollerRPCAPI,
138 wallet: any,
139 pointToTransfer: Ship,
140 proxy: string,
141 nonce: number,
142 address: string,
143 walletType: symbol,
144 web3: any,
145 connector: WalletConnect | null,
146 reset?: boolean
147) => {
148 const from = {
149 ship: pointToTransfer,
150 proxy,
151 };
153 const data = {
154 address,
155 reset: reset || false,
156 };
158 const sig = await generateHashAndSign(
159 api,
160 wallet,
161 nonce,
162 from,
163 'transferPoint',
164 data,
165 walletType,
166 web3,
167 connector
168 );
170 return api.transferPoint(sig, from, wallet.address, data);
171};
173const proxyType = (proxy: Proxy) => {
174 switch (proxy) {
175 case 'manage':
176 return 'setManagementProxy';
177 case 'spawn':
178 return 'setSpawnProxy';
179 case 'transfer':
180 return 'setTransferProxy';
181 default:
182 throw new Error(`Unknown proxyType ${proxy}`);
183 }
184};
186const setProxy = async (
187 api: RollerRPCAPI,
188 proxyAddressType: string,
189 sig: Signature,
190 from: From,
191 address: EthAddress,
192 data: AddressParams
193) => {
194 switch (proxyAddressType) {
195 case 'manage':
196 return await api.setManagementProxy(sig, from, address, data);
197 case 'spawn':
198 return await api.setSpawnProxy(sig, from, address, data);
199 case 'transfer':
200 return await api.setTransferProxy(sig, from, address, data);
201 default:
202 throw new Error(`Unknown proxyType ${proxyAddressType}`);
203 }
204};
206export const registerProxyAddress = async (
207 api: RollerRPCAPI,
208 wallet: any,
209 _point: Ship,
210 proxy: string,
211 proxyAddressType: string,
212 nonce: number,
213 address: string,
214 walletType: symbol,
215 web3: any,
216 connector: WalletConnect | null
217) => {
218 const from = {
219 ship: _point,
220 proxy,
221 };
222 const data = { address };
224 const sig = await generateHashAndSign(
225 api,
226 wallet,
227 nonce,
228 from,
229 proxyType(proxyAddressType),
230 data,
231 walletType,
232 web3,
233 connector
234 );
236 return setProxy(api, proxyAddressType, sig, from, wallet.address, data);
237};
239const getDataAndMethod = (args: L2TransactionArgs) => {
240 const {
241 address,
242 api,
243 breach,
244 networkSeed,
245 reset,
246 pointToSpawn,
247 newSponsor,
248 sponsee,
249 ship,
250 } = args;
251 const result: TransactionData = { data: { address }, method: () => null };
253 switch (args.type) {
254 case 'transferPoint':
255 result.method = api.transferPoint;
256 result.data.reset = reset || false;
257 return result;
258 case 'spawn':
259 result.method = api.spawn;
260 result.data.ship = pointToSpawn;
261 return result;
262 case 'configureKeys':
263 // TODO: do something here?
264 if (Nothing.hasInstance(networkSeed)) {
265 console.log("Network key Error: couldn't derive network keys");
266 // throw new Error("Network key Error: couldn't derive network keys");
267 }
269 const seed = Just.hasInstance(networkSeed)
270 ? networkSeed.value
271 : randomHex(32);
273 const pair = deriveNetworkKeys(seed);
275 result.data = {
276 encrypt: addHexPrefix(pair.crypt.public),
277 auth: addHexPrefix(pair.auth.public),
278 cryptoSuite: String(CRYPTO_SUITE_VERSION),
279 breach: breach || false,
280 };
281 result.method = api.configureKeys;
282 return result;
283 case 'escape':
284 result.method = api.escape;
285 result.data.ship = newSponsor;
286 delete result.data.address;
287 return result;
288 case 'cancelEscape':
289 result.method = api.cancelEscape;
290 result.data.ship = ship;
291 delete result.data.address;
292 return result;
293 case 'adopt':
294 result.method = api.adopt;
295 result.data.ship = sponsee;
296 delete result.data.address;
297 return result;
298 case 'detach':
299 result.method = api.detach;
300 result.data.ship = sponsee;
301 delete result.data.address;
302 return result;
303 case 'reject':
304 result.method = api.reject;
305 result.data.ship = sponsee;
306 delete result.data.address;
307 return result;
308 }
309};
311export const submitL2Transaction = async (args: L2TransactionArgs) => {
312 const {
313 api,
314 wallet,
315 ship,
316 type,
317 proxy,
318 nonce,
319 walletType,
320 web3,
321 connector,
322 } = args;
324 const from = { ship, proxy };
325 const { data, method } = getDataAndMethod(args);
327 const sig = await generateHashAndSign(
328 api,
329 wallet,
330 nonce,
331 from,
332 type,
333 data,
334 walletType,
335 web3,
336 connector
337 );
339 return method(sig, from, wallet.address, data);
340};
342export const reticketL2Point = async () => {};
344export const hasInvite = (point: number) => (invite: Invite) =>
345 invite.planet === point && invite.ticket.length > 0;
347function isSpawn(tx: L2Data | undefined): tx is SpawnParams {
348 return (tx as SpawnParams) !== undefined;
351function isShipNumber(ship: number | string | undefined): ship is number {
352 return (ship as number) !== undefined;
355export const getPendingSpawns = (pendingTxs: PendingTransaction[]) =>
356 pendingTxs.reduce((acc, pending) => {
357 return isSpawn(pending.rawTx?.tx) &&
358 isShipNumber(pending.rawTx?.tx.data.ship)
359 ? acc.add(pending.rawTx?.tx.data.ship!)
360 : acc;
361 }, new Set<number>());