@totemsdk/omnia-host 0.7.0 → 0.9.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/dist/api/jsonrpc.js +6 -2
- package/dist/api/methods.d.ts +11 -0
- package/dist/api/methods.js +164 -3
- package/dist/chain/totem-node-adapter.d.ts +6 -0
- package/dist/chain/totem-node-adapter.js +19 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +1 -0
- package/dist/lifecycle.js +6 -0
- package/package.json +9 -9
package/dist/api/jsonrpc.js
CHANGED
|
@@ -27,8 +27,12 @@ async function dispatch(request, methods) {
|
|
|
27
27
|
return response(request.id, await method(request.params));
|
|
28
28
|
}
|
|
29
29
|
catch (error) {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// Surface the handler's message to the caller. Masking it as "Internal
|
|
31
|
+
// error" made operational failures (broadcast rejection, missing manager,
|
|
32
|
+
// malformed params) impossible to diagnose from the client side.
|
|
33
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
34
|
+
console.error('[jsonrpc] handler error:', error instanceof Error ? error.stack ?? message : message);
|
|
35
|
+
return errorResponse(request.id, -32000, message);
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
async function readBody(req) {
|
package/dist/api/methods.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export interface HostApiContext {
|
|
|
17
17
|
chainProvider?: ChainStateProvider;
|
|
18
18
|
swarm?: OmniaSwarm;
|
|
19
19
|
localParticipant?: ChannelParticipant;
|
|
20
|
+
/**
|
|
21
|
+
* Signs the funding-transaction digest with the funding coin's key, so
|
|
22
|
+
* openChannel can assemble the funding witness without the caller reproducing
|
|
23
|
+
* the funding draft. Configured from the node's `sign` command.
|
|
24
|
+
*/
|
|
25
|
+
fundingSigner?: (digest: Uint8Array) => Promise<Uint8Array> | Uint8Array;
|
|
20
26
|
factories?: Map<string, ChannelFactory>;
|
|
21
27
|
factoryBundles?: Record<string, WotsLeaseBundle>;
|
|
22
28
|
spliceProposals?: Map<string, SpliceProposal>;
|
|
@@ -34,4 +40,9 @@ export interface HostApiContext {
|
|
|
34
40
|
/** Boot-time signed EdgeServiceManifest. */
|
|
35
41
|
manifest?: unknown;
|
|
36
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* CAN-006: a fully serializable channel-state view so RPC/HTTP consumers can
|
|
45
|
+
* read everything they need without reaching into `getStore()`/`getMethods()`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function channelStateView(channel: OmniaChannel): Record<string, unknown>;
|
|
37
48
|
export declare function createHostMethods(context: HostApiContext): Map<string, JsonRpcHandler>;
|
package/dist/api/methods.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.channelStateView = channelStateView;
|
|
3
4
|
exports.createHostMethods = createHostMethods;
|
|
4
5
|
const omnia_1 = require("@totemsdk/omnia");
|
|
5
6
|
const omnia_router_1 = require("@totemsdk/omnia-router");
|
|
@@ -17,6 +18,59 @@ function channelSummary(channel) {
|
|
|
17
18
|
currentSequence: channel.currentSequence,
|
|
18
19
|
};
|
|
19
20
|
}
|
|
21
|
+
/** Recursively JSON-safe: bigint → decimal string, bytes → 0x-hex. */
|
|
22
|
+
function jsonSafe(value) {
|
|
23
|
+
if (typeof value === 'bigint')
|
|
24
|
+
return value.toString();
|
|
25
|
+
if (value instanceof Uint8Array)
|
|
26
|
+
return `0x${Buffer.from(value).toString('hex')}`;
|
|
27
|
+
if (Array.isArray(value))
|
|
28
|
+
return value.map(jsonSafe);
|
|
29
|
+
if (value && typeof value === 'object') {
|
|
30
|
+
const out = {};
|
|
31
|
+
for (const [key, val] of Object.entries(value))
|
|
32
|
+
out[key] = jsonSafe(val);
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* CAN-006: a fully serializable channel-state view so RPC/HTTP consumers can
|
|
39
|
+
* read everything they need without reaching into `getStore()`/`getMethods()`.
|
|
40
|
+
*/
|
|
41
|
+
function channelStateView(channel) {
|
|
42
|
+
return jsonSafe({
|
|
43
|
+
channelId: channel.channelId,
|
|
44
|
+
status: channel.status,
|
|
45
|
+
channelType: channel.channelType,
|
|
46
|
+
tokenId: channel.tokenId,
|
|
47
|
+
tokenScale: channel.tokenScale ?? 0,
|
|
48
|
+
totalValue: channel.totalValue,
|
|
49
|
+
currentSequence: channel.currentSequence,
|
|
50
|
+
fundingAddress: channel.fundingAddress,
|
|
51
|
+
fundingScript: channel.fundingScript,
|
|
52
|
+
fundingTxId: channel.fundingTxId,
|
|
53
|
+
fundingCoinId: channel.fundingCoinId,
|
|
54
|
+
contestDelayBlocks: channel.contestDelayBlocks,
|
|
55
|
+
parties: channel.parties,
|
|
56
|
+
balances: channel.balances,
|
|
57
|
+
pendingHTLCs: channel.pendingHTLCs,
|
|
58
|
+
latestState: channel.latestState
|
|
59
|
+
? {
|
|
60
|
+
sequence: channel.latestState.sequence,
|
|
61
|
+
balances: channel.latestState.balances,
|
|
62
|
+
pendingHTLCs: channel.latestState.pendingHTLCs?.length ?? 0,
|
|
63
|
+
stateVariables: channel.latestState.stateVariables,
|
|
64
|
+
transactionHex: channel.latestState.transactionHex,
|
|
65
|
+
signatures: Object.keys(channel.latestState.signatures ?? {}),
|
|
66
|
+
signingIndices: channel.latestState.signingIndices,
|
|
67
|
+
}
|
|
68
|
+
: null,
|
|
69
|
+
stateLogLength: channel.stateLog?.length ?? 0,
|
|
70
|
+
createdAt: channel.createdAt,
|
|
71
|
+
updatedAt: channel.updatedAt,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
20
74
|
function paramsObject(params) {
|
|
21
75
|
if (!params || typeof params !== 'object' || Array.isArray(params))
|
|
22
76
|
return {};
|
|
@@ -132,6 +186,32 @@ async function persistProgramUpdate(context, channelId, run) {
|
|
|
132
186
|
balances: balancesJson(result.channel),
|
|
133
187
|
};
|
|
134
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Wait for an ACK/ERROR from the counterparty for a given channel. Returns the
|
|
191
|
+
* reply message (so callers can read a co-signature) or null on timeout.
|
|
192
|
+
*/
|
|
193
|
+
function waitForPeerReply(peer, channelId, timeoutMs) {
|
|
194
|
+
return new Promise((resolve) => {
|
|
195
|
+
let done = false;
|
|
196
|
+
let unsub;
|
|
197
|
+
const finish = (reply) => {
|
|
198
|
+
if (done)
|
|
199
|
+
return;
|
|
200
|
+
done = true;
|
|
201
|
+
clearTimeout(timer);
|
|
202
|
+
if (unsub)
|
|
203
|
+
unsub();
|
|
204
|
+
resolve(reply);
|
|
205
|
+
};
|
|
206
|
+
unsub = peer.onMessage((msg) => {
|
|
207
|
+
if (msg.channelId !== channelId)
|
|
208
|
+
return;
|
|
209
|
+
if (msg.type === 'ACK' || msg.type === 'ERROR')
|
|
210
|
+
finish(msg);
|
|
211
|
+
});
|
|
212
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
135
215
|
function createHostMethods(context) {
|
|
136
216
|
const methods = new Map();
|
|
137
217
|
const register = (name, handler, alias) => {
|
|
@@ -149,6 +229,15 @@ function createHostMethods(context) {
|
|
|
149
229
|
return { channels };
|
|
150
230
|
};
|
|
151
231
|
register('totem_omniaGetChannels', getChannels, 'omnia/getChannels');
|
|
232
|
+
const getChannelState = (raw) => {
|
|
233
|
+
const params = paramsObject(raw);
|
|
234
|
+
const channelId = requiredString(params, 'channelId');
|
|
235
|
+
const channel = context.channels.get(channelId);
|
|
236
|
+
if (!channel)
|
|
237
|
+
throw new Error(`Channel ${channelId} not found`);
|
|
238
|
+
return channelStateView(channel);
|
|
239
|
+
};
|
|
240
|
+
register('totem_omniaGetChannelState', getChannelState, 'omnia/getChannelState');
|
|
152
241
|
const getRoute = async (raw) => {
|
|
153
242
|
context.refreshRouting?.();
|
|
154
243
|
const params = paramsObject(raw);
|
|
@@ -224,18 +313,58 @@ function createHostMethods(context) {
|
|
|
224
313
|
remoteAmount: requiredBigInt(params, 'remoteAmount'),
|
|
225
314
|
tokenId: typeof params.tokenId === 'string' ? params.tokenId : undefined,
|
|
226
315
|
fundingCoinId: requiredString(params, 'fundingCoinId'),
|
|
227
|
-
|
|
316
|
+
contestDelayBlocks: typeof params.contestDelayBlocks === 'number' ? params.contestDelayBlocks : undefined,
|
|
228
317
|
};
|
|
318
|
+
const fundingWitnessHex = typeof params.fundingWitnessHex === 'string' ? params.fundingWitnessHex : undefined;
|
|
319
|
+
if (fundingWitnessHex) {
|
|
320
|
+
createParams.fundingWitnessBytes = Buffer.from(fundingWitnessHex.replace(/^0x/i, ''), 'hex');
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
// Assemble the funding witness: createChannel builds the funding draft,
|
|
324
|
+
// a funding signer signs its digest, and the caller supplies the input
|
|
325
|
+
// proofs (serialized CoinProof + ScriptProof). The signer comes from the
|
|
326
|
+
// configured funding signer, or from the node's `sign` command using the
|
|
327
|
+
// `fundingPubkey` the caller passes.
|
|
328
|
+
const fundingPubkey = typeof params.fundingPubkey === 'string' ? params.fundingPubkey : undefined;
|
|
329
|
+
const nodeAdapter = context.chainProvider;
|
|
330
|
+
const signer = context.fundingSigner ?? (fundingPubkey && typeof nodeAdapter?.signData === 'function'
|
|
331
|
+
? async (digest) => {
|
|
332
|
+
const sig = await nodeAdapter.signData(fundingPubkey, '0x' + Buffer.from(digest).toString('hex'));
|
|
333
|
+
return Uint8Array.from(Buffer.from(sig.replace(/^0x/i, ''), 'hex'));
|
|
334
|
+
}
|
|
335
|
+
: undefined);
|
|
336
|
+
if (!signer) {
|
|
337
|
+
throw new Error('openChannel requires fundingWitnessHex, or a fundingPubkey + funding proofs');
|
|
338
|
+
}
|
|
339
|
+
if (typeof params.fundingScript !== 'string' && !Array.isArray(params.fundingScriptProofsHex)) {
|
|
340
|
+
throw new Error('openChannel requires fundingScript (or serialized fundingScriptProofsHex) for the funding input address');
|
|
341
|
+
}
|
|
342
|
+
const toBytes = (v) => Array.isArray(v) ? v.map((h) => Buffer.from(String(h).replace(/^0x/i, ''), 'hex')) : [];
|
|
343
|
+
createParams.fundingProofs = {
|
|
344
|
+
coinProofs: toBytes(params.fundingCoinProofsHex),
|
|
345
|
+
scriptProofs: toBytes(params.fundingScriptProofsHex),
|
|
346
|
+
};
|
|
347
|
+
// The funding coin's address script: createChannel derives the required
|
|
348
|
+
// single-leaf ScriptProof from it, so callers need not hand-roll it.
|
|
349
|
+
if (typeof params.fundingScript === 'string') {
|
|
350
|
+
createParams.fundingScript = params.fundingScript;
|
|
351
|
+
}
|
|
352
|
+
createParams.fundingSigner = signer;
|
|
353
|
+
}
|
|
229
354
|
const created = await (0, omnia_1.createChannel)(createParams, context.chainProvider);
|
|
230
355
|
context.channels.set(created.channel.channelId, {
|
|
231
356
|
...created.channel,
|
|
232
357
|
localSigner: context.signer,
|
|
233
358
|
});
|
|
359
|
+
let acknowledged = true;
|
|
234
360
|
if (context.swarm) {
|
|
235
361
|
const peer = await context.swarm.connectToPeer(remote.publicKeyDigest, created.channel.channelId);
|
|
236
362
|
await peer.sendMessage({ type: 'CHANNEL_PROPOSAL', channelId: created.channel.channelId, nonce: Date.now(), payload: created.proposal });
|
|
363
|
+
// Give the peer time to accept so a follow-up pay() is not lost.
|
|
364
|
+
const reply = await waitForPeerReply(peer, created.channel.channelId, 20_000);
|
|
365
|
+
acknowledged = reply?.type === 'ACK';
|
|
237
366
|
}
|
|
238
|
-
return { success: true, channelId: created.channel.channelId, fundingTxId: created.channel.fundingTxId };
|
|
367
|
+
return { success: true, channelId: created.channel.channelId, fundingTxId: created.channel.fundingTxId, acknowledged };
|
|
239
368
|
});
|
|
240
369
|
return result;
|
|
241
370
|
};
|
|
@@ -326,11 +455,43 @@ function createHostMethods(context) {
|
|
|
326
455
|
if (!channel)
|
|
327
456
|
throw new Error(`Channel ${channelId} not found`);
|
|
328
457
|
const closing = (0, omnia_1.markChannelClosing)(channel, 'mutual');
|
|
458
|
+
const partyAddresses = Object.fromEntries(closing.parties.map((party) => [party.partyId, party.settlementAddress ?? party.publicKeyDigest]));
|
|
459
|
+
// Cooperative close needs both eltoo MULTISIG(2 A B) signatures. Build the
|
|
460
|
+
// exact settlement draft, ask the counterparty to co-sign it, then include
|
|
461
|
+
// both signatures in the broadcast witness.
|
|
462
|
+
const localPartyId = context.localParticipant?.partyId;
|
|
463
|
+
const remoteParty = localPartyId
|
|
464
|
+
? closing.parties.find((party) => party.partyId !== localPartyId)
|
|
465
|
+
: undefined;
|
|
466
|
+
let counterpartySignatures;
|
|
467
|
+
let settlementTxHex;
|
|
468
|
+
if (context.swarm && remoteParty) {
|
|
469
|
+
const request = (0, omnia_1.buildSettlementSigningRequest)(closing, { partyAddresses });
|
|
470
|
+
const peer = await context.swarm.connectToPeer(remoteParty.publicKeyDigest, channelId);
|
|
471
|
+
await peer.sendMessage({
|
|
472
|
+
type: 'SETTLEMENT_PROPOSAL',
|
|
473
|
+
channelId,
|
|
474
|
+
nonce: Date.now(),
|
|
475
|
+
payload: { signRequest: true, ...request },
|
|
476
|
+
});
|
|
477
|
+
const reply = await waitForPeerReply(peer, channelId, 30_000);
|
|
478
|
+
const replyPayload = reply?.payload;
|
|
479
|
+
if (reply?.type !== 'ACK' || typeof replyPayload?.signature !== 'string') {
|
|
480
|
+
throw new Error(`Counterparty did not co-sign the settlement (${reply?.type ?? 'timeout'})`);
|
|
481
|
+
}
|
|
482
|
+
counterpartySignatures = [{
|
|
483
|
+
partyId: replyPayload.partyId ?? remoteParty.partyId,
|
|
484
|
+
signature: Buffer.from(replyPayload.signature.replace(/^0x/i, ''), 'hex'),
|
|
485
|
+
}];
|
|
486
|
+
settlementTxHex = request.settlementTxHex;
|
|
487
|
+
}
|
|
329
488
|
const settlement = await (0, omnia_1.proposeSettlement)(closing, context.leaseProvider, {
|
|
330
489
|
signer: context.signer,
|
|
331
490
|
chainProvider: context.chainProvider,
|
|
332
491
|
broadcastProofs: parsedProofs,
|
|
333
|
-
partyAddresses
|
|
492
|
+
partyAddresses,
|
|
493
|
+
settlementTxHex,
|
|
494
|
+
counterpartySignatures,
|
|
334
495
|
});
|
|
335
496
|
context.channels.set(channelId, (0, omnia_1.markChannelClosed)(closing));
|
|
336
497
|
return { success: true, channelId, settlementTxId: settlement.settlementPayload.txpowId, finalBalances: Object.fromEntries(Object.entries(settlement.settlementPayload.balances).map(([key, value]) => [key, value.toString()])) };
|
|
@@ -16,6 +16,12 @@ export interface ConfirmationOptions {
|
|
|
16
16
|
}
|
|
17
17
|
export interface TotemNodeAdapter extends ChainStateProvider {
|
|
18
18
|
getMode(): Promise<string | undefined>;
|
|
19
|
+
/**
|
|
20
|
+
* Sign arbitrary data with a wallet key (`sign publickey:<0x> data:<0x>`).
|
|
21
|
+
* Used to produce the funding witness signature: createChannel builds the
|
|
22
|
+
* funding digest, and this signs it with the funding coin's key.
|
|
23
|
+
*/
|
|
24
|
+
signData(pubkeyHex: string, dataHex: string): Promise<string>;
|
|
19
25
|
waitForConfirmation(coinId: string, options?: ConfirmationOptions): Promise<void>;
|
|
20
26
|
subscribe(listener: (tip: ChainTip) => void): () => void;
|
|
21
27
|
close(): void;
|
|
@@ -37,6 +37,10 @@ function createTotemNodeAdapter(options) {
|
|
|
37
37
|
searchTokens: (query) => provider.searchTokens(query),
|
|
38
38
|
getTokensByCreator: (address) => provider.getTokensByCreator(address),
|
|
39
39
|
broadcastTxPoW: (txpowHex) => provider.broadcastTxPoW(txpowHex),
|
|
40
|
+
getMinTxPowWork: () => provider.getMinTxPowWork ? provider.getMinTxPowWork() : Promise.resolve(null),
|
|
41
|
+
...(provider.submitTransaction
|
|
42
|
+
? { submitTransaction: provider.submitTransaction.bind(provider) }
|
|
43
|
+
: {}),
|
|
40
44
|
async getMode() {
|
|
41
45
|
const result = await client.runCommand('getmode');
|
|
42
46
|
if (!result || typeof result !== 'object')
|
|
@@ -44,6 +48,21 @@ function createTotemNodeAdapter(options) {
|
|
|
44
48
|
const mode = result.mode;
|
|
45
49
|
return typeof mode === 'string' ? mode : undefined;
|
|
46
50
|
},
|
|
51
|
+
async signData(pubkeyHex, dataHex) {
|
|
52
|
+
const result = await client.runCommand('sign', { publickey: pubkeyHex, data: dataHex });
|
|
53
|
+
const unwrap = (v) => {
|
|
54
|
+
if (typeof v === 'string')
|
|
55
|
+
return v;
|
|
56
|
+
if (v && typeof v === 'object' && typeof v.response === 'string') {
|
|
57
|
+
return v.response;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
};
|
|
61
|
+
const sig = unwrap(result) ?? unwrap(result?.response);
|
|
62
|
+
if (!sig)
|
|
63
|
+
throw new Error('signData: unexpected node response');
|
|
64
|
+
return sig;
|
|
65
|
+
},
|
|
47
66
|
async waitForConfirmation(coinId, confirmationOptions = {}) {
|
|
48
67
|
const timeoutMs = confirmationOptions.timeoutMs ?? 120_000;
|
|
49
68
|
const interval = confirmationOptions.pollIntervalMs ?? pollIntervalMs;
|
package/dist/config.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ export interface OmniaHostConfig {
|
|
|
11
11
|
localAddressIndex?: number;
|
|
12
12
|
localSettlementAddress?: string;
|
|
13
13
|
nodeMode?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Public key (0x…) that owns the funding coin. When set, the host signs the
|
|
16
|
+
* funding-transaction digest via the node's `sign` command to assemble the
|
|
17
|
+
* channel funding witness (OMNIA_FUNDING_PUBKEY).
|
|
18
|
+
*/
|
|
19
|
+
fundingPubkey?: string;
|
|
14
20
|
wsPath: string;
|
|
15
21
|
/** BIP39 mnemonic or hex seed (same formats @totemsdk/core accepts). */
|
|
16
22
|
seed?: string;
|
package/dist/config.js
CHANGED
|
@@ -75,6 +75,7 @@ function loadConfigFromEnv(env = process.env) {
|
|
|
75
75
|
localAddressIndex: optionalInteger(env.OMNIA_LOCAL_ADDRESS_INDEX),
|
|
76
76
|
localSettlementAddress: optional(env.OMNIA_LOCAL_SETTLEMENT_ADDRESS),
|
|
77
77
|
nodeMode: optional(env.OMNIA_NODE_MODE),
|
|
78
|
+
fundingPubkey: optional(env.OMNIA_FUNDING_PUBKEY),
|
|
78
79
|
wsPath: env.OMNIA_WS_PATH?.trim() || '/rpc',
|
|
79
80
|
seed,
|
|
80
81
|
keyfile,
|
package/dist/lifecycle.js
CHANGED
|
@@ -107,6 +107,12 @@ function createOmniaHost(config, dependencies = {}) {
|
|
|
107
107
|
spliceProposals: dependencies.spliceProposals,
|
|
108
108
|
spliceAcceptances: dependencies.spliceAcceptances,
|
|
109
109
|
spliceLeaseProvider: dependencies.spliceLeaseProvider,
|
|
110
|
+
fundingSigner: config.fundingPubkey
|
|
111
|
+
? async (digest) => {
|
|
112
|
+
const sig = await chainAdapter.signData(config.fundingPubkey, '0x' + Buffer.from(digest).toString('hex'));
|
|
113
|
+
return Uint8Array.from(Buffer.from(sig.replace(/^0x/i, ''), 'hex'));
|
|
114
|
+
}
|
|
115
|
+
: undefined,
|
|
110
116
|
readOnly,
|
|
111
117
|
identity: dependencies.identity,
|
|
112
118
|
manifest: dependencies.manifest,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/omnia-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Durable Omnia node daemon for channel lifecycle, routing, and control APIs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,17 +22,17 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"better-sqlite3": "^12.2.0",
|
|
24
24
|
"ws": "^8.21.1",
|
|
25
|
-
"@totemsdk/chain-provider": "^0.
|
|
26
|
-
"@totemsdk/core": "^1.
|
|
27
|
-
"@totemsdk/manifest": "^2.0.0",
|
|
28
|
-
"@totemsdk/omnia": "^2.0.1",
|
|
25
|
+
"@totemsdk/chain-provider": "^0.3.0",
|
|
26
|
+
"@totemsdk/core": "^1.4.0",
|
|
29
27
|
"@totemsdk/identity": "^1.0.1",
|
|
28
|
+
"@totemsdk/omnia": "^2.2.0",
|
|
29
|
+
"@totemsdk/manifest": "^2.0.0",
|
|
30
|
+
"@totemsdk/omnia-factory": "^0.6.0",
|
|
30
31
|
"@totemsdk/omnia-router": "^0.3.0",
|
|
31
|
-
"@totemsdk/omnia-factory": "^0.5.0",
|
|
32
|
-
"@totemsdk/omnia-vtxo": "^0.5.0",
|
|
33
32
|
"@totemsdk/omnia-splice": "^0.3.0",
|
|
33
|
+
"@totemsdk/omnia-vtxo": "^0.6.0",
|
|
34
34
|
"@totemsdk/storage": "^0.1.0",
|
|
35
|
-
"@totemsdk/minima-rpc": "^0.
|
|
35
|
+
"@totemsdk/minima-rpc": "^0.4.0",
|
|
36
36
|
"@totemsdk/wots-lease": "^1.0.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"jest": "^30.4.2",
|
|
44
44
|
"ts-jest": "^29.4.11",
|
|
45
45
|
"typescript": "5.9.3",
|
|
46
|
-
"@totemsdk/txpow": "^1.0
|
|
46
|
+
"@totemsdk/txpow": "^1.1.0"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": ">=20.0.0"
|