@temple-digital-group/temple-canton-js 2.0.5 → 2.0.6-beta-1

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.
@@ -2,7 +2,7 @@ import config from "../../src/config/index.js";
2
2
  import axios from "axios";
3
3
  import { getDisclosures } from "../api/index.js";
4
4
  import { getUserId } from "../api/tokenStore.js";
5
- import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "../../src/canton/walletAdapter.js";
5
+ import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "./walletAdapter.js";
6
6
  import { normalizeAssetId, instrumentCatalog, instrumentIdToSymbol, resolveOnChainInstrumentId } from "../../src/canton/instrumentCatalog.js";
7
7
  import {
8
8
  randomUUID,
@@ -407,6 +407,7 @@ export async function depositFunds(
407
407
  }
408
408
 
409
409
  const isAmulet = assetId === "Amulet";
410
+ const usingWalletAdapter = Boolean(getWalletAdapter());
410
411
 
411
412
  // --- 1. Resolve admin and factory ---
412
413
  let admin: string;
@@ -431,7 +432,7 @@ export async function depositFunds(
431
432
  if (isAmulet && (!allocateBefore || !settleBefore)) {
432
433
  // Compute the safe deadline from on-chain fee params to avoid
433
434
  // "amulet expires before lock" errors on small amounts.
434
- const expiryParams = await fetchAmuletExpiryParams(config.VALIDATOR_DSO_PARTY_ID);
435
+ const expiryParams = usingWalletAdapter ? null : await fetchAmuletExpiryParams(config.VALIDATOR_DSO_PARTY_ID);
435
436
  const maxExpiry = expiryParams ? maxLockExpirationForFreshAmulet(amount, expiryParams) : null;
436
437
  if (maxExpiry) {
437
438
  // settleBefore = safe max expiry; allocateBefore = halfway between now and settleBefore
@@ -1,7 +1,7 @@
1
1
  import config from "../../src/config/index.js";
2
2
  import axios from "axios";
3
3
  import { getJWTToken } from "../../src/auth0/index.js";
4
- import { getAdapterProvider } from "../../src/canton/walletAdapter.js";
4
+ import { getActiveContractReader } from "./wallet/service.js";
5
5
  import { instrumentCatalog, normalizeAssetId } from "../../src/canton/instrumentCatalog.js";
6
6
 
7
7
  // ─── Constants ───────────────────────────────────────────────────────────────
@@ -26,8 +26,6 @@ export const DEFAULT_UTILITY_CONTEXT_KEYS = {
26
26
  featuredAppRightPrefixed: "utility.digitalasset.com/featured-app-right",
27
27
  } as const;
28
28
 
29
- export const LOCAL_VALIDATOR_HOSTNAMES = new Set(["localhost", "127.0.0.1", "::1", "0.0.0.0"]);
30
-
31
29
  export const FORCE_LEDGER_METADATA: boolean =
32
30
  typeof process !== "undefined" && process.env
33
31
  ? String(process.env.FORCE_LEDGER_METADATA || "").toLowerCase() === "true"
@@ -95,22 +93,10 @@ export const randomUUID = (): string => {
95
93
  });
96
94
  };
97
95
 
98
- /** Check if a validator URL points to localhost. */
99
- export function isLocalValidatorUrl(urlValue: string | undefined | null): boolean {
100
- if (!urlValue) return false;
101
- try {
102
- const parsed = new URL(urlValue);
103
- return LOCAL_VALIDATOR_HOSTNAMES.has(parsed.hostname.toLowerCase());
104
- } catch {
105
- return false;
106
- }
107
- }
108
-
109
96
  /** Determine if ledger should be used for metadata resolution. */
110
97
  export function shouldUseLedgerForMetadata(): boolean {
111
- if (FORCE_LEDGER_METADATA) return true;
112
98
  if (!config.VALIDATOR_API_URL) return false;
113
- return isLocalValidatorUrl(config.VALIDATOR_API_URL);
99
+ return FORCE_LEDGER_METADATA;
114
100
  }
115
101
 
116
102
  /** Trim a contract ID string. */
@@ -141,9 +127,13 @@ export function normalizeContractReferences(value: unknown): unknown {
141
127
  return value;
142
128
  }
143
129
 
144
- /** Resolve provider: use the passed value, or fall back to the wallet adapter's provider. */
130
+ /**
131
+ * Resolve the active-contracts reader: the explicit provider override when given,
132
+ * otherwise the active wallet adapter (the universal wrapper). Returns null when
133
+ * no wallet source is available, so callers fall back to the ledger API.
134
+ */
145
135
  export function resolveProvider(provider: unknown): unknown {
146
- return provider || getAdapterProvider() || null;
136
+ return getActiveContractReader(provider);
147
137
  }
148
138
 
149
139
  // ─── Instrument Catalog ──────────────────────────────────────────────────────
@@ -2,7 +2,7 @@ import config, { NETWORK_LOCALHOST } from "../config/index.js";
2
2
  import axios from "axios";
3
3
  import { getDisclosures, getDelegation } from "../../dist/api/index.js";
4
4
  import { getUserId } from "../../dist/api/tokenStore.js";
5
- import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "./walletAdapter.js";
5
+ import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "../../dist/canton/walletAdapter.js";
6
6
 
7
7
  // Import shared helpers from compiled TypeScript module
8
8
  import {
@@ -895,7 +895,7 @@ async function queryLedgerContractsByTemplate(templateId, returnCommand = false)
895
895
  * @returns {Promise<Object|null>} The AmuletRules contract data, or null on failure
896
896
  */
897
897
  export async function getAmuletRules(dso, returnCommand = false) {
898
- if (!shouldUseLedgerForMetadata()) {
898
+ if (shouldUseLedgerForMetadata()) {
899
899
  const headers = await buildHeaders();
900
900
  const url = `${config.VALIDATOR_SCAN_API_URL}/amulet-rules?provider=${dso}`;
901
901
  if (returnCommand) {
@@ -1012,7 +1012,7 @@ export async function getFeaturedAppRight(returnCommand = false) {
1012
1012
  * @returns {Promise<Object|null>} Mining rounds data with open_mining_rounds array, or null on failure
1013
1013
  */
1014
1014
  export async function getOpenMiningRounds(dso, returnCommand = false) {
1015
- if (!shouldUseLedgerForMetadata()) {
1015
+ if (shouldUseLedgerForMetadata()) {
1016
1016
  const headers = await buildHeaders();
1017
1017
  const url = `${config.VALIDATOR_SCAN_API_URL}/open-and-issuing-mining-rounds?provider=${dso}`;
1018
1018
  if (returnCommand) {
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Universal Wallet Adapter contract.
3
+ *
4
+ * Every wallet (Loop, and future providers such as Console or BitGo) is
5
+ * represented as a *normalized adapter* implementing the capability interface
6
+ * defined here. The rest of the SDK never talks to a wallet/provider SDK
7
+ * directly — it goes through the wallet service (./service.js), which dispatches
8
+ * to the active adapter.
9
+ *
10
+ * Capabilities are split into two tiers:
11
+ *
12
+ * - REQUIRED: getPartyId, getActiveContracts, submitCommand.
13
+ * A wallet that cannot provide these is not usable. `defineWalletAdapter`
14
+ * throws if a factory omits one, and the service throws if a required
15
+ * capability is invoked while no adapter is configured.
16
+ *
17
+ * - OPTIONAL: payDueGasIfAny, getHolding, getAmuletRules, getOpenMiningRounds,
18
+ * disconnect. Not every wallet supports these (e.g. a custodial wallet may
19
+ * handle fees itself, a non-amulet wallet has no mining rounds). When a
20
+ * factory omits an optional capability, it is filled with a safe no-op so
21
+ * callers can invoke it unconditionally — it simply "passes".
22
+ *
23
+ * Add a new wallet by writing a `create<Name>WalletAdapter()` factory that
24
+ * builds a definition object and returns `defineWalletAdapter(definition)`.
25
+ */
26
+
27
+ /** A normalized wallet adapter produced by {@link defineWalletAdapter}. */
28
+ export interface WalletAdapter {
29
+ /** Provider id, e.g. "loop". */
30
+ readonly id: string;
31
+ /** True for server-side adapters that sign/submit locally. */
32
+ readonly isServer: boolean;
33
+
34
+ // ── Required capabilities ──
35
+ /** Resolve the wallet's primary party id. */
36
+ getPartyId(): string | null;
37
+ /** Read active contracts matching `args` via the wallet. */
38
+ getActiveContracts(args: unknown): Promise<unknown>;
39
+ /** Sign and submit a ledger command via the wallet. */
40
+ submitCommand(command: unknown): Promise<unknown>;
41
+
42
+ // ── Optional capabilities (always present after normalization) ──
43
+ /** Pay any outstanding network gas. No-op when unsupported. */
44
+ payDueGasIfAny(): Promise<void>;
45
+ /** Read a holding via the wallet. Resolves null when unsupported. */
46
+ getHolding(...args: unknown[]): Promise<unknown>;
47
+ /** Resolve amulet rules via the wallet. Resolves null when unsupported. */
48
+ getAmuletRules(...args: unknown[]): Promise<unknown>;
49
+ /** Resolve open mining rounds via the wallet. Resolves null when unsupported. */
50
+ getOpenMiningRounds(...args: unknown[]): Promise<unknown>;
51
+ /** Disconnect the wallet. No-op when unsupported. */
52
+ disconnect(): Promise<void>;
53
+
54
+ /** Raw provider object for legacy getAdapterProvider() consumers, or null. */
55
+ getRawProvider(): unknown;
56
+ }
57
+
58
+ /** Capability definition supplied by a `create<Name>WalletAdapter()` factory. */
59
+ export interface WalletAdapterDefinition {
60
+ id?: string;
61
+ isServer?: boolean;
62
+ getPartyId(): string | null;
63
+ getActiveContracts(args: unknown): Promise<unknown> | unknown;
64
+ submitCommand(command: unknown): Promise<unknown> | unknown;
65
+ payDueGasIfAny?(): Promise<void> | void;
66
+ getHolding?(...args: unknown[]): Promise<unknown> | unknown;
67
+ getAmuletRules?(...args: unknown[]): Promise<unknown> | unknown;
68
+ getOpenMiningRounds?(...args: unknown[]): Promise<unknown> | unknown;
69
+ disconnect?(): Promise<void> | void;
70
+ getRawProvider?(): unknown;
71
+ }
72
+
73
+ /** Required capabilities — a usable wallet adapter must implement all of these. */
74
+ export const REQUIRED_CAPABILITIES = ["getPartyId", "getActiveContracts", "submitCommand"] as const;
75
+
76
+ /** Optional capabilities — filled with safe no-ops when a wallet does not support them. */
77
+ export const OPTIONAL_CAPABILITIES = ["payDueGasIfAny", "getHolding", "getAmuletRules", "getOpenMiningRounds", "disconnect"] as const;
78
+
79
+ /** Brand used to recognize objects produced by {@link defineWalletAdapter}. */
80
+ const ADAPTER_BRAND = Symbol.for("temple.canton.walletAdapter");
81
+
82
+ /**
83
+ * Safe no-op implementations for optional capabilities. Each returns a benign
84
+ * "nothing to do" value so a wallet that lacks the capability still passes when
85
+ * a caller invokes it. The comments explain why a no-op is the correct default.
86
+ */
87
+ const OPTIONAL_NO_OPS = {
88
+ // Networks/wallets with no programmatic gas concept (or where fees are paid
89
+ // wallet-side, e.g. a custodial provider) have nothing to settle here.
90
+ payDueGasIfAny: async (): Promise<void> => undefined,
91
+ // Wallet does not expose its own holdings read — callers fall back to the
92
+ // ledger API, so "no wallet-side holdings" is represented as null.
93
+ getHolding: async (): Promise<unknown> => null,
94
+ // Amulet-specific context. A non-Canton-amulet wallet simply has no rules to
95
+ // provide; null lets callers fall back to the ledger/scan lookups.
96
+ getAmuletRules: async (): Promise<unknown> => null,
97
+ getOpenMiningRounds: async (): Promise<unknown> => null,
98
+ // Stateless/headless wallets hold no connection to tear down.
99
+ disconnect: async (): Promise<void> => undefined,
100
+ };
101
+
102
+ /** Returns true if `value` was produced by {@link defineWalletAdapter}. */
103
+ export function isWalletAdapter(value: unknown): value is WalletAdapter {
104
+ return Boolean(value && (value as Record<symbol, unknown>)[ADAPTER_BRAND] === true);
105
+ }
106
+
107
+ /**
108
+ * Build a normalized wallet adapter from a factory definition.
109
+ *
110
+ * Validates that every required capability is implemented and fills any missing
111
+ * optional capability with a safe no-op. The returned object is branded so the
112
+ * service and facade can distinguish it from a raw provider SDK instance.
113
+ */
114
+ export function defineWalletAdapter(definition: WalletAdapterDefinition): WalletAdapter {
115
+ if (!definition || typeof definition !== "object") {
116
+ throw new Error("[Temple SDK] Wallet adapter definition must be an object.");
117
+ }
118
+
119
+ const id = typeof definition.id === "string" && definition.id ? definition.id : "unknown";
120
+
121
+ for (const capability of REQUIRED_CAPABILITIES) {
122
+ if (typeof definition[capability] !== "function") {
123
+ throw new Error(`[Temple SDK] Wallet adapter "${id}" is missing required capability: ${capability}().`);
124
+ }
125
+ }
126
+
127
+ const adapter = {
128
+ [ADAPTER_BRAND]: true,
129
+ id,
130
+ isServer: Boolean(definition.isServer),
131
+
132
+ // Required capabilities (validated above).
133
+ getPartyId: definition.getPartyId,
134
+ getActiveContracts: definition.getActiveContracts,
135
+ submitCommand: definition.submitCommand,
136
+
137
+ // Optional capabilities — fall back to a safe no-op when a wallet omits them.
138
+ payDueGasIfAny: typeof definition.payDueGasIfAny === "function" ? definition.payDueGasIfAny : OPTIONAL_NO_OPS.payDueGasIfAny,
139
+ getHolding: typeof definition.getHolding === "function" ? definition.getHolding : OPTIONAL_NO_OPS.getHolding,
140
+ getAmuletRules: typeof definition.getAmuletRules === "function" ? definition.getAmuletRules : OPTIONAL_NO_OPS.getAmuletRules,
141
+ getOpenMiningRounds: typeof definition.getOpenMiningRounds === "function" ? definition.getOpenMiningRounds : OPTIONAL_NO_OPS.getOpenMiningRounds,
142
+ disconnect: typeof definition.disconnect === "function" ? definition.disconnect : OPTIONAL_NO_OPS.disconnect,
143
+
144
+ // Escape hatch: the raw provider object for legacy consumers of
145
+ // getAdapterProvider(). Defaults to null when a wallet exposes no such object.
146
+ getRawProvider: typeof definition.getRawProvider === "function" ? definition.getRawProvider : ((): unknown => null),
147
+ };
148
+
149
+ return adapter as unknown as WalletAdapter;
150
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Loop wallet adapter factory.
3
+ *
4
+ * Wraps a raw Loop SDK instance in the universal wallet adapter interface so the
5
+ * rest of the SDK interacts with Loop through the same contract as every other
6
+ * wallet. This preserves the exact behavior that previously lived inline in
7
+ * walletAdapter.js:
8
+ *
9
+ * - Server-side (Loop): signs locally via executeTransaction().
10
+ * - Client-side (Loop): delegates signing to the Loop Wallet via the provider's
11
+ * submitTransaction().
12
+ * - Reads go through the provider sub-object (loop.provider) when present.
13
+ *
14
+ * Server vs client is detected from the Loop connection: a server-side instance
15
+ * has a null connection.ticketId and connection.ws.
16
+ */
17
+
18
+ import { defineWalletAdapter, type WalletAdapter } from "./adapter.js";
19
+
20
+ /** The Loop read/submit provider sub-object (or the Loop instance itself). */
21
+ interface LoopProvider {
22
+ party_id?: string | null;
23
+ getActiveContracts?: (args: unknown) => unknown;
24
+ submitTransaction?: (command: unknown) => unknown;
25
+ }
26
+
27
+ /** Outstanding-gas descriptor returned by Loop's checkDueGas(). */
28
+ interface DueGas {
29
+ pending?: boolean;
30
+ tracking_id?: string;
31
+ }
32
+
33
+ /** Structural shape of the Loop SDK instance this adapter relies on. */
34
+ interface LoopLike extends LoopProvider {
35
+ provider?: LoopProvider | null;
36
+ session?: { partyId?: string | null } | null;
37
+ connection?: { ticketId?: unknown; ws?: unknown } | null;
38
+ executeTransaction?: (command: unknown) => unknown;
39
+ checkDueGas?: () => Promise<DueGas | null | undefined> | DueGas | null | undefined;
40
+ payGas?: (trackingId: string) => Promise<unknown> | unknown;
41
+ }
42
+
43
+ /**
44
+ * Detect whether a Loop instance is server-side.
45
+ * Loop-specific: server-side has null connection.ticketId and connection.ws.
46
+ * With no connection object, assume server-side (headless/API usage).
47
+ */
48
+ function detectServerSide(loop: LoopLike): boolean {
49
+ if (loop.connection) {
50
+ return loop.connection.ticketId == null && loop.connection.ws == null;
51
+ }
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Create a universal wallet adapter backed by a Loop SDK instance.
57
+ */
58
+ export function createLoopWalletAdapter(loop: unknown): WalletAdapter {
59
+ if (!loop || typeof loop !== "object") {
60
+ throw new Error("[Temple SDK] createLoopWalletAdapter requires a Loop wallet instance.");
61
+ }
62
+
63
+ const sdk = loop as LoopLike;
64
+ const isServer = detectServerSide(sdk);
65
+ // The provider sub-object handles reads (getActiveContracts) and client-side
66
+ // submission (submitTransaction). Fall back to the instance itself.
67
+ const provider: LoopProvider = sdk.provider || sdk;
68
+
69
+ return defineWalletAdapter({
70
+ id: "loop",
71
+ isServer,
72
+ getRawProvider: () => provider,
73
+
74
+ // ── Required capabilities ──
75
+ getPartyId: () => sdk.provider?.party_id || sdk.session?.partyId || null,
76
+
77
+ getActiveContracts: (args) => {
78
+ if (typeof provider.getActiveContracts !== "function") {
79
+ throw new Error("[Temple SDK] Loop provider does not support getActiveContracts().");
80
+ }
81
+ return provider.getActiveContracts(args);
82
+ },
83
+
84
+ submitCommand: (command) => {
85
+ // Server-side: sign locally with the private key.
86
+ if (isServer && typeof sdk.executeTransaction === "function") {
87
+ return sdk.executeTransaction(command);
88
+ }
89
+ // Client-side: delegate signing to the Loop Wallet via WebSocket.
90
+ if (typeof provider.submitTransaction === "function") {
91
+ return provider.submitTransaction(command);
92
+ }
93
+ throw new Error("[Temple SDK] Loop wallet adapter does not support transaction submission.");
94
+ },
95
+
96
+ // ── Optional capabilities ──
97
+ // Loop pays network gas programmatically only on the server side; the
98
+ // client-side flow is handled by the Loop Wallet UI, so it is a no-op here.
99
+ payDueGasIfAny: async () => {
100
+ if (!isServer) return;
101
+ if (typeof sdk.checkDueGas !== "function" || typeof sdk.payGas !== "function") return;
102
+ try {
103
+ const dueGas = await sdk.checkDueGas();
104
+ const trackingId = dueGas?.tracking_id;
105
+ if (dueGas?.pending && trackingId) {
106
+ console.log(`[Temple SDK] Paying outstanding network gas (tracking_id=${trackingId})`);
107
+ await sdk.payGas(trackingId);
108
+ }
109
+ } catch (error) {
110
+ console.warn(`[Temple SDK] checkDueGas/payGas failed: ${(error as Error)?.message || String(error)}`);
111
+ }
112
+ },
113
+ // getHolding / getAmuletRules / getOpenMiningRounds / disconnect: not
114
+ // provided by Loop here — the adapter inherits the safe no-op defaults.
115
+ });
116
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Universal wallet service.
3
+ *
4
+ * Holds the single active wallet adapter and dispatches capability calls to it.
5
+ * This is the one chokepoint the rest of the SDK uses to reach a wallet — no
6
+ * module talks to a provider/wallet SDK directly.
7
+ *
8
+ * - Required capabilities (getPartyId, submitCommand) throw a clear error when
9
+ * invoked while no adapter is configured.
10
+ * - Active-contract reads are resolved through `getActiveContractReader`, which
11
+ * supports an explicit per-call provider override and returns null when no
12
+ * wallet source is available so callers can fall back to the ledger API.
13
+ * - Optional capabilities (payDueGasIfAny) become safe no-ops when no adapter
14
+ * is set.
15
+ */
16
+
17
+ import { isWalletAdapter, type WalletAdapter } from "./adapter.js";
18
+
19
+ /** The single active, normalized wallet adapter (or null when none is set). */
20
+ let _activeAdapter: WalletAdapter | null = null;
21
+
22
+ /** Register the active wallet adapter. Pass null to clear it. */
23
+ export function setActiveAdapter(adapter: WalletAdapter | null): void {
24
+ if (adapter && !isWalletAdapter(adapter)) {
25
+ throw new Error("[Temple SDK] setActiveAdapter expects a normalized wallet adapter created by a create*WalletAdapter() factory.");
26
+ }
27
+ _activeAdapter = adapter || null;
28
+ }
29
+
30
+ /** Get the active wallet adapter, or null when none is set. */
31
+ export function getActiveAdapter(): WalletAdapter | null {
32
+ return _activeAdapter;
33
+ }
34
+
35
+ /** True when a wallet adapter is configured. */
36
+ export function hasActiveAdapter(): boolean {
37
+ return _activeAdapter !== null;
38
+ }
39
+
40
+ function requireAdapter(): WalletAdapter {
41
+ if (!_activeAdapter) {
42
+ throw new Error("[Temple SDK] No wallet adapter configured. Call initialize() with WALLET_ADAPTER or setWalletAdapter() first.");
43
+ }
44
+ return _activeAdapter;
45
+ }
46
+
47
+ // ── Required capabilities ──────────────────────────────────────────────────
48
+
49
+ /** Resolve the active wallet's party id. Throws when no adapter is configured. */
50
+ export function getPartyId(): string | null {
51
+ return requireAdapter().getPartyId();
52
+ }
53
+
54
+ /** Sign and submit a ledger command. Throws when no adapter is configured. */
55
+ export function submitCommand(command: unknown): Promise<unknown> {
56
+ return requireAdapter().submitCommand(command);
57
+ }
58
+
59
+ /**
60
+ * Resolve the object used to read active contracts.
61
+ *
62
+ * An explicit `providerOverride` wins (a caller intentionally bypassing the
63
+ * active wallet); otherwise the active adapter — the universal wrapper — is
64
+ * returned, exposing its normalized `getActiveContracts` capability. Returns
65
+ * null when neither is available, signalling callers to fall back to the
66
+ * ledger API.
67
+ */
68
+ export function getActiveContractReader(providerOverride: unknown = null): unknown {
69
+ if (providerOverride) return providerOverride;
70
+ return _activeAdapter || null;
71
+ }
72
+
73
+ // ── Optional capabilities ──────────────────────────────────────────────────
74
+
75
+ /**
76
+ * Pay any outstanding network gas before a read/submit. Safe no-op when no
77
+ * adapter is configured (e.g. pure ledger/server mode without a wallet).
78
+ */
79
+ export async function payDueGasIfAny(): Promise<void> {
80
+ if (!_activeAdapter) return;
81
+ return _activeAdapter.payDueGasIfAny();
82
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Wallet Adapter Facade
3
+ *
4
+ * Backward-compatible public surface for wallet interactions. The real wallet
5
+ * architecture lives in ./wallet/ — a universal adapter contract (adapter.ts),
6
+ * a service registry that dispatches capability calls (service.ts), and one
7
+ * factory per wallet (loop.ts, and future providers). This module is a thin
8
+ * facade over that service so existing imports keep working unchanged.
9
+ *
10
+ * Adding a new wallet does not touch this file: write a create<Name>WalletAdapter()
11
+ * factory and pass its result to setWalletAdapter().
12
+ */
13
+
14
+ import {
15
+ setActiveAdapter,
16
+ getActiveAdapter,
17
+ submitCommand as serviceSubmitCommand,
18
+ payDueGasIfAny as servicePayDueGasIfAny,
19
+ } from "./wallet/service.js";
20
+ import { isWalletAdapter } from "./wallet/adapter.js";
21
+ import { createLoopWalletAdapter } from "./wallet/loop.js";
22
+ import type { WalletAdapter } from "./wallet/adapter.js";
23
+
24
+ // Re-export the adapter toolkit so consumers can build/register custom wallets.
25
+ export { createLoopWalletAdapter } from "./wallet/loop.js";
26
+ export { defineWalletAdapter, isWalletAdapter } from "./wallet/adapter.js";
27
+ export type { WalletAdapter, WalletAdapterDefinition } from "./wallet/adapter.js";
28
+
29
+ /**
30
+ * Set the active wallet adapter.
31
+ *
32
+ * Accepts either a normalized adapter (from a create*WalletAdapter() factory) or
33
+ * a raw Loop SDK instance, which is auto-wrapped for backward compatibility.
34
+ * Pass a falsy value to clear the adapter.
35
+ */
36
+ export function setWalletAdapter(adapter: unknown): void {
37
+ if (!adapter) {
38
+ setActiveAdapter(null);
39
+ return;
40
+ }
41
+ const normalized = isWalletAdapter(adapter) ? adapter : createLoopWalletAdapter(adapter);
42
+ setActiveAdapter(normalized);
43
+ }
44
+
45
+ /**
46
+ * Get the active (normalized) wallet adapter, or null when none is set.
47
+ */
48
+ export function getWalletAdapter(): WalletAdapter | null {
49
+ return getActiveAdapter();
50
+ }
51
+
52
+ /**
53
+ * Returns the provider sub-object used for read operations (getActiveContracts).
54
+ * Kept for backward compatibility; resolves the active adapter's raw provider.
55
+ */
56
+ export function getAdapterProvider(): unknown {
57
+ const adapter = getActiveAdapter();
58
+ if (!adapter) return null;
59
+ return adapter.getRawProvider() || adapter;
60
+ }
61
+
62
+ /**
63
+ * Check if the active adapter runs in server mode.
64
+ */
65
+ export function isServerMode(): boolean {
66
+ const adapter = getActiveAdapter();
67
+ return adapter ? Boolean(adapter.isServer) : false;
68
+ }
69
+
70
+ /**
71
+ * Get the party ID from the active wallet adapter, or null when none is set.
72
+ */
73
+ export function getAdapterPartyId(): string | null {
74
+ const adapter = getActiveAdapter();
75
+ return adapter ? adapter.getPartyId() : null;
76
+ }
77
+
78
+ /**
79
+ * Pay any outstanding network gas before submitting a transaction or reading
80
+ * balances. Safe no-op when no adapter is configured or the wallet handles gas
81
+ * itself.
82
+ */
83
+ export function payDueGasIfAny(): Promise<void> {
84
+ return servicePayDueGasIfAny();
85
+ }
86
+
87
+ /**
88
+ * Submit a command via the active wallet adapter.
89
+ * Throws when no adapter is configured.
90
+ */
91
+ export function submitCommand(command: unknown): Promise<unknown> {
92
+ return serviceSubmitCommand(command);
93
+ }
@@ -7,7 +7,7 @@ import {
7
7
  getDelegation,
8
8
  } from "../api/index.js";
9
9
  import { getUserId } from "../api/tokenStore.js";
10
- import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "../../src/canton/walletAdapter.js";
10
+ import { getAdapterPartyId, getWalletAdapter, submitCommand, payDueGasIfAny } from "./walletAdapter.js";
11
11
  import {
12
12
  randomUUID,
13
13
  shouldUseLedgerForMetadata,
@@ -19,6 +19,9 @@ export function getConfigValue(key: any): any;
19
19
  export function setWalletAdapter(adapter: any): void;
20
20
  export function getWalletAdapter(): any;
21
21
  export function getAdapterProvider(): any;
22
+ export function createLoopWalletAdapter(loop: any): any;
23
+ export function defineWalletAdapter(definition: any): any;
24
+ export function isWalletAdapter(value: any): boolean;
22
25
  export const NETWORK_LOCALHOST: "localhost";
23
26
  export const NETWORK_TESTNET: "testnet";
24
27
  export const NETWORK_MAINNET: "mainnet";
@@ -6,8 +6,15 @@ if (typeof process !== 'undefined' && process.env) {
6
6
  // Global config object that can be set by the consuming application
7
7
  let appConfig = null;
8
8
 
9
- // Re-export wallet adapter functions
10
- export { setWalletAdapter, getWalletAdapter, getAdapterProvider } from "../canton/walletAdapter.js";
9
+ // Re-export wallet adapter functions and the adapter toolkit (factory + helpers).
10
+ export {
11
+ setWalletAdapter,
12
+ getWalletAdapter,
13
+ getAdapterProvider,
14
+ createLoopWalletAdapter,
15
+ defineWalletAdapter,
16
+ isWalletAdapter,
17
+ } from "../../dist/canton/walletAdapter.js";
11
18
 
12
19
  /**
13
20
  * Initialize the library with a configuration object.
@@ -1,7 +0,0 @@
1
- export function setWalletAdapter(adapter: unknown): void;
2
- export function getWalletAdapter(): unknown;
3
- export function getAdapterProvider(): unknown;
4
- export function isServerMode(): boolean;
5
- export function getAdapterPartyId(): string | null;
6
- export function submitCommand(command: unknown): Promise<unknown>;
7
- export function payDueGasIfAny(): Promise<void>;