@keetanetwork/anchor 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,10 @@
1
1
  import * as __typia_transform__assertGuard from "typia/lib/internal/_assertGuard.js";
2
- import * as http from 'http';
2
+ import * as KeetaAnchorHTTPServer from '../../lib/http-server.js';
3
3
  import KeetaNet from '@keetanetwork/keetanet-client';
4
4
  import { createAssert } from 'typia';
5
5
  import { KeetaAnchorUserError } from '../../lib/error.js';
6
6
  import { acceptSwapRequest } from './common.js';
7
7
  import * as Signing from '../../lib/utils/signing.js';
8
- import { Log } from '../../lib/log/index.js';
9
- /**
10
- * The maximum size of a request (128KiB)
11
- */
12
- const MAX_REQUEST_SIZE = 1024 * 128;
13
8
  const assertConversionInputCanonical = (() => { const _io0 = input => "string" === typeof input.from && (RegExp(/^keeta_am(.*)/).test(input.from) || RegExp(/^keeta_an(.*)/).test(input.from) || RegExp(/^keeta_ao(.*)/).test(input.from) || RegExp(/^keeta_ap(.*)/).test(input.from) || RegExp(/^tyblocks_am(.*)/).test(input.from) || RegExp(/^tyblocks_an(.*)/).test(input.from) || RegExp(/^tyblocks_ao(.*)/).test(input.from) || RegExp(/^tyblocks_ap(.*)/).test(input.from)) && ("string" === typeof input.to && (RegExp(/^keeta_am(.*)/).test(input.to) || RegExp(/^keeta_an(.*)/).test(input.to) || RegExp(/^keeta_ao(.*)/).test(input.to) || RegExp(/^keeta_ap(.*)/).test(input.to) || RegExp(/^tyblocks_am(.*)/).test(input.to) || RegExp(/^tyblocks_an(.*)/).test(input.to) || RegExp(/^tyblocks_ao(.*)/).test(input.to) || RegExp(/^tyblocks_ap(.*)/).test(input.to))) && "string" === typeof input.amount && ("from" === input.affinity || "to" === input.affinity); const _ao0 = (input, _path, _exceptionable = true) => ("string" === typeof input.from && (RegExp(/^keeta_am(.*)/).test(input.from) || RegExp(/^keeta_an(.*)/).test(input.from) || RegExp(/^keeta_ao(.*)/).test(input.from) || RegExp(/^keeta_ap(.*)/).test(input.from) || RegExp(/^tyblocks_am(.*)/).test(input.from) || RegExp(/^tyblocks_an(.*)/).test(input.from) || RegExp(/^tyblocks_ao(.*)/).test(input.from) || RegExp(/^tyblocks_ap(.*)/).test(input.from)) || __typia_transform__assertGuard._assertGuard(_exceptionable, {
14
9
  method: "createAssert",
15
10
  path: _path + ".from",
@@ -149,38 +144,6 @@ const assertConversionQuote = (() => { const _io0 = input => "object" === typeof
149
144
  }
150
145
  return input;
151
146
  }; })();
152
- const assertErrorData = (() => { const _io0 = input => "string" === typeof input.error && (undefined === input.statusCode || "number" === typeof input.statusCode) && (undefined === input.contentType || "string" === typeof input.contentType); const _ao0 = (input, _path, _exceptionable = true) => ("string" === typeof input.error || __typia_transform__assertGuard._assertGuard(_exceptionable, {
153
- method: "createAssert",
154
- path: _path + ".error",
155
- expected: "string",
156
- value: input.error
157
- }, _errorFactory)) && (undefined === input.statusCode || "number" === typeof input.statusCode || __typia_transform__assertGuard._assertGuard(_exceptionable, {
158
- method: "createAssert",
159
- path: _path + ".statusCode",
160
- expected: "(number | undefined)",
161
- value: input.statusCode
162
- }, _errorFactory)) && (undefined === input.contentType || "string" === typeof input.contentType || __typia_transform__assertGuard._assertGuard(_exceptionable, {
163
- method: "createAssert",
164
- path: _path + ".contentType",
165
- expected: "(string | undefined)",
166
- value: input.contentType
167
- }, _errorFactory)); const __is = input => "object" === typeof input && null !== input && _io0(input); let _errorFactory; return (input, errorFactory) => {
168
- if (false === __is(input)) {
169
- _errorFactory = errorFactory;
170
- ((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || __typia_transform__assertGuard._assertGuard(true, {
171
- method: "createAssert",
172
- path: _path + "",
173
- expected: "__type",
174
- value: input
175
- }, _errorFactory)) && _ao0(input, _path + "", true) || __typia_transform__assertGuard._assertGuard(true, {
176
- method: "createAssert",
177
- path: _path + "",
178
- expected: "__type",
179
- value: input
180
- }, _errorFactory))(input, "$input", true);
181
- }
182
- return input;
183
- }; })();
184
147
  ;
185
148
  async function formatQuoteSignable(unsignedQuote) {
186
149
  const retval = [
@@ -218,476 +181,169 @@ async function requestToAccounts(config, request) {
218
181
  account: account.assertAccount()
219
182
  });
220
183
  }
221
- async function initRoutes(config) {
222
- const routes = {};
223
- /**
224
- * If a homepage is provided, setup the route for it
225
- */
226
- if ('homepage' in config) {
227
- routes['GET /'] = async function () {
228
- let homepageData;
229
- if (typeof config.homepage === 'string') {
230
- homepageData = config.homepage;
231
- }
232
- else {
233
- if (!config.homepage) {
234
- throw (new Error('internal error: No homepage function provided'));
235
- }
236
- homepageData = await config.homepage();
237
- }
238
- return ({
239
- output: homepageData,
240
- contentType: 'text/html'
241
- });
242
- };
243
- }
244
- /**
245
- * Setup the request handler for an estimate request
246
- */
247
- routes['POST /api/getEstimate'] = async function (_ignore_params, postData) {
248
- if (!postData || typeof postData !== 'object') {
249
- throw (new Error('No POST data provided'));
250
- }
251
- if (!('request' in postData)) {
252
- throw (new Error('POST data missing request'));
253
- }
254
- const conversion = assertConversionInputCanonical(postData.request);
255
- const rateAndFee = await config.fx.getConversionRateAndFee(conversion);
256
- const estimateResponse = {
257
- ok: true,
258
- estimate: {
259
- request: conversion,
260
- convertedAmount: rateAndFee.convertedAmount,
261
- expectedCost: {
262
- min: rateAndFee.cost.amount,
263
- max: rateAndFee.cost.amount,
264
- token: rateAndFee.cost.token
265
- }
266
- }
267
- };
268
- return ({
269
- output: JSON.stringify(estimateResponse)
270
- });
271
- };
272
- routes['POST /api/getQuote'] = async function (_ignore_params, postData) {
273
- if (!postData || typeof postData !== 'object') {
274
- throw (new Error('No POST data provided'));
275
- }
276
- if (!('request' in postData)) {
277
- throw (new Error('POST data missing request'));
278
- }
279
- const conversion = assertConversionInputCanonical(postData.request);
280
- const rateAndFee = await config.fx.getConversionRateAndFee(conversion);
281
- const unsignedQuote = {
282
- request: conversion,
283
- ...rateAndFee
284
- };
285
- const signedQuote = await generateSignedQuote(config.quoteSigner, unsignedQuote);
286
- const quoteResponse = {
287
- ok: true,
288
- quote: signedQuote
289
- };
290
- return ({
291
- output: JSON.stringify(quoteResponse)
292
- });
293
- };
294
- routes['POST /api/createExchange'] = async function (_ignore_params, postData) {
295
- if (!postData || typeof postData !== 'object') {
296
- throw (new Error('No POST data provided'));
297
- }
298
- if (!('request' in postData)) {
299
- throw (new Error('POST data missing request'));
300
- }
301
- const request = postData.request;
302
- if (!request || typeof request !== 'object') {
303
- throw (new Error('Request is not an object'));
304
- }
305
- if (!('quote' in request)) {
306
- throw (new Error('Quote is missing from request'));
307
- }
308
- if (!('block' in request) || typeof request.block !== 'string') {
309
- throw (new Error('Block was not provided in exchange request'));
310
- }
311
- const quote = assertConversionQuote(request.quote);
312
- const isValidQuote = await verifySignedData(config.quoteSigner, quote);
313
- if (!isValidQuote) {
314
- throw (new Error('Invalid quote signature'));
315
- }
316
- const block = new KeetaNet.lib.Block(request.block);
317
- let userClient;
318
- if (KeetaNet.UserClient.isInstance(config.client)) {
319
- userClient = config.client;
320
- }
321
- else {
322
- const { account, signer } = await requestToAccounts(config, quote.request);
323
- userClient = new KeetaNet.UserClient({
324
- client: config.client.client,
325
- network: config.client.network,
326
- networkAlias: config.client.networkAlias,
327
- account: account,
328
- signer: signer
329
- });
330
- }
331
- const expectedToken = KeetaNet.lib.Account.fromPublicKeyString(quote.request.from);
332
- const expectedAmount = quote.request.affinity === 'from' ? quote.request.amount : quote.convertedAmount;
333
- // eslint-disable-next-line @typescript-eslint/no-deprecated
334
- const swapBlocks = await acceptSwapRequest(userClient, block, { token: expectedToken, amount: BigInt(expectedAmount) });
335
- const publishResult = await userClient.client.transmit(swapBlocks);
336
- if (!publishResult.publish) {
337
- throw (new Error('Exchange Publish Failed'));
338
- }
339
- const exchangeResponse = {
340
- ok: true,
341
- exchangeID: block.hash.toString()
342
- };
343
- return ({
344
- output: JSON.stringify(exchangeResponse)
345
- });
346
- };
347
- routes['GET /api/getExchangeStatus/:id'] = async function (params) {
348
- if (params === undefined || params === null) {
349
- throw (new KeetaAnchorUserError('Expected params'));
350
- }
351
- const exchangeID = params.get('id');
352
- if (typeof exchangeID !== 'string') {
353
- throw (new Error('Missing exchangeID in params'));
354
- }
355
- const blockLookup = await config.client.client.getVoteStaple(exchangeID);
356
- if (blockLookup === null) {
357
- throw (new Error('Block Not Found'));
358
- }
359
- const exchangeResponse = {
360
- ok: true,
361
- exchangeID: exchangeID
362
- };
363
- return ({
364
- output: JSON.stringify(exchangeResponse)
365
- });
366
- };
367
- routes['ERROR'] = async function (_ignore_params, postData) {
368
- const errorInfo = assertErrorData(postData);
369
- const retval = {
370
- output: errorInfo.error,
371
- statusCode: errorInfo.statusCode ?? 400,
372
- contentType: errorInfo.contentType ?? 'text/plain'
373
- };
374
- return (retval);
375
- };
376
- return (routes);
377
- }
378
- export class KeetaNetFXAnchorHTTPServer {
379
- port;
184
+ export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAnchorHTTPServer {
380
185
  homepage;
381
186
  client;
382
- logger;
383
187
  account;
384
188
  signer;
385
189
  quoteSigner;
386
190
  fx;
387
- #serverPromise;
388
- #server;
389
191
  constructor(config) {
192
+ super(config);
390
193
  this.homepage = config.homepage ?? '';
391
- this.port = config.port ?? 0;
392
194
  this.client = config.client;
393
195
  this.fx = config.fx;
394
196
  this.account = config.account;
395
197
  this.signer = config.signer ?? config.account;
396
198
  this.quoteSigner = config.quoteSigner;
397
- this.logger = config.logger ?? new Log();
398
- }
399
- static routeMatch(requestURL, routeURL) {
400
- const requestURLPaths = requestURL.pathname.split('/');
401
- const routeURLPaths = routeURL.pathname.split('/');
402
- if (requestURLPaths.length !== routeURLPaths.length) {
403
- return ({ match: false });
404
- }
405
- const params = new Map();
406
- for (let partIndex = 0; partIndex < requestURLPaths.length; partIndex++) {
407
- const requestPath = requestURLPaths[partIndex];
408
- const routePath = routeURLPaths[partIndex];
409
- if (routePath === undefined || requestPath === undefined) {
410
- return ({ match: false });
411
- }
412
- if (routePath.startsWith(':')) {
413
- params.set(routePath.slice(1), requestPath);
414
- }
415
- else if (requestPath !== routePath) {
416
- return ({ match: false });
417
- }
418
- }
419
- return ({ match: true, params: params });
420
199
  }
421
- static routeFind(method, requestURL, routes) {
422
- for (const routeKey in routes) {
423
- const route = routes[routeKey];
424
- if (route === undefined) {
425
- continue;
426
- }
427
- const [routeMethod, ...routePathParts] = routeKey.split(' ');
428
- const routePath = `/${routePathParts.join(' ')}`.replace(/^\/+/, '/');
429
- if (method !== routeMethod) {
430
- continue;
431
- }
432
- const routeURL = new URL(routePath, 'http://localhost');
433
- const matchResult = this.routeMatch(requestURL, routeURL);
434
- if (matchResult.match) {
200
+ async initRoutes(config) {
201
+ const routes = {};
202
+ /**
203
+ * If a homepage is provided, setup the route for it
204
+ */
205
+ if ('homepage' in config) {
206
+ routes['GET /'] = async function () {
207
+ let homepageData;
208
+ if (typeof config.homepage === 'string') {
209
+ homepageData = config.homepage;
210
+ }
211
+ else {
212
+ if (!config.homepage) {
213
+ throw (new Error('internal error: No homepage function provided'));
214
+ }
215
+ homepageData = await config.homepage();
216
+ }
435
217
  return ({
436
- route: route,
437
- params: matchResult.params
218
+ output: homepageData,
219
+ contentType: 'text/html'
438
220
  });
439
- }
221
+ };
440
222
  }
441
- return (null);
442
- }
443
- static addCORS(routes) {
444
- const newRoutes = {};
445
- const validMethods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD']);
446
- const methodsByPath = {};
447
- for (const routeKey in routes) {
448
- const methodAndPath = routeKey.split(' ');
449
- const method = methodAndPath[0];
450
- const path = methodAndPath.slice(1).join(' ');
451
- if (method === undefined || path === undefined) {
452
- continue;
223
+ /**
224
+ * Setup the request handler for an estimate request
225
+ */
226
+ routes['POST /api/getEstimate'] = async function (_ignore_params, postData) {
227
+ if (!postData || typeof postData !== 'object') {
228
+ throw (new Error('No POST data provided'));
453
229
  }
454
- if (!validMethods.has(method)) {
455
- continue;
230
+ if (!('request' in postData)) {
231
+ throw (new Error('POST data missing request'));
456
232
  }
457
- if (!(path in methodsByPath)) {
458
- methodsByPath[path] = new Set();
233
+ const conversion = assertConversionInputCanonical(postData.request);
234
+ const rateAndFee = await config.fx.getConversionRateAndFee(conversion);
235
+ const estimateResponse = {
236
+ ok: true,
237
+ estimate: {
238
+ request: conversion,
239
+ convertedAmount: rateAndFee.convertedAmount,
240
+ expectedCost: {
241
+ min: rateAndFee.cost.amount,
242
+ max: rateAndFee.cost.amount,
243
+ token: rateAndFee.cost.token
244
+ }
245
+ }
246
+ };
247
+ return ({
248
+ output: JSON.stringify(estimateResponse)
249
+ });
250
+ };
251
+ routes['POST /api/getQuote'] = async function (_ignore_params, postData) {
252
+ if (!postData || typeof postData !== 'object') {
253
+ throw (new Error('No POST data provided'));
459
254
  }
460
- if (methodsByPath[path] === undefined) {
461
- throw (new Error(`internal error: methodsByPath missing path for ${path}`));
255
+ if (!('request' in postData)) {
256
+ throw (new Error('POST data missing request'));
462
257
  }
463
- methodsByPath[path].add(method);
464
- }
465
- const seenPaths = new Set();
466
- for (const routeKey in routes) {
467
- const methodAndPath = routeKey.split(' ');
468
- const method = methodAndPath[0];
469
- const path = methodAndPath.slice(1).join(' ');
470
- const routeHandler = routes[routeKey];
471
- if (routeHandler === undefined) {
472
- throw (new Error(`internal error: routeHandler missing for routeKey ${routeKey}`));
258
+ const conversion = assertConversionInputCanonical(postData.request);
259
+ const rateAndFee = await config.fx.getConversionRateAndFee(conversion);
260
+ const unsignedQuote = {
261
+ request: conversion,
262
+ ...rateAndFee
263
+ };
264
+ const signedQuote = await generateSignedQuote(config.quoteSigner, unsignedQuote);
265
+ const quoteResponse = {
266
+ ok: true,
267
+ quote: signedQuote
268
+ };
269
+ return ({
270
+ output: JSON.stringify(quoteResponse)
271
+ });
272
+ };
273
+ routes['POST /api/createExchange'] = async function (_ignore_params, postData) {
274
+ if (!postData || typeof postData !== 'object') {
275
+ throw (new Error('No POST data provided'));
473
276
  }
474
- if (method !== 'ERROR') {
475
- if (method === undefined || path === undefined) {
476
- newRoutes[routeKey] = routeHandler;
477
- continue;
478
- }
479
- if (!validMethods.has(method)) {
480
- newRoutes[routeKey] = routeHandler;
481
- continue;
482
- }
277
+ if (!('request' in postData)) {
278
+ throw (new Error('POST data missing request'));
483
279
  }
484
- const validMethodsForPath = methodsByPath[path];
485
- let validMethodsForPathParts = [];
486
- if (validMethodsForPath !== undefined) {
487
- validMethodsForPath.add('OPTIONS');
488
- validMethodsForPathParts = Array.from(validMethodsForPath);
280
+ const request = postData.request;
281
+ if (!request || typeof request !== 'object') {
282
+ throw (new Error('Request is not an object'));
489
283
  }
490
- else {
491
- validMethodsForPathParts = [...Array.from(validMethods), 'OPTIONS'];
284
+ if (!('quote' in request)) {
285
+ throw (new Error('Quote is missing from request'));
492
286
  }
493
- newRoutes[routeKey] = async function (...args) {
494
- const retval = await routeHandler(...args);
495
- /* Add CORS headers to the response for the original route handler */
496
- if (retval.contentType === 'application/json' || retval.contentType === undefined) {
497
- if (!('headers' in retval) || retval.headers === undefined) {
498
- retval.headers = {};
499
- }
500
- retval.headers['Access-Control-Allow-Origin'] = '*';
501
- }
502
- return (retval);
503
- };
504
- if (!seenPaths.has(path) && path !== '' && path !== undefined) {
505
- const corsRouteKey = `OPTIONS ${path}`;
506
- newRoutes[corsRouteKey] = async function () {
507
- return ({
508
- output: '',
509
- statusCode: 204,
510
- contentType: 'text/plain',
511
- headers: {
512
- 'Access-Control-Allow-Origin': '*',
513
- 'Access-Control-Allow-Methods': validMethodsForPathParts.join(', '),
514
- 'Access-Control-Allow-Headers': 'Content-Type',
515
- 'Access-Control-Max-Age': '86400'
516
- }
517
- });
518
- };
519
- seenPaths.add(path);
287
+ if (!('block' in request) || typeof request.block !== 'string') {
288
+ throw (new Error('Block was not provided in exchange request'));
520
289
  }
521
- }
522
- return (newRoutes);
523
- }
524
- async main(onSetPort) {
525
- this.logger?.debug('KeetaAnchorFX.Server', 'Starting HTTP server...');
526
- const port = this.port;
527
- const routes = KeetaNetFXAnchorHTTPServer.addCORS(await initRoutes(this));
528
- const server = new http.Server(async (request, response) => {
529
- const url = new URL(request.url ?? '/', `http://${request.headers.host ?? 'localhost'}`);
530
- const method = request.method ?? 'GET';
531
- /*
532
- * Lookup the route based on the request
533
- */
534
- const requestedRouteAndParams = KeetaNetFXAnchorHTTPServer.routeFind(method, url, routes);
535
- if (requestedRouteAndParams === null) {
536
- response.statusCode = 404;
537
- response.setHeader('Content-Type', 'text/plain');
538
- response.write('Not Found');
539
- response.end();
540
- return;
290
+ const quote = assertConversionQuote(request.quote);
291
+ const isValidQuote = await verifySignedData(config.quoteSigner, quote);
292
+ if (!isValidQuote) {
293
+ throw (new Error('Invalid quote signature'));
541
294
  }
542
- /*
543
- * Extract the route handler and the parameters from
544
- * the request
545
- */
546
- const { route, params } = requestedRouteAndParams;
547
- /**
548
- * Attempt to run the route, catch any errors
549
- */
550
- let result, generatedResult = false;
551
- try {
552
- /**
553
- * If POST'ing, read and parse the POST data
554
- */
555
- let postData;
556
- if (request.method === 'POST') {
557
- const data = await request.map(function (chunk) {
558
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
559
- return (Buffer.from(chunk));
560
- }).reduce(function (prev, curr) {
561
- if (prev.length > MAX_REQUEST_SIZE) {
562
- throw (new Error('Request too large'));
563
- }
564
- if (!Buffer.isBuffer(curr)) {
565
- throw (new Error(`internal error: Current item is not a buffer -- ${typeof curr}`));
566
- }
567
- return (Buffer.concat([prev, curr]));
568
- }, Buffer.from(''));
569
- if (request.headers['content-type'] === 'application/json') {
570
- try {
571
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
572
- postData = JSON.parse(data.toString('utf-8'));
573
- }
574
- catch {
575
- throw (new Error('Invalid JSON data'));
576
- }
577
- }
578
- else {
579
- throw (new KeetaAnchorUserError('Unsupported content type'));
580
- }
581
- /**
582
- * Call the route handler
583
- */
584
- result = await route(params, postData);
585
- }
586
- else {
587
- result = await route(params, undefined);
588
- }
589
- generatedResult = true;
590
- }
591
- catch (err) {
592
- /**
593
- * If an error occurs, log it and return an error page
594
- */
595
- this.logger?.error('KeetaAnchorFX.Server', err);
596
- /**
597
- * If it is a user error, provide a user-friendly error page
598
- */
599
- const errorHandlerRoute = routes['ERROR'];
600
- if (errorHandlerRoute !== undefined) {
601
- if (KeetaAnchorUserError.isInstance(err)) {
602
- result = await errorHandlerRoute(new Map(), err.asErrorResponse('application/json'));
603
- generatedResult = true;
604
- }
605
- else {
606
- result = await errorHandlerRoute(new Map(), {
607
- error: JSON.stringify({ ok: false, error: 'Internal Server Error' }),
608
- statusCode: 500,
609
- contentType: 'application/json'
610
- });
611
- generatedResult = true;
612
- }
613
- }
614
- if (!generatedResult) {
615
- /**
616
- * Otherwise provide a generic error page
617
- */
618
- response.statusCode = 500;
619
- response.setHeader('Content-Type', 'text/plain');
620
- response.write('Internal Server Error');
621
- response.end();
622
- return;
623
- }
295
+ const block = new KeetaNet.lib.Block(request.block);
296
+ let userClient;
297
+ if (KeetaNet.UserClient.isInstance(config.client)) {
298
+ userClient = config.client;
624
299
  }
625
- if (result === undefined) {
626
- throw (new Error('internal error: No result'));
300
+ else {
301
+ const { account, signer } = await requestToAccounts(config, quote.request);
302
+ userClient = new KeetaNet.UserClient({
303
+ client: config.client.client,
304
+ network: config.client.network,
305
+ networkAlias: config.client.networkAlias,
306
+ account: account,
307
+ signer: signer
308
+ });
627
309
  }
628
- /**
629
- * Write the response to the client
630
- */
631
- response.statusCode = result.statusCode ?? 200;
632
- for (const headerKey in result.headers ?? {}) {
633
- const headerValue = result.headers?.[headerKey];
634
- if (headerValue !== undefined) {
635
- response.setHeader(headerKey, headerValue);
636
- }
310
+ const expectedToken = KeetaNet.lib.Account.fromPublicKeyString(quote.request.from);
311
+ const expectedAmount = quote.request.affinity === 'from' ? quote.request.amount : quote.convertedAmount;
312
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
313
+ const swapBlocks = await acceptSwapRequest(userClient, block, { token: expectedToken, amount: BigInt(expectedAmount) });
314
+ const publishResult = await userClient.client.transmit(swapBlocks);
315
+ if (!publishResult.publish) {
316
+ throw (new Error('Exchange Publish Failed'));
637
317
  }
638
- response.setHeader('Content-Type', result.contentType ?? 'application/json');
639
- response.write(result.output);
640
- response.end();
641
- });
642
- this.#server = server;
643
- /**
644
- * Create a promise to wait for the server to close
645
- */
646
- const waiter = new Promise((resolve) => {
647
- server.on('close', () => {
648
- this.logger?.debug('KeetaAnchorFX.Server', 'Server closed');
649
- resolve();
318
+ const exchangeResponse = {
319
+ ok: true,
320
+ exchangeID: block.hash.toString()
321
+ };
322
+ return ({
323
+ output: JSON.stringify(exchangeResponse)
650
324
  });
651
- });
652
- /**
653
- * Start listening on the port
654
- */
655
- server.listen(port, () => {
656
- const address = server.address();
657
- if (address !== null && typeof address === 'object') {
658
- // @ts-ignore
659
- this.port = address.port;
660
- onSetPort?.(this.port);
325
+ };
326
+ routes['GET /api/getExchangeStatus/:id'] = async function (params) {
327
+ if (params === undefined || params === null) {
328
+ throw (new KeetaAnchorUserError('Expected params'));
661
329
  }
662
- this.logger?.debug('KeetaAnchorFX.Server', 'Listening on port:', this.port);
663
- });
664
- /**
665
- * Wait for the server to close
666
- */
667
- await waiter;
668
- }
669
- async start() {
670
- await new Promise((resolve) => {
671
- this.#serverPromise = this.main(function () {
672
- resolve();
330
+ const exchangeID = params.get('id');
331
+ if (typeof exchangeID !== 'string') {
332
+ throw (new Error('Missing exchangeID in params'));
333
+ }
334
+ const blockLookup = await config.client.client.getVoteStaple(exchangeID);
335
+ if (blockLookup === null) {
336
+ throw (new Error('Block Not Found'));
337
+ }
338
+ const exchangeResponse = {
339
+ ok: true,
340
+ exchangeID: exchangeID
341
+ };
342
+ return ({
343
+ output: JSON.stringify(exchangeResponse)
673
344
  });
674
- });
675
- }
676
- async wait() {
677
- await this.#serverPromise;
678
- }
679
- async stop() {
680
- this.#server?.close();
681
- await this.wait();
682
- }
683
- get url() {
684
- if (this.port === 0 || this.#server === undefined) {
685
- throw (new Error('Server not started'));
686
- }
687
- return (`http://localhost:${this.port}`);
688
- }
689
- [Symbol.asyncDispose]() {
690
- return (this.stop());
345
+ };
346
+ return (routes);
691
347
  }
692
348
  }
693
349
  //# sourceMappingURL=server.js.map