@partylayer/provider 0.6.0 → 0.6.2
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 +14 -4
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +136 -100
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -100
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
### Features
|
|
20
20
|
|
|
21
|
-
- **
|
|
21
|
+
- **Provider request surface**: Handles the ten method names according to backing wallet capabilities (`connect`, `disconnect`, `isConnected`, `status`, `getActiveNetwork`, `listAccounts`, `getPrimaryAccount`, `signMessage`, `prepareExecute`, `ledgerApi`)
|
|
22
22
|
- **Wallet Discovery**: Automatic scanning for injected CIP-0103 providers at `window.canton.*`
|
|
23
23
|
- **Async Wallet Support**: Handles both synchronous (browser extension) and asynchronous (mobile/QR) wallet flows
|
|
24
24
|
- **Standard Error Model**: `ProviderRpcError` with EIP-1193 / EIP-1474 numeric codes
|
|
@@ -58,7 +58,15 @@ const result = await provider.request({ method: 'connect' });
|
|
|
58
58
|
const account = await provider.request({ method: 'getPrimaryAccount' });
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
###
|
|
61
|
+
### Client compatibility bridge
|
|
62
|
+
|
|
63
|
+
The bridge projects the active SDK party, retains the legacy `{ response: string }`
|
|
64
|
+
Ledger API result, and emits transaction states only with genuine evidence. It does
|
|
65
|
+
not establish full account/key enumeration or complete native protocol ownership.
|
|
66
|
+
See [alignment and limits](../../docs/CIP-0103-ALIGNMENT.md) before relying on it
|
|
67
|
+
as an exact standard wire implementation.
|
|
68
|
+
|
|
69
|
+
Existing low-level composition:
|
|
62
70
|
|
|
63
71
|
```typescript
|
|
64
72
|
import { createProviderBridge } from '@partylayer/provider';
|
|
@@ -67,8 +75,10 @@ import { createPartyLayer } from '@partylayer/sdk';
|
|
|
67
75
|
const client = createPartyLayer({ network: 'devnet' });
|
|
68
76
|
const provider = createProviderBridge(client);
|
|
69
77
|
|
|
70
|
-
//
|
|
71
|
-
await
|
|
78
|
+
// Choose a wallet through the SDK's documented application interface.
|
|
79
|
+
await client.connect({ walletId: 'console' });
|
|
80
|
+
// Then use the supported provider requests.
|
|
81
|
+
await provider.request({ method: 'status' });
|
|
72
82
|
```
|
|
73
83
|
|
|
74
84
|
---
|
package/dist/index.d.mts
CHANGED
|
@@ -258,6 +258,9 @@ interface BridgeableClient {
|
|
|
258
258
|
transactionHash?: unknown;
|
|
259
259
|
signedTx?: unknown;
|
|
260
260
|
partyId?: unknown;
|
|
261
|
+
/** Actual signature metadata, never a transaction hash. */
|
|
262
|
+
signature?: unknown;
|
|
263
|
+
signedBy?: unknown;
|
|
261
264
|
}>;
|
|
262
265
|
submitTransaction(params: {
|
|
263
266
|
signedTx: unknown;
|
|
@@ -266,6 +269,7 @@ interface BridgeableClient {
|
|
|
266
269
|
submittedAt?: number;
|
|
267
270
|
commandId?: string;
|
|
268
271
|
updateId?: string;
|
|
272
|
+
completionOffset?: number;
|
|
269
273
|
}>;
|
|
270
274
|
ledgerApi?(params: {
|
|
271
275
|
requestMethod: string;
|
|
@@ -396,11 +400,15 @@ interface ExtensionChannelOptions {
|
|
|
396
400
|
/** Per-request timeout in ms. Default 30000. */
|
|
397
401
|
timeoutMs?: number;
|
|
398
402
|
}
|
|
403
|
+
interface ExtensionChannelProvider extends CIP0103Provider {
|
|
404
|
+
/** Remove the channel listener, reject pending RPCs and release subscriptions. */
|
|
405
|
+
destroy(): void;
|
|
406
|
+
}
|
|
399
407
|
/**
|
|
400
408
|
* Create a CIP-0103 provider that talks the splice-wallet postMessage protocol
|
|
401
409
|
* over the page `window`, routed by `target`.
|
|
402
410
|
*/
|
|
403
|
-
declare function createExtensionChannelProvider(options?: ExtensionChannelOptions):
|
|
411
|
+
declare function createExtensionChannelProvider(options?: ExtensionChannelOptions): ExtensionChannelProvider;
|
|
404
412
|
|
|
405
413
|
/**
|
|
406
414
|
* Async Wallet Handling
|
|
@@ -496,4 +504,4 @@ declare class MethodRouter {
|
|
|
496
504
|
private registerDefaults;
|
|
497
505
|
}
|
|
498
506
|
|
|
499
|
-
export { type AnnounceDiscoveryOptions, type AnnounceSubscribeOptions, type AnnouncedWallet, type AsyncConnectOptions, type AsyncPrepareExecuteOptions, type BridgeableClient, CIP0103EventBus, type DiscoveredProvider, type ExtensionChannelOptions, JSON_RPC_ERRORS, type MethodHandler, MethodRouter, PartyLayerProvider, type PartyLayerProviderOptions, ProviderRpcError, RPC_ERRORS, type WaitForAnnouncedOptions, chainDisconnected, createExtensionChannelProvider, createProviderBridge, disconnected, discoverAnnouncedProviders, discoverInjectedProviders, discoverProviders, handleAsyncConnect, handleAsyncPrepareExecute, internalError, invalidParams, isCIP0103Provider, methodNotFound, resourceNotFound, resourceUnavailable, subscribeAnnouncedProviders, toPartyLayerError, toProviderRpcError, transactionRejected, unauthorized, unsupportedMethod, userRejected, waitForAnnouncedProvider, waitForProvider };
|
|
507
|
+
export { type AnnounceDiscoveryOptions, type AnnounceSubscribeOptions, type AnnouncedWallet, type AsyncConnectOptions, type AsyncPrepareExecuteOptions, type BridgeableClient, CIP0103EventBus, type DiscoveredProvider, type ExtensionChannelOptions, type ExtensionChannelProvider, JSON_RPC_ERRORS, type MethodHandler, MethodRouter, PartyLayerProvider, type PartyLayerProviderOptions, ProviderRpcError, RPC_ERRORS, type WaitForAnnouncedOptions, chainDisconnected, createExtensionChannelProvider, createProviderBridge, disconnected, discoverAnnouncedProviders, discoverInjectedProviders, discoverProviders, handleAsyncConnect, handleAsyncPrepareExecute, internalError, invalidParams, isCIP0103Provider, methodNotFound, resourceNotFound, resourceUnavailable, subscribeAnnouncedProviders, toPartyLayerError, toProviderRpcError, transactionRejected, unauthorized, unsupportedMethod, userRejected, waitForAnnouncedProvider, waitForProvider };
|
package/dist/index.d.ts
CHANGED
|
@@ -258,6 +258,9 @@ interface BridgeableClient {
|
|
|
258
258
|
transactionHash?: unknown;
|
|
259
259
|
signedTx?: unknown;
|
|
260
260
|
partyId?: unknown;
|
|
261
|
+
/** Actual signature metadata, never a transaction hash. */
|
|
262
|
+
signature?: unknown;
|
|
263
|
+
signedBy?: unknown;
|
|
261
264
|
}>;
|
|
262
265
|
submitTransaction(params: {
|
|
263
266
|
signedTx: unknown;
|
|
@@ -266,6 +269,7 @@ interface BridgeableClient {
|
|
|
266
269
|
submittedAt?: number;
|
|
267
270
|
commandId?: string;
|
|
268
271
|
updateId?: string;
|
|
272
|
+
completionOffset?: number;
|
|
269
273
|
}>;
|
|
270
274
|
ledgerApi?(params: {
|
|
271
275
|
requestMethod: string;
|
|
@@ -396,11 +400,15 @@ interface ExtensionChannelOptions {
|
|
|
396
400
|
/** Per-request timeout in ms. Default 30000. */
|
|
397
401
|
timeoutMs?: number;
|
|
398
402
|
}
|
|
403
|
+
interface ExtensionChannelProvider extends CIP0103Provider {
|
|
404
|
+
/** Remove the channel listener, reject pending RPCs and release subscriptions. */
|
|
405
|
+
destroy(): void;
|
|
406
|
+
}
|
|
399
407
|
/**
|
|
400
408
|
* Create a CIP-0103 provider that talks the splice-wallet postMessage protocol
|
|
401
409
|
* over the page `window`, routed by `target`.
|
|
402
410
|
*/
|
|
403
|
-
declare function createExtensionChannelProvider(options?: ExtensionChannelOptions):
|
|
411
|
+
declare function createExtensionChannelProvider(options?: ExtensionChannelOptions): ExtensionChannelProvider;
|
|
404
412
|
|
|
405
413
|
/**
|
|
406
414
|
* Async Wallet Handling
|
|
@@ -496,4 +504,4 @@ declare class MethodRouter {
|
|
|
496
504
|
private registerDefaults;
|
|
497
505
|
}
|
|
498
506
|
|
|
499
|
-
export { type AnnounceDiscoveryOptions, type AnnounceSubscribeOptions, type AnnouncedWallet, type AsyncConnectOptions, type AsyncPrepareExecuteOptions, type BridgeableClient, CIP0103EventBus, type DiscoveredProvider, type ExtensionChannelOptions, JSON_RPC_ERRORS, type MethodHandler, MethodRouter, PartyLayerProvider, type PartyLayerProviderOptions, ProviderRpcError, RPC_ERRORS, type WaitForAnnouncedOptions, chainDisconnected, createExtensionChannelProvider, createProviderBridge, disconnected, discoverAnnouncedProviders, discoverInjectedProviders, discoverProviders, handleAsyncConnect, handleAsyncPrepareExecute, internalError, invalidParams, isCIP0103Provider, methodNotFound, resourceNotFound, resourceUnavailable, subscribeAnnouncedProviders, toPartyLayerError, toProviderRpcError, transactionRejected, unauthorized, unsupportedMethod, userRejected, waitForAnnouncedProvider, waitForProvider };
|
|
507
|
+
export { type AnnounceDiscoveryOptions, type AnnounceSubscribeOptions, type AnnouncedWallet, type AsyncConnectOptions, type AsyncPrepareExecuteOptions, type BridgeableClient, CIP0103EventBus, type DiscoveredProvider, type ExtensionChannelOptions, type ExtensionChannelProvider, JSON_RPC_ERRORS, type MethodHandler, MethodRouter, PartyLayerProvider, type PartyLayerProviderOptions, ProviderRpcError, RPC_ERRORS, type WaitForAnnouncedOptions, chainDisconnected, createExtensionChannelProvider, createProviderBridge, disconnected, discoverAnnouncedProviders, discoverInjectedProviders, discoverProviders, handleAsyncConnect, handleAsyncPrepareExecute, internalError, invalidParams, isCIP0103Provider, methodNotFound, resourceNotFound, resourceUnavailable, subscribeAnnouncedProviders, toPartyLayerError, toProviderRpcError, transactionRejected, unauthorized, unsupportedMethod, userRejected, waitForAnnouncedProvider, waitForProvider };
|
package/dist/index.js
CHANGED
|
@@ -220,6 +220,17 @@ function isRpcShaped(err) {
|
|
|
220
220
|
return typeof e.message === "string" && typeof e.code === "number";
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
// src/request-validation.ts
|
|
224
|
+
function validateRequestPayload(payload) {
|
|
225
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload) || !("method" in payload) || typeof payload.method !== "string") {
|
|
226
|
+
throw new ProviderRpcError("Request must be an object with a string method", JSON_RPC_ERRORS.INVALID_REQUEST);
|
|
227
|
+
}
|
|
228
|
+
const params = "params" in payload ? payload.params : void 0;
|
|
229
|
+
if (params !== void 0 && (params === null || typeof params !== "object")) {
|
|
230
|
+
throw new ProviderRpcError("Request params must be an object or array", JSON_RPC_ERRORS.INVALID_PARAMS);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
223
234
|
// src/method-router.ts
|
|
224
235
|
var MethodRouter = class {
|
|
225
236
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -241,6 +252,7 @@ var MethodRouter = class {
|
|
|
241
252
|
}
|
|
242
253
|
/** Route a request to the appropriate handler */
|
|
243
254
|
async route(payload) {
|
|
255
|
+
validateRequestPayload(payload);
|
|
244
256
|
const { method, params } = payload;
|
|
245
257
|
const handler = this.handlers.get(method);
|
|
246
258
|
if (!handler) {
|
|
@@ -514,7 +526,7 @@ var PartyLayerProvider = class {
|
|
|
514
526
|
};
|
|
515
527
|
|
|
516
528
|
// src/bridge.ts
|
|
517
|
-
var PROVIDER_VERSION = "0.6.
|
|
529
|
+
var PROVIDER_VERSION = "0.6.2" ;
|
|
518
530
|
function createProviderBridge(client) {
|
|
519
531
|
const eventBus = new CIP0103EventBus();
|
|
520
532
|
const pendingEmitted = /* @__PURE__ */ new Set();
|
|
@@ -539,6 +551,7 @@ function createProviderBridge(client) {
|
|
|
539
551
|
return bridge;
|
|
540
552
|
}
|
|
541
553
|
async function handleRequest(client, args, eventBus, pendingEmitted) {
|
|
554
|
+
validateRequestPayload(args);
|
|
542
555
|
const { method, params } = args;
|
|
543
556
|
try {
|
|
544
557
|
switch (method) {
|
|
@@ -579,9 +592,8 @@ async function handleRequest(client, args, eventBus, pendingEmitted) {
|
|
|
579
592
|
}
|
|
580
593
|
case "getActiveNetwork": {
|
|
581
594
|
const session = await client.getActiveSession();
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
);
|
|
595
|
+
if (!session) throw disconnected("No active session");
|
|
596
|
+
return core.toCAIP2Network(session.network);
|
|
585
597
|
}
|
|
586
598
|
case "listAccounts": {
|
|
587
599
|
const session = await client.getActiveSession();
|
|
@@ -606,61 +618,42 @@ async function handleRequest(client, args, eventBus, pendingEmitted) {
|
|
|
606
618
|
}
|
|
607
619
|
case "prepareExecute": {
|
|
608
620
|
const p = normalizeParams(params);
|
|
609
|
-
const
|
|
621
|
+
const session = await client.getActiveSession();
|
|
622
|
+
if (!session) throw disconnected("No active session");
|
|
623
|
+
const cmdId = nonempty(p.commandId) ? p.commandId : `cmd_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
624
|
+
if (pendingEmitted.has(cmdId)) throw resourceUnavailable("This commandId is already pending");
|
|
625
|
+
const payload = { ...p, commandId: cmdId };
|
|
610
626
|
pendingEmitted.add(cmdId);
|
|
611
|
-
eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, {
|
|
612
|
-
|
|
613
|
-
commandId: cmdId
|
|
614
|
-
});
|
|
627
|
+
eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, { status: "pending", commandId: cmdId });
|
|
628
|
+
let submitting = false;
|
|
615
629
|
try {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
party: partyId
|
|
627
|
-
}
|
|
628
|
-
});
|
|
630
|
+
let signedTx = payload;
|
|
631
|
+
if (!session.capabilitiesSnapshot || session.capabilitiesSnapshot.includes("signTransaction")) {
|
|
632
|
+
const signed = await client.signTransaction({ tx: payload });
|
|
633
|
+
if (signed.partyId !== void 0 && signed.partyId !== session.partyId) {
|
|
634
|
+
throw disconnected("Wallet account changed during signing");
|
|
635
|
+
}
|
|
636
|
+
signedTx = signed.signedTx;
|
|
637
|
+
if (signedTx === void 0) throw new Error("Wallet returned no signed transaction");
|
|
638
|
+
const event2 = signedEvent({ signature: signed.signature, signedBy: signed.signedBy, party: signed.partyId }, cmdId);
|
|
639
|
+
if (event2) eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, event2);
|
|
629
640
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
});
|
|
634
|
-
pendingEmitted.delete(cmdId);
|
|
635
|
-
eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, {
|
|
636
|
-
status: "executed",
|
|
637
|
-
commandId: cmdId,
|
|
638
|
-
payload: {
|
|
639
|
-
// All three are real identifiers — the receipt's update id, the
|
|
640
|
-
// wallet's command id, or the bridge's own command id for this
|
|
641
|
-
// operation. `String(receipt.transactionHash)` was the old tail
|
|
642
|
-
// and would now stringify `undefined`.
|
|
643
|
-
updateId: receipt.updateId ?? receipt.commandId ?? cmdId,
|
|
644
|
-
completionOffset: 0
|
|
645
|
-
}
|
|
646
|
-
});
|
|
647
|
-
} catch (submitErr) {
|
|
648
|
-
pendingEmitted.delete(cmdId);
|
|
649
|
-
eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, {
|
|
650
|
-
status: "failed",
|
|
651
|
-
commandId: cmdId
|
|
652
|
-
});
|
|
653
|
-
throw submitErr;
|
|
641
|
+
const current = await client.getActiveSession();
|
|
642
|
+
if (!current || current.sessionId !== session.sessionId || current.partyId !== session.partyId || current.network !== session.network) {
|
|
643
|
+
throw disconnected("Wallet session changed before submission");
|
|
654
644
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
645
|
+
submitting = true;
|
|
646
|
+
const receipt = await client.submitTransaction({ signedTx });
|
|
647
|
+
const event = executedEvent(receipt, cmdId);
|
|
648
|
+
if (event) eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, event);
|
|
649
|
+
} catch (reason) {
|
|
650
|
+
const code = reason?.code;
|
|
651
|
+
if (!submitting || code === 4001 || code === "USER_REJECTED" || code === -32003) {
|
|
652
|
+
eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, { status: "failed", commandId: cmdId });
|
|
662
653
|
}
|
|
663
|
-
throw
|
|
654
|
+
throw reason;
|
|
655
|
+
} finally {
|
|
656
|
+
pendingEmitted.delete(cmdId);
|
|
664
657
|
}
|
|
665
658
|
return void 0;
|
|
666
659
|
}
|
|
@@ -735,59 +728,51 @@ function wireEvents(client, eventBus, pendingEmitted) {
|
|
|
735
728
|
});
|
|
736
729
|
});
|
|
737
730
|
client.on("tx:status", (event) => {
|
|
738
|
-
const e = event;
|
|
739
|
-
if (!e
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
committed: "executed",
|
|
745
|
-
rejected: "failed",
|
|
746
|
-
failed: "failed"
|
|
747
|
-
};
|
|
748
|
-
const mappedStatus = statusMap[e.status] ?? "pending";
|
|
749
|
-
const commandId = String(e.txId);
|
|
750
|
-
if (mappedStatus === "pending" && pendingEmitted.has(commandId)) {
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
if (mappedStatus === "executed" || mappedStatus === "failed") {
|
|
754
|
-
pendingEmitted.delete(commandId);
|
|
755
|
-
}
|
|
731
|
+
const e = record(event);
|
|
732
|
+
if (!e) return;
|
|
733
|
+
const raw = record(e.raw);
|
|
734
|
+
const tx = record(raw?.tx) ?? raw;
|
|
735
|
+
const commandId = nonempty(tx?.commandId) ? tx.commandId : e.txId;
|
|
736
|
+
if (!nonempty(commandId)) return;
|
|
756
737
|
let txEvent;
|
|
757
|
-
switch (
|
|
738
|
+
switch (e.status) {
|
|
739
|
+
case "pending":
|
|
740
|
+
case "submitted":
|
|
741
|
+
if (pendingEmitted.has(commandId)) return;
|
|
742
|
+
txEvent = { status: "pending", commandId };
|
|
743
|
+
break;
|
|
758
744
|
case "signed":
|
|
759
|
-
txEvent =
|
|
760
|
-
status: "signed",
|
|
761
|
-
commandId,
|
|
762
|
-
payload: {
|
|
763
|
-
signature: "",
|
|
764
|
-
signedBy: "",
|
|
765
|
-
party: ""
|
|
766
|
-
}
|
|
767
|
-
};
|
|
745
|
+
txEvent = signedEvent(tx, commandId);
|
|
768
746
|
break;
|
|
769
|
-
case "
|
|
770
|
-
|
|
771
|
-
txEvent = {
|
|
772
|
-
status: "executed",
|
|
773
|
-
commandId,
|
|
774
|
-
payload: {
|
|
775
|
-
updateId: raw?.updateId ?? raw?.commandId ?? commandId,
|
|
776
|
-
completionOffset: 0
|
|
777
|
-
}
|
|
778
|
-
};
|
|
747
|
+
case "committed":
|
|
748
|
+
txEvent = executedEvent(tx, commandId);
|
|
779
749
|
break;
|
|
780
|
-
|
|
750
|
+
case "rejected":
|
|
781
751
|
case "failed":
|
|
782
752
|
txEvent = { status: "failed", commandId };
|
|
783
753
|
break;
|
|
784
754
|
default:
|
|
785
|
-
|
|
786
|
-
break;
|
|
755
|
+
return;
|
|
787
756
|
}
|
|
788
|
-
eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, txEvent);
|
|
757
|
+
if (txEvent) eventBus.emit(core.CIP0103_EVENTS.TX_CHANGED, txEvent);
|
|
789
758
|
});
|
|
790
759
|
}
|
|
760
|
+
function record(value) {
|
|
761
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
762
|
+
}
|
|
763
|
+
function nonempty(value) {
|
|
764
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
765
|
+
}
|
|
766
|
+
function signedEvent(value, commandId) {
|
|
767
|
+
const tx = record(value);
|
|
768
|
+
const p = record(tx?.payload) ?? tx;
|
|
769
|
+
return nonempty(p?.signature) && nonempty(p?.signedBy) && nonempty(p?.party) ? { status: "signed", commandId, payload: { signature: p.signature, signedBy: p.signedBy, party: p.party } } : null;
|
|
770
|
+
}
|
|
771
|
+
function executedEvent(value, commandId) {
|
|
772
|
+
const tx = record(value);
|
|
773
|
+
const p = record(tx?.payload) ?? tx;
|
|
774
|
+
return nonempty(p?.updateId) && typeof p.completionOffset === "number" && Number.isSafeInteger(p.completionOffset) && p.completionOffset >= 0 ? { status: "executed", commandId, payload: { updateId: p.updateId, completionOffset: p.completionOffset } } : null;
|
|
775
|
+
}
|
|
791
776
|
function normalizeParams(params) {
|
|
792
777
|
if (!params) return {};
|
|
793
778
|
if (Array.isArray(params)) return params[0] ?? {};
|
|
@@ -819,11 +804,33 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
819
804
|
const eventBus = new CIP0103EventBus();
|
|
820
805
|
const channelId = `pl-${++channelSeq}-${target ?? "ext"}`;
|
|
821
806
|
let requestSeq = 0;
|
|
807
|
+
let listening = false;
|
|
808
|
+
let destroyed = false;
|
|
809
|
+
const subscribedEvents = /* @__PURE__ */ new Set();
|
|
810
|
+
function listen() {
|
|
811
|
+
if (!listening && typeof window !== "undefined") {
|
|
812
|
+
window.addEventListener("message", onMessage);
|
|
813
|
+
listening = true;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
function detachIfIdle() {
|
|
817
|
+
if (pending.size > 0 || [...subscribedEvents].some((name) => eventBus.listenerCount(name) > 0)) return;
|
|
818
|
+
if (listening && typeof window !== "undefined") window.removeEventListener("message", onMessage);
|
|
819
|
+
listening = false;
|
|
820
|
+
}
|
|
822
821
|
const pending = /* @__PURE__ */ new Map();
|
|
823
822
|
function onMessage(event) {
|
|
824
823
|
if (event.source !== window) return;
|
|
825
824
|
const selfOrigin = typeof window !== "undefined" ? window.location?.origin : void 0;
|
|
826
825
|
if (selfOrigin && selfOrigin !== "null" && event.origin !== selfOrigin) return;
|
|
826
|
+
const notification = event.data;
|
|
827
|
+
if (notification?.type === SPLICE_WALLET_REQUEST && notification.request?.jsonrpc === "2.0" && notification.request.id == null && (!target || notification.target === target)) {
|
|
828
|
+
const method = notification.request.method;
|
|
829
|
+
if (method && ["statusChanged", "accountsChanged", "txChanged", "connected"].includes(method)) {
|
|
830
|
+
eventBus.emit(method, notification.request.params);
|
|
831
|
+
}
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
827
834
|
const data = event.data;
|
|
828
835
|
if (!data || data.type !== SPLICE_WALLET_RESPONSE || !data.response) return;
|
|
829
836
|
const { id, result, error } = data.response;
|
|
@@ -832,6 +839,7 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
832
839
|
if (!entry) return;
|
|
833
840
|
pending.delete(String(id));
|
|
834
841
|
clearTimeout(entry.timer);
|
|
842
|
+
detachIfIdle();
|
|
835
843
|
if (error) {
|
|
836
844
|
entry.reject(
|
|
837
845
|
new ProviderRpcError(
|
|
@@ -844,24 +852,29 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
844
852
|
entry.resolve(result);
|
|
845
853
|
}
|
|
846
854
|
}
|
|
847
|
-
if (typeof window !== "undefined") {
|
|
848
|
-
window.addEventListener("message", onMessage);
|
|
849
|
-
}
|
|
850
855
|
function postTargetOrigin() {
|
|
851
856
|
const origin = typeof window !== "undefined" ? window.location?.origin : void 0;
|
|
852
857
|
return origin && origin !== "null" ? origin : "*";
|
|
853
858
|
}
|
|
854
859
|
const provider = {
|
|
855
860
|
request(args) {
|
|
861
|
+
try {
|
|
862
|
+
validateRequestPayload(args);
|
|
863
|
+
} catch (error) {
|
|
864
|
+
return Promise.reject(error);
|
|
865
|
+
}
|
|
866
|
+
if (destroyed) return Promise.reject(new ProviderRpcError("Extension channel was destroyed", RPC_ERRORS.DISCONNECTED));
|
|
856
867
|
if (typeof window === "undefined") {
|
|
857
868
|
return Promise.reject(
|
|
858
869
|
new ProviderRpcError("No window: extension channel unavailable", JSON_RPC_ERRORS.INTERNAL_ERROR)
|
|
859
870
|
);
|
|
860
871
|
}
|
|
872
|
+
listen();
|
|
861
873
|
const id = `${channelId}-${++requestSeq}`;
|
|
862
874
|
return new Promise((resolve, reject) => {
|
|
863
875
|
const timer = setTimeout(() => {
|
|
864
876
|
pending.delete(id);
|
|
877
|
+
detachIfIdle();
|
|
865
878
|
reject(
|
|
866
879
|
new ProviderRpcError(
|
|
867
880
|
`Wallet request "${args.method}" timed out after ${timeoutMs}ms`,
|
|
@@ -880,11 +893,21 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
880
893
|
},
|
|
881
894
|
...target ? { target } : {}
|
|
882
895
|
};
|
|
883
|
-
|
|
896
|
+
try {
|
|
897
|
+
window.postMessage(message, postTargetOrigin());
|
|
898
|
+
} catch (error) {
|
|
899
|
+
pending.delete(id);
|
|
900
|
+
clearTimeout(timer);
|
|
901
|
+
detachIfIdle();
|
|
902
|
+
reject(error);
|
|
903
|
+
}
|
|
884
904
|
});
|
|
885
905
|
},
|
|
886
906
|
on(event, listener) {
|
|
907
|
+
if (destroyed) return provider;
|
|
887
908
|
eventBus.on(event, listener);
|
|
909
|
+
subscribedEvents.add(event);
|
|
910
|
+
listen();
|
|
888
911
|
return provider;
|
|
889
912
|
},
|
|
890
913
|
emit(event, ...args) {
|
|
@@ -892,7 +915,20 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
892
915
|
},
|
|
893
916
|
removeListener(event, listener) {
|
|
894
917
|
eventBus.removeListener(event, listener);
|
|
918
|
+
detachIfIdle();
|
|
895
919
|
return provider;
|
|
920
|
+
},
|
|
921
|
+
destroy() {
|
|
922
|
+
if (destroyed) return;
|
|
923
|
+
destroyed = true;
|
|
924
|
+
eventBus.removeAllListeners();
|
|
925
|
+
subscribedEvents.clear();
|
|
926
|
+
for (const entry of pending.values()) {
|
|
927
|
+
clearTimeout(entry.timer);
|
|
928
|
+
entry.reject(new ProviderRpcError("Extension channel was destroyed", RPC_ERRORS.DISCONNECTED));
|
|
929
|
+
}
|
|
930
|
+
pending.clear();
|
|
931
|
+
detachIfIdle();
|
|
896
932
|
}
|
|
897
933
|
};
|
|
898
934
|
eventBus.setOwner(provider);
|