@odatano/nightgate-mcp 0.8.1 → 0.10.0
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/CHANGELOG.md +59 -0
- package/README.md +56 -5
- package/dist/builder.d.ts +11 -0
- package/dist/builder.js +33 -3
- package/dist/builder.js.map +1 -1
- package/dist/client.d.ts +11 -2
- package/dist/client.js +23 -17
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +9 -2
- package/dist/config.js.map +1 -1
- package/dist/factory.d.ts +23 -0
- package/dist/factory.js +106 -0
- package/dist/factory.js.map +1 -0
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/swap.d.ts +78 -0
- package/dist/swap.js +249 -0
- package/dist/swap.js.map +1 -0
- package/dist/tools.js +413 -11
- package/dist/tools.js.map +1 -1
- package/dist/tx-module.d.ts +3 -0
- package/dist/tx-module.js +24 -0
- package/dist/tx-module.js.map +1 -0
- package/package.json +9 -4
package/dist/factory.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local minting on a token factory: the caller's side of a sponsored mint.
|
|
3
|
+
*
|
|
4
|
+
* The issuer secret is derived from `NIGHTGATE_SEED_HEX` by the rule the
|
|
5
|
+
* server applies to a session on the same seed, the `mint` call is built,
|
|
6
|
+
* proven and signed here, and only the fee-unpaid transaction travels to a
|
|
7
|
+
* sponsor. The builder comes from `@odatano/nightgate-tx` on the lineage
|
|
8
|
+
* package `@odatano/contract-token-factory`; both are optional peers.
|
|
9
|
+
*/
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { txExport } from './tx-module.js';
|
|
13
|
+
import { seedIdentity } from './builder.js';
|
|
14
|
+
const FACTORY_PACKAGE = '@odatano/contract-token-factory';
|
|
15
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
let factoryModule;
|
|
17
|
+
let builder;
|
|
18
|
+
async function loadFactoryModule() {
|
|
19
|
+
factoryModule ??= import(FACTORY_PACKAGE).catch((err) => {
|
|
20
|
+
factoryModule = undefined;
|
|
21
|
+
throw new Error(`minting needs ${FACTORY_PACKAGE} next to the MCP server (npm install ${FACTORY_PACKAGE}): ` +
|
|
22
|
+
(err instanceof Error ? err.message : String(err)));
|
|
23
|
+
});
|
|
24
|
+
return factoryModule;
|
|
25
|
+
}
|
|
26
|
+
function defaults(network) {
|
|
27
|
+
return {
|
|
28
|
+
indexerHttpUrl: `https://indexer.${network}.midnight.network/api/v4/graphql`,
|
|
29
|
+
indexerWsUrl: `wss://indexer.${network}.midnight.network/api/v4/graphql/ws`,
|
|
30
|
+
nodeUrl: `wss://rpc.${network}.midnight.network/`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** One builder per process; the package resolves from this install, not the caller's cwd. */
|
|
34
|
+
async function getFactoryBuilder(config) {
|
|
35
|
+
builder ??= (async () => {
|
|
36
|
+
const createTxBuilder = await txExport('createTxBuilder', 'minting', '0.10.2');
|
|
37
|
+
const d = defaults(config.network);
|
|
38
|
+
const opts = {
|
|
39
|
+
seedHex: config.seedHex,
|
|
40
|
+
networkId: config.network,
|
|
41
|
+
indexerHttpUrl: config.indexerHttpUrl ?? d.indexerHttpUrl,
|
|
42
|
+
indexerWsUrl: config.indexerWsUrl ?? d.indexerWsUrl,
|
|
43
|
+
nodeUrl: config.nodeUrl ?? d.nodeUrl,
|
|
44
|
+
package: FACTORY_PACKAGE,
|
|
45
|
+
from: HERE,
|
|
46
|
+
walletSync: false,
|
|
47
|
+
};
|
|
48
|
+
if (config.proofServerUrl) {
|
|
49
|
+
opts.provingMode = 'server';
|
|
50
|
+
opts.proofServerUrl = config.proofServerUrl;
|
|
51
|
+
}
|
|
52
|
+
return createTxBuilder(opts);
|
|
53
|
+
})();
|
|
54
|
+
builder.catch(() => { builder = undefined; });
|
|
55
|
+
return builder;
|
|
56
|
+
}
|
|
57
|
+
/** Build, prove and sign a mint locally; unbound, for sponsor_unbound_transaction. */
|
|
58
|
+
export async function buildMint(config, input) {
|
|
59
|
+
if (!config.seedHex) {
|
|
60
|
+
throw new Error('minting needs NIGHTGATE_SEED_HEX (128 hex) in the MCP server environment: the seed is the issuer');
|
|
61
|
+
}
|
|
62
|
+
const t0 = Date.now();
|
|
63
|
+
const self = await seedIdentity(config);
|
|
64
|
+
const recipient = (input.recipientCoinPublicKey ?? self.coinPublicKey).toLowerCase();
|
|
65
|
+
const foreign = recipient !== self.coinPublicKey.toLowerCase();
|
|
66
|
+
if (foreign && !input.recipientEncryptionPublicKey) {
|
|
67
|
+
throw new Error('minting to another wallet needs recipientEncryptionPublicKey as well: both keys of that wallet, as its get_attester_identity reports them');
|
|
68
|
+
}
|
|
69
|
+
const [issuerSecretOf, prepareMint, tokenTypeOf] = await Promise.all([
|
|
70
|
+
txExport('tokenFactoryIssuerSecret', 'minting', '0.10.2'),
|
|
71
|
+
txExport('prepareMint', 'minting', '0.10.2'),
|
|
72
|
+
txExport('tokenTypeOf', 'minting', '0.10.2'),
|
|
73
|
+
]);
|
|
74
|
+
const { pureCircuits } = await loadFactoryModule();
|
|
75
|
+
const issuerSecret = await issuerSecretOf({ seedHex: config.seedHex });
|
|
76
|
+
const contractAddress = input.contractAddress.toLowerCase();
|
|
77
|
+
const { issuer, domain, type } = await tokenTypeOf(pureCircuits, { issuerSecret, name: input.name, contractAddress });
|
|
78
|
+
const b = await getFactoryBuilder(config);
|
|
79
|
+
const built = await b.buildSponsorable({
|
|
80
|
+
contractAddress,
|
|
81
|
+
call: prepareMint({ name: input.name, amount: input.amount, recipientCoinPublicKey: recipient, issuerSecret }),
|
|
82
|
+
...(foreign ? { recipients: [{ coinPublicKey: recipient, encryptionPublicKey: String(input.recipientEncryptionPublicKey).toLowerCase() }] } : {}),
|
|
83
|
+
bind: false,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
unboundTxB64: String(built.unboundTxB64),
|
|
87
|
+
tokenType: type,
|
|
88
|
+
domain,
|
|
89
|
+
issuerKey: issuer,
|
|
90
|
+
recipientCoinPublicKey: recipient,
|
|
91
|
+
serializedBytes: Number(built.serializedBytes ?? 0),
|
|
92
|
+
buildMs: Date.now() - t0,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** Test seam / shutdown. */
|
|
96
|
+
export async function closeFactoryBuilder() {
|
|
97
|
+
const open = builder;
|
|
98
|
+
builder = undefined;
|
|
99
|
+
if (open) {
|
|
100
|
+
try {
|
|
101
|
+
await (await open).close?.();
|
|
102
|
+
}
|
|
103
|
+
catch { /* best effort */ }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import { registerAnalyticsTools } from './analytics.js';
|
|
|
8
8
|
* host (schema inspection, tests) get a deterministic tool set.
|
|
9
9
|
*/
|
|
10
10
|
export function buildServer(config, caps = { analytics: false }) {
|
|
11
|
-
const server = new McpServer({ name: 'nightgate', version: '0.
|
|
11
|
+
const server = new McpServer({ name: 'nightgate', version: '0.10.0' });
|
|
12
12
|
const client = new NightgateClient(config);
|
|
13
13
|
registerTools(server, client, config);
|
|
14
14
|
if (caps.analytics)
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAOxD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAA0B,EAAE,OAAqB,EAAE,SAAS,EAAE,KAAK,EAAE;IAC/F,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAOxD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAA0B,EAAE,OAAqB,EAAE,SAAS,EAAE,KAAK,EAAE;IAC/F,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,SAAS;QAAE,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/swap.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { NightgateMcpConfig } from './config.js';
|
|
2
|
+
export interface SwapLeg {
|
|
3
|
+
tokenType: string;
|
|
4
|
+
amount: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SwapTermsOut {
|
|
7
|
+
gives: SwapLeg;
|
|
8
|
+
wants: SwapLeg;
|
|
9
|
+
inputs?: number;
|
|
10
|
+
outputs?: number;
|
|
11
|
+
}
|
|
12
|
+
interface TxSwapModule {
|
|
13
|
+
createSwapWallet: (opts: Record<string, unknown>) => Promise<any>;
|
|
14
|
+
readSwapTerms: (tx: unknown) => any;
|
|
15
|
+
decodeOffer: (input: string) => Promise<{
|
|
16
|
+
tx: unknown;
|
|
17
|
+
bound: boolean;
|
|
18
|
+
bytes: Uint8Array;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
/** What an offer gives and wants, read from the transaction it carries. No wallet, no network. */
|
|
22
|
+
export declare function readOffer(offer: string): Promise<SwapTermsOut & {
|
|
23
|
+
bound: boolean;
|
|
24
|
+
serializedBytes: number;
|
|
25
|
+
}>;
|
|
26
|
+
/** Do two offer texts (file or base64, either form) carry the same transaction? */
|
|
27
|
+
export declare function sameOffer(a: string, b: string): Promise<boolean>;
|
|
28
|
+
export declare function swapWalletInfo(config: NightgateMcpConfig): Promise<Record<string, unknown>>;
|
|
29
|
+
export interface BuiltOffer {
|
|
30
|
+
id: string;
|
|
31
|
+
offer?: string;
|
|
32
|
+
halfB64: string;
|
|
33
|
+
bound: boolean;
|
|
34
|
+
serializedBytes: number;
|
|
35
|
+
terms: SwapTermsOut;
|
|
36
|
+
buildMs: number;
|
|
37
|
+
}
|
|
38
|
+
/** One half of a swap from this wallet; bound with its offer file unless `bind` is false. */
|
|
39
|
+
export declare function buildOffer(config: NightgateMcpConfig, input: {
|
|
40
|
+
give: SwapLeg;
|
|
41
|
+
want: SwapLeg;
|
|
42
|
+
bind?: boolean;
|
|
43
|
+
}): Promise<BuiltOffer>;
|
|
44
|
+
export interface TakenOfferOut {
|
|
45
|
+
id: string;
|
|
46
|
+
makerHalfB64: string;
|
|
47
|
+
takerHalfB64: string;
|
|
48
|
+
bound: boolean;
|
|
49
|
+
terms: SwapTermsOut;
|
|
50
|
+
buildMs: number;
|
|
51
|
+
}
|
|
52
|
+
/** The mirror half of an offer from this wallet, after its terms were read and compared with `expect`. */
|
|
53
|
+
export declare function takeOffer(config: NightgateMcpConfig, input: {
|
|
54
|
+
offer: string;
|
|
55
|
+
expect?: {
|
|
56
|
+
gives: SwapLeg;
|
|
57
|
+
wants: SwapLeg;
|
|
58
|
+
};
|
|
59
|
+
}): Promise<TakenOfferOut>;
|
|
60
|
+
/** Releases the coins of a half built here that is not going to be handed over. */
|
|
61
|
+
export declare function revertHalf(config: NightgateMcpConfig, id: string): Promise<{
|
|
62
|
+
id: string;
|
|
63
|
+
reverted: true;
|
|
64
|
+
}>;
|
|
65
|
+
/** Both halves of an offer taken here, for a submission by id. */
|
|
66
|
+
export declare function takenHalves(id: string): {
|
|
67
|
+
makerHalfB64: string;
|
|
68
|
+
takerHalfB64: string;
|
|
69
|
+
};
|
|
70
|
+
/** Remembers the sponsor job a half went into. An id this process does not hold is ignored. */
|
|
71
|
+
export declare function trackHalfJob(id: string, jobId: unknown): void;
|
|
72
|
+
/** A landed swap spent its coins: its half is no longer pending. A failed one stays, so it can be reverted. */
|
|
73
|
+
export declare function settleHalfJob(jobId: string, status: unknown): void;
|
|
74
|
+
/** Test seam / shutdown. */
|
|
75
|
+
export declare function closeSwapWallet(): Promise<void>;
|
|
76
|
+
/** Test seam: replace the module the swap functions load. */
|
|
77
|
+
export declare function __setSwapModuleForTests(mod: TxSwapModule | undefined): void;
|
|
78
|
+
export {};
|
package/dist/swap.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shielded swaps, the local side: one wallet that holds the shielded coins of
|
|
3
|
+
* `NIGHTGATE_SEED_HEX` builds swap halves and takes offers. It wraps the swap
|
|
4
|
+
* wallet of `@odatano/nightgate-tx` (0.8.0 or later); the fee is the sponsor's,
|
|
5
|
+
* so the wallet needs neither NIGHT nor dust.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
9
|
+
import { dirname } from 'node:path';
|
|
10
|
+
import { txExport } from './tx-module.js';
|
|
11
|
+
let modulePromise;
|
|
12
|
+
async function loadSwapModule() {
|
|
13
|
+
modulePromise ??= (async () => {
|
|
14
|
+
try {
|
|
15
|
+
const [createSwapWallet, readSwapTerms, decodeOffer] = await Promise.all([
|
|
16
|
+
txExport('createSwapWallet', 'swaps', '0.8.0'),
|
|
17
|
+
txExport('readSwapTerms', 'swaps', '0.8.0'),
|
|
18
|
+
txExport('decodeOffer', 'swaps', '0.8.0'),
|
|
19
|
+
]);
|
|
20
|
+
return { createSwapWallet, readSwapTerms, decodeOffer };
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
modulePromise = undefined;
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
return modulePromise;
|
|
28
|
+
}
|
|
29
|
+
function defaults(network) {
|
|
30
|
+
return {
|
|
31
|
+
indexerHttpUrl: `https://indexer.${network}.midnight.network/api/v4/graphql`,
|
|
32
|
+
indexerWsUrl: `wss://indexer.${network}.midnight.network/api/v4/graphql/ws`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const termsOut = (terms) => ({
|
|
36
|
+
gives: { tokenType: String(terms.gives.tokenType), amount: String(terms.gives.amount) },
|
|
37
|
+
wants: { tokenType: String(terms.wants.tokenType), amount: String(terms.wants.amount) },
|
|
38
|
+
...(Number.isInteger(terms.inputs) ? { inputs: terms.inputs } : {}),
|
|
39
|
+
...(Number.isInteger(terms.outputs) ? { outputs: terms.outputs } : {}),
|
|
40
|
+
});
|
|
41
|
+
/** What an offer gives and wants, read from the transaction it carries. No wallet, no network. */
|
|
42
|
+
export async function readOffer(offer) {
|
|
43
|
+
const { decodeOffer, readSwapTerms } = await loadSwapModule();
|
|
44
|
+
const decoded = await decodeOffer(offer);
|
|
45
|
+
return { ...termsOut(readSwapTerms(decoded.tx)), bound: decoded.bound, serializedBytes: decoded.bytes.length };
|
|
46
|
+
}
|
|
47
|
+
/** Do two offer texts (file or base64, either form) carry the same transaction? */
|
|
48
|
+
export async function sameOffer(a, b) {
|
|
49
|
+
const { decodeOffer } = await loadSwapModule();
|
|
50
|
+
const [x, y] = await Promise.all([decodeOffer(a), decodeOffer(b)]);
|
|
51
|
+
return x.bytes.length === y.bytes.length && x.bytes.every((byte, i) => byte === y.bytes[i]);
|
|
52
|
+
}
|
|
53
|
+
let open;
|
|
54
|
+
/** Halves built here whose swap has not landed, by id. */
|
|
55
|
+
const pending = new Map();
|
|
56
|
+
const halfId = (halfB64) => createHash('sha256').update(Buffer.from(halfB64, 'base64')).digest('hex').slice(0, 32);
|
|
57
|
+
async function readState(path) {
|
|
58
|
+
if (!path)
|
|
59
|
+
return undefined;
|
|
60
|
+
try {
|
|
61
|
+
const text = (await readFile(path, 'utf8')).trim();
|
|
62
|
+
return text.length > 0 ? text : undefined;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err?.code === 'ENOENT')
|
|
66
|
+
return undefined;
|
|
67
|
+
throw new Error(`swap wallet state ${path} is not readable: ${err.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** The state holds the wallet's coins: written next to itself, then renamed, readable by the owner only. */
|
|
71
|
+
async function writeState(path, wallet) {
|
|
72
|
+
if (!path)
|
|
73
|
+
return;
|
|
74
|
+
const state = String(await wallet.serializeState());
|
|
75
|
+
await mkdir(dirname(path), { recursive: true });
|
|
76
|
+
const tmp = `${path}.part`;
|
|
77
|
+
await writeFile(tmp, state, { mode: 0o600 });
|
|
78
|
+
await rename(tmp, path);
|
|
79
|
+
await chmod(path, 0o600).catch(() => { });
|
|
80
|
+
}
|
|
81
|
+
async function openWallet(config) {
|
|
82
|
+
if (!config.seedHex) {
|
|
83
|
+
throw new Error('swaps need NIGHTGATE_SEED_HEX in the MCP server environment; it is never a tool argument');
|
|
84
|
+
}
|
|
85
|
+
open ??= (async () => {
|
|
86
|
+
try {
|
|
87
|
+
const { createSwapWallet } = await loadSwapModule();
|
|
88
|
+
const d = defaults(config.network);
|
|
89
|
+
const walletState = await readState(config.swapStateFile);
|
|
90
|
+
const wallet = await createSwapWallet({
|
|
91
|
+
seedHex: config.seedHex,
|
|
92
|
+
networkId: config.network,
|
|
93
|
+
indexerHttpUrl: config.indexerHttpUrl ?? d.indexerHttpUrl,
|
|
94
|
+
indexerWsUrl: config.indexerWsUrl ?? d.indexerWsUrl,
|
|
95
|
+
...(config.nodeUrl ? { nodeUrl: config.nodeUrl } : {}),
|
|
96
|
+
...(config.proofServerUrl ? { provingMode: 'server', proofServerUrl: config.proofServerUrl } : {}),
|
|
97
|
+
...(config.swapMaxInputs ? { maxInputs: config.swapMaxInputs } : {}),
|
|
98
|
+
...(walletState ? { walletState } : {}),
|
|
99
|
+
});
|
|
100
|
+
const entry = { wallet, synced: Promise.resolve(), isSynced: false, syncError: null, startedAt: Date.now(), resumed: !!walletState };
|
|
101
|
+
entry.synced = (async () => {
|
|
102
|
+
try {
|
|
103
|
+
await wallet.sync();
|
|
104
|
+
await writeState(config.swapStateFile, wallet).catch((err) => console.error(`nightgate-mcp: swap wallet state not saved: ${err.message}`));
|
|
105
|
+
entry.isSynced = true;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
entry.syncError = err instanceof Error ? err : new Error(String(err));
|
|
109
|
+
}
|
|
110
|
+
})();
|
|
111
|
+
return entry;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
open = undefined;
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
})();
|
|
118
|
+
return open;
|
|
119
|
+
}
|
|
120
|
+
/** The wallet once it has caught up; a wallet that needs longer than `waitMs` says so instead of blocking the call. */
|
|
121
|
+
async function syncedWallet(config, waitMs) {
|
|
122
|
+
const entry = await openWallet(config);
|
|
123
|
+
if (!entry.isSynced && !entry.syncError) {
|
|
124
|
+
await Promise.race([entry.synced, new Promise((resolve) => setTimeout(resolve, waitMs))]);
|
|
125
|
+
}
|
|
126
|
+
if (entry.syncError) {
|
|
127
|
+
open = undefined;
|
|
128
|
+
throw new Error(`the swap wallet could not sync: ${entry.syncError.message}`);
|
|
129
|
+
}
|
|
130
|
+
if (!entry.isSynced) {
|
|
131
|
+
const seconds = Math.round((Date.now() - entry.startedAt) / 1000);
|
|
132
|
+
throw new Error(`the swap wallet is still syncing its shielded coins (${seconds} s so far; a first sync from genesis takes about 5 minutes` +
|
|
133
|
+
`${config.swapStateFile ? '' : ', set NIGHTGATE_SWAP_STATE_FILE to resume after a restart'}); call again shortly`);
|
|
134
|
+
}
|
|
135
|
+
return entry.wallet;
|
|
136
|
+
}
|
|
137
|
+
/** How long a tool call waits for a wallet that is still syncing. */
|
|
138
|
+
const SYNC_WAIT_MS = 20_000;
|
|
139
|
+
export async function swapWalletInfo(config) {
|
|
140
|
+
const entry = await openWallet(config);
|
|
141
|
+
const { wallet } = entry;
|
|
142
|
+
const base = {
|
|
143
|
+
address: String(wallet.address),
|
|
144
|
+
coinPublicKey: String(wallet.coinPublicKey),
|
|
145
|
+
encryptionPublicKey: String(wallet.encryptionPublicKey),
|
|
146
|
+
network: config.network,
|
|
147
|
+
maxInputs: Number(wallet.maxInputs),
|
|
148
|
+
provingMode: String(wallet.provingMode),
|
|
149
|
+
synced: entry.isSynced,
|
|
150
|
+
resumedFromState: entry.resumed,
|
|
151
|
+
};
|
|
152
|
+
if (entry.syncError)
|
|
153
|
+
throw new Error(`the swap wallet could not sync: ${entry.syncError.message}`);
|
|
154
|
+
if (!entry.isSynced) {
|
|
155
|
+
await Promise.race([entry.synced, new Promise((resolve) => setTimeout(resolve, SYNC_WAIT_MS))]);
|
|
156
|
+
if (!entry.isSynced)
|
|
157
|
+
return { ...base, syncingSeconds: Math.round((Date.now() - entry.startedAt) / 1000) };
|
|
158
|
+
}
|
|
159
|
+
const coins = await wallet.coins();
|
|
160
|
+
const byType = new Map();
|
|
161
|
+
for (const c of coins) {
|
|
162
|
+
const t = byType.get(c.tokenType) ?? { balance: 0n, coins: 0 };
|
|
163
|
+
byType.set(c.tokenType, { balance: t.balance + c.amount, coins: t.coins + 1 });
|
|
164
|
+
}
|
|
165
|
+
const tokens = [];
|
|
166
|
+
for (const [tokenType, t] of byType) {
|
|
167
|
+
tokens.push({ tokenType, balance: t.balance.toString(), coins: t.coins, spendable: String(await wallet.spendable(tokenType)) });
|
|
168
|
+
}
|
|
169
|
+
const pendingHalves = [...pending.entries()].map(([id, p]) => ({ id, role: p.role, ...(p.jobId ? { jobId: p.jobId } : {}), ...p.terms }));
|
|
170
|
+
return { ...base, synced: true, tokens, pendingHalves };
|
|
171
|
+
}
|
|
172
|
+
/** One half of a swap from this wallet; bound with its offer file unless `bind` is false. */
|
|
173
|
+
export async function buildOffer(config, input) {
|
|
174
|
+
const t0 = Date.now();
|
|
175
|
+
const wallet = await syncedWallet(config, SYNC_WAIT_MS);
|
|
176
|
+
const half = await wallet.buildHalf({ give: input.give, want: input.want, bind: input.bind !== false });
|
|
177
|
+
const terms = termsOut(half.terms);
|
|
178
|
+
const id = halfId(half.halfB64);
|
|
179
|
+
pending.set(id, { revert: half.revert, terms, role: 'maker' });
|
|
180
|
+
await writeState(config.swapStateFile, wallet).catch(() => { });
|
|
181
|
+
return {
|
|
182
|
+
id, ...(half.offer ? { offer: half.offer } : {}), halfB64: half.halfB64, bound: half.bound === true,
|
|
183
|
+
serializedBytes: Number(half.serializedBytes), terms, buildMs: Date.now() - t0,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/** The mirror half of an offer from this wallet, after its terms were read and compared with `expect`. */
|
|
187
|
+
export async function takeOffer(config, input) {
|
|
188
|
+
const t0 = Date.now();
|
|
189
|
+
const wallet = await syncedWallet(config, SYNC_WAIT_MS);
|
|
190
|
+
const taken = await wallet.takeOffer({ offer: input.offer, ...(input.expect ? { expect: input.expect } : {}) });
|
|
191
|
+
const terms = termsOut(taken.terms);
|
|
192
|
+
const id = halfId(taken.takerHalfB64);
|
|
193
|
+
pending.set(id, { revert: taken.revert, terms, role: 'taker', halves: { makerHalfB64: taken.makerHalfB64, takerHalfB64: taken.takerHalfB64 } });
|
|
194
|
+
await writeState(config.swapStateFile, wallet).catch(() => { });
|
|
195
|
+
return { id, makerHalfB64: taken.makerHalfB64, takerHalfB64: taken.takerHalfB64, bound: taken.bound === true, terms, buildMs: Date.now() - t0 };
|
|
196
|
+
}
|
|
197
|
+
/** Releases the coins of a half built here that is not going to be handed over. */
|
|
198
|
+
export async function revertHalf(config, id) {
|
|
199
|
+
const half = pending.get(id);
|
|
200
|
+
if (!half)
|
|
201
|
+
throw new Error(`no half with id ${id} was built by this server process (ids are listed by get_swap_wallet)`);
|
|
202
|
+
await half.revert();
|
|
203
|
+
pending.delete(id);
|
|
204
|
+
const entry = await openWallet(config);
|
|
205
|
+
await writeState(config.swapStateFile, entry.wallet).catch(() => { });
|
|
206
|
+
return { id, reverted: true };
|
|
207
|
+
}
|
|
208
|
+
/** Both halves of an offer taken here, for a submission by id. */
|
|
209
|
+
export function takenHalves(id) {
|
|
210
|
+
const half = pending.get(id);
|
|
211
|
+
if (!half)
|
|
212
|
+
throw new Error(`no half with id ${id} is held by this server process (ids are listed by get_swap_wallet)`);
|
|
213
|
+
if (!half.halves)
|
|
214
|
+
throw new Error(`half ${id} is an offer built here, not a taken one: pass both halves`);
|
|
215
|
+
return half.halves;
|
|
216
|
+
}
|
|
217
|
+
/** Remembers the sponsor job a half went into. An id this process does not hold is ignored. */
|
|
218
|
+
export function trackHalfJob(id, jobId) {
|
|
219
|
+
const half = pending.get(id);
|
|
220
|
+
if (half && typeof jobId === 'string')
|
|
221
|
+
half.jobId = jobId;
|
|
222
|
+
}
|
|
223
|
+
/** A landed swap spent its coins: its half is no longer pending. A failed one stays, so it can be reverted. */
|
|
224
|
+
export function settleHalfJob(jobId, status) {
|
|
225
|
+
if (status !== 'succeeded')
|
|
226
|
+
return;
|
|
227
|
+
for (const [id, half] of pending)
|
|
228
|
+
if (half.jobId === jobId)
|
|
229
|
+
pending.delete(id);
|
|
230
|
+
}
|
|
231
|
+
/** Test seam / shutdown. */
|
|
232
|
+
export async function closeSwapWallet() {
|
|
233
|
+
const current = open;
|
|
234
|
+
open = undefined;
|
|
235
|
+
pending.clear();
|
|
236
|
+
if (!current)
|
|
237
|
+
return;
|
|
238
|
+
try {
|
|
239
|
+
await (await current).wallet.close?.();
|
|
240
|
+
}
|
|
241
|
+
catch { /* best effort */ }
|
|
242
|
+
}
|
|
243
|
+
/** Test seam: replace the module the swap functions load. */
|
|
244
|
+
export function __setSwapModuleForTests(mod) {
|
|
245
|
+
modulePromise = mod ? Promise.resolve(mod) : undefined;
|
|
246
|
+
open = undefined;
|
|
247
|
+
pending.clear();
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=swap.js.map
|
package/dist/swap.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swap.js","sourceRoot":"","sources":["../src/swap.ts"],"names":[],"mappings":"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"}
|