@totemsdk/chain-provider 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -16,13 +16,14 @@ npm install @totemsdk/chain-provider
16
16
 
17
17
  ```typescript
18
18
  interface ChainStateProvider {
19
- getCoins(query: CoinQuery): Promise<Coin[]>;
20
- getCoin(id: string): Promise<Coin | null>;
21
- getMMRProof(coinId: string): Promise<MMRProof>;
22
- getChainTip(): Promise<ChainTip>;
23
- getToken(id: string): Promise<Token | null>;
24
- searchTokens(query: string): Promise<Token[]>;
25
- broadcastTxPoW(hex: string): Promise<BroadcastResult>;
19
+ getCoins(query: CoinsQuery): Promise<Coin[]>;
20
+ getCoin(coinId: string): Promise<Coin | null>;
21
+ getProof(coinId: string): Promise<MMRProof>;
22
+ getTip(): Promise<ChainTip>;
23
+ getToken(tokenId: string): Promise<TokenInfo>;
24
+ searchTokens(query: TokenSearchQuery): Promise<TokenInfo[]>;
25
+ getTokensByCreator(address: string): Promise<TokenInfo[]>;
26
+ broadcastTxPoW(txpowHex: string): Promise<BroadcastResult>;
26
27
  }
27
28
  ```
28
29
 
@@ -32,7 +33,7 @@ interface ChainStateProvider {
32
33
  |-------|------------|
33
34
  | `HostedProvider` | Axia / MEG hosted API (requires project credentials) |
34
35
  | `PureMinimaRpcProvider` | A local or self-hosted PureMinima node directly |
35
- | `LookupNodeProvider` | A personal lookup node over Hyperswarm DHT |
36
+ | `LookupClientProvider` | A personal lookup node over Hyperswarm DHT (via `@totemsdk/lookup-client`) |
36
37
  | `CompositeProvider` | Fans out across multiple providers with fallback logic |
37
38
 
38
39
  Five other packages (`omnia`, `statechain`, `lookup-node`, `lookup-client`, `chain-provider` itself) accept `ChainStateProvider` — this is the pivot point between the upper SDK and chain data.
@@ -50,7 +51,7 @@ const provider = new HostedProvider({
50
51
  projectSecret: process.env.AXIA_SECRET,
51
52
  });
52
53
 
53
- const tip = await provider.getChainTip();
54
+ const tip = await provider.getTip();
54
55
  const coins = await provider.getCoins({ address: 'Mx...' });
55
56
  ```
56
57
 
@@ -64,31 +65,72 @@ const provider = new PureMinimaRpcProvider({
64
65
  });
65
66
  ```
66
67
 
68
+ ### Personal lookup node (sovereign, zero-Axia-dependency)
69
+
70
+ Connect directly to your own lookup node over Hyperswarm DHT. No hosted infrastructure required.
71
+
72
+ ```typescript
73
+ import { LookupClientProvider } from '@totemsdk/chain-provider';
74
+ import { connectLookupNode } from '@totemsdk/lookup-client';
75
+
76
+ // Connect to your personal lookup node
77
+ const client = await connectLookupNode({ hyperswarmTopic: 'deadbeef...' });
78
+
79
+ const provider = new LookupClientProvider(client);
80
+
81
+ const tip = await provider.getTip();
82
+ const coins = await provider.getCoins({ address: 'Mx...' });
83
+
84
+ // Clean up when done
85
+ client.disconnect();
86
+ ```
87
+
88
+ `@totemsdk/lookup-client` is a peer dependency — install it separately:
89
+
90
+ ```bash
91
+ npm install @totemsdk/lookup-client
92
+ ```
93
+
67
94
  ### Composite with fallback
68
95
 
96
+ Fan out across providers: try sovereign first, fall back to hosted on any error.
97
+
69
98
  ```typescript
70
- import { CompositeProvider, HostedProvider, PureMinimaRpcProvider } from '@totemsdk/chain-provider';
99
+ import { CompositeProvider, HostedProvider, LookupClientProvider } from '@totemsdk/chain-provider';
100
+ import { connectLookupNode } from '@totemsdk/lookup-client';
71
101
 
72
- const provider = new CompositeProvider([
73
- new PureMinimaRpcProvider({ nodeUrl: 'http://localhost:9005' }),
74
- new HostedProvider({ baseUrl: 'https://api.axia.to', projectId: '...' }),
75
- ]);
76
- // Queries hit the first provider; fallback to the second on error
102
+ const client = await connectLookupNode({ hyperswarmTopic: 'deadbeef...' });
103
+ const hosted = new HostedProvider({ baseUrl: 'https://api.axia.to', projectId: '...' });
104
+
105
+ const provider = new CompositeProvider(
106
+ new LookupClientProvider(client), // primary sovereign, no Axia dependency
107
+ hosted, // fallback — hosted, always available
108
+ );
109
+
110
+ // Queries hit the lookup node first; fall back to hosted on error
111
+ const tip = await provider.getTip();
77
112
  ```
78
113
 
79
- ### Personal lookup node
114
+ ## LookupClientLike interface
115
+
116
+ `LookupClientProvider` accepts any object that satisfies the `LookupClientLike` structural interface exported from this package. This allows testing with a mock without importing `@totemsdk/lookup-client`:
80
117
 
81
118
  ```typescript
82
- import { LookupNodeProvider } from '@totemsdk/chain-provider';
119
+ import { LookupClientProvider } from '@totemsdk/chain-provider';
120
+ import type { LookupClientLike } from '@totemsdk/chain-provider';
83
121
 
84
- const provider = new LookupNodeProvider({
85
- publicKey: '...your-lookup-node-pubkey...',
86
- });
122
+ const mockClient: LookupClientLike = {
123
+ getTip: async () => ({ block: 100, hash: '0x...' }),
124
+ getCoins: async () => [],
125
+ // ... etc
126
+ };
127
+
128
+ const provider = new LookupClientProvider(mockClient);
87
129
  ```
88
130
 
89
131
  ## See also
90
132
 
91
133
  - [`@totemsdk/pureminima-rpc`](https://www.npmjs.com/package/@totemsdk/pureminima-rpc) — the RPC client used by `PureMinimaRpcProvider`
92
- - [`@totemsdk/lookup-client`](https://www.npmjs.com/package/@totemsdk/lookup-client) — `LookupClientProvider` implementation
134
+ - [`@totemsdk/lookup-client`](https://www.npmjs.com/package/@totemsdk/lookup-client) — connect to a personal lookup node over Hyperswarm DHT
93
135
  - [`@totemsdk/node`](https://www.npmjs.com/package/@totemsdk/node) — Node.js `MinimaProvider`
94
136
  - [`@totemsdk/omnia`](https://www.npmjs.com/package/@totemsdk/omnia) — uses `ChainStateProvider` for coin queries
package/dist/index.d.ts CHANGED
@@ -2,5 +2,6 @@ export type { ChainStateProvider, CoinsQuery, Coin, MMRProof, ChainTip, TokenInf
2
2
  export { HostedProvider } from './providers/hosted.js';
3
3
  export type { HostedProviderConfig } from './providers/hosted.js';
4
4
  export { PureMinimaRpcProvider } from './providers/pureminima.js';
5
- export { LookupNodeProvider, LookupNodeNotImplementedError } from './providers/lookup.js';
5
+ export { LookupClientProvider } from './providers/lookup-client.js';
6
+ export type { LookupClientLike } from './providers/lookup-client.js';
6
7
  export { CompositeProvider } from './providers/composite.js';
package/dist/index.js CHANGED
@@ -1,12 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CompositeProvider = exports.LookupNodeNotImplementedError = exports.LookupNodeProvider = exports.PureMinimaRpcProvider = exports.HostedProvider = void 0;
3
+ exports.CompositeProvider = exports.LookupClientProvider = exports.PureMinimaRpcProvider = exports.HostedProvider = void 0;
4
4
  var hosted_js_1 = require("./providers/hosted.js");
5
5
  Object.defineProperty(exports, "HostedProvider", { enumerable: true, get: function () { return hosted_js_1.HostedProvider; } });
6
6
  var pureminima_js_1 = require("./providers/pureminima.js");
7
7
  Object.defineProperty(exports, "PureMinimaRpcProvider", { enumerable: true, get: function () { return pureminima_js_1.PureMinimaRpcProvider; } });
8
- var lookup_js_1 = require("./providers/lookup.js");
9
- Object.defineProperty(exports, "LookupNodeProvider", { enumerable: true, get: function () { return lookup_js_1.LookupNodeProvider; } });
10
- Object.defineProperty(exports, "LookupNodeNotImplementedError", { enumerable: true, get: function () { return lookup_js_1.LookupNodeNotImplementedError; } });
8
+ var lookup_client_js_1 = require("./providers/lookup-client.js");
9
+ Object.defineProperty(exports, "LookupClientProvider", { enumerable: true, get: function () { return lookup_client_js_1.LookupClientProvider; } });
11
10
  var composite_js_1 = require("./providers/composite.js");
12
11
  Object.defineProperty(exports, "CompositeProvider", { enumerable: true, get: function () { return composite_js_1.CompositeProvider; } });
@@ -1,6 +1,13 @@
1
1
  /**
2
- * HostedProvider — wraps existing Axia REST API endpoints.
3
- * Uses fetch only; no Node.js-specific dependencies.
2
+ * HostedProvider — wraps Axia REST + RPC API endpoints.
3
+ *
4
+ * Endpoint map (all require x-api-key header):
5
+ * GET /v1/wallet/utxos/:address — coin list (totem-shared key)
6
+ * POST /v1/wallet/rpc — Minima RPC commands (totem-shared key)
7
+ * POST /api/meg/postminedtxn — broadcast mined TxPoW (no auth required)
8
+ *
9
+ * For dApp developers using custom project API keys the `/v1/wallet/*` routes
10
+ * require x-api-key: 'totem-shared'. Use the apiKey config option accordingly.
4
11
  */
5
12
  import type { ChainStateProvider, CoinsQuery, Coin, MMRProof, ChainTip, TokenInfo, TokenSearchQuery, BroadcastResult } from '../types.js';
6
13
  export interface HostedProviderConfig {
@@ -14,12 +21,38 @@ export declare class HostedProvider implements ChainStateProvider {
14
21
  private readonly timeoutMs;
15
22
  constructor(config: HostedProviderConfig);
16
23
  private fetchJson;
24
+ /**
25
+ * Send a Minima command to POST /v1/wallet/rpc.
26
+ * Returns the `response` field of the Minima envelope.
27
+ */
28
+ private rpc;
29
+ /**
30
+ * Get coins (UTXOs) for an address.
31
+ * Maps GET /v1/wallet/utxos/:address → Coin[].
32
+ */
17
33
  getCoins(query: CoinsQuery): Promise<Coin[]>;
18
34
  getCoin(coinId: string): Promise<Coin | null>;
35
+ /**
36
+ * Get MMR proof for a coin via Minima `coinproof` RPC command.
37
+ */
19
38
  getProof(coinId: string): Promise<MMRProof>;
39
+ /**
40
+ * Get chain tip via Minima `status` RPC — parses response.chain.
41
+ */
20
42
  getTip(): Promise<ChainTip>;
43
+ /**
44
+ * Get token info via Minima `tokens tokenid:X` RPC command.
45
+ */
21
46
  getToken(tokenId: string): Promise<TokenInfo>;
47
+ /**
48
+ * Search tokens via Minima `tokens` RPC command, then filter client-side.
49
+ */
22
50
  searchTokens(query: TokenSearchQuery): Promise<TokenInfo[]>;
23
51
  getTokensByCreator(address: string): Promise<TokenInfo[]>;
52
+ /**
53
+ * Broadcast a mined TxPoW hex.
54
+ * Uses POST /api/meg/postminedtxn (the correct Axia MEG broadcast bridge).
55
+ */
24
56
  broadcastTxPoW(txpowHex: string): Promise<BroadcastResult>;
57
+ private _mapToken;
25
58
  }
@@ -1,13 +1,36 @@
1
1
  "use strict";
2
2
  /**
3
- * HostedProvider — wraps existing Axia REST API endpoints.
4
- * Uses fetch only; no Node.js-specific dependencies.
3
+ * HostedProvider — wraps Axia REST + RPC API endpoints.
4
+ *
5
+ * Endpoint map (all require x-api-key header):
6
+ * GET /v1/wallet/utxos/:address — coin list (totem-shared key)
7
+ * POST /v1/wallet/rpc — Minima RPC commands (totem-shared key)
8
+ * POST /api/meg/postminedtxn — broadcast mined TxPoW (no auth required)
9
+ *
10
+ * For dApp developers using custom project API keys the `/v1/wallet/*` routes
11
+ * require x-api-key: 'totem-shared'. Use the apiKey config option accordingly.
5
12
  */
6
13
  Object.defineProperty(exports, "__esModule", { value: true });
7
14
  exports.HostedProvider = void 0;
15
+ function sanitizeRpcParam(value, name) {
16
+ if (value.length > 1024) {
17
+ throw new Error(`RPC parameter ${name} exceeds maximum length (1024)`);
18
+ }
19
+ if (/[\x00-\x1f\x7f]/.test(value)) {
20
+ throw new Error(`RPC parameter ${name} contains control characters`);
21
+ }
22
+ const trimmed = value.trim();
23
+ if (trimmed.length === 0) {
24
+ throw new Error(`RPC parameter ${name} is empty after trimming`);
25
+ }
26
+ return trimmed;
27
+ }
8
28
  class HostedProvider {
9
29
  constructor(config) {
10
30
  this.baseUrl = config.baseUrl.replace(/\/$/, '');
31
+ if (!this.baseUrl.startsWith('https://')) {
32
+ throw new Error('HostedProvider requires HTTPS — credentials sent via x-api-key header');
33
+ }
11
34
  this.apiKey = config.apiKey;
12
35
  this.timeoutMs = config.timeoutMs ?? 30000;
13
36
  }
@@ -33,76 +56,188 @@ class HostedProvider {
33
56
  clearTimeout(timer);
34
57
  }
35
58
  }
59
+ /**
60
+ * Send a Minima command to POST /v1/wallet/rpc.
61
+ * Returns the `response` field of the Minima envelope.
62
+ */
63
+ async rpc(command) {
64
+ const controller = new AbortController();
65
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
66
+ try {
67
+ const res = await fetch(`${this.baseUrl}/v1/wallet/rpc`, {
68
+ method: 'POST',
69
+ signal: controller.signal,
70
+ headers: {
71
+ 'Content-Type': 'text/plain',
72
+ 'x-api-key': this.apiKey,
73
+ },
74
+ body: command,
75
+ });
76
+ if (!res.ok)
77
+ throw new Error(`HostedProvider RPC "${command}" HTTP ${res.status}`);
78
+ const json = await res.json();
79
+ if (json.status === false)
80
+ throw new Error(`RPC "${command}" failed: ${json.error ?? 'unknown'}`);
81
+ return json.response;
82
+ }
83
+ finally {
84
+ clearTimeout(timer);
85
+ }
86
+ }
87
+ /**
88
+ * Get coins (UTXOs) for an address.
89
+ * Maps GET /v1/wallet/utxos/:address → Coin[].
90
+ */
36
91
  async getCoins(query) {
37
- const params = new URLSearchParams();
38
- if (query.address)
39
- params.set('address', query.address);
40
- if (query.tokenId)
41
- params.set('tokenid', query.tokenId);
42
- if (query.sendable !== undefined)
43
- params.set('sendable', String(query.sendable));
44
- if (query.relevant !== undefined)
45
- params.set('relevant', String(query.relevant));
46
- if (query.coinId)
47
- params.set('coinid', query.coinId);
48
- if (query.megammr !== undefined)
49
- params.set('megammr', String(query.megammr));
50
- const qs = params.toString();
51
- const result = await this.fetchJson(`/v1/wallet/coins${qs ? `?${qs}` : ''}`);
52
- return Array.isArray(result) ? result : (result.coins ?? []);
92
+ if (!query.address && !query.coinId)
93
+ return [];
94
+ if (query.coinId) {
95
+ const coin = await this.getCoin(query.coinId);
96
+ return coin ? [coin] : [];
97
+ }
98
+ const data = await this.fetchJson(`/v1/wallet/utxos/${encodeURIComponent(query.address)}`);
99
+ const raw = data.utxos ?? [];
100
+ return raw
101
+ .filter((u) => {
102
+ if (query.tokenId && u.tokenid !== query.tokenId)
103
+ return false;
104
+ if (query.sendable !== undefined && !!u.spent !== !query.sendable)
105
+ return false;
106
+ return true;
107
+ })
108
+ .map((u) => ({
109
+ coinid: u.coinid ?? u.id ?? '',
110
+ amount: String(u.amount ?? '0'),
111
+ address: u.address ?? query.address ?? '',
112
+ miniaddress: u.miniaddress,
113
+ tokenid: u.tokenid ?? '0x00',
114
+ token: u.token,
115
+ storestate: u.storestate,
116
+ state: u.state,
117
+ spent: !!u.spent,
118
+ mmrentry: u.mmrentry,
119
+ created: u.created,
120
+ }));
53
121
  }
54
122
  async getCoin(coinId) {
55
123
  try {
56
- const coins = await this.getCoins({ coinId });
57
- return coins[0] ?? null;
124
+ const clean = sanitizeRpcParam(coinId, 'coinId');
125
+ const coins = await this.rpc(`coins coinid:${clean}`);
126
+ const u = Array.isArray(coins) ? coins[0] : null;
127
+ if (!u)
128
+ return null;
129
+ return {
130
+ coinid: u.coinid ?? coinId,
131
+ amount: String(u.amount ?? '0'),
132
+ address: u.address ?? '',
133
+ miniaddress: u.miniaddress,
134
+ tokenid: u.tokenid ?? '0x00',
135
+ token: u.token,
136
+ storestate: u.storestate,
137
+ state: u.state,
138
+ spent: !!u.spent,
139
+ mmrentry: u.mmrentry,
140
+ created: u.created,
141
+ };
58
142
  }
59
143
  catch {
60
144
  return null;
61
145
  }
62
146
  }
147
+ /**
148
+ * Get MMR proof for a coin via Minima `coinproof` RPC command.
149
+ */
63
150
  async getProof(coinId) {
64
- return this.fetchJson(`/v1/wallet/proofs/${encodeURIComponent(coinId)}`);
151
+ const clean = sanitizeRpcParam(coinId, 'coinId');
152
+ const data = await this.rpc(`coinproof coinid:${clean}`);
153
+ return { coinid: coinId, data };
65
154
  }
155
+ /**
156
+ * Get chain tip via Minima `status` RPC — parses response.chain.
157
+ */
66
158
  async getTip() {
67
- const result = await this.fetchJson('/v1/status/tip');
68
- if ('block' in result)
69
- return result;
70
- const r = result;
71
- return (r.chain ?? r.tip);
159
+ const data = await this.rpc('status');
160
+ const chain = data?.chain;
161
+ return {
162
+ block: chain?.block ?? 0,
163
+ hash: chain?.hash ?? '',
164
+ time: chain?.time,
165
+ };
72
166
  }
167
+ /**
168
+ * Get token info via Minima `tokens tokenid:X` RPC command.
169
+ */
73
170
  async getToken(tokenId) {
74
- return this.fetchJson(`/v1/tokens/${encodeURIComponent(tokenId)}`);
171
+ const clean = sanitizeRpcParam(tokenId, 'tokenId');
172
+ const data = await this.rpc(`tokens tokenid:${clean}`);
173
+ const t = Array.isArray(data) ? data[0] : data;
174
+ if (!t)
175
+ throw new Error(`Token not found: ${tokenId}`);
176
+ return this._mapToken(t);
75
177
  }
178
+ /**
179
+ * Search tokens via Minima `tokens` RPC command, then filter client-side.
180
+ */
76
181
  async searchTokens(query) {
77
- const params = new URLSearchParams();
78
- if (query.name)
79
- params.set('name', query.name);
80
- if (query.category?.length)
81
- params.set('category', query.category.join(','));
82
- if (query.creatorAddress)
83
- params.set('creator', query.creatorAddress);
84
- if (query.limit !== undefined)
85
- params.set('limit', String(query.limit));
86
- if (query.offset !== undefined)
87
- params.set('offset', String(query.offset));
88
- const qs = params.toString();
89
- const result = await this.fetchJson(`/v1/tokens${qs ? `?${qs}` : ''}`);
90
- return Array.isArray(result) ? result : (result.tokens ?? []);
182
+ const data = await this.rpc('tokens');
183
+ const all = Array.isArray(data) ? data : [];
184
+ return all
185
+ .filter((t) => {
186
+ if (t.tokenid === '0x00')
187
+ return false; // skip native Minima
188
+ if (query.name) {
189
+ const n = typeof t.name === 'object' ? (t.name?.name ?? '') : String(t.name ?? '');
190
+ if (!n.toLowerCase().includes(query.name.toLowerCase()))
191
+ return false;
192
+ }
193
+ return true;
194
+ })
195
+ .slice(query.offset ?? 0, query.limit ? (query.offset ?? 0) + query.limit : undefined)
196
+ .map(this._mapToken);
91
197
  }
92
198
  async getTokensByCreator(address) {
93
199
  return this.searchTokens({ creatorAddress: address });
94
200
  }
201
+ /**
202
+ * Broadcast a mined TxPoW hex.
203
+ * Uses POST /api/meg/postminedtxn (the correct Axia MEG broadcast bridge).
204
+ */
95
205
  async broadcastTxPoW(txpowHex) {
96
206
  try {
97
- const result = await this.fetchJson('/v1/txpow/broadcast', {
98
- method: 'POST',
99
- body: JSON.stringify({ txpow: txpowHex }),
100
- });
101
- return { success: true, txpowid: result.txpowid, message: result.message };
207
+ const controller = new AbortController();
208
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
209
+ let result;
210
+ try {
211
+ const res = await fetch(`${this.baseUrl}/api/meg/postminedtxn`, {
212
+ method: 'POST',
213
+ signal: controller.signal,
214
+ headers: { 'Content-Type': 'application/json' },
215
+ body: JSON.stringify({ data: txpowHex }),
216
+ });
217
+ result = await res.json();
218
+ if (!res.ok)
219
+ return { success: false, message: `HTTP ${res.status}: ${result?.error ?? ''}` };
220
+ }
221
+ finally {
222
+ clearTimeout(timer);
223
+ }
224
+ return { success: true, txpowid: result?.txpowid, message: result?.message };
102
225
  }
103
226
  catch (e) {
104
227
  return { success: false, message: String(e) };
105
228
  }
106
229
  }
230
+ _mapToken(t) {
231
+ return {
232
+ tokenid: t.tokenid ?? '',
233
+ name: typeof t.name === 'object' ? t.name : { name: String(t.name ?? '') },
234
+ total: t.total,
235
+ confirmed: t.confirmed,
236
+ sendable: t.sendable,
237
+ coins: t.coins,
238
+ script: t.script,
239
+ description: t.description,
240
+ };
241
+ }
107
242
  }
108
243
  exports.HostedProvider = HostedProvider;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * LookupClientProvider — ChainStateProvider backed by a @totemsdk/lookup-client instance.
3
+ *
4
+ * Accepts any object that satisfies LookupClientLike (structural duck-typing),
5
+ * so @totemsdk/lookup-client remains a peer dependency: chain-provider does not
6
+ * bundle or import it at runtime. The caller is responsible for constructing the
7
+ * LookupClient and passing it in.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { connectLookupNode, LookupClientProvider } from '@totemsdk/lookup-client';
12
+ *
13
+ * const client = await connectLookupNode({ hyperswarmTopic: 'deadbeef...' });
14
+ * const provider = new LookupClientProvider(client);
15
+ *
16
+ * const tip = await provider.getTip();
17
+ * const coins = await provider.getCoins({ address: 'Mx...' });
18
+ * ```
19
+ *
20
+ * Use inside a CompositeProvider for sovereign-first / hosted-fallback:
21
+ *
22
+ * ```ts
23
+ * import { CompositeProvider, HostedProvider, LookupClientProvider } from '@totemsdk/chain-provider';
24
+ * import { connectLookupNode } from '@totemsdk/lookup-client';
25
+ *
26
+ * const client = await connectLookupNode({ hyperswarmTopic: 'abc...' });
27
+ * const fallback = new HostedProvider({ baseUrl: 'https://api.axia.to', projectId: '...' });
28
+ *
29
+ * const provider = new CompositeProvider(
30
+ * new LookupClientProvider(client),
31
+ * fallback,
32
+ * );
33
+ * ```
34
+ */
35
+ import type { BroadcastResult, ChainStateProvider, ChainTip, Coin, CoinsQuery, MMRProof, TokenInfo, TokenSearchQuery } from '../types.js';
36
+ /**
37
+ * Structural interface describing the subset of LookupClient methods that
38
+ * LookupClientProvider requires. Any object satisfying this interface can be
39
+ * passed in — most commonly a `LookupClient` from @totemsdk/lookup-client.
40
+ */
41
+ export interface LookupClientLike {
42
+ getCoins(query: CoinsQuery): Promise<Coin[]>;
43
+ getCoin(coinId: string): Promise<Coin | null>;
44
+ getProof(coinId: string): Promise<MMRProof>;
45
+ getTip(): Promise<ChainTip>;
46
+ getToken(tokenId: string): Promise<TokenInfo>;
47
+ searchTokens(query: TokenSearchQuery): Promise<TokenInfo[]>;
48
+ getTokensByCreator(address: string): Promise<TokenInfo[]>;
49
+ broadcastTxPoW(txpowHex: string): Promise<BroadcastResult>;
50
+ }
51
+ export declare class LookupClientProvider implements ChainStateProvider {
52
+ private readonly _client;
53
+ constructor(_client: LookupClientLike);
54
+ getCoins(query: CoinsQuery): Promise<Coin[]>;
55
+ getCoin(coinId: string): Promise<Coin | null>;
56
+ getProof(coinId: string): Promise<MMRProof>;
57
+ getTip(): Promise<ChainTip>;
58
+ getToken(tokenId: string): Promise<TokenInfo>;
59
+ searchTokens(query: TokenSearchQuery): Promise<TokenInfo[]>;
60
+ getTokensByCreator(address: string): Promise<TokenInfo[]>;
61
+ broadcastTxPoW(txpowHex: string): Promise<BroadcastResult>;
62
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /**
3
+ * LookupClientProvider — ChainStateProvider backed by a @totemsdk/lookup-client instance.
4
+ *
5
+ * Accepts any object that satisfies LookupClientLike (structural duck-typing),
6
+ * so @totemsdk/lookup-client remains a peer dependency: chain-provider does not
7
+ * bundle or import it at runtime. The caller is responsible for constructing the
8
+ * LookupClient and passing it in.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { connectLookupNode, LookupClientProvider } from '@totemsdk/lookup-client';
13
+ *
14
+ * const client = await connectLookupNode({ hyperswarmTopic: 'deadbeef...' });
15
+ * const provider = new LookupClientProvider(client);
16
+ *
17
+ * const tip = await provider.getTip();
18
+ * const coins = await provider.getCoins({ address: 'Mx...' });
19
+ * ```
20
+ *
21
+ * Use inside a CompositeProvider for sovereign-first / hosted-fallback:
22
+ *
23
+ * ```ts
24
+ * import { CompositeProvider, HostedProvider, LookupClientProvider } from '@totemsdk/chain-provider';
25
+ * import { connectLookupNode } from '@totemsdk/lookup-client';
26
+ *
27
+ * const client = await connectLookupNode({ hyperswarmTopic: 'abc...' });
28
+ * const fallback = new HostedProvider({ baseUrl: 'https://api.axia.to', projectId: '...' });
29
+ *
30
+ * const provider = new CompositeProvider(
31
+ * new LookupClientProvider(client),
32
+ * fallback,
33
+ * );
34
+ * ```
35
+ */
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.LookupClientProvider = void 0;
38
+ class LookupClientProvider {
39
+ constructor(_client) {
40
+ this._client = _client;
41
+ }
42
+ getCoins(query) {
43
+ return this._client.getCoins(query);
44
+ }
45
+ getCoin(coinId) {
46
+ return this._client.getCoin(coinId);
47
+ }
48
+ getProof(coinId) {
49
+ return this._client.getProof(coinId);
50
+ }
51
+ getTip() {
52
+ return this._client.getTip();
53
+ }
54
+ getToken(tokenId) {
55
+ return this._client.getToken(tokenId);
56
+ }
57
+ searchTokens(query) {
58
+ return this._client.searchTokens(query);
59
+ }
60
+ getTokensByCreator(address) {
61
+ return this._client.getTokensByCreator(address);
62
+ }
63
+ broadcastTxPoW(txpowHex) {
64
+ return this._client.broadcastTxPoW(txpowHex);
65
+ }
66
+ }
67
+ exports.LookupClientProvider = LookupClientProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totemsdk/chain-provider",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Unified ChainStateProvider interface for Totem SDK — Hosted, PureMinima, Composite",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,22 +11,27 @@
11
11
  "import": "./dist/index.js"
12
12
  }
13
13
  },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "prepare": "tsc",
17
+ "clean": "rm -rf dist",
18
+ "test": "jest --passWithNoTests"
19
+ },
14
20
  "files": [
15
21
  "dist",
16
22
  "README.md",
17
23
  "LICENSE"
18
24
  ],
19
25
  "dependencies": {
20
- "@totemsdk/core": "1.0.9",
21
- "@totemsdk/pureminima-rpc": "0.1.3"
26
+ "@totemsdk/core": "^1.1.0",
27
+ "@totemsdk/pureminima-rpc": "^0.1.3"
22
28
  },
23
29
  "devDependencies": {
24
- "@noble/hashes": "^2.2.0",
25
- "@types/jest": "^29.0.0",
30
+ "@types/jest": "^30.0.0",
26
31
  "@types/node": "^20.0.0",
27
- "jest": "^29.0.0",
32
+ "jest": "^30.4.2",
28
33
  "ts-jest": "^29.0.0",
29
- "typescript": "^5.0.0"
34
+ "typescript": "^7.0.2"
30
35
  },
31
36
  "engines": {
32
37
  "node": ">=18.0.0"
@@ -36,13 +41,13 @@
36
41
  },
37
42
  "license": "MIT",
38
43
  "author": "Totem SDK",
39
- "homepage": "https://totemsdk.com",
44
+ "homepage": "https://totem.ing",
40
45
  "bugs": {
41
- "url": "https://github.com/MrGheek/axia-totem/issues"
46
+ "url": "https://github.com/totem-sdk/totem-sdk/issues"
42
47
  },
43
48
  "repository": {
44
49
  "type": "git",
45
- "url": "git+https://github.com/MrGheek/axia-totem.git",
50
+ "url": "git+https://github.com/totem-sdk/totem-sdk.git",
46
51
  "directory": "packages/totem-sdk/packages/chain-provider"
47
52
  },
48
53
  "keywords": [
@@ -59,10 +64,5 @@
59
64
  "provider",
60
65
  "chain-data",
61
66
  "strategy-pattern"
62
- ],
63
- "scripts": {
64
- "build": "tsc",
65
- "clean": "rm -rf dist",
66
- "test": "jest --passWithNoTests"
67
- }
68
- }
67
+ ]
68
+ }
@@ -1,20 +0,0 @@
1
- /**
2
- * LookupNodeProvider — stub.
3
- *
4
- * Full implementation lives in @totemsdk/lookup-client which replaces this
5
- * stub once the lookup node package is available.
6
- */
7
- import type { ChainStateProvider, CoinsQuery, Coin, MMRProof, ChainTip, TokenInfo, TokenSearchQuery, BroadcastResult } from '../types.js';
8
- export declare class LookupNodeNotImplementedError extends Error {
9
- constructor();
10
- }
11
- export declare class LookupNodeProvider implements ChainStateProvider {
12
- getCoins(_query: CoinsQuery): Promise<Coin[]>;
13
- getCoin(_coinId: string): Promise<Coin | null>;
14
- getProof(_coinId: string): Promise<MMRProof>;
15
- getTip(): Promise<ChainTip>;
16
- getToken(_tokenId: string): Promise<TokenInfo>;
17
- searchTokens(_query: TokenSearchQuery): Promise<TokenInfo[]>;
18
- getTokensByCreator(_address: string): Promise<TokenInfo[]>;
19
- broadcastTxPoW(_txpowHex: string): Promise<BroadcastResult>;
20
- }
@@ -1,45 +0,0 @@
1
- "use strict";
2
- /**
3
- * LookupNodeProvider — stub.
4
- *
5
- * Full implementation lives in @totemsdk/lookup-client which replaces this
6
- * stub once the lookup node package is available.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.LookupNodeProvider = exports.LookupNodeNotImplementedError = void 0;
10
- class LookupNodeNotImplementedError extends Error {
11
- constructor() {
12
- super('LookupNodeProvider is not yet implemented. ' +
13
- 'Install @totemsdk/lookup-client and use LookupClientProvider instead. ' +
14
- 'See https://github.com/Totem/totem-sdk for setup instructions.');
15
- this.name = 'LookupNodeNotImplementedError';
16
- }
17
- }
18
- exports.LookupNodeNotImplementedError = LookupNodeNotImplementedError;
19
- class LookupNodeProvider {
20
- getCoins(_query) {
21
- throw new LookupNodeNotImplementedError();
22
- }
23
- getCoin(_coinId) {
24
- throw new LookupNodeNotImplementedError();
25
- }
26
- getProof(_coinId) {
27
- throw new LookupNodeNotImplementedError();
28
- }
29
- getTip() {
30
- throw new LookupNodeNotImplementedError();
31
- }
32
- getToken(_tokenId) {
33
- throw new LookupNodeNotImplementedError();
34
- }
35
- searchTokens(_query) {
36
- throw new LookupNodeNotImplementedError();
37
- }
38
- getTokensByCreator(_address) {
39
- throw new LookupNodeNotImplementedError();
40
- }
41
- broadcastTxPoW(_txpowHex) {
42
- throw new LookupNodeNotImplementedError();
43
- }
44
- }
45
- exports.LookupNodeProvider = LookupNodeProvider;