@totemsdk/edge-adapters 0.2.1 → 0.3.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/artifact-cache.js +9 -12
- package/dist/cjs/artifact-cache.d.ts +36 -0
- package/dist/cjs/artifact-cache.js +82 -0
- package/dist/cjs/identity.d.ts +19 -0
- package/dist/cjs/identity.js +61 -0
- package/dist/cjs/index.d.ts +25 -0
- package/dist/cjs/index.js +41 -0
- package/dist/cjs/liquidity.d.ts +15 -0
- package/dist/cjs/liquidity.js +45 -0
- package/dist/cjs/location.d.ts +48 -0
- package/dist/cjs/location.js +208 -0
- package/dist/cjs/lookup.d.ts +13 -0
- package/dist/cjs/lookup.js +96 -0
- package/dist/cjs/manifest.d.ts +11 -0
- package/dist/cjs/manifest.js +36 -0
- package/dist/cjs/negotiation-transport.d.ts +50 -0
- package/dist/cjs/negotiation-transport.js +122 -0
- package/dist/cjs/omnia.d.ts +7 -0
- package/dist/cjs/omnia.js +46 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/payment-l1.d.ts +38 -0
- package/dist/cjs/payment-l1.js +46 -0
- package/dist/cjs/payment-l2.d.ts +39 -0
- package/dist/cjs/payment-l2.js +54 -0
- package/dist/cjs/policy.d.ts +13 -0
- package/dist/cjs/policy.js +95 -0
- package/dist/cjs/proof.d.ts +48 -0
- package/dist/cjs/proof.js +88 -0
- package/dist/cjs/pubsub.d.ts +11 -0
- package/dist/cjs/pubsub.js +30 -0
- package/dist/cjs/purchase-authority.d.ts +42 -0
- package/dist/cjs/purchase-authority.js +79 -0
- package/dist/cjs/purchase-lookup.d.ts +44 -0
- package/dist/cjs/purchase-lookup.js +60 -0
- package/dist/cjs/purchase-payment.d.ts +78 -0
- package/dist/cjs/purchase-payment.js +108 -0
- package/dist/cjs/sqlite-commerce-store.d.ts +69 -0
- package/dist/cjs/sqlite-commerce-store.js +416 -0
- package/dist/cjs/stream.d.ts +7 -0
- package/dist/cjs/stream.js +26 -0
- package/dist/identity.js +4 -7
- package/dist/index.js +18 -41
- package/dist/liquidity.js +1 -4
- package/dist/location.js +10 -13
- package/dist/lookup.js +3 -6
- package/dist/manifest.js +5 -7
- package/dist/negotiation-transport.js +2 -6
- package/dist/omnia.js +1 -4
- package/dist/payment-l1.js +1 -4
- package/dist/payment-l2.js +5 -8
- package/dist/policy.js +1 -4
- package/dist/proof.js +7 -10
- package/dist/pubsub.js +1 -4
- package/dist/purchase-authority.js +1 -4
- package/dist/purchase-lookup.js +1 -4
- package/dist/purchase-payment.js +4 -7
- package/dist/sqlite-commerce-store.js +4 -9
- package/dist/stream.js +1 -4
- package/package.json +28 -22
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LookupClient } from '@totemsdk/lookup-client';
|
|
2
|
+
import type { EdgeLookupPort } from '@totemsdk/edge';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a LookupClient as an EdgeLookupPort.
|
|
5
|
+
*
|
|
6
|
+
* lookup: queries coins by address, or a single coin by ID when kind === 'coin'.
|
|
7
|
+
* watch: registers for real-time coin-update push events.
|
|
8
|
+
* announce: encodes the caller's WOTS-signed manifest to bytes, then hands off
|
|
9
|
+
* to announceApp() or announceAgent() on the client. The client signs
|
|
10
|
+
* those bytes with its session Ed25519 keypair — no WOTS key index is
|
|
11
|
+
* consumed. Fire-and-forget: the lookup node sends no ACK.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createLookupPortAdapter(client: LookupClient): EdgeLookupPort;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLookupPortAdapter = createLookupPortAdapter;
|
|
4
|
+
const manifest_1 = require("@totemsdk/manifest");
|
|
5
|
+
/**
|
|
6
|
+
* Wraps a LookupClient as an EdgeLookupPort.
|
|
7
|
+
*
|
|
8
|
+
* lookup: queries coins by address, or a single coin by ID when kind === 'coin'.
|
|
9
|
+
* watch: registers for real-time coin-update push events.
|
|
10
|
+
* announce: encodes the caller's WOTS-signed manifest to bytes, then hands off
|
|
11
|
+
* to announceApp() or announceAgent() on the client. The client signs
|
|
12
|
+
* those bytes with its session Ed25519 keypair — no WOTS key index is
|
|
13
|
+
* consumed. Fire-and-forget: the lookup node sends no ACK.
|
|
14
|
+
*/
|
|
15
|
+
function createLookupPortAdapter(client) {
|
|
16
|
+
return {
|
|
17
|
+
async lookup(params) {
|
|
18
|
+
try {
|
|
19
|
+
if (params.kind === 'coin') {
|
|
20
|
+
const coin = await client.getCoin(params.query);
|
|
21
|
+
return { ok: true, data: { results: coin ? [coin] : [] } };
|
|
22
|
+
}
|
|
23
|
+
const coins = await client.getCoins({ address: params.query });
|
|
24
|
+
return { ok: true, data: { results: coins } };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async watch(params) {
|
|
31
|
+
try {
|
|
32
|
+
const unsubscribe = client.subscribeCoinUpdates(params.onUpdate);
|
|
33
|
+
await client.watchAddress(params.address);
|
|
34
|
+
return { ok: true, data: { unsubscribe } };
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
async announce(params) {
|
|
41
|
+
try {
|
|
42
|
+
const manifestBytes = (0, manifest_1.encodeManifest)(params.signed);
|
|
43
|
+
if (params.kind === 'app') {
|
|
44
|
+
await client.announceApp({
|
|
45
|
+
manifest: manifestBytes,
|
|
46
|
+
appId: params.appId,
|
|
47
|
+
expiresAt: params.expiresAt,
|
|
48
|
+
authorAddress: params.authorAddress,
|
|
49
|
+
isFree: params.isFree,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await client.announceAgent({
|
|
54
|
+
manifest: manifestBytes,
|
|
55
|
+
capabilityId: params.capabilityId,
|
|
56
|
+
expiresAt: params.expiresAt,
|
|
57
|
+
tags: params.tags,
|
|
58
|
+
pricePerCall: params.pricePerCall,
|
|
59
|
+
latencyMs: params.latencyMs,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return { ok: true };
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
async query(params) {
|
|
69
|
+
try {
|
|
70
|
+
if (params.kind === 'app') {
|
|
71
|
+
const apps = await client.queryApps({
|
|
72
|
+
category: params.category,
|
|
73
|
+
authorAddress: params.authorAddress,
|
|
74
|
+
minVersion: params.minVersion,
|
|
75
|
+
freeOnly: params.freeOnly,
|
|
76
|
+
limit: params.limit,
|
|
77
|
+
});
|
|
78
|
+
return { ok: true, data: { results: apps.map((a) => ({ id: a.appId, manifest: a.manifest, nodeId: a.nodeId })) } };
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const agents = await client.queryAgents({
|
|
82
|
+
capabilityName: params.capabilityName,
|
|
83
|
+
tags: params.tags,
|
|
84
|
+
maxPricePerCall: params.maxPricePerCall,
|
|
85
|
+
maxLatencyMs: params.maxLatencyMs,
|
|
86
|
+
limit: params.limit,
|
|
87
|
+
});
|
|
88
|
+
return { ok: true, data: { results: agents.map((a) => ({ id: a.capabilityId, manifest: a.manifest, nodeId: a.nodeId })) } };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EdgeManifestPort } from '@totemsdk/edge';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps @totemsdk/manifest's signManifest / verifyManifest as an EdgeManifestPort.
|
|
4
|
+
*
|
|
5
|
+
* The port interface already carries seed and keyIndex at call time, so this
|
|
6
|
+
* adapter has no constructor config — it is purely a thin type bridge.
|
|
7
|
+
*
|
|
8
|
+
* Callers are responsible for key-lease reservation before calling sign().
|
|
9
|
+
* This adapter does not interact with @totemsdk/wots-lease.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createManifestPortAdapter(): EdgeManifestPort;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createManifestPortAdapter = createManifestPortAdapter;
|
|
4
|
+
const manifest_1 = require("@totemsdk/manifest");
|
|
5
|
+
const core_1 = require("@totemsdk/core");
|
|
6
|
+
/**
|
|
7
|
+
* Wraps @totemsdk/manifest's signManifest / verifyManifest as an EdgeManifestPort.
|
|
8
|
+
*
|
|
9
|
+
* The port interface already carries seed and keyIndex at call time, so this
|
|
10
|
+
* adapter has no constructor config — it is purely a thin type bridge.
|
|
11
|
+
*
|
|
12
|
+
* Callers are responsible for key-lease reservation before calling sign().
|
|
13
|
+
* This adapter does not interact with @totemsdk/wots-lease.
|
|
14
|
+
*/
|
|
15
|
+
function createManifestPortAdapter() {
|
|
16
|
+
return {
|
|
17
|
+
async sign(manifest, seed, keyIndex) {
|
|
18
|
+
try {
|
|
19
|
+
const signed = await (0, manifest_1.signManifest)(manifest, (0, core_1.createUnifiedChildTreeKey)(seed, 0), keyIndex);
|
|
20
|
+
return { ok: true, data: { signed } };
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
async verify(signed) {
|
|
27
|
+
try {
|
|
28
|
+
const result = (0, manifest_1.verifyManifest)(signed);
|
|
29
|
+
return { ok: true, data: { valid: result.valid, reason: result.reason } };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* negotiation-transport.ts — NegotiationTransport adapters over Edge ports.
|
|
3
|
+
*
|
|
4
|
+
* Two adapters:
|
|
5
|
+
* - createStreamNegotiationTransport: raw byte stream (EdgeStreamPort).
|
|
6
|
+
* Messages are framed as length-prefixed JSON. The sender address is
|
|
7
|
+
* resolved by the caller (the stream is a single authenticated peer).
|
|
8
|
+
* - createPubSubNegotiationTransport: topic-based (EdgePubSubPort).
|
|
9
|
+
* Each negotiation uses a topic derived from the negotiationId; the
|
|
10
|
+
* sender address is carried in the envelope.
|
|
11
|
+
*
|
|
12
|
+
* Transport metadata stays separate from economic protocol objects. The
|
|
13
|
+
* NegotiationEngine never sees sockets/topics/connection IDs.
|
|
14
|
+
*/
|
|
15
|
+
import type { NegotiationTransport } from '@totemsdk/edge';
|
|
16
|
+
import type { EdgePubSubPort, EdgeStreamPort } from '@totemsdk/edge';
|
|
17
|
+
export interface StreamNegotiationTransportConfig {
|
|
18
|
+
stream: EdgeStreamPort;
|
|
19
|
+
/** The authenticated sender address for this stream (resolved by caller). */
|
|
20
|
+
sender: string;
|
|
21
|
+
/** The local recipient address. */
|
|
22
|
+
recipient: string;
|
|
23
|
+
/** Max message size in bytes (default 64 KiB). */
|
|
24
|
+
maxBytes?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a NegotiationTransport over a raw byte stream.
|
|
28
|
+
*
|
|
29
|
+
* Messages are framed as `[4-byte BE length][JSON bytes]`. The stream is
|
|
30
|
+
* assumed to be a single authenticated peer connection, so the sender address
|
|
31
|
+
* is fixed by the caller.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createStreamNegotiationTransport(config: StreamNegotiationTransportConfig): NegotiationTransport;
|
|
34
|
+
export interface PubSubNegotiationTransportConfig {
|
|
35
|
+
pubsub: EdgePubSubPort;
|
|
36
|
+
/** The local recipient address. */
|
|
37
|
+
recipient: string;
|
|
38
|
+
/** Topic prefix (default 'totem.negotiation'). */
|
|
39
|
+
topicPrefix?: string;
|
|
40
|
+
/** Max message size in bytes (default 64 KiB). */
|
|
41
|
+
maxBytes?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a NegotiationTransport over a pub/sub transport.
|
|
45
|
+
*
|
|
46
|
+
* Each negotiation uses a topic derived from its negotiationId. The sender
|
|
47
|
+
* address is carried in the envelope (a `sender` field on the message), so
|
|
48
|
+
* the ingress pipeline can authenticate it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createPubSubNegotiationTransport(config: PubSubNegotiationTransportConfig): NegotiationTransport;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* negotiation-transport.ts — NegotiationTransport adapters over Edge ports.
|
|
4
|
+
*
|
|
5
|
+
* Two adapters:
|
|
6
|
+
* - createStreamNegotiationTransport: raw byte stream (EdgeStreamPort).
|
|
7
|
+
* Messages are framed as length-prefixed JSON. The sender address is
|
|
8
|
+
* resolved by the caller (the stream is a single authenticated peer).
|
|
9
|
+
* - createPubSubNegotiationTransport: topic-based (EdgePubSubPort).
|
|
10
|
+
* Each negotiation uses a topic derived from the negotiationId; the
|
|
11
|
+
* sender address is carried in the envelope.
|
|
12
|
+
*
|
|
13
|
+
* Transport metadata stays separate from economic protocol objects. The
|
|
14
|
+
* NegotiationEngine never sees sockets/topics/connection IDs.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createStreamNegotiationTransport = createStreamNegotiationTransport;
|
|
18
|
+
exports.createPubSubNegotiationTransport = createPubSubNegotiationTransport;
|
|
19
|
+
/**
|
|
20
|
+
* Create a NegotiationTransport over a raw byte stream.
|
|
21
|
+
*
|
|
22
|
+
* Messages are framed as `[4-byte BE length][JSON bytes]`. The stream is
|
|
23
|
+
* assumed to be a single authenticated peer connection, so the sender address
|
|
24
|
+
* is fixed by the caller.
|
|
25
|
+
*/
|
|
26
|
+
function createStreamNegotiationTransport(config) {
|
|
27
|
+
const { stream, sender, recipient } = config;
|
|
28
|
+
const maxBytes = config.maxBytes ?? 64 * 1024;
|
|
29
|
+
return {
|
|
30
|
+
async send(_recipient, message) {
|
|
31
|
+
const bytes = new TextEncoder().encode(JSON.stringify(message));
|
|
32
|
+
if (bytes.length > maxBytes) {
|
|
33
|
+
throw new Error(`negotiation message exceeds ${maxBytes} bytes`);
|
|
34
|
+
}
|
|
35
|
+
const frame = new Uint8Array(4 + bytes.length);
|
|
36
|
+
new DataView(frame.buffer).setUint32(0, bytes.length);
|
|
37
|
+
frame.set(bytes, 4);
|
|
38
|
+
// Raw byte-stream send does NOT prove durable remote processing —
|
|
39
|
+
// return no receipt so the outbox drainer does not mark delivered.
|
|
40
|
+
stream.send(frame);
|
|
41
|
+
return undefined;
|
|
42
|
+
},
|
|
43
|
+
subscribe(handler) {
|
|
44
|
+
let buffer = new Uint8Array(0);
|
|
45
|
+
const off = stream.onData((chunk) => {
|
|
46
|
+
buffer = concatBytes(buffer, chunk);
|
|
47
|
+
while (buffer.length >= 4) {
|
|
48
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, 4);
|
|
49
|
+
const len = view.getUint32(0);
|
|
50
|
+
if (buffer.length < 4 + len)
|
|
51
|
+
break;
|
|
52
|
+
const payload = buffer.slice(4, 4 + len);
|
|
53
|
+
buffer = buffer.slice(4 + len);
|
|
54
|
+
try {
|
|
55
|
+
const message = JSON.parse(new TextDecoder().decode(payload));
|
|
56
|
+
void handler(message, { sender, recipient });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Malformed frame — drop.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return off;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function topicFor(topicPrefix, negotiationId) {
|
|
68
|
+
return `${topicPrefix}.${negotiationId}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a NegotiationTransport over a pub/sub transport.
|
|
72
|
+
*
|
|
73
|
+
* Each negotiation uses a topic derived from its negotiationId. The sender
|
|
74
|
+
* address is carried in the envelope (a `sender` field on the message), so
|
|
75
|
+
* the ingress pipeline can authenticate it.
|
|
76
|
+
*/
|
|
77
|
+
function createPubSubNegotiationTransport(config) {
|
|
78
|
+
const { pubsub, recipient } = config;
|
|
79
|
+
const topicPrefix = config.topicPrefix ?? 'totem.negotiation';
|
|
80
|
+
const maxBytes = config.maxBytes ?? 64 * 1024;
|
|
81
|
+
return {
|
|
82
|
+
async send(recipientAddress, message) {
|
|
83
|
+
const negotiationId = message.negotiationId;
|
|
84
|
+
if (!negotiationId)
|
|
85
|
+
throw new Error('negotiation message missing negotiationId');
|
|
86
|
+
const topic = topicFor(topicPrefix, negotiationId);
|
|
87
|
+
const envelope = { ...message, sender: recipientAddress, recipient };
|
|
88
|
+
const bytes = new TextEncoder().encode(JSON.stringify(envelope));
|
|
89
|
+
if (bytes.length > maxBytes) {
|
|
90
|
+
throw new Error(`negotiation message exceeds ${maxBytes} bytes`);
|
|
91
|
+
}
|
|
92
|
+
// Pub/sub publish() proves local broker transmission, not durable remote
|
|
93
|
+
// processing — return no receipt so the outbox drainer does not mark
|
|
94
|
+
// delivered on local send alone.
|
|
95
|
+
await pubsub.publish(topic, bytes);
|
|
96
|
+
return undefined;
|
|
97
|
+
},
|
|
98
|
+
subscribe(handler) {
|
|
99
|
+
return pubsub.onMessage(({ topic, payload }) => {
|
|
100
|
+
if (!topic.startsWith(topicPrefix))
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
const envelope = JSON.parse(new TextDecoder().decode(payload));
|
|
104
|
+
const sender = envelope.sender ?? 'unknown';
|
|
105
|
+
void handler(envelope, { sender, recipient });
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Malformed message — drop.
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
+
// Helpers
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
function concatBytes(a, b) {
|
|
118
|
+
const out = new Uint8Array(a.length + b.length);
|
|
119
|
+
out.set(a, 0);
|
|
120
|
+
out.set(b, a.length);
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EdgeOmniaPort } from '@totemsdk/edge';
|
|
2
|
+
export interface OmniaHostPortConfig {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
fetch?: typeof globalThis.fetch;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createOmniaHostPort(config: OmniaHostPortConfig): EdgeOmniaPort;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createOmniaHostPort = createOmniaHostPort;
|
|
4
|
+
function methodName(operation) {
|
|
5
|
+
return `totem_omnia${operation[0].toUpperCase()}${operation.slice(1)}`;
|
|
6
|
+
}
|
|
7
|
+
function createOmniaHostPort(config) {
|
|
8
|
+
const request = config.fetch ?? globalThis.fetch;
|
|
9
|
+
const endpoint = config.endpoint.replace(/\/$/, '');
|
|
10
|
+
async function call(operation, params = {}) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await request(`${endpoint}/rpc`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: { 'content-type': 'application/json', ...config.headers },
|
|
15
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method: methodName(operation), params }),
|
|
16
|
+
});
|
|
17
|
+
const body = await response.json();
|
|
18
|
+
if (!response.ok || body.error) {
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: body.error?.message ?? `Omnia host returned HTTP ${response.status}`,
|
|
22
|
+
errorCode: body.error?.code ? String(body.error.code) : 'OMNIA_HOST_ERROR',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return { ok: true, data: body.result };
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error), errorCode: 'OMNIA_HOST_UNAVAILABLE' };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
getChannels: (params) => call('getChannels', params),
|
|
33
|
+
openChannel: (params) => call('openChannel', params),
|
|
34
|
+
pay: (params) => call('pay', params),
|
|
35
|
+
settle: (params) => call('settle', params),
|
|
36
|
+
closeChannel: (params) => call('closeChannel', params),
|
|
37
|
+
getRoute: (params) => call('getRoute', params),
|
|
38
|
+
payMultiHop: (params) => call('payMultiHop', params),
|
|
39
|
+
getSwapRate: (params) => call('getSwapRate', params),
|
|
40
|
+
createFactory: (params) => call('createFactory', params),
|
|
41
|
+
openVirtualChannel: (params) => call('openVirtualChannel', params),
|
|
42
|
+
closeFactory: (params) => call('closeFactory', params),
|
|
43
|
+
spliceIn: (params) => call('spliceIn', params),
|
|
44
|
+
spliceOut: (params) => call('spliceOut', params),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ChainStateProvider } from '@totemsdk/chain-provider';
|
|
2
|
+
import type { EdgePaymentPort } from '@totemsdk/edge';
|
|
3
|
+
export interface MinimaL1PaymentPortConfig {
|
|
4
|
+
/** ChainStateProvider used to broadcast the signed TxPoW. */
|
|
5
|
+
provider: ChainStateProvider;
|
|
6
|
+
/**
|
|
7
|
+
* Injected signing function. Receives the payment intent and returns a
|
|
8
|
+
* fully mined TxPoW hex string ready for broadcast.
|
|
9
|
+
*
|
|
10
|
+
* This keeps the adapter agnostic to key management — callers wire in their
|
|
11
|
+
* own signer (e.g. @totemsdk/server's sendTransaction, a hardware wallet
|
|
12
|
+
* bridge, or a minima-rpc command sequence).
|
|
13
|
+
*/
|
|
14
|
+
sign(params: {
|
|
15
|
+
toAddress: string;
|
|
16
|
+
amount: string;
|
|
17
|
+
tokenId: string;
|
|
18
|
+
memo?: string;
|
|
19
|
+
}): Promise<string>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Minima L1 payment adapter.
|
|
23
|
+
*
|
|
24
|
+
* Delegates transaction construction and signing to the injected `sign` function,
|
|
25
|
+
* then broadcasts the result via the ChainStateProvider. This keeps key material
|
|
26
|
+
* out of the adapter entirely.
|
|
27
|
+
*
|
|
28
|
+
* For a batteries-included L1 adapter backed by @totemsdk/server's sendTransaction,
|
|
29
|
+
* wrap sendTransaction in the sign callback:
|
|
30
|
+
*
|
|
31
|
+
* createMinimaL1PaymentPort({
|
|
32
|
+
* provider: new PureMinimaRpcProvider(rpcConfig),
|
|
33
|
+
* sign: ({ toAddress, amount, tokenId }) =>
|
|
34
|
+
* sendTransaction({ seed, addressIndex, toAddress, amount, tokenId, ... })
|
|
35
|
+
* .then(r => r.minedHex),
|
|
36
|
+
* })
|
|
37
|
+
*/
|
|
38
|
+
export declare function createMinimaL1PaymentPort(config: MinimaL1PaymentPortConfig): EdgePaymentPort;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMinimaL1PaymentPort = createMinimaL1PaymentPort;
|
|
4
|
+
/**
|
|
5
|
+
* Minima L1 payment adapter.
|
|
6
|
+
*
|
|
7
|
+
* Delegates transaction construction and signing to the injected `sign` function,
|
|
8
|
+
* then broadcasts the result via the ChainStateProvider. This keeps key material
|
|
9
|
+
* out of the adapter entirely.
|
|
10
|
+
*
|
|
11
|
+
* For a batteries-included L1 adapter backed by @totemsdk/server's sendTransaction,
|
|
12
|
+
* wrap sendTransaction in the sign callback:
|
|
13
|
+
*
|
|
14
|
+
* createMinimaL1PaymentPort({
|
|
15
|
+
* provider: new PureMinimaRpcProvider(rpcConfig),
|
|
16
|
+
* sign: ({ toAddress, amount, tokenId }) =>
|
|
17
|
+
* sendTransaction({ seed, addressIndex, toAddress, amount, tokenId, ... })
|
|
18
|
+
* .then(r => r.minedHex),
|
|
19
|
+
* })
|
|
20
|
+
*/
|
|
21
|
+
function createMinimaL1PaymentPort(config) {
|
|
22
|
+
return {
|
|
23
|
+
async pay(params) {
|
|
24
|
+
try {
|
|
25
|
+
const txpowHex = await config.sign({
|
|
26
|
+
toAddress: params.recipient,
|
|
27
|
+
amount: params.amount,
|
|
28
|
+
tokenId: params.tokenId ?? '0x00',
|
|
29
|
+
memo: params.memo,
|
|
30
|
+
});
|
|
31
|
+
const result = await config.provider.broadcastTxPoW(txpowHex);
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
error: result.message ?? 'Broadcast failed',
|
|
36
|
+
errorCode: 'BROADCAST_FAILED',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return { ok: true, data: { txpowId: result.txpowid } };
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ChannelGraph, ChannelOps, RouterChannel, LeaseProvider, RouteOptions } from '@totemsdk/omnia-router';
|
|
2
|
+
import type { EdgePaymentPort } from '@totemsdk/edge';
|
|
3
|
+
export interface OmniaL2PaymentPortConfig {
|
|
4
|
+
/** Routing graph (edges + swap index). Updated externally as channels open/close. */
|
|
5
|
+
graph: ChannelGraph;
|
|
6
|
+
/**
|
|
7
|
+
* Live channel state keyed by channelId.
|
|
8
|
+
* executeMultiHopPayment mutates this map in-place as HTLCs are added/settled.
|
|
9
|
+
* Callers are responsible for keeping it in sync with the on-chain state.
|
|
10
|
+
*/
|
|
11
|
+
channels: Map<string, RouterChannel>;
|
|
12
|
+
/** HTLC operations (addHTLC, fulfillHTLC, timeoutHTLC) for each channel. */
|
|
13
|
+
ops: ChannelOps;
|
|
14
|
+
/** WOTS lease providers keyed by channelId — required for HTLC signing. */
|
|
15
|
+
leaseProviders: Map<string, LeaseProvider>;
|
|
16
|
+
/** Public key digest identifying the local party in each channel. */
|
|
17
|
+
localPublicKeyDigest: string;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the current chain block height. Used to compute HTLC expiry.
|
|
20
|
+
* Typically: `async () => BigInt((await provider.getTip()).block)`.
|
|
21
|
+
*/
|
|
22
|
+
getCurrentBlock(): Promise<bigint>;
|
|
23
|
+
/** HTLC timeout in blocks past the current tip. Defaults to 144 (≈24h on Minima). */
|
|
24
|
+
htlcTimeoutBlocks?: bigint;
|
|
25
|
+
/** Optional pathfinding overrides forwarded to findRoute. */
|
|
26
|
+
routeOptions?: RouteOptions;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Omnia L2 payment adapter.
|
|
30
|
+
*
|
|
31
|
+
* Routes payments over Omnia payment channels using multi-hop HTLC execution.
|
|
32
|
+
* The local node must already have open channels forming a path to the recipient.
|
|
33
|
+
*
|
|
34
|
+
* pay() finds a route, builds a PaymentRequest, and executes atomically:
|
|
35
|
+
* 1. Forward phase — locks HTLCs across each hop.
|
|
36
|
+
* 2. Reveal phase — reveals the preimage in reverse to settle all hops.
|
|
37
|
+
* Rollback (best-effort timeoutHTLC) fires on any failure.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createOmniaL2PaymentPort(config: OmniaL2PaymentPortConfig): EdgePaymentPort;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createOmniaL2PaymentPort = createOmniaL2PaymentPort;
|
|
4
|
+
const omnia_router_1 = require("@totemsdk/omnia-router");
|
|
5
|
+
const _SCALE = 100000000n;
|
|
6
|
+
function _toScaled(s) {
|
|
7
|
+
const [i, f = ''] = s.split('.');
|
|
8
|
+
return BigInt(i || '0') * _SCALE + BigInt(f.padEnd(8, '0').slice(0, 8));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Omnia L2 payment adapter.
|
|
12
|
+
*
|
|
13
|
+
* Routes payments over Omnia payment channels using multi-hop HTLC execution.
|
|
14
|
+
* The local node must already have open channels forming a path to the recipient.
|
|
15
|
+
*
|
|
16
|
+
* pay() finds a route, builds a PaymentRequest, and executes atomically:
|
|
17
|
+
* 1. Forward phase — locks HTLCs across each hop.
|
|
18
|
+
* 2. Reveal phase — reveals the preimage in reverse to settle all hops.
|
|
19
|
+
* Rollback (best-effort timeoutHTLC) fires on any failure.
|
|
20
|
+
*/
|
|
21
|
+
function createOmniaL2PaymentPort(config) {
|
|
22
|
+
const htlcTimeoutBlocks = config.htlcTimeoutBlocks ?? 144n;
|
|
23
|
+
return {
|
|
24
|
+
async pay(params) {
|
|
25
|
+
try {
|
|
26
|
+
const tokenId = params.tokenId ?? '0x00';
|
|
27
|
+
const amount = _toScaled(params.amount);
|
|
28
|
+
const route = (0, omnia_router_1.findRoute)(config.graph, config.localPublicKeyDigest, params.recipient, amount, tokenId, config.routeOptions);
|
|
29
|
+
if (!route) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
error: `No route found to ${params.recipient} for amount ${params.amount} (${tokenId})`,
|
|
33
|
+
errorCode: 'NO_ROUTE',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const currentBlock = await config.getCurrentBlock();
|
|
37
|
+
const expiryBlock = currentBlock + htlcTimeoutBlocks;
|
|
38
|
+
const request = (0, omnia_router_1.buildPaymentRequest)(amount, tokenId, expiryBlock);
|
|
39
|
+
const result = await (0, omnia_router_1.executeMultiHopPayment)(config.ops, config.channels, route, request, config.leaseProviders);
|
|
40
|
+
if (!result.success) {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
error: result.error ?? 'Multi-hop payment failed',
|
|
44
|
+
errorCode: 'PAYMENT_FAILED',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return { ok: true, data: { txpowId: result.preimage } };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentPolicy, PolicyMiddleware } from '@totemsdk/agent-policy';
|
|
2
|
+
import type { EdgePolicyPort } from '@totemsdk/edge';
|
|
3
|
+
type PolicyLike = AgentPolicy | PolicyMiddleware;
|
|
4
|
+
/**
|
|
5
|
+
* Wraps an AgentPolicy or PolicyMiddleware as an EdgePolicyPort.
|
|
6
|
+
*
|
|
7
|
+
* When the EdgePolicyPort receives a full `proposal` object, it delegates
|
|
8
|
+
* directly to the policy without lossy reconstruction. When only flat
|
|
9
|
+
* `action`/`subject` params are provided, it builds a minimal AgentProposal
|
|
10
|
+
* (legacy path).
|
|
11
|
+
*/
|
|
12
|
+
export declare function createPolicyPortAdapter(policy: PolicyLike): EdgePolicyPort;
|
|
13
|
+
export {};
|