@keetanetwork/anchor 0.0.12 → 0.0.13
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.
- package/lib/utils/json.d.ts +2 -2
- package/lib/utils/json.d.ts.map +1 -1
- package/lib/utils/json.js.map +1 -1
- package/npm-shrinkwrap.json +2 -8
- package/package.json +1 -2
- package/services/fx/client.d.ts +1 -0
- package/services/fx/client.d.ts.map +1 -1
- package/services/fx/client.js +72 -72
- package/services/fx/client.js.map +1 -1
- package/services/fx/common.d.ts +23 -14
- package/services/fx/common.d.ts.map +1 -1
- package/services/fx/common.js +177 -1
- package/services/fx/common.js.map +1 -1
- package/services/fx/server.d.ts +13 -4
- package/services/fx/server.d.ts.map +1 -1
- package/services/fx/server.js +58 -151
- package/services/fx/server.js.map +1 -1
package/services/fx/server.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as KeetaAnchorHTTPServer from '../../lib/http-server.js';
|
|
2
2
|
import KeetaNet from '@keetanetwork/keetanet-client';
|
|
3
|
-
import type {
|
|
3
|
+
import type { ConversionInputCanonicalJSON, KeetaFXAnchorQuote, KeetaNetAccount, KeetaNetStorageAccount } from './common.ts';
|
|
4
4
|
import * as Signing from '../../lib/utils/signing.js';
|
|
5
|
+
import type { ServiceMetadata } from '../../lib/resolver.js';
|
|
5
6
|
export interface KeetaAnchorFXServerConfig extends KeetaAnchorHTTPServer.KeetaAnchorHTTPServerConfig {
|
|
6
7
|
/**
|
|
7
8
|
* The data to use for the index page (optional)
|
|
@@ -12,7 +13,7 @@ export interface KeetaAnchorFXServerConfig extends KeetaAnchorHTTPServer.KeetaAn
|
|
|
12
13
|
*
|
|
13
14
|
* This may be either a function or a KeetaNet Account instance.
|
|
14
15
|
*/
|
|
15
|
-
account:
|
|
16
|
+
account: KeetaNetAccount | KeetaNetStorageAccount | ((request: ConversionInputCanonicalJSON) => Promise<KeetaNetAccount | KeetaNetStorageAccount> | KeetaNetAccount | KeetaNetStorageAccount);
|
|
16
17
|
/**
|
|
17
18
|
* Account which can be used to sign transactions
|
|
18
19
|
* for the account above (if not supplied the
|
|
@@ -20,7 +21,7 @@ export interface KeetaAnchorFXServerConfig extends KeetaAnchorHTTPServer.KeetaAn
|
|
|
20
21
|
*
|
|
21
22
|
* This may be either a function or a KeetaNet Account instance.
|
|
22
23
|
*/
|
|
23
|
-
signer?: InstanceType<typeof KeetaNet.lib.Account> | ((request:
|
|
24
|
+
signer?: InstanceType<typeof KeetaNet.lib.Account> | ((request: ConversionInputCanonicalJSON) => Promise<InstanceType<typeof KeetaNet.lib.Account>> | InstanceType<typeof KeetaNet.lib.Account>);
|
|
24
25
|
/**
|
|
25
26
|
* Account which performs the signing and validation of quotes
|
|
26
27
|
*/
|
|
@@ -29,12 +30,16 @@ export interface KeetaAnchorFXServerConfig extends KeetaAnchorHTTPServer.KeetaAn
|
|
|
29
30
|
* Configuration for FX handling
|
|
30
31
|
*/
|
|
31
32
|
fx: {
|
|
33
|
+
/**
|
|
34
|
+
* Supported conversions
|
|
35
|
+
*/
|
|
36
|
+
from?: NonNullable<ServiceMetadata['services']['fx']>[string]['from'];
|
|
32
37
|
/**
|
|
33
38
|
* Handle the conversion request of one token to another
|
|
34
39
|
*
|
|
35
40
|
* This is used to handle quotes and estimates
|
|
36
41
|
*/
|
|
37
|
-
getConversionRateAndFee: (request:
|
|
42
|
+
getConversionRateAndFee: (request: ConversionInputCanonicalJSON) => Promise<Omit<KeetaFXAnchorQuote, 'request' | 'signed'>>;
|
|
38
43
|
};
|
|
39
44
|
/**
|
|
40
45
|
* The network client to use for submitting blocks
|
|
@@ -54,5 +59,9 @@ export declare class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.Ke
|
|
|
54
59
|
readonly fx: KeetaAnchorFXServerConfig['fx'];
|
|
55
60
|
constructor(config: KeetaAnchorFXServerConfig);
|
|
56
61
|
protected initRoutes(config: KeetaAnchorFXServerConfig): Promise<KeetaAnchorHTTPServer.Routes>;
|
|
62
|
+
/**
|
|
63
|
+
* Return the servers endpoints and possible currency conversions metadata
|
|
64
|
+
*/
|
|
65
|
+
serviceMetadata(): Promise<NonNullable<ServiceMetadata['services']['fx']>[string]>;
|
|
57
66
|
}
|
|
58
67
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/services/fx/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,qBAAqB,MAAM,0BAA0B,CAAC;AAClE,OAAO,QAAQ,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/services/fx/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,qBAAqB,MAAM,0BAA0B,CAAC;AAClE,OAAO,QAAQ,MAAM,+BAA+B,CAAC;AAQrD,OAAO,KAAK,EACX,4BAA4B,EAG5B,kBAAkB,EAGlB,eAAe,EACf,sBAAsB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,OAAO,MAAM,4BAA4B,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB,CAAC,2BAA2B;IACnG;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IAErD;;;;OAIG;IACH,OAAO,EAAE,eAAe,GAAG,sBAAsB,GAAG,CAAC,CAAC,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC,eAAe,GAAG,sBAAsB,CAAC,GAAG,eAAe,GAAG,sBAAsB,CAAC,CAAC;IAC9L;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC,YAAY,CAAC,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAEjM;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC,eAAe,CAAC;IAErC;;OAEG;IACH,EAAE,EAAE;QACH;;WAEG;QACH,IAAI,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;QACtE;;;;WAIG;QACH,uBAAuB,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,QAAQ,CAAE,CAAC,CAAC;KAC7H,CAAC;IAEF;;OAEG;IACH,MAAM,EAAE;QAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;KAAE,GAAG,QAAQ,CAAC,UAAU,CAAC;CAC9I;AAqED,qBAAa,0BAA2B,SAAQ,qBAAqB,CAAC,wBAAwB,CAAC,yBAAyB,CAAE,YAAW,QAAQ,CAAC,yBAAyB,CAAC;IACvK,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,yBAAyB,CAAC,UAAU,CAAC,CAAC,CAAC;IACtE,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACrD,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC,SAAS,CAAC,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,WAAW,EAAE,yBAAyB,CAAC,aAAa,CAAC,CAAC;IAC/D,QAAQ,CAAC,EAAE,EAAE,yBAAyB,CAAC,IAAI,CAAC,CAAC;gBAEjC,MAAM,EAAE,yBAAyB;cAW7B,UAAU,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC;IAsMpG;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;CAaxF"}
|
package/services/fx/server.js
CHANGED
|
@@ -1,148 +1,8 @@
|
|
|
1
|
-
import * as __typia_transform__assertGuard from "typia/lib/internal/_assertGuard.js";
|
|
2
1
|
import * as KeetaAnchorHTTPServer from '../../lib/http-server.js';
|
|
3
2
|
import KeetaNet from '@keetanetwork/keetanet-client';
|
|
4
|
-
import { createAssert } from 'typia';
|
|
5
3
|
import { KeetaAnchorUserError } from '../../lib/error.js';
|
|
4
|
+
import { assertConversionInputCanonicalJSON, assertConversionQuoteJSON } from './common.js';
|
|
6
5
|
import * as Signing from '../../lib/utils/signing.js';
|
|
7
|
-
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, {
|
|
8
|
-
method: "createAssert",
|
|
9
|
-
path: _path + ".from",
|
|
10
|
-
expected: "(`keeta_am${string}` | `keeta_an${string}` | `keeta_ao${string}` | `keeta_ap${string}` | `tyblocks_am${string}` | `tyblocks_an${string}` | `tyblocks_ao${string}` | `tyblocks_ap${string}`)",
|
|
11
|
-
value: input.from
|
|
12
|
-
}, _errorFactory)) && ("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)) || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
13
|
-
method: "createAssert",
|
|
14
|
-
path: _path + ".to",
|
|
15
|
-
expected: "(`keeta_am${string}` | `keeta_an${string}` | `keeta_ao${string}` | `keeta_ap${string}` | `tyblocks_am${string}` | `tyblocks_an${string}` | `tyblocks_ao${string}` | `tyblocks_ap${string}`)",
|
|
16
|
-
value: input.to
|
|
17
|
-
}, _errorFactory)) && ("string" === typeof input.amount || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
18
|
-
method: "createAssert",
|
|
19
|
-
path: _path + ".amount",
|
|
20
|
-
expected: "string",
|
|
21
|
-
value: input.amount
|
|
22
|
-
}, _errorFactory)) && ("from" === input.affinity || "to" === input.affinity || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
23
|
-
method: "createAssert",
|
|
24
|
-
path: _path + ".affinity",
|
|
25
|
-
expected: "(\"from\" | \"to\")",
|
|
26
|
-
value: input.affinity
|
|
27
|
-
}, _errorFactory)); const __is = input => "object" === typeof input && null !== input && _io0(input); let _errorFactory; return (input, errorFactory) => {
|
|
28
|
-
if (false === __is(input)) {
|
|
29
|
-
_errorFactory = errorFactory;
|
|
30
|
-
((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || __typia_transform__assertGuard._assertGuard(true, {
|
|
31
|
-
method: "createAssert",
|
|
32
|
-
path: _path + "",
|
|
33
|
-
expected: "ConversionInputCanonical",
|
|
34
|
-
value: input
|
|
35
|
-
}, _errorFactory)) && _ao0(input, _path + "", true) || __typia_transform__assertGuard._assertGuard(true, {
|
|
36
|
-
method: "createAssert",
|
|
37
|
-
path: _path + "",
|
|
38
|
-
expected: "ConversionInputCanonical",
|
|
39
|
-
value: input
|
|
40
|
-
}, _errorFactory))(input, "$input", true);
|
|
41
|
-
}
|
|
42
|
-
return input;
|
|
43
|
-
}; })();
|
|
44
|
-
const assertConversionQuote = (() => { const _io0 = input => "object" === typeof input.request && null !== input.request && _io1(input.request) && "string" === typeof input.account && "string" === typeof input.convertedAmount && ("object" === typeof input.cost && null !== input.cost && _io2(input.cost)) && ("object" === typeof input.signed && null !== input.signed && _io3(input.signed)); const _io1 = 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 _io2 = input => "string" === typeof input.amount && ("string" === typeof input.token && (RegExp(/^keeta_am(.*)/).test(input.token) || RegExp(/^keeta_an(.*)/).test(input.token) || RegExp(/^keeta_ao(.*)/).test(input.token) || RegExp(/^keeta_ap(.*)/).test(input.token) || RegExp(/^tyblocks_am(.*)/).test(input.token) || RegExp(/^tyblocks_an(.*)/).test(input.token) || RegExp(/^tyblocks_ao(.*)/).test(input.token) || RegExp(/^tyblocks_ap(.*)/).test(input.token))); const _io3 = input => "string" === typeof input.nonce && "string" === typeof input.timestamp && "string" === typeof input.signature; const _ao0 = (input, _path, _exceptionable = true) => (("object" === typeof input.request && null !== input.request || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
45
|
-
method: "createAssert",
|
|
46
|
-
path: _path + ".request",
|
|
47
|
-
expected: "ConversionInputCanonical",
|
|
48
|
-
value: input.request
|
|
49
|
-
}, _errorFactory)) && _ao1(input.request, _path + ".request", true && _exceptionable) || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
50
|
-
method: "createAssert",
|
|
51
|
-
path: _path + ".request",
|
|
52
|
-
expected: "ConversionInputCanonical",
|
|
53
|
-
value: input.request
|
|
54
|
-
}, _errorFactory)) && ("string" === typeof input.account || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
55
|
-
method: "createAssert",
|
|
56
|
-
path: _path + ".account",
|
|
57
|
-
expected: "string",
|
|
58
|
-
value: input.account
|
|
59
|
-
}, _errorFactory)) && ("string" === typeof input.convertedAmount || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
60
|
-
method: "createAssert",
|
|
61
|
-
path: _path + ".convertedAmount",
|
|
62
|
-
expected: "string",
|
|
63
|
-
value: input.convertedAmount
|
|
64
|
-
}, _errorFactory)) && (("object" === typeof input.cost && null !== input.cost || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
65
|
-
method: "createAssert",
|
|
66
|
-
path: _path + ".cost",
|
|
67
|
-
expected: "__type",
|
|
68
|
-
value: input.cost
|
|
69
|
-
}, _errorFactory)) && _ao2(input.cost, _path + ".cost", true && _exceptionable) || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
70
|
-
method: "createAssert",
|
|
71
|
-
path: _path + ".cost",
|
|
72
|
-
expected: "__type",
|
|
73
|
-
value: input.cost
|
|
74
|
-
}, _errorFactory)) && (("object" === typeof input.signed && null !== input.signed || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
75
|
-
method: "createAssert",
|
|
76
|
-
path: _path + ".signed",
|
|
77
|
-
expected: "__type.o1",
|
|
78
|
-
value: input.signed
|
|
79
|
-
}, _errorFactory)) && _ao3(input.signed, _path + ".signed", true && _exceptionable) || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
80
|
-
method: "createAssert",
|
|
81
|
-
path: _path + ".signed",
|
|
82
|
-
expected: "__type.o1",
|
|
83
|
-
value: input.signed
|
|
84
|
-
}, _errorFactory)); const _ao1 = (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, {
|
|
85
|
-
method: "createAssert",
|
|
86
|
-
path: _path + ".from",
|
|
87
|
-
expected: "(`keeta_am${string}` | `keeta_an${string}` | `keeta_ao${string}` | `keeta_ap${string}` | `tyblocks_am${string}` | `tyblocks_an${string}` | `tyblocks_ao${string}` | `tyblocks_ap${string}`)",
|
|
88
|
-
value: input.from
|
|
89
|
-
}, _errorFactory)) && ("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)) || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
90
|
-
method: "createAssert",
|
|
91
|
-
path: _path + ".to",
|
|
92
|
-
expected: "(`keeta_am${string}` | `keeta_an${string}` | `keeta_ao${string}` | `keeta_ap${string}` | `tyblocks_am${string}` | `tyblocks_an${string}` | `tyblocks_ao${string}` | `tyblocks_ap${string}`)",
|
|
93
|
-
value: input.to
|
|
94
|
-
}, _errorFactory)) && ("string" === typeof input.amount || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
95
|
-
method: "createAssert",
|
|
96
|
-
path: _path + ".amount",
|
|
97
|
-
expected: "string",
|
|
98
|
-
value: input.amount
|
|
99
|
-
}, _errorFactory)) && ("from" === input.affinity || "to" === input.affinity || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
100
|
-
method: "createAssert",
|
|
101
|
-
path: _path + ".affinity",
|
|
102
|
-
expected: "(\"from\" | \"to\")",
|
|
103
|
-
value: input.affinity
|
|
104
|
-
}, _errorFactory)); const _ao2 = (input, _path, _exceptionable = true) => ("string" === typeof input.amount || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
105
|
-
method: "createAssert",
|
|
106
|
-
path: _path + ".amount",
|
|
107
|
-
expected: "string",
|
|
108
|
-
value: input.amount
|
|
109
|
-
}, _errorFactory)) && ("string" === typeof input.token && (RegExp(/^keeta_am(.*)/).test(input.token) || RegExp(/^keeta_an(.*)/).test(input.token) || RegExp(/^keeta_ao(.*)/).test(input.token) || RegExp(/^keeta_ap(.*)/).test(input.token) || RegExp(/^tyblocks_am(.*)/).test(input.token) || RegExp(/^tyblocks_an(.*)/).test(input.token) || RegExp(/^tyblocks_ao(.*)/).test(input.token) || RegExp(/^tyblocks_ap(.*)/).test(input.token)) || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
110
|
-
method: "createAssert",
|
|
111
|
-
path: _path + ".token",
|
|
112
|
-
expected: "(`keeta_am${string}` | `keeta_an${string}` | `keeta_ao${string}` | `keeta_ap${string}` | `tyblocks_am${string}` | `tyblocks_an${string}` | `tyblocks_ao${string}` | `tyblocks_ap${string}`)",
|
|
113
|
-
value: input.token
|
|
114
|
-
}, _errorFactory)); const _ao3 = (input, _path, _exceptionable = true) => ("string" === typeof input.nonce || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
115
|
-
method: "createAssert",
|
|
116
|
-
path: _path + ".nonce",
|
|
117
|
-
expected: "string",
|
|
118
|
-
value: input.nonce
|
|
119
|
-
}, _errorFactory)) && ("string" === typeof input.timestamp || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
120
|
-
method: "createAssert",
|
|
121
|
-
path: _path + ".timestamp",
|
|
122
|
-
expected: "string",
|
|
123
|
-
value: input.timestamp
|
|
124
|
-
}, _errorFactory)) && ("string" === typeof input.signature || __typia_transform__assertGuard._assertGuard(_exceptionable, {
|
|
125
|
-
method: "createAssert",
|
|
126
|
-
path: _path + ".signature",
|
|
127
|
-
expected: "string",
|
|
128
|
-
value: input.signature
|
|
129
|
-
}, _errorFactory)); const __is = input => "object" === typeof input && null !== input && _io0(input); let _errorFactory; return (input, errorFactory) => {
|
|
130
|
-
if (false === __is(input)) {
|
|
131
|
-
_errorFactory = errorFactory;
|
|
132
|
-
((input, _path, _exceptionable = true) => ("object" === typeof input && null !== input || __typia_transform__assertGuard._assertGuard(true, {
|
|
133
|
-
method: "createAssert",
|
|
134
|
-
path: _path + "",
|
|
135
|
-
expected: "KeetaFXAnchorQuote",
|
|
136
|
-
value: input
|
|
137
|
-
}, _errorFactory)) && _ao0(input, _path + "", true) || __typia_transform__assertGuard._assertGuard(true, {
|
|
138
|
-
method: "createAssert",
|
|
139
|
-
path: _path + "",
|
|
140
|
-
expected: "KeetaFXAnchorQuote",
|
|
141
|
-
value: input
|
|
142
|
-
}, _errorFactory))(input, "$input", true);
|
|
143
|
-
}
|
|
144
|
-
return input;
|
|
145
|
-
}; })();
|
|
146
6
|
;
|
|
147
7
|
async function formatQuoteSignable(unsignedQuote) {
|
|
148
8
|
const retval = [
|
|
@@ -175,9 +35,15 @@ async function requestToAccounts(config, request) {
|
|
|
175
35
|
if (config.signer !== undefined) {
|
|
176
36
|
signer = (KeetaNet.lib.Account.isInstance(config.signer) ? config.signer : await config.signer(request)).assertAccount();
|
|
177
37
|
}
|
|
38
|
+
if (signer === null) {
|
|
39
|
+
signer = account.assertAccount();
|
|
40
|
+
}
|
|
41
|
+
if (!account.isAccount() && !account.isStorage()) {
|
|
42
|
+
throw (new Error('FX Account should be an Account or Storage Account'));
|
|
43
|
+
}
|
|
178
44
|
return ({
|
|
179
|
-
signer: signer
|
|
180
|
-
account: account
|
|
45
|
+
signer: signer,
|
|
46
|
+
account: account
|
|
181
47
|
});
|
|
182
48
|
}
|
|
183
49
|
export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAnchorHTTPServer {
|
|
@@ -229,11 +95,11 @@ export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAn
|
|
|
229
95
|
if (!('request' in postData)) {
|
|
230
96
|
throw (new Error('POST data missing request'));
|
|
231
97
|
}
|
|
232
|
-
const conversion =
|
|
98
|
+
const conversion = assertConversionInputCanonicalJSON(postData.request);
|
|
233
99
|
const rateAndFee = await config.fx.getConversionRateAndFee(conversion);
|
|
234
100
|
const estimateResponse = {
|
|
235
101
|
ok: true,
|
|
236
|
-
estimate: {
|
|
102
|
+
estimate: KeetaNet.lib.Utils.Conversion.toJSONSerializable({
|
|
237
103
|
request: conversion,
|
|
238
104
|
convertedAmount: rateAndFee.convertedAmount,
|
|
239
105
|
expectedCost: {
|
|
@@ -241,7 +107,7 @@ export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAn
|
|
|
241
107
|
max: rateAndFee.cost.amount,
|
|
242
108
|
token: rateAndFee.cost.token
|
|
243
109
|
}
|
|
244
|
-
}
|
|
110
|
+
})
|
|
245
111
|
};
|
|
246
112
|
return ({
|
|
247
113
|
output: JSON.stringify(estimateResponse)
|
|
@@ -254,12 +120,12 @@ export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAn
|
|
|
254
120
|
if (!('request' in postData)) {
|
|
255
121
|
throw (new Error('POST data missing request'));
|
|
256
122
|
}
|
|
257
|
-
const conversion =
|
|
123
|
+
const conversion = assertConversionInputCanonicalJSON(postData.request);
|
|
258
124
|
const rateAndFee = await config.fx.getConversionRateAndFee(conversion);
|
|
259
|
-
const unsignedQuote = {
|
|
125
|
+
const unsignedQuote = KeetaNet.lib.Utils.Conversion.toJSONSerializable({
|
|
260
126
|
request: conversion,
|
|
261
127
|
...rateAndFee
|
|
262
|
-
};
|
|
128
|
+
});
|
|
263
129
|
const signedQuote = await generateSignedQuote(config.quoteSigner, unsignedQuote);
|
|
264
130
|
const quoteResponse = {
|
|
265
131
|
ok: true,
|
|
@@ -286,7 +152,7 @@ export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAn
|
|
|
286
152
|
if (!('block' in request) || typeof request.block !== 'string') {
|
|
287
153
|
throw (new Error('Block was not provided in exchange request'));
|
|
288
154
|
}
|
|
289
|
-
const quote =
|
|
155
|
+
const quote = assertConversionQuoteJSON(request.quote);
|
|
290
156
|
const isValidQuote = await verifySignedData(config.quoteSigner, quote);
|
|
291
157
|
if (!isValidQuote) {
|
|
292
158
|
throw (new Error('Invalid quote signature'));
|
|
@@ -306,8 +172,34 @@ export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAn
|
|
|
306
172
|
signer: signer
|
|
307
173
|
});
|
|
308
174
|
}
|
|
175
|
+
/* Get Expected Amount and Token to Verify Swap */
|
|
309
176
|
const expectedToken = KeetaNet.lib.Account.fromPublicKeyString(quote.request.from);
|
|
310
|
-
|
|
177
|
+
let expectedAmount = quote.request.affinity === 'from' ? BigInt(quote.request.amount) : BigInt(quote.convertedAmount);
|
|
178
|
+
/* If cost is required verify the amounts and token. */
|
|
179
|
+
if (BigInt(quote.cost.amount) > 0) {
|
|
180
|
+
/* If swap token matches the cost token the add the amount since they should be combined in one block and will be checked in `acceptSwapRequest` */
|
|
181
|
+
if (expectedToken.comparePublicKey(quote.cost.token)) {
|
|
182
|
+
expectedAmount += BigInt(quote.cost.amount);
|
|
183
|
+
/* If token is different then check block operations for matching amount and token */
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
let requestIncludesCost = false;
|
|
187
|
+
for (const operation of block.operations) {
|
|
188
|
+
if (operation.type === KeetaNet.lib.Block.OperationType.SEND) {
|
|
189
|
+
const recipientMatches = operation.to.comparePublicKey(quote.account);
|
|
190
|
+
const tokenMatches = operation.token.comparePublicKey(quote.cost.token);
|
|
191
|
+
const amountMatches = operation.amount === BigInt(quote.cost.amount);
|
|
192
|
+
if (recipientMatches && tokenMatches && amountMatches) {
|
|
193
|
+
requestIncludesCost = true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!requestIncludesCost) {
|
|
198
|
+
throw (new Error('Exchange missing required cost'));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/* Verify Request and Generate the Accept Swap Block */
|
|
311
203
|
const swapBlocks = await userClient.acceptSwapRequest({ block, expected: { token: expectedToken, amount: BigInt(expectedAmount) } });
|
|
312
204
|
const publishOptions = {};
|
|
313
205
|
if (userClient.config.generateFeeBlock !== undefined) {
|
|
@@ -347,5 +239,20 @@ export class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAn
|
|
|
347
239
|
};
|
|
348
240
|
return (routes);
|
|
349
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Return the servers endpoints and possible currency conversions metadata
|
|
244
|
+
*/
|
|
245
|
+
async serviceMetadata() {
|
|
246
|
+
const operations = {
|
|
247
|
+
getEstimate: (new URL('/api/getEstimate', this.url)).toString(),
|
|
248
|
+
getQuote: (new URL('/api/getQuote', this.url)).toString(),
|
|
249
|
+
createExchange: (new URL('/api/createExchange', this.url)).toString(),
|
|
250
|
+
getExchangeStatus: (new URL('/api/getExchangeStatus/:id', this.url)).toString()
|
|
251
|
+
};
|
|
252
|
+
return ({
|
|
253
|
+
from: this.fx.from ?? [],
|
|
254
|
+
operations: operations
|
|
255
|
+
});
|
|
256
|
+
}
|
|
350
257
|
}
|
|
351
258
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/services/fx/server.ts"],"names":[],"mappings":"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","sourcesContent":["import * as KeetaAnchorHTTPServer from '../../lib/http-server.js';\nimport KeetaNet from '@keetanetwork/keetanet-client';\nimport { createAssert } from 'typia';\nimport {\n\tKeetaAnchorUserError\n} from '../../lib/error.js';\nimport type {\n\tConversionInputCanonical,\n\tKeetaFXAnchorEstimateResponse,\n\tKeetaFXAnchorExchangeResponse,\n\tKeetaFXAnchorQuote,\n\tKeetaFXAnchorQuoteResponse\n} from './common.ts';\nimport * as Signing from '../../lib/utils/signing.js';\nimport type { AssertNever } from '../../lib/utils/never.ts';\n\nconst assertConversionInputCanonical = createAssert<ConversionInputCanonical>();\nconst assertConversionQuote = createAssert<KeetaFXAnchorQuote>();\n\nexport interface KeetaAnchorFXServerConfig extends KeetaAnchorHTTPServer.KeetaAnchorHTTPServerConfig {\n\t/**\n\t * The data to use for the index page (optional)\n\t */\n\thomepage?: string | (() => Promise<string> | string);\n\n\t/**\n\t * The account to use for performing swaps for a given pair\n\t *\n\t * This may be either a function or a KeetaNet Account instance.\n\t */\n\taccount: InstanceType<typeof KeetaNet.lib.Account> | ((request: ConversionInputCanonical) => Promise<InstanceType<typeof KeetaNet.lib.Account>> | InstanceType<typeof KeetaNet.lib.Account>);\n\t/**\n\t * Account which can be used to sign transactions\n\t * for the account above (if not supplied the\n\t * account will be used).\n\t *\n\t * This may be either a function or a KeetaNet Account instance.\n\t */\n\tsigner?: InstanceType<typeof KeetaNet.lib.Account> | ((request: ConversionInputCanonical) => Promise<InstanceType<typeof KeetaNet.lib.Account>> | InstanceType<typeof KeetaNet.lib.Account>);\n\n\t/**\n\t * Account which performs the signing and validation of quotes\n\t */\n\tquoteSigner: Signing.SignableAccount;\n\n\t/**\n\t * Configuration for FX handling\n\t */\n\tfx: {\n\t\t/**\n\t\t * Handle the conversion request of one token to another\n\t\t *\n\t\t * This is used to handle quotes and estimates\n\t\t */\n\t\tgetConversionRateAndFee: (request: ConversionInputCanonical) => Promise<Omit<KeetaFXAnchorQuote, 'request' | 'signed' >>;\n\t};\n\n\t/**\n\t * The network client to use for submitting blocks\n\t */\n\tclient: { client: KeetaNet.Client; network: bigint; networkAlias: typeof KeetaNet.Client.Config.networksArray[number] } | KeetaNet.UserClient;\n};\n\nasync function formatQuoteSignable(unsignedQuote: Omit<KeetaFXAnchorQuote, 'signed'>): Promise<Signing.Signable> {\n\tconst retval: Signing.Signable = [\n\t\tunsignedQuote.request.from,\n\t\tunsignedQuote.request.to,\n\t\tunsignedQuote.request.amount,\n\t\tunsignedQuote.request.affinity,\n\t\tunsignedQuote.account,\n\t\tunsignedQuote.convertedAmount,\n\t\tunsignedQuote.cost.token,\n\t\tunsignedQuote.cost.amount\n\t];\n\n\treturn(retval);\n\n\t/**\n\t * This is a static assertion to ensure that this function is updated\n\t * if new fields are added to the KeetaFXAnchorQuote type to ensure\n\t * that we are always signing all the fields.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-unused-vars\n\ttype _ignore_static_assert = AssertNever<\n\t\t// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n\t\tAssertNever<keyof Omit<typeof unsignedQuote['request'], 'from' | 'to' | 'amount' | 'affinity'>> &\n\t\t// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents,@typescript-eslint/no-duplicate-type-constituents\n\t\tAssertNever<keyof Omit<typeof unsignedQuote['cost'], 'token' | 'amount'>> &\n\t\t// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents,@typescript-eslint/no-duplicate-type-constituents\n\t\tAssertNever<keyof Omit<typeof unsignedQuote, 'request' | 'convertedAmount' | 'cost' | 'account'>>\n\t>;\n}\n\nasync function generateSignedQuote(signer: Signing.SignableAccount, unsignedQuote: Omit<KeetaFXAnchorQuote, 'signed'>): Promise<KeetaFXAnchorQuote> {\n\tconst signableQuote = await formatQuoteSignable(unsignedQuote);\n\tconst signed = await Signing.SignData(signer, signableQuote);\n\n\treturn({\n\t\t...unsignedQuote,\n\t\tsigned: signed\n\t});\n}\n\nasync function verifySignedData(signedBy: Signing.VerifableAccount, quote: KeetaFXAnchorQuote): Promise<boolean> {\n\tconst signableQuote = await formatQuoteSignable(quote);\n\n\treturn(await Signing.VerifySignedData(signedBy, signableQuote, quote.signed));\n}\n\nasync function requestToAccounts(config: KeetaAnchorFXServerConfig, request: ConversionInputCanonical): Promise<{ signer: Signing.SignableAccount; account: Signing.SignableAccount; }> {\n\tconst account = KeetaNet.lib.Account.isInstance(config.account) ? config.account : await config.account(request);\n\tlet signer: Signing.SignableAccount | null = null;\n\tif (config.signer !== undefined) {\n\t\tsigner = (KeetaNet.lib.Account.isInstance(config.signer) ? config.signer : await config.signer(request)).assertAccount();\n\t}\n\n\treturn({\n\t\tsigner: signer ?? account.assertAccount(),\n\t\taccount: account.assertAccount()\n\t});\n}\n\nexport class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAnchorHTTPServer<KeetaAnchorFXServerConfig> implements Required<KeetaAnchorFXServerConfig> {\n\treadonly homepage: NonNullable<KeetaAnchorFXServerConfig['homepage']>;\n\treadonly client: KeetaAnchorFXServerConfig['client'];\n\treadonly account: KeetaAnchorFXServerConfig['account'];\n\treadonly signer: NonNullable<KeetaAnchorFXServerConfig['signer']>;\n\treadonly quoteSigner: KeetaAnchorFXServerConfig['quoteSigner'];\n\treadonly fx: KeetaAnchorFXServerConfig['fx'];\n\n\tconstructor(config: KeetaAnchorFXServerConfig) {\n\t\tsuper(config);\n\n\t\tthis.homepage = config.homepage ?? '';\n\t\tthis.client = config.client;\n\t\tthis.fx = config.fx;\n\t\tthis.account = config.account;\n\t\tthis.signer = config.signer ?? config.account;\n\t\tthis.quoteSigner = config.quoteSigner;\n\t}\n\n\tprotected async initRoutes(config: KeetaAnchorFXServerConfig): Promise<KeetaAnchorHTTPServer.Routes> {\n\t\tconst routes: KeetaAnchorHTTPServer.Routes = {};\n\n\t\t/**\n\t\t * If a homepage is provided, setup the route for it\n\t\t */\n\t\tif ('homepage' in config) {\n\t\t\troutes['GET /'] = async function() {\n\t\t\t\tlet homepageData: string;\n\t\t\t\tif (typeof config.homepage === 'string') {\n\t\t\t\t\thomepageData = config.homepage;\n\t\t\t\t} else {\n\t\t\t\t\tif (!config.homepage) {\n\t\t\t\t\t\tthrow(new Error('internal error: No homepage function provided'));\n\t\t\t\t\t}\n\n\t\t\t\t\thomepageData = await config.homepage();\n\t\t\t\t}\n\n\t\t\t\treturn({\n\t\t\t\t\toutput: homepageData,\n\t\t\t\t\tcontentType: 'text/html'\n\t\t\t\t});\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Setup the request handler for an estimate request\n\t\t */\n\t\troutes['POST /api/getEstimate'] = async function(_ignore_params, postData) {\n\t\t\tif (!postData || typeof postData !== 'object') {\n\t\t\t\tthrow(new Error('No POST data provided'));\n\t\t\t}\n\t\t\tif (!('request' in postData)) {\n\t\t\t\tthrow(new Error('POST data missing request'));\n\t\t\t}\n\n\t\t\tconst conversion = assertConversionInputCanonical(postData.request);\n\t\t\tconst rateAndFee = await config.fx.getConversionRateAndFee(conversion);\n\t\t\tconst estimateResponse: KeetaFXAnchorEstimateResponse = {\n\t\t\t\tok: true,\n\t\t\t\testimate: {\n\t\t\t\t\trequest: conversion,\n\t\t\t\t\tconvertedAmount: rateAndFee.convertedAmount,\n\t\t\t\t\texpectedCost: {\n\t\t\t\t\t\tmin: rateAndFee.cost.amount,\n\t\t\t\t\t\tmax: rateAndFee.cost.amount,\n\t\t\t\t\t\ttoken: rateAndFee.cost.token\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(estimateResponse)\n\t\t\t});\n\t\t}\n\n\t\troutes['POST /api/getQuote'] = async function(_ignore_params, postData) {\n\t\t\tif (!postData || typeof postData !== 'object') {\n\t\t\t\tthrow(new Error('No POST data provided'));\n\t\t\t}\n\t\t\tif (!('request' in postData)) {\n\t\t\t\tthrow(new Error('POST data missing request'));\n\t\t\t}\n\n\t\t\tconst conversion = assertConversionInputCanonical(postData.request);\n\t\t\tconst rateAndFee = await config.fx.getConversionRateAndFee(conversion);\n\n\t\t\tconst unsignedQuote: Omit<KeetaFXAnchorQuote, 'signed'> = {\n\t\t\t\trequest: conversion,\n\t\t\t\t...rateAndFee\n\t\t\t};\n\n\t\t\tconst signedQuote = await generateSignedQuote(config.quoteSigner, unsignedQuote);\n\t\t\tconst quoteResponse: KeetaFXAnchorQuoteResponse = {\n\t\t\t\tok: true,\n\t\t\t\tquote: signedQuote\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(quoteResponse)\n\t\t\t});\n\t\t}\n\n\t\troutes['POST /api/createExchange'] = async function(_ignore_params, postData) {\n\t\t\tif (!postData || typeof postData !== 'object') {\n\t\t\t\tthrow(new Error('No POST data provided'));\n\t\t\t}\n\n\t\t\tif (!('request' in postData)) {\n\t\t\t\tthrow(new Error('POST data missing request'));\n\t\t\t}\n\t\t\tconst request = postData.request;\n\t\t\tif (!request || typeof request !== 'object') {\n\t\t\t\tthrow(new Error('Request is not an object'));\n\t\t\t}\n\n\t\t\tif (!('quote' in request)) {\n\t\t\t\tthrow(new Error('Quote is missing from request'));\n\t\t\t}\n\t\t\tif (!('block' in request) || typeof request.block !== 'string') {\n\t\t\t\tthrow(new Error('Block was not provided in exchange request'));\n\t\t\t}\n\n\t\t\tconst quote = assertConversionQuote(request.quote);\n\t\t\tconst isValidQuote = await verifySignedData(config.quoteSigner, quote);\n\t\t\tif (!isValidQuote) {\n\t\t\t\tthrow(new Error('Invalid quote signature'));\n\t\t\t}\n\n\t\t\tconst block = new KeetaNet.lib.Block(request.block);\n\t\t\tlet userClient: KeetaNet.UserClient;\n\t\t\tif (KeetaNet.UserClient.isInstance(config.client)) {\n\t\t\t\tuserClient = config.client;\n\t\t\t} else {\n\t\t\t\tconst { account, signer } = await requestToAccounts(config, quote.request);\n\n\t\t\t\tuserClient = new KeetaNet.UserClient({\n\t\t\t\t\tclient: config.client.client,\n\t\t\t\t\tnetwork: config.client.network,\n\t\t\t\t\tnetworkAlias: config.client.networkAlias,\n\t\t\t\t\taccount: account,\n\t\t\t\t\tsigner: signer\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst expectedToken = KeetaNet.lib.Account.fromPublicKeyString(quote.request.from);\n\t\t\tconst expectedAmount = quote.request.affinity === 'from' ? quote.request.amount : quote.convertedAmount;\n\t\t\tconst swapBlocks = await userClient.acceptSwapRequest({ block, expected: { token: expectedToken, amount: BigInt(expectedAmount) }});\n\t\t\tconst publishOptions: Parameters<typeof userClient.client.transmit>[1] = {};\n\t\t\tif (userClient.config.generateFeeBlock !== undefined) {\n\t\t\t\tpublishOptions.generateFeeBlock = userClient.config.generateFeeBlock;\n\t\t\t}\n\t\t\tconst publishResult = await userClient.client.transmit(swapBlocks, publishOptions);\n\t\t\tif (!publishResult.publish) {\n\t\t\t\tthrow(new Error('Exchange Publish Failed'));\n\t\t\t}\n\t\t\tconst exchangeResponse: KeetaFXAnchorExchangeResponse = {\n\t\t\t\tok: true,\n\t\t\t\texchangeID: block.hash.toString()\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(exchangeResponse)\n\t\t\t});\n\t\t}\n\n\t\troutes['GET /api/getExchangeStatus/:id'] = async function(params) {\n\t\t\tif (params === undefined || params === null) {\n\t\t\t\tthrow(new KeetaAnchorUserError('Expected params'));\n\t\t\t}\n\t\t\tconst exchangeID = params.get('id');\n\t\t\tif (typeof exchangeID !== 'string') {\n\t\t\t\tthrow(new Error('Missing exchangeID in params'));\n\t\t\t}\n\t\t\tconst blockLookup = await config.client.client.getVoteStaple(exchangeID);\n\t\t\tif (blockLookup === null) {\n\t\t\t\tthrow(new Error('Block Not Found'));\n\t\t\t}\n\t\t\tconst exchangeResponse: KeetaFXAnchorExchangeResponse = {\n\t\t\t\tok: true,\n\t\t\t\texchangeID: exchangeID\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(exchangeResponse)\n\t\t\t});\n\t\t}\n\n\t\treturn(routes);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/services/fx/server.ts"],"names":[],"mappings":"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","sourcesContent":["import * as KeetaAnchorHTTPServer from '../../lib/http-server.js';\nimport KeetaNet from '@keetanetwork/keetanet-client';\nimport {\n\tKeetaAnchorUserError\n} from '../../lib/error.js';\nimport {\n\tassertConversionInputCanonicalJSON,\n\tassertConversionQuoteJSON\n} from './common.js';\nimport type {\n\tConversionInputCanonicalJSON,\n\tKeetaFXAnchorEstimateResponse,\n\tKeetaFXAnchorExchangeResponse,\n\tKeetaFXAnchorQuote,\n\tKeetaFXAnchorQuoteJSON,\n\tKeetaFXAnchorQuoteResponse,\n\tKeetaNetAccount,\n\tKeetaNetStorageAccount\n} from './common.ts';\nimport * as Signing from '../../lib/utils/signing.js';\nimport type { AssertNever } from '../../lib/utils/never.ts';\nimport type { ServiceMetadata } from '../../lib/resolver.js';\n\nexport interface KeetaAnchorFXServerConfig extends KeetaAnchorHTTPServer.KeetaAnchorHTTPServerConfig {\n\t/**\n\t * The data to use for the index page (optional)\n\t */\n\thomepage?: string | (() => Promise<string> | string);\n\n\t/**\n\t * The account to use for performing swaps for a given pair\n\t *\n\t * This may be either a function or a KeetaNet Account instance.\n\t */\n\taccount: KeetaNetAccount | KeetaNetStorageAccount | ((request: ConversionInputCanonicalJSON) => Promise<KeetaNetAccount | KeetaNetStorageAccount> | KeetaNetAccount | KeetaNetStorageAccount);\n\t/**\n\t * Account which can be used to sign transactions\n\t * for the account above (if not supplied the\n\t * account will be used).\n\t *\n\t * This may be either a function or a KeetaNet Account instance.\n\t */\n\tsigner?: InstanceType<typeof KeetaNet.lib.Account> | ((request: ConversionInputCanonicalJSON) => Promise<InstanceType<typeof KeetaNet.lib.Account>> | InstanceType<typeof KeetaNet.lib.Account>);\n\n\t/**\n\t * Account which performs the signing and validation of quotes\n\t */\n\tquoteSigner: Signing.SignableAccount;\n\n\t/**\n\t * Configuration for FX handling\n\t */\n\tfx: {\n\t\t/**\n\t\t * Supported conversions\n\t\t */\n\t\tfrom?: NonNullable<ServiceMetadata['services']['fx']>[string]['from'];\n\t\t/**\n\t\t * Handle the conversion request of one token to another\n\t\t *\n\t\t * This is used to handle quotes and estimates\n\t\t */\n\t\tgetConversionRateAndFee: (request: ConversionInputCanonicalJSON) => Promise<Omit<KeetaFXAnchorQuote, 'request' | 'signed' >>;\n\t};\n\n\t/**\n\t * The network client to use for submitting blocks\n\t */\n\tclient: { client: KeetaNet.Client; network: bigint; networkAlias: typeof KeetaNet.Client.Config.networksArray[number] } | KeetaNet.UserClient;\n};\n\nasync function formatQuoteSignable(unsignedQuote: Omit<KeetaFXAnchorQuoteJSON, 'signed'>): Promise<Signing.Signable> {\n\tconst retval: Signing.Signable = [\n\t\tunsignedQuote.request.from,\n\t\tunsignedQuote.request.to,\n\t\tunsignedQuote.request.amount,\n\t\tunsignedQuote.request.affinity,\n\t\tunsignedQuote.account,\n\t\tunsignedQuote.convertedAmount,\n\t\tunsignedQuote.cost.token,\n\t\tunsignedQuote.cost.amount\n\t];\n\n\treturn(retval);\n\n\t/**\n\t * This is a static assertion to ensure that this function is updated\n\t * if new fields are added to the KeetaFXAnchorQuote type to ensure\n\t * that we are always signing all the fields.\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-unused-vars\n\ttype _ignore_static_assert = AssertNever<\n\t\t// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n\t\tAssertNever<keyof Omit<typeof unsignedQuote['request'], 'from' | 'to' | 'amount' | 'affinity'>> &\n\t\t// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents,@typescript-eslint/no-duplicate-type-constituents\n\t\tAssertNever<keyof Omit<typeof unsignedQuote['cost'], 'token' | 'amount'>> &\n\t\t// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents,@typescript-eslint/no-duplicate-type-constituents\n\t\tAssertNever<keyof Omit<typeof unsignedQuote, 'request' | 'convertedAmount' | 'cost' | 'account'>>\n\t>;\n}\n\nasync function generateSignedQuote(signer: Signing.SignableAccount, unsignedQuote: Omit<KeetaFXAnchorQuoteJSON, 'signed'>): Promise<KeetaFXAnchorQuoteJSON> {\n\tconst signableQuote = await formatQuoteSignable(unsignedQuote);\n\tconst signed = await Signing.SignData(signer, signableQuote);\n\n\treturn({\n\t\t...unsignedQuote,\n\t\tsigned: signed\n\t});\n}\n\nasync function verifySignedData(signedBy: Signing.VerifableAccount, quote: KeetaFXAnchorQuoteJSON): Promise<boolean> {\n\tconst signableQuote = await formatQuoteSignable(quote);\n\n\treturn(await Signing.VerifySignedData(signedBy, signableQuote, quote.signed));\n}\n\nasync function requestToAccounts(config: KeetaAnchorFXServerConfig, request: ConversionInputCanonicalJSON): Promise<{ signer: Signing.SignableAccount; account: KeetaNetAccount | KeetaNetStorageAccount; }> {\n\tconst account = KeetaNet.lib.Account.isInstance(config.account) ? config.account : await config.account(request);\n\tlet signer: Signing.SignableAccount | null = null;\n\tif (config.signer !== undefined) {\n\t\tsigner = (KeetaNet.lib.Account.isInstance(config.signer) ? config.signer : await config.signer(request)).assertAccount();\n\t}\n\n\tif (signer === null) {\n\t\tsigner = account.assertAccount();\n\t}\n\n\tif (!account.isAccount() && !account.isStorage()) {\n\t\tthrow(new Error('FX Account should be an Account or Storage Account'))\n\t}\n\n\treturn({\n\t\tsigner: signer,\n\t\taccount: account\n\t});\n}\n\nexport class KeetaNetFXAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAnchorHTTPServer<KeetaAnchorFXServerConfig> implements Required<KeetaAnchorFXServerConfig> {\n\treadonly homepage: NonNullable<KeetaAnchorFXServerConfig['homepage']>;\n\treadonly client: KeetaAnchorFXServerConfig['client'];\n\treadonly account: KeetaAnchorFXServerConfig['account'];\n\treadonly signer: NonNullable<KeetaAnchorFXServerConfig['signer']>;\n\treadonly quoteSigner: KeetaAnchorFXServerConfig['quoteSigner'];\n\treadonly fx: KeetaAnchorFXServerConfig['fx'];\n\n\tconstructor(config: KeetaAnchorFXServerConfig) {\n\t\tsuper(config);\n\n\t\tthis.homepage = config.homepage ?? '';\n\t\tthis.client = config.client;\n\t\tthis.fx = config.fx;\n\t\tthis.account = config.account;\n\t\tthis.signer = config.signer ?? config.account;\n\t\tthis.quoteSigner = config.quoteSigner;\n\t}\n\n\tprotected async initRoutes(config: KeetaAnchorFXServerConfig): Promise<KeetaAnchorHTTPServer.Routes> {\n\t\tconst routes: KeetaAnchorHTTPServer.Routes = {};\n\n\t\t/**\n\t\t * If a homepage is provided, setup the route for it\n\t\t */\n\t\tif ('homepage' in config) {\n\t\t\troutes['GET /'] = async function() {\n\t\t\t\tlet homepageData: string;\n\t\t\t\tif (typeof config.homepage === 'string') {\n\t\t\t\t\thomepageData = config.homepage;\n\t\t\t\t} else {\n\t\t\t\t\tif (!config.homepage) {\n\t\t\t\t\t\tthrow(new Error('internal error: No homepage function provided'));\n\t\t\t\t\t}\n\n\t\t\t\t\thomepageData = await config.homepage();\n\t\t\t\t}\n\n\t\t\t\treturn({\n\t\t\t\t\toutput: homepageData,\n\t\t\t\t\tcontentType: 'text/html'\n\t\t\t\t});\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Setup the request handler for an estimate request\n\t\t */\n\t\troutes['POST /api/getEstimate'] = async function(_ignore_params, postData) {\n\t\t\tif (!postData || typeof postData !== 'object') {\n\t\t\t\tthrow(new Error('No POST data provided'));\n\t\t\t}\n\t\t\tif (!('request' in postData)) {\n\t\t\t\tthrow(new Error('POST data missing request'));\n\t\t\t}\n\n\t\t\tconst conversion = assertConversionInputCanonicalJSON(postData.request);\n\t\t\tconst rateAndFee = await config.fx.getConversionRateAndFee(conversion);\n\t\t\tconst estimateResponse: KeetaFXAnchorEstimateResponse = {\n\t\t\t\tok: true,\n\t\t\t\testimate: KeetaNet.lib.Utils.Conversion.toJSONSerializable({\n\t\t\t\t\trequest: conversion,\n\t\t\t\t\tconvertedAmount: rateAndFee.convertedAmount,\n\t\t\t\t\texpectedCost: {\n\t\t\t\t\t\tmin: rateAndFee.cost.amount,\n\t\t\t\t\t\tmax: rateAndFee.cost.amount,\n\t\t\t\t\t\ttoken: rateAndFee.cost.token\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(estimateResponse)\n\t\t\t});\n\t\t}\n\n\t\troutes['POST /api/getQuote'] = async function(_ignore_params, postData) {\n\t\t\tif (!postData || typeof postData !== 'object') {\n\t\t\t\tthrow(new Error('No POST data provided'));\n\t\t\t}\n\t\t\tif (!('request' in postData)) {\n\t\t\t\tthrow(new Error('POST data missing request'));\n\t\t\t}\n\n\t\t\tconst conversion = assertConversionInputCanonicalJSON(postData.request);\n\t\t\tconst rateAndFee = await config.fx.getConversionRateAndFee(conversion);\n\n\t\t\tconst unsignedQuote: Omit<KeetaFXAnchorQuoteJSON, 'signed'> = KeetaNet.lib.Utils.Conversion.toJSONSerializable({\n\t\t\t\trequest: conversion,\n\t\t\t\t...rateAndFee\n\t\t\t});\n\n\t\t\tconst signedQuote = await generateSignedQuote(config.quoteSigner, unsignedQuote);\n\t\t\tconst quoteResponse: KeetaFXAnchorQuoteResponse = {\n\t\t\t\tok: true,\n\t\t\t\tquote: signedQuote\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(quoteResponse)\n\t\t\t});\n\t\t}\n\n\t\troutes['POST /api/createExchange'] = async function(_ignore_params, postData) {\n\t\t\tif (!postData || typeof postData !== 'object') {\n\t\t\t\tthrow(new Error('No POST data provided'));\n\t\t\t}\n\n\t\t\tif (!('request' in postData)) {\n\t\t\t\tthrow(new Error('POST data missing request'));\n\t\t\t}\n\t\t\tconst request = postData.request;\n\t\t\tif (!request || typeof request !== 'object') {\n\t\t\t\tthrow(new Error('Request is not an object'));\n\t\t\t}\n\n\t\t\tif (!('quote' in request)) {\n\t\t\t\tthrow(new Error('Quote is missing from request'));\n\t\t\t}\n\t\t\tif (!('block' in request) || typeof request.block !== 'string') {\n\t\t\t\tthrow(new Error('Block was not provided in exchange request'));\n\t\t\t}\n\n\t\t\tconst quote = assertConversionQuoteJSON(request.quote);\n\t\t\tconst isValidQuote = await verifySignedData(config.quoteSigner, quote);\n\t\t\tif (!isValidQuote) {\n\t\t\t\tthrow(new Error('Invalid quote signature'));\n\t\t\t}\n\n\t\t\tconst block = new KeetaNet.lib.Block(request.block);\n\t\t\tlet userClient: KeetaNet.UserClient;\n\t\t\tif (KeetaNet.UserClient.isInstance(config.client)) {\n\t\t\t\tuserClient = config.client;\n\t\t\t} else {\n\t\t\t\tconst { account, signer } = await requestToAccounts(config, quote.request);\n\n\t\t\t\tuserClient = new KeetaNet.UserClient({\n\t\t\t\t\tclient: config.client.client,\n\t\t\t\t\tnetwork: config.client.network,\n\t\t\t\t\tnetworkAlias: config.client.networkAlias,\n\t\t\t\t\taccount: account,\n\t\t\t\t\tsigner: signer\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t/* Get Expected Amount and Token to Verify Swap */\n\t\t\tconst expectedToken = KeetaNet.lib.Account.fromPublicKeyString(quote.request.from);\n\t\t\tlet expectedAmount = quote.request.affinity === 'from' ? BigInt(quote.request.amount) : BigInt(quote.convertedAmount);\n\t\t\t/* If cost is required verify the amounts and token. */\n\t\t\tif (BigInt(quote.cost.amount) > 0) {\n\t\t\t\t/* If swap token matches the cost token the add the amount since they should be combined in one block and will be checked in `acceptSwapRequest` */\n\t\t\t\tif (expectedToken.comparePublicKey(quote.cost.token)) {\n\t\t\t\t\texpectedAmount += BigInt(quote.cost.amount);\n\t\t\t\t/* If token is different then check block operations for matching amount and token */\n\t\t\t\t} else {\n\t\t\t\t\tlet requestIncludesCost = false;\n\t\t\t\t\tfor (const operation of block.operations) {\n\t\t\t\t\t\tif (operation.type === KeetaNet.lib.Block.OperationType.SEND) {\n\t\t\t\t\t\t\tconst recipientMatches = operation.to.comparePublicKey(quote.account);\n\t\t\t\t\t\t\tconst tokenMatches = operation.token.comparePublicKey(quote.cost.token);\n\t\t\t\t\t\t\tconst amountMatches = operation.amount === BigInt(quote.cost.amount);\n\t\t\t\t\t\t\tif (recipientMatches && tokenMatches && amountMatches) {\n\t\t\t\t\t\t\t\trequestIncludesCost = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!requestIncludesCost) {\n\t\t\t\t\t\tthrow(new Error('Exchange missing required cost'));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Verify Request and Generate the Accept Swap Block */\n\t\t\tconst swapBlocks = await userClient.acceptSwapRequest({ block, expected: { token: expectedToken, amount: BigInt(expectedAmount) }});\n\t\t\tconst publishOptions: Parameters<typeof userClient.client.transmit>[1] = {};\n\t\t\tif (userClient.config.generateFeeBlock !== undefined) {\n\t\t\t\tpublishOptions.generateFeeBlock = userClient.config.generateFeeBlock;\n\t\t\t}\n\t\t\tconst publishResult = await userClient.client.transmit(swapBlocks, publishOptions);\n\t\t\tif (!publishResult.publish) {\n\t\t\t\tthrow(new Error('Exchange Publish Failed'));\n\t\t\t}\n\t\t\tconst exchangeResponse: KeetaFXAnchorExchangeResponse = {\n\t\t\t\tok: true,\n\t\t\t\texchangeID: block.hash.toString()\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(exchangeResponse)\n\t\t\t});\n\t\t}\n\n\t\troutes['GET /api/getExchangeStatus/:id'] = async function(params) {\n\t\t\tif (params === undefined || params === null) {\n\t\t\t\tthrow(new KeetaAnchorUserError('Expected params'));\n\t\t\t}\n\t\t\tconst exchangeID = params.get('id');\n\t\t\tif (typeof exchangeID !== 'string') {\n\t\t\t\tthrow(new Error('Missing exchangeID in params'));\n\t\t\t}\n\t\t\tconst blockLookup = await config.client.client.getVoteStaple(exchangeID);\n\t\t\tif (blockLookup === null) {\n\t\t\t\tthrow(new Error('Block Not Found'));\n\t\t\t}\n\t\t\tconst exchangeResponse: KeetaFXAnchorExchangeResponse = {\n\t\t\t\tok: true,\n\t\t\t\texchangeID: exchangeID\n\t\t\t};\n\n\t\t\treturn({\n\t\t\t\toutput: JSON.stringify(exchangeResponse)\n\t\t\t});\n\t\t}\n\n\t\treturn(routes);\n\t}\n\n\t/**\n\t * Return the servers endpoints and possible currency conversions metadata\n\t */\n\tasync serviceMetadata(): Promise<NonNullable<ServiceMetadata['services']['fx']>[string]> {\n\t\tconst operations: NonNullable<ServiceMetadata['services']['fx']>[string]['operations'] = {\n\t\t\tgetEstimate: (new URL('/api/getEstimate', this.url)).toString(),\n\t\t\tgetQuote: (new URL('/api/getQuote', this.url)).toString(),\n\t\t\tcreateExchange: (new URL('/api/createExchange', this.url)).toString(),\n\t\t\tgetExchangeStatus: (new URL('/api/getExchangeStatus/:id', this.url)).toString()\n\t\t};\n\n\t\treturn({\n\t\t\tfrom: this.fx.from ?? [],\n\t\t\toperations: operations\n\t\t});\n\t}\n}\n"]}
|