on ade464d help sign in
bridge/src/lib/utils/roller.ts
411 lines 9.6 kB history raw
1import { Just, Nothing } from 'folktale/maybe';
2import { randomHex } from 'web3-utils';
3import RollerRPCAPI, {
4 Proxy,
5 Signature,
6 From,
7 EthAddress,
8 AddressParams,
9 Ship,
10 SpawnParams,
11 L2Data,
12 PendingTransaction,
13} from '@urbit/roller-api';
15import { Invite } from 'lib/types/Invite';
16import { signTransactionHash } from 'lib/authToken';
17import { deriveNetworkKeys, CRYPTO_SUITE_VERSION } from 'lib/keys';
18import { addHexPrefix } from 'lib/utils/address';
19import { makeDeterministicTicket, generateWallet } from 'lib/walletgen';
20import { WALLET_TYPES } from 'lib/constants';
21import { L2TransactionArgs, TransactionData } from 'lib/types/L2Transaction';
23export const SECOND = 1000;
24export const MINUTE = SECOND * 60;
25export const HOUR = MINUTE * 60;
27export const padZero = (amount: number) =>
28 `${amount < 10 && amount >= 0 ? '0' : ''}${amount}`;
30export const hasPoint = (point: number) => (invite: Invite) =>
31 invite.planet === point;
33export const getTimeToNextBatch = (nextBatch: number, now: number) => {
34 const toNext = Math.max(nextBatch - now, 0);
35 const hours = Math.floor(toNext / HOUR);
36 const minutes = Math.floor((toNext - hours * HOUR) / MINUTE);
37 const seconds = Math.floor(
38 (toNext - hours * HOUR - minutes * MINUTE) / SECOND
39 );
41 return `${padZero(hours)}h ${padZero(minutes)}m ${padZero(seconds)}s`;
42};
44export const isL2 = (dom?: string) => dom === 'l2';
46export const isL2Spawn = (dom?: string) => dom === 'spawn';
48export const generateInviteWallet = async (point: number, seed: string) => {
49 const ticket = makeDeterministicTicket(point, seed);
50 const inviteWallet = await generateWallet(point, ticket, true);
52 return { ticket, inviteWallet };
53};
55export const generateHashAndSign = async (
56 api: RollerRPCAPI,
57 wallet: any,
58 nonce: number,
59 from: From,
60 type: string,
61 data: any,
62 walletType: symbol,
63 web3: any
64) => {
65 if (walletType === WALLET_TYPES.METAMASK) {
66 const hash = await api.prepareForSigning(nonce, from, type, data);
67 const sig = await web3.eth.personal.sign(hash, wallet.address, '');
68 return sig;
69 } else {
70 const hash = await api.getUnsignedTx(nonce, from, type, data);
71 const sig = await signTransactionHash(hash, wallet.privateKey);
72 return sig;
73 }
74};
76export const spawn = async (
77 api: RollerRPCAPI,
78 wallet: any,
79 _point: number,
80 proxy: string,
81 nonce: number,
82 planet: number,
83 walletType: symbol,
84 web3: any
85) => {
86 const from = {
87 ship: _point, //ship that is spawning the planet
88 proxy,
89 };
91 const data = {
92 address: wallet.address,
93 ship: planet, // ship to spawn
94 };
96 const sig = await generateHashAndSign(
97 api,
98 wallet,
99 nonce,
100 from,
101 'spawn',
102 data,
103 walletType,
104 web3
105 );
106 return api.spawn(sig, from, wallet.address, data);
107};
109export const configureKeys = async (
110 api: RollerRPCAPI,
111 wallet: any,
112 point: Ship,
113 proxy: string,
114 nonce: number,
115 networkSeed: any,
116 walletType: symbol,
117 web3: any,
118 breach?: boolean
119) => {
120 const from = {
121 ship: point, //ship to configure keys
122 proxy,
123 };
125 // TODO: do something here?
126 if (Nothing.hasInstance(networkSeed)) {
127 console.log("Network key Error: couldn't derive network keys");
128 // throw new Error("Network key Error: couldn't derive network keys");
129 }
131 const seed = Just.hasInstance(networkSeed)
132 ? networkSeed.value
133 : randomHex(32);
135 const pair = deriveNetworkKeys(seed);
137 const data = {
138 encrypt: addHexPrefix(pair.crypt.public),
139 auth: addHexPrefix(pair.auth.public),
140 cryptoSuite: String(CRYPTO_SUITE_VERSION),
141 breach: breach || false,
142 };
144 const sig = await generateHashAndSign(
145 api,
146 wallet,
147 nonce,
148 from,
149 'configureKeys',
150 data,
151 walletType,
152 web3
153 );
154 return api.configureKeys(sig, from, wallet.address, data);
155};
157export const transferPointRequest = async (
158 api: RollerRPCAPI,
159 wallet: any,
160 pointToTransfer: Ship,
161 proxy: string,
162 nonce: number,
163 address: string,
164 walletType: symbol,
165 web3: any,
166 reset?: boolean
167) => {
168 const from = {
169 ship: pointToTransfer,
170 proxy,
171 };
173 const data = {
174 address,
175 reset: reset || false,
176 };
178 const sig = await generateHashAndSign(
179 api,
180 wallet,
181 nonce,
182 from,
183 'transferPoint',
184 data,
185 walletType,
186 web3
187 );
189 return api.transferPoint(sig, from, wallet.address, data);
190};
192export const adopt = async (
193 api: RollerRPCAPI,
194 _wallet: any,
195 sponsor: Ship,
196 proxy: string,
197 nonce: number,
198 ship: Ship
199) => {
200 const from = {
201 ship: sponsor, // ship that is adopting the planet
202 proxy,
203 };
205 const data = {
206 ship, // ship to adopt
207 };
209 const hash = await api.getUnsignedTx(nonce, from, 'detach', data);
210 const sSig = signTransactionHash(hash, _wallet.privateKey);
211 return api.adopt(sSig, from, _wallet.address, data);
212};
214export const detach = async (
215 api: RollerRPCAPI,
216 _wallet: any,
217 sponsor: Ship,
218 proxy: string,
219 nonce: number,
220 ship: Ship
221) => {
222 const from = {
223 ship: sponsor, // ship that is detaching the planet
224 proxy,
225 };
227 const data = {
228 ship, // ship to detach
229 };
231 const hash = await api.getUnsignedTx(nonce, from, 'detach', data);
232 const sSig = signTransactionHash(hash, _wallet.privateKey);
233 return api.detach(sSig, from, _wallet.address, data);
234};
236export const reject = async (
237 api: RollerRPCAPI,
238 _wallet: any,
239 sponsor: Ship,
240 proxy: string,
241 nonce: number,
242 ship: Ship
243) => {
244 const from = {
245 ship: sponsor, // ship that is rejecting the planet
246 proxy,
247 };
249 const data = {
250 ship, // ship to reject
251 };
253 const hash = await api.getUnsignedTx(nonce, from, 'detach', data);
254 const sSig = signTransactionHash(hash, _wallet.privateKey);
255 return api.reject(sSig, from, _wallet.address, data);
256};
258const proxyType = (proxy: Proxy) => {
259 switch (proxy) {
260 case 'manage':
261 return 'setManagementProxy';
262 case 'spawn':
263 return 'setSpawnProxy';
264 case 'transfer':
265 return 'setTransferProxy';
266 default:
267 throw new Error(`Unknown proxyType ${proxy}`);
268 }
269};
271const setProxy = async (
272 api: RollerRPCAPI,
273 proxyAddressType: string,
274 sig: Signature,
275 from: From,
276 address: EthAddress,
277 data: AddressParams
278) => {
279 switch (proxyAddressType) {
280 case 'manage':
281 return await api.setManagementProxy(sig, from, address, data);
282 case 'spawn':
283 return await api.setSpawnProxy(sig, from, address, data);
284 case 'transfer':
285 return await api.setTransferProxy(sig, from, address, data);
286 default:
287 throw new Error(`Unknown proxyType ${proxyAddressType}`);
288 }
289};
291export const registerProxyAddress = async (
292 api: RollerRPCAPI,
293 wallet: any,
294 _point: Ship,
295 proxy: string,
296 proxyAddressType: string,
297 nonce: number,
298 address: string,
299 walletType: symbol,
300 web3: any
301) => {
302 const from = {
303 ship: _point,
304 proxy,
305 };
306 const data = { address };
308 const sig = await generateHashAndSign(
309 api,
310 wallet,
311 nonce,
312 from,
313 proxyType(proxyAddressType),
314 data,
315 walletType,
316 web3
317 );
319 return setProxy(api, proxyAddressType, sig, from, wallet.address, data);
320};
322const getDataAndMethod = (args: L2TransactionArgs) => {
323 const {
324 address,
325 api,
326 breach,
327 networkSeed,
328 reset,
329 pointToSpawn,
330 newSponsor,
331 } = args;
332 const result: TransactionData = { data: { address }, method: () => null };
334 switch (args.type) {
335 case 'transferPoint':
336 result.method = api.transferPoint;
337 result.data.reset = reset || false;
338 return result;
339 case 'spawn':
340 result.method = api.spawn;
341 result.data.ship = pointToSpawn;
342 return result;
343 case 'configureKeys':
344 // TODO: do something here?
345 if (Nothing.hasInstance(networkSeed)) {
346 console.log("Network key Error: couldn't derive network keys");
347 // throw new Error("Network key Error: couldn't derive network keys");
348 }
350 const seed = Just.hasInstance(networkSeed)
351 ? networkSeed.value
352 : randomHex(32);
354 const pair = deriveNetworkKeys(seed);
356 result.data = {
357 encrypt: addHexPrefix(pair.crypt.public),
358 auth: addHexPrefix(pair.auth.public),
359 cryptoSuite: String(CRYPTO_SUITE_VERSION),
360 breach: breach || false,
361 };
362 result.method = api.configureKeys;
363 return result;
364 case 'escape':
365 result.method = api.escape;
366 result.data.ship = newSponsor;
367 delete result.data.address;
368 return result;
369 }
370};
372export const submitL2Transaction = async (args: L2TransactionArgs) => {
373 const { api, wallet, ship, type, proxy, nonce, walletType, web3 } = args;
375 const from = { ship, proxy };
376 const { data, method } = getDataAndMethod(args);
378 const sig = await generateHashAndSign(
379 api,
380 wallet,
381 nonce,
382 from,
383 type,
384 data,
385 walletType,
386 web3
387 );
389 return method(sig, from, wallet.address, data);
390};
392export const reticketL2Point = async () => {};
394export const hasInvite = (point: number) => (invite: Invite) =>
395 invite.planet === point && invite.ticket.length > 0;
397function isSpawn(tx: L2Data | undefined): tx is SpawnParams {
398 return (tx as SpawnParams) !== undefined;
401function isShipNumber(ship: number | string | undefined): ship is number {
402 return (ship as number) !== undefined;
405export const getPendingSpawns = (pendingTxs: PendingTransaction[]) =>
406 pendingTxs.reduce((acc, pending) => {
407 return isSpawn(pending.rawTx?.tx) &&
408 isShipNumber(pending.rawTx?.tx.data.ship)
409 ? acc.add(pending.rawTx?.tx.data.ship!)
410 : acc;
411 }, new Set<number>());