@partylayer/adapter-cantor8 0.2.20 → 0.4.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/index.js CHANGED
@@ -3,311 +3,197 @@
3
3
  var core = require('@partylayer/core');
4
4
 
5
5
  // src/cantor8-adapter.ts
6
- var StubCantor8VendorModule = class {
7
- createConnectUrl(request, config) {
8
- if (config.universalLinkBase) {
9
- const endpoint = config.connectEndpoint || "/connect";
10
- const url = new URL(endpoint, config.universalLinkBase);
11
- Object.entries(request).forEach(([key, value]) => {
12
- if (value !== void 0) {
13
- url.searchParams.set(key, typeof value === "string" ? value : JSON.stringify(value));
14
- }
15
- });
16
- return url.toString();
17
- }
18
- if (config.deepLinkScheme) {
19
- const params = new URLSearchParams();
20
- Object.entries(request).forEach(([key, value]) => {
21
- if (value !== void 0) {
22
- params.set(key, typeof value === "string" ? value : JSON.stringify(value));
23
- }
24
- });
25
- return `${config.deepLinkScheme}://connect?${params.toString()}`;
26
- }
27
- throw new Error(
28
- "Cantor8 vendor not configured. Please set deepLinkScheme or universalLinkBase in adapter config."
29
- );
30
- }
31
- parseConnectCallback(callbackData, expectedState) {
32
- const data = callbackData;
33
- if (data.state !== expectedState) {
34
- throw new Error("State mismatch in callback");
35
- }
36
- return {
37
- state: String(data.state),
38
- partyId: data.partyId ? core.toPartyId(String(data.partyId)) : void 0,
39
- sessionToken: data.sessionToken ? String(data.sessionToken) : void 0,
40
- expiresAt: data.expiresAt ? Number(data.expiresAt) : void 0,
41
- capabilities: data.capabilities ? Array.isArray(data.capabilities) ? data.capabilities.map(String) : [] : void 0,
42
- error: data.error ? {
43
- code: String(data.error.code || "UNKNOWN"),
44
- message: String(data.error.message || "Unknown error")
45
- } : void 0
46
- };
47
- }
48
- createSignUrl(request, config) {
49
- if (config.universalLinkBase) {
50
- const endpoint = config.signEndpoint || "/sign";
51
- const url = new URL(endpoint, config.universalLinkBase);
52
- Object.entries(request).forEach(([key, value]) => {
53
- if (value !== void 0) {
54
- url.searchParams.set(key, typeof value === "string" ? value : JSON.stringify(value));
55
- }
56
- });
57
- return url.toString();
58
- }
59
- if (config.deepLinkScheme) {
60
- const params = new URLSearchParams();
61
- Object.entries(request).forEach(([key, value]) => {
62
- if (value !== void 0) {
63
- params.set(key, typeof value === "string" ? value : JSON.stringify(value));
64
- }
65
- });
66
- return `${config.deepLinkScheme}://sign?${params.toString()}`;
67
- }
68
- throw new Error("Cantor8 vendor not configured");
69
- }
70
- parseSignCallback(callbackData, expectedState) {
71
- const data = callbackData;
72
- if (data.state !== expectedState) {
73
- throw new Error("State mismatch in callback");
74
- }
75
- return {
76
- state: String(data.state),
77
- signature: data.signature ? String(data.signature) : void 0,
78
- transactionHash: data.transactionHash ? String(data.transactionHash) : void 0,
79
- jobId: data.jobId ? String(data.jobId) : void 0,
80
- error: data.error ? {
81
- code: String(data.error.code || "UNKNOWN"),
82
- message: String(data.error.message || "Unknown error")
83
- } : void 0
84
- };
6
+ var c8ModulePromise = null;
7
+ function loadC8() {
8
+ if (!c8ModulePromise) {
9
+ c8ModulePromise = import('@cantor8/wallet-connect-sdk');
85
10
  }
86
- };
87
-
88
- // src/cantor8-adapter.ts
11
+ return c8ModulePromise;
12
+ }
89
13
  var Cantor8Adapter = class {
90
14
  walletId = core.toWalletId("cantor8");
91
15
  name = "Cantor8";
92
- vendorModule;
93
- vendorConfig;
94
- transport;
16
+ config;
17
+ provider = null;
95
18
  constructor(config = {}) {
96
- this.vendorModule = config.vendorModule || new StubCantor8VendorModule();
97
- this.vendorConfig = config.vendorConfig || {};
98
- if (config.useMockTransport || process.env.NODE_ENV === "development") {
99
- this.transport = new core.MockTransport();
100
- } else {
101
- this.transport = new core.DeepLinkTransport();
102
- }
19
+ this.config = config;
103
20
  }
104
21
  getCapabilities() {
105
- return [
106
- "connect",
107
- "disconnect",
108
- "restore",
109
- "deeplink",
110
- "signMessage",
111
- "signTransaction"
112
- ];
22
+ return ["connect", "disconnect", "submitTransaction", "events", "popup"];
23
+ }
24
+ /** The SDK supports devnet and mainnet only; anything else fails at connect. */
25
+ mapNetwork(network) {
26
+ if (network === "devnet") return "devnet";
27
+ if (network === "mainnet") return "mainnet";
28
+ throw new Error(
29
+ `Cantor8 does not support the "${network}" network (supported: devnet, mainnet).`
30
+ );
113
31
  }
114
- detectInstalled() {
32
+ /**
33
+ * Cantor8 is a HOSTED POPUP wallet: connect() opens the wallet at
34
+ * wallet.dev.digik.cantor8.tech (devnet) or wallet.cantor8.tech (mainnet)
35
+ * through the SDK's C8WalletProvider. Nothing of Cantor8 runs in THIS page, so
36
+ * there is nothing here to detect.
37
+ *
38
+ * The SDK's discovery protocol has two sides. `announceC8Provider(get)` is the
39
+ * DISPATCH side, called by a wallet that lives in the page to advertise a
40
+ * provider factory; `onC8ProviderDiscovered(cb)` is the SUBSCRIBE side, called
41
+ * by a dApp to hear such an announcement. A hosted popup on another origin never
42
+ * calls `announceC8Provider` in our page, so subscribing and waiting for the
43
+ * `c8#provider_discovery` event can only ever time out. That is precisely what
44
+ * the previous strategy did, and it reported Cantor8 permanently unavailable,
45
+ * which hid it from the picker.
46
+ *
47
+ * The right model is the gateway contract the official ProviderAdapter path uses
48
+ * (see GenericDiscoveryAdapter.detectInstalled): a gateway is reachable wherever
49
+ * it can be opened, so detect reports available and the REAL availability check
50
+ * happens at connect(), when the popup opens. Walley, a popup wallet on that
51
+ * generic path, works in our demos for exactly this reason.
52
+ *
53
+ * This is the SECOND detection strategy to be wrong here, both from the same
54
+ * root cause: inferring the wallet's shape without the wallet in hand. First a
55
+ * user-agent sniff mislabeled this desktop popup as a mobile wallet; then the
56
+ * announcement wait assumed an in-page announcer that a hosted popup never
57
+ * provides. Do not reintroduce a probe that depends on something being in the
58
+ * page. If a future in-page Cantor8 ever announces over `c8#provider_discovery`,
59
+ * it may be adopted as a fast path, but it must NEVER be the condition for
60
+ * availability.
61
+ *
62
+ * detect still LOADS the SDK (the pre-fix detect did too; only its announcement
63
+ * wait was wrong). This is deliberate warming, not detection: a bespoke adapter
64
+ * does not get the SDK's popup-safe fast-path (that is for GenericDiscoveryAdapter),
65
+ * so connect() reaches window.open only after awaits. detect runs at picker
66
+ * render, off the click gesture, so the dynamic import resolves before the user
67
+ * clicks; connect()'s `await loadC8()` is then a resolved microtask instead of a
68
+ * cold import that would defer window.open past the gesture and let the browser
69
+ * block the popup.
70
+ */
71
+ async detectInstalled() {
115
72
  if (typeof window === "undefined") {
116
- return Promise.resolve({ installed: false, reason: "Browser environment required" });
73
+ return { installed: false, reason: "Browser environment required" };
117
74
  }
118
- const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
119
- return Promise.resolve({
120
- installed: isMobile,
121
- reason: isMobile ? void 0 : "Cantor8 is a mobile wallet"
122
- });
75
+ try {
76
+ await loadC8();
77
+ } catch {
78
+ }
79
+ return { installed: true };
123
80
  }
124
- async connect(ctx, opts) {
81
+ async connect(ctx, _opts) {
125
82
  try {
126
- const state = this.generateState();
127
- const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;
128
- const request = {
129
- appName: ctx.appName,
130
- origin: ctx.origin,
131
- network: ctx.network,
132
- requestedCapabilities: void 0,
133
- // Cantor8 doesn't support capability filtering yet
134
- state,
135
- redirectUri
136
- };
137
- const connectUrl = this.vendorModule.createConnectUrl(request, this.vendorConfig);
138
- const transportOptions = {
139
- timeoutMs: opts?.timeoutMs || 6e4,
140
- allowedOrigins: [ctx.origin],
141
- origin: ctx.origin
142
- };
143
- const response = await this.transport.openConnectRequest(
144
- connectUrl,
145
- request,
146
- transportOptions
147
- );
148
- if (response.error) {
149
- if (response.error.code === "USER_REJECTED" || response.error.code === "CANCELLED") {
150
- throw new core.UserRejectedError("User rejected connection");
151
- }
152
- throw new Error(response.error.message || "Connection failed");
153
- }
154
- if (!response.partyId) {
155
- throw new Error("No partyId in response");
83
+ const { C8WalletProvider } = await loadC8();
84
+ const provider = new C8WalletProvider({
85
+ dappName: ctx.appName,
86
+ dappUrl: this.config.dappUrl ?? ctx.origin,
87
+ network: this.mapNetwork(ctx.network)
88
+ });
89
+ await provider.connect();
90
+ const { accounts } = await provider.getAccounts();
91
+ const partyId = accounts?.[0]?.partyId;
92
+ if (!partyId) {
93
+ throw new Error("Cantor8 returned no account after connect");
156
94
  }
95
+ this.provider = provider;
157
96
  return {
158
- partyId: response.partyId,
97
+ partyId: core.toPartyId(partyId),
159
98
  session: {
160
99
  walletId: this.walletId,
161
- // Cantor8's connect response carries no network → not wallet-reported,
162
- // so network-mismatch detection is limited for this adapter (echoes
163
- // the requested ctx.network).
100
+ // Cantor8's connect carries no network → echo the requested one.
164
101
  network: ctx.network,
165
- createdAt: Date.now(),
166
- expiresAt: response.expiresAt,
167
- capabilitiesSnapshot: response.capabilities || ["connect", "signMessage"],
168
- metadata: {
169
- sessionToken: response.sessionToken || ""
170
- }
102
+ createdAt: Date.now()
171
103
  },
172
- capabilities: response.capabilities || ["connect", "signMessage"]
104
+ capabilities: this.getCapabilities()
173
105
  };
174
106
  } catch (err) {
175
107
  throw core.mapUnknownErrorToPartyLayerError(err, {
176
108
  walletId: this.walletId,
177
109
  phase: "connect",
178
- transport: "deeplink"
110
+ transport: "popup"
179
111
  });
180
112
  }
181
113
  }
182
- disconnect(_ctx, _session) {
183
- if (_session.metadata?.sessionToken) ;
184
- return Promise.resolve();
185
- }
186
- restore(_ctx, persisted) {
187
- const sessionToken = persisted.metadata?.sessionToken;
188
- if (typeof sessionToken !== "string") {
189
- return Promise.resolve(null);
190
- }
191
- if (persisted.expiresAt && Date.now() >= persisted.expiresAt) {
192
- return Promise.resolve(null);
193
- }
194
- return Promise.resolve({
195
- ...persisted,
196
- walletId: this.walletId
197
- });
198
- }
199
- async signMessage(ctx, session, params) {
114
+ async disconnect(_ctx, _session) {
115
+ if (!this.provider) return;
200
116
  try {
201
- const state = this.generateState();
202
- const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;
203
- const request = {
204
- message: params.message,
205
- state,
206
- redirectUri
207
- };
208
- const signUrl = this.vendorModule.createSignUrl(request, this.vendorConfig);
209
- const transportOptions = {
210
- timeoutMs: 6e4,
211
- allowedOrigins: [ctx.origin],
212
- origin: ctx.origin
213
- };
214
- const response = await this.transport.openSignRequest(
215
- signUrl,
216
- request,
217
- transportOptions
218
- );
219
- if (response.error) {
220
- if (response.error.code === "USER_REJECTED") {
221
- throw new core.UserRejectedError("User rejected signing");
222
- }
223
- throw new Error(response.error.message || "Signing failed");
224
- }
225
- if (!response.signature) {
226
- throw new Error("No signature in response");
227
- }
228
- return {
229
- message: params.message,
230
- signature: core.toSignature(response.signature),
231
- partyId: session.partyId
232
- };
233
- } catch (err) {
234
- throw core.mapUnknownErrorToPartyLayerError(err, {
235
- walletId: this.walletId,
236
- phase: "signMessage",
237
- transport: "deeplink"
238
- });
117
+ await this.provider.disconnect();
118
+ } finally {
119
+ this.provider = null;
239
120
  }
240
121
  }
241
- async signTransaction(ctx, session, params) {
122
+ /**
123
+ * Cantor8 has NO arbitrary-message signing. Per the honesty rule: do not
124
+ * simulate it, do not route it through `signAndExecute` (which is for
125
+ * transactions), and do not silently succeed. Fail with the typed capability
126
+ * error. `signMessage` is also absent from `getCapabilities()`, so the SDK's
127
+ * capability guard blocks it first; this is the belt-and-suspenders backstop.
128
+ */
129
+ signMessage(_ctx, _session, _params) {
130
+ return Promise.reject(
131
+ new core.CapabilityNotSupportedError(String(this.walletId), "signMessage")
132
+ );
133
+ }
134
+ /**
135
+ * Submit a transaction through the SDK's `signAndExecute` (sign + execute
136
+ * fused; the wallet has no separate sign step). `params.signedTx` is passed
137
+ * through: the caller owns its shape, which must be a {@link Cantor8SubmitPayload}.
138
+ *
139
+ * The SDK's `signAndExecute` resolves with no id; Cantor8 delivers the
140
+ * on-ledger txId and status asynchronously via the `txChanged` event (surfaced
141
+ * through `on('txStatus')`). The receipt is therefore keyed on the
142
+ * caller-supplied `commandId`; the confirmed ledger id arrives on the event
143
+ * stream. A live run against the real wallet is required to confirm
144
+ * end-to-end execution.
145
+ */
146
+ async submitTransaction(_ctx, _session, params) {
242
147
  try {
243
- const state = this.generateState();
244
- const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;
245
- const request = {
246
- transaction: params.tx,
247
- state,
248
- redirectUri
249
- };
250
- const signUrl = this.vendorModule.createSignUrl(request, this.vendorConfig);
251
- const transportOptions = {
252
- timeoutMs: 6e4,
253
- allowedOrigins: [ctx.origin],
254
- origin: ctx.origin
255
- };
256
- const response = await this.transport.openSignRequest(
257
- signUrl,
258
- request,
259
- transportOptions
260
- );
261
- if (response.error) {
262
- if (response.error.code === "USER_REJECTED") {
263
- throw new core.UserRejectedError("User rejected signing");
264
- }
265
- throw new Error(response.error.message || "Signing failed");
148
+ if (!this.provider) {
149
+ throw new Error("Not connected to Cantor8");
266
150
  }
267
- if (response.jobId && this.vendorModule.pollJobStatus) {
268
- const statusUrl = this.vendorConfig.statusEndpoint || "";
269
- const jobStatus = await this.vendorModule.pollJobStatus(
270
- response.jobId,
271
- statusUrl,
272
- 6e4
151
+ const input = params.signedTx;
152
+ if (!input || typeof input.commandId !== "string" || typeof input.commandsJson !== "string" || typeof input.partyId !== "string") {
153
+ throw new Error(
154
+ "Cantor8 submitTransaction requires signedTx to be the SDK signAndExecute input { note?, partyId, commandId, commandsJson, disclosedContracts? }."
273
155
  );
274
- if (jobStatus.status === "denied") {
275
- throw new core.UserRejectedError("Transaction signing denied");
276
- }
277
- if (jobStatus.status === "approved" && jobStatus.result?.signature) {
278
- const signedTx2 = typeof params.tx === "object" && params.tx !== null ? { ...params.tx, signature: jobStatus.result.signature } : { tx: params.tx, signature: jobStatus.result.signature };
279
- return {
280
- signedTx: signedTx2,
281
- transactionHash: jobStatus.result.transactionHash ? core.toTransactionHash(jobStatus.result.transactionHash) : core.toTransactionHash("pending"),
282
- partyId: session.partyId
283
- };
284
- }
285
- throw new Error("Job not completed");
286
156
  }
287
- if (!response.signature) {
288
- throw new Error("No signature in response");
289
- }
290
- const signedTx = typeof params.tx === "object" && params.tx !== null ? { ...params.tx, signature: response.signature } : { tx: params.tx, signature: response.signature };
157
+ await this.provider.signAndExecute({
158
+ note: input.note ?? "",
159
+ partyId: input.partyId,
160
+ commandId: input.commandId,
161
+ commandsJson: input.commandsJson,
162
+ disclosedContracts: input.disclosedContracts ?? ""
163
+ });
291
164
  return {
292
- signedTx,
293
- transactionHash: response.transactionHash ? core.toTransactionHash(response.transactionHash) : core.toTransactionHash("pending"),
294
- partyId: session.partyId
165
+ transactionHash: core.toTransactionHash(input.commandId),
166
+ submittedAt: Date.now(),
167
+ commandId: input.commandId
295
168
  };
296
169
  } catch (err) {
297
170
  throw core.mapUnknownErrorToPartyLayerError(err, {
298
171
  walletId: this.walletId,
299
- phase: "signTransaction",
300
- transport: "deeplink"
172
+ phase: "submitTransaction",
173
+ transport: "popup"
301
174
  });
302
175
  }
303
176
  }
304
177
  /**
305
- * Generate random state nonce
178
+ * Forward the wallet's transaction-status stream. Cantor8 emits `txChanged`
179
+ * ({ txId, status }); PartyLayer surfaces this as the `events` capability under
180
+ * the `txStatus` event. Any other event name is a no-op. Returns an
181
+ * unsubscribe function.
306
182
  */
307
- generateState() {
308
- const array = new Uint8Array(32);
309
- crypto.getRandomValues(array);
310
- return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
183
+ on(event, handler) {
184
+ if (event !== "txStatus" || !this.provider) {
185
+ return () => {
186
+ };
187
+ }
188
+ const off = this.provider.on("txChanged", (e) => {
189
+ handler({ txId: e.txId, status: e.status });
190
+ });
191
+ return () => {
192
+ try {
193
+ off();
194
+ } catch {
195
+ }
196
+ };
311
197
  }
312
198
  };
313
199
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vendor.ts","../src/cantor8-adapter.ts"],"names":["toPartyId","toWalletId","MockTransport","DeepLinkTransport","UserRejectedError","mapUnknownErrorToPartyLayerError","toSignature","signedTx","toTransactionHash"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Cantor8 Vendor Integration Module\n * \n * This module abstracts vendor-specific endpoints and can be swapped\n * without breaking the adapter API.\n * \n * References:\n * - Cantor8 ecosystem: https://www.canton.network/ecosystem/cantor8\n * - Cantor8 site: https://cantor8.tech/about\n */\n\nimport type {\n ConnectRequest,\n ConnectResponse,\n SignRequest,\n SignResponse,\n JobStatus,\n} from '@partylayer/core';\nimport { toPartyId } from '@partylayer/core';\n\n/**\n * Vendor configuration\n */\nexport interface Cantor8VendorConfig {\n /** Deep link scheme (e.g., \"cantor8\") */\n deepLinkScheme?: string;\n /** Universal link base URL (e.g., \"https://app.cantor8.tech\") */\n universalLinkBase?: string;\n /** Connect endpoint path */\n connectEndpoint?: string;\n /** Sign endpoint path */\n signEndpoint?: string;\n /** Job status endpoint path (for async approvals) */\n statusEndpoint?: string;\n /** Redirect URI for callbacks */\n redirectUri?: string;\n}\n\n/**\n * Vendor module interface\n * \n * Implementations can be swapped without changing adapter code.\n */\nexport interface Cantor8VendorModule {\n /**\n * Create connect URL\n */\n createConnectUrl(request: ConnectRequest, config: Cantor8VendorConfig): string;\n\n /**\n * Parse connect callback\n */\n parseConnectCallback(\n callbackData: unknown,\n expectedState: string\n ): ConnectResponse;\n\n /**\n * Create sign URL\n */\n createSignUrl(request: SignRequest, config: Cantor8VendorConfig): string;\n\n /**\n * Parse sign callback\n */\n parseSignCallback(\n callbackData: unknown,\n expectedState: string\n ): SignResponse;\n\n /**\n * Poll job status (optional, for async approvals)\n */\n pollJobStatus?(\n jobId: string,\n statusUrl: string,\n timeoutMs: number\n ): Promise<JobStatus>;\n}\n\n/**\n * Default/stub vendor module\n * \n * This implementation provides a structured interface but throws\n * NotConfiguredError with clear instructions when vendor endpoints\n * are not configured.\n */\nexport class StubCantor8VendorModule implements Cantor8VendorModule {\n createConnectUrl(request: ConnectRequest, config: Cantor8VendorConfig): string {\n // Prefer universal link if configured\n if (config.universalLinkBase) {\n const endpoint = config.connectEndpoint || '/connect';\n const url = new URL(endpoint, config.universalLinkBase);\n Object.entries(request).forEach(([key, value]) => {\n if (value !== undefined) {\n url.searchParams.set(key, typeof value === 'string' ? value : JSON.stringify(value));\n }\n });\n return url.toString();\n }\n\n // Fallback to deep link scheme\n if (config.deepLinkScheme) {\n const params = new URLSearchParams();\n Object.entries(request).forEach(([key, value]) => {\n if (value !== undefined) {\n params.set(key, typeof value === 'string' ? value : JSON.stringify(value));\n }\n });\n return `${config.deepLinkScheme}://connect?${params.toString()}`;\n }\n\n throw new Error(\n 'Cantor8 vendor not configured. Please set deepLinkScheme or universalLinkBase in adapter config.'\n );\n }\n\n parseConnectCallback(\n callbackData: unknown,\n expectedState: string\n ): ConnectResponse {\n // Parse callback data (could be URL params, postMessage, etc.)\n const data = callbackData as Record<string, unknown>;\n\n // Validate state\n if (data.state !== expectedState) {\n throw new Error('State mismatch in callback');\n }\n\n // Return response\n return {\n state: String(data.state),\n partyId: data.partyId ? toPartyId(String(data.partyId)) : undefined,\n sessionToken: data.sessionToken ? String(data.sessionToken) : undefined,\n expiresAt: data.expiresAt ? Number(data.expiresAt) : undefined,\n capabilities: data.capabilities\n ? (Array.isArray(data.capabilities) ? data.capabilities.map(String) : [])\n : undefined,\n error: data.error\n ? {\n code: String((data.error as Record<string, unknown>).code || 'UNKNOWN'),\n message: String((data.error as Record<string, unknown>).message || 'Unknown error'),\n }\n : undefined,\n };\n }\n\n createSignUrl(request: SignRequest, config: Cantor8VendorConfig): string {\n if (config.universalLinkBase) {\n const endpoint = config.signEndpoint || '/sign';\n const url = new URL(endpoint, config.universalLinkBase);\n Object.entries(request).forEach(([key, value]) => {\n if (value !== undefined) {\n url.searchParams.set(key, typeof value === 'string' ? value : JSON.stringify(value));\n }\n });\n return url.toString();\n }\n\n if (config.deepLinkScheme) {\n const params = new URLSearchParams();\n Object.entries(request).forEach(([key, value]) => {\n if (value !== undefined) {\n params.set(key, typeof value === 'string' ? value : JSON.stringify(value));\n }\n });\n return `${config.deepLinkScheme}://sign?${params.toString()}`;\n }\n\n throw new Error('Cantor8 vendor not configured');\n }\n\n parseSignCallback(\n callbackData: unknown,\n expectedState: string\n ): SignResponse {\n const data = callbackData as Record<string, unknown>;\n\n if (data.state !== expectedState) {\n throw new Error('State mismatch in callback');\n }\n\n return {\n state: String(data.state),\n signature: data.signature ? String(data.signature) : undefined,\n transactionHash: data.transactionHash ? String(data.transactionHash) : undefined,\n jobId: data.jobId ? String(data.jobId) : undefined,\n error: data.error\n ? {\n code: String((data.error as Record<string, unknown>).code || 'UNKNOWN'),\n message: String((data.error as Record<string, unknown>).message || 'Unknown error'),\n }\n : undefined,\n };\n }\n}\n","/**\n * Cantor8 (C8) Wallet Adapter\n * \n * Implements WalletAdapter interface for Cantor8 wallet using deep link transport.\n * \n * References:\n * - Cantor8 ecosystem: https://www.canton.network/ecosystem/cantor8\n * - Cantor8 site: https://cantor8.tech/about\n * - Wallet Integration Guide: https://docs.digitalasset.com/integrate/devnet/index.html\n */\n\nimport type {\n WalletAdapter,\n AdapterContext,\n AdapterDetectResult,\n AdapterConnectResult,\n SignMessageParams,\n SignTransactionParams,\n} from '@partylayer/core';\nimport {\n toWalletId,\n toSignature,\n toTransactionHash,\n UserRejectedError,\n mapUnknownErrorToPartyLayerError,\n type CapabilityKey,\n} from '@partylayer/core';\nimport { DeepLinkTransport, MockTransport } from '@partylayer/core';\nimport type {\n Cantor8VendorModule,\n Cantor8VendorConfig,\n} from './vendor';\nimport {\n StubCantor8VendorModule,\n} from './vendor';\n\n/**\n * Cantor8 adapter configuration\n */\nexport interface Cantor8AdapterConfig {\n /** Vendor module (defaults to stub) */\n vendorModule?: Cantor8VendorModule;\n /** Vendor configuration */\n vendorConfig?: Cantor8VendorConfig;\n /** Use mock transport in development */\n useMockTransport?: boolean;\n}\n\n/**\n * Cantor8 Wallet Adapter\n */\nexport class Cantor8Adapter implements WalletAdapter {\n readonly walletId = toWalletId('cantor8');\n readonly name = 'Cantor8';\n\n private vendorModule: Cantor8VendorModule;\n private vendorConfig: Cantor8VendorConfig;\n private transport: DeepLinkTransport | MockTransport;\n\n constructor(config: Cantor8AdapterConfig = {}) {\n this.vendorModule = config.vendorModule || new StubCantor8VendorModule();\n this.vendorConfig = config.vendorConfig || {};\n \n // Use mock transport in development if configured\n if (config.useMockTransport || process.env.NODE_ENV === 'development') {\n this.transport = new MockTransport();\n } else {\n this.transport = new DeepLinkTransport();\n }\n }\n\n getCapabilities(): CapabilityKey[] {\n return [\n 'connect',\n 'disconnect',\n 'restore',\n 'deeplink',\n 'signMessage',\n 'signTransaction',\n ];\n }\n\n detectInstalled(): Promise<AdapterDetectResult> {\n // Cantor8 is a mobile wallet - detection is not straightforward\n // We can check for deep link support or user agent hints\n if (typeof window === 'undefined') {\n return Promise.resolve({ installed: false, reason: 'Browser environment required' });\n }\n\n // Check if device supports deep links (mobile)\n const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);\n \n // For now, assume installed if mobile device\n // In production, might check for specific user agent or other hints\n return Promise.resolve({\n installed: isMobile,\n reason: isMobile ? undefined : 'Cantor8 is a mobile wallet',\n });\n }\n\n async connect(\n ctx: AdapterContext,\n opts?: {\n timeoutMs?: number;\n partyId?: import('@partylayer/core').PartyId;\n }\n ): Promise<AdapterConnectResult> {\n try {\n // Create connect request\n const state = this.generateState();\n const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;\n const request: import('@partylayer/core').ConnectRequest = {\n appName: ctx.appName,\n origin: ctx.origin,\n network: ctx.network,\n requestedCapabilities: undefined, // Cantor8 doesn't support capability filtering yet\n state,\n redirectUri,\n };\n\n // Create connect URL using vendor module\n const connectUrl = this.vendorModule.createConnectUrl(request, this.vendorConfig);\n\n // Open via transport\n const transportOptions: import('@partylayer/core').TransportOptions = {\n timeoutMs: opts?.timeoutMs || 60000,\n allowedOrigins: [ctx.origin],\n origin: ctx.origin,\n };\n\n const response = await this.transport.openConnectRequest(\n connectUrl,\n request,\n transportOptions\n );\n\n // Check for error\n if (response.error) {\n if (response.error.code === 'USER_REJECTED' || response.error.code === 'CANCELLED') {\n throw new UserRejectedError('User rejected connection');\n }\n throw new Error(response.error.message || 'Connection failed');\n }\n\n if (!response.partyId) {\n throw new Error('No partyId in response');\n }\n\n // Return result\n return {\n partyId: response.partyId,\n session: {\n walletId: this.walletId,\n // Cantor8's connect response carries no network → not wallet-reported,\n // so network-mismatch detection is limited for this adapter (echoes\n // the requested ctx.network).\n network: ctx.network,\n createdAt: Date.now(),\n expiresAt: response.expiresAt,\n capabilitiesSnapshot: (response.capabilities || ['connect', 'signMessage']) as CapabilityKey[],\n metadata: {\n sessionToken: response.sessionToken || '',\n },\n },\n capabilities: (response.capabilities || ['connect', 'signMessage']) as CapabilityKey[],\n };\n } catch (err) {\n throw mapUnknownErrorToPartyLayerError(err, {\n walletId: this.walletId,\n phase: 'connect',\n transport: 'deeplink',\n });\n }\n }\n\n disconnect(\n _ctx: AdapterContext,\n _session: import('@partylayer/core').Session\n ): Promise<void> {\n // Cantor8 doesn't have explicit disconnect - session expires naturally\n // Clear any stored session tokens\n if (_session.metadata?.sessionToken) {\n // In production, might call a logout endpoint\n }\n return Promise.resolve();\n }\n\n restore(\n _ctx: AdapterContext,\n persisted: import('@partylayer/core').PersistedSession\n ): Promise<import('@partylayer/core').Session | null> {\n // Check if we have a session token\n const sessionToken = persisted.metadata?.sessionToken;\n if (typeof sessionToken !== 'string') {\n return Promise.resolve(null); // No token to restore\n }\n\n // In production, might validate token with vendor API\n // For now, if token exists and session not expired, restore\n if (persisted.expiresAt && Date.now() >= persisted.expiresAt) {\n return Promise.resolve(null); // Expired\n }\n\n // Return restored session\n return Promise.resolve({\n ...persisted,\n walletId: this.walletId,\n });\n }\n\n async signMessage(\n ctx: AdapterContext,\n session: import('@partylayer/core').Session,\n params: SignMessageParams\n ): Promise<import('@partylayer/core').SignedMessage> {\n try {\n const state = this.generateState();\n const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;\n const request: import('@partylayer/core').SignRequest = {\n message: params.message,\n state,\n redirectUri,\n };\n\n const signUrl = this.vendorModule.createSignUrl(request, this.vendorConfig);\n\n const transportOptions: import('@partylayer/core').TransportOptions = {\n timeoutMs: 60000,\n allowedOrigins: [ctx.origin],\n origin: ctx.origin,\n };\n\n const response = await this.transport.openSignRequest(\n signUrl,\n request,\n transportOptions\n );\n\n if (response.error) {\n if (response.error.code === 'USER_REJECTED') {\n throw new UserRejectedError('User rejected signing');\n }\n throw new Error(response.error.message || 'Signing failed');\n }\n\n if (!response.signature) {\n throw new Error('No signature in response');\n }\n\n return {\n message: params.message,\n signature: toSignature(response.signature),\n partyId: session.partyId,\n };\n } catch (err) {\n throw mapUnknownErrorToPartyLayerError(err, {\n walletId: this.walletId,\n phase: 'signMessage',\n transport: 'deeplink',\n });\n }\n }\n\n async signTransaction(\n ctx: AdapterContext,\n session: import('@partylayer/core').Session,\n params: SignTransactionParams\n ): Promise<import('@partylayer/core').SignedTransaction> {\n try {\n const state = this.generateState();\n const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;\n const request: import('@partylayer/core').SignRequest = {\n transaction: params.tx,\n state,\n redirectUri,\n };\n\n const signUrl = this.vendorModule.createSignUrl(request, this.vendorConfig);\n\n const transportOptions: import('@partylayer/core').TransportOptions = {\n timeoutMs: 60000,\n allowedOrigins: [ctx.origin],\n origin: ctx.origin,\n };\n\n const response = await this.transport.openSignRequest(\n signUrl,\n request,\n transportOptions\n );\n\n if (response.error) {\n if (response.error.code === 'USER_REJECTED') {\n throw new UserRejectedError('User rejected signing');\n }\n throw new Error(response.error.message || 'Signing failed');\n }\n\n // Handle async approval (jobId)\n if (response.jobId && this.vendorModule.pollJobStatus) {\n const statusUrl = this.vendorConfig.statusEndpoint || '';\n const jobStatus = await this.vendorModule.pollJobStatus(\n response.jobId,\n statusUrl,\n 60000\n );\n\n if (jobStatus.status === 'denied') {\n throw new UserRejectedError('Transaction signing denied');\n }\n\n if (jobStatus.status === 'approved' && jobStatus.result?.signature) {\n const signedTx = typeof params.tx === 'object' && params.tx !== null\n ? { ...params.tx as Record<string, unknown>, signature: jobStatus.result.signature }\n : { tx: params.tx, signature: jobStatus.result.signature };\n return {\n signedTx,\n transactionHash: jobStatus.result.transactionHash\n ? toTransactionHash(jobStatus.result.transactionHash)\n : toTransactionHash('pending'),\n partyId: session.partyId,\n };\n }\n\n throw new Error('Job not completed');\n }\n\n if (!response.signature) {\n throw new Error('No signature in response');\n }\n\n const signedTx = typeof params.tx === 'object' && params.tx !== null\n ? { ...params.tx as Record<string, unknown>, signature: response.signature }\n : { tx: params.tx, signature: response.signature };\n return {\n signedTx,\n transactionHash: response.transactionHash\n ? toTransactionHash(response.transactionHash)\n : toTransactionHash('pending'),\n partyId: session.partyId,\n };\n } catch (err) {\n throw mapUnknownErrorToPartyLayerError(err, {\n walletId: this.walletId,\n phase: 'signTransaction',\n transport: 'deeplink',\n });\n }\n }\n\n /**\n * Generate random state nonce\n */\n private generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/cantor8-adapter.ts"],"names":["toWalletId","toPartyId","mapUnknownErrorToPartyLayerError","CapabilityNotSupportedError","toTransactionHash"],"mappings":";;;;;AAuDA,IAAI,eAAA,GAA4C,IAAA;AAChD,SAAS,MAAA,GAA4B;AACnC,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,eAAA,GAAkB,OAAO,6BAA6B,CAAA;AAAA,EACxD;AACA,EAAA,OAAO,eAAA;AACT;AA4BO,IAAM,iBAAN,MAA8C;AAAA,EAC1C,QAAA,GAAWA,gBAAW,SAAS,CAAA;AAAA,EAC/B,IAAA,GAAO,SAAA;AAAA,EAEC,MAAA;AAAA,EACT,QAAA,GAA4C,IAAA;AAAA,EAEpD,WAAA,CAAY,MAAA,GAA+B,EAAC,EAAG;AAC7C,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEA,eAAA,GAAmC;AAGjC,IAAA,OAAO,CAAC,SAAA,EAAW,YAAA,EAAc,mBAAA,EAAqB,UAAU,OAAO,CAAA;AAAA,EACzE;AAAA;AAAA,EAGQ,WAAW,OAAA,EAA4B;AAC7C,IAAA,IAAI,OAAA,KAAY,UAAU,OAAO,QAAA;AACjC,IAAA,IAAI,OAAA,KAAY,WAAW,OAAO,SAAA;AAClC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,iCAAiC,OAAO,CAAA,uCAAA;AAAA,KAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyCA,MAAM,eAAA,GAAgD;AACpD,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA,OAAO,EAAE,SAAA,EAAW,KAAA,EAAO,MAAA,EAAQ,8BAAA,EAA+B;AAAA,IACpE;AAGA,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,EAAO;AAAA,IACf,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,OAAO,EAAE,WAAW,IAAA,EAAK;AAAA,EAC3B;AAAA,EAEA,MAAM,OAAA,CACJ,GAAA,EACA,KAAA,EAC+B;AAC/B,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,gBAAA,EAAiB,GAAI,MAAM,MAAA,EAAO;AAC1C,MAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB;AAAA,QACpC,UAAU,GAAA,CAAI,OAAA;AAAA,QACd,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,OAAA,IAAW,GAAA,CAAI,MAAA;AAAA,QACpC,OAAA,EAAS,IAAA,CAAK,UAAA,CAAW,GAAA,CAAI,OAAO;AAAA,OACrC,CAAA;AAED,MAAA,MAAM,SAAS,OAAA,EAAQ;AACvB,MAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,SAAS,WAAA,EAAY;AAChD,MAAA,MAAM,OAAA,GAAU,QAAA,GAAW,CAAC,CAAA,EAAG,OAAA;AAC/B,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,MAAM,IAAI,MAAM,2CAA2C,CAAA;AAAA,MAC7D;AACA,MAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAChB,MAAA,OAAO;AAAA,QACL,OAAA,EAASC,eAAU,OAAO,CAAA;AAAA,QAC1B,OAAA,EAAS;AAAA,UACP,UAAU,IAAA,CAAK,QAAA;AAAA;AAAA,UAEf,SAAS,GAAA,CAAI,OAAA;AAAA,UACb,SAAA,EAAW,KAAK,GAAA;AAAI,SACtB;AAAA,QACA,YAAA,EAAc,KAAK,eAAA;AAAgB,OACrC;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,MAAMC,sCAAiC,GAAA,EAAK;AAAA,QAC1C,UAAU,IAAA,CAAK,QAAA;AAAA,QACf,KAAA,EAAO,SAAA;AAAA,QACP,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,UAAA,CAAW,IAAA,EAAsB,QAAA,EAAkC;AACvE,IAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AACpB,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,SAAS,UAAA,EAAW;AAAA,IACjC,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAAA,CACE,IAAA,EACA,QAAA,EACA,OAAA,EACwB;AACxB,IAAA,OAAO,OAAA,CAAQ,MAAA;AAAA,MACb,IAAIC,gCAAA,CAA4B,MAAA,CAAO,IAAA,CAAK,QAAQ,GAAG,aAAa;AAAA,KACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,iBAAA,CACJ,IAAA,EACA,QAAA,EACA,MAAA,EACoB;AACpB,IAAA,IAAI;AACF,MAAA,IAAI,CAAC,KAAK,QAAA,EAAU;AAClB,QAAA,MAAM,IAAI,MAAM,0BAA0B,CAAA;AAAA,MAC5C;AACA,MAAA,MAAM,QAAQ,MAAA,CAAO,QAAA;AACrB,MAAA,IACE,CAAC,KAAA,IACD,OAAO,KAAA,CAAM,SAAA,KAAc,QAAA,IAC3B,OAAO,KAAA,CAAM,YAAA,KAAiB,QAAA,IAC9B,OAAO,KAAA,CAAM,YAAY,QAAA,EACzB;AACA,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SAEF;AAAA,MACF;AACA,MAAA,MAAM,IAAA,CAAK,SAAS,cAAA,CAAe;AAAA,QACjC,IAAA,EAAM,MAAM,IAAA,IAAQ,EAAA;AAAA,QACpB,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,WAAW,KAAA,CAAM,SAAA;AAAA,QACjB,cAAc,KAAA,CAAM,YAAA;AAAA,QACpB,kBAAA,EAAoB,MAAM,kBAAA,IAAsB;AAAA,OACjD,CAAA;AAGD,MAAA,OAAO;AAAA,QACL,eAAA,EAAiBC,sBAAA,CAAkB,KAAA,CAAM,SAAS,CAAA;AAAA,QAClD,WAAA,EAAa,KAAK,GAAA,EAAI;AAAA,QACtB,WAAW,KAAA,CAAM;AAAA,OACnB;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,MAAMF,sCAAiC,GAAA,EAAK;AAAA,QAC1C,UAAU,IAAA,CAAK,QAAA;AAAA,QACf,KAAA,EAAO,mBAAA;AAAA,QACP,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,EAAA,CAAG,OAAyB,OAAA,EAAiD;AAC3E,IAAA,IAAI,KAAA,KAAU,UAAA,IAAc,CAAC,IAAA,CAAK,QAAA,EAAU;AAC1C,MAAA,OAAO,MAAM;AAAA,MAEb,CAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAM,IAAA,CAAK,QAAA,CAAS,EAAA,CAAG,WAAA,EAAa,CAAC,CAAA,KAAM;AAC/C,MAAA,OAAA,CAAQ,EAAE,IAAA,EAAM,CAAA,CAAE,MAAM,MAAA,EAAQ,CAAA,CAAE,QAAQ,CAAA;AAAA,IAC5C,CAAC,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAA,IAAI;AACF,QAAA,GAAA,EAAI;AAAA,MACN,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF,CAAA;AAAA,EACF;AACF","file":"index.js","sourcesContent":["/**\n * Cantor8 (C8) Wallet Adapter\n *\n * Wraps the wallet's OWN SDK, `@cantor8/wallet-connect-sdk` (`C8WalletProvider`),\n * which opens a popup to the hosted Cantor8 wallet and speaks its own popup +\n * `postMessage` protocol.\n *\n * This is NOT CIP-0103: the SDK exposes no `request({ method })`, no\n * `signMessage`, and no `prepareExecute`, and it discovers itself over a\n * proprietary `c8#provider_discovery` window event rather than\n * `canton:announceProvider`. Until Cantor8 ships an official `ProviderAdapter`\n * (the generic discovery path), a bespoke wrapper on the real SDK is the honest\n * way to integrate it, replacing the previous `cantor8://` deep-link stub.\n *\n * What maps to a real SDK method, and what does not:\n * - `connect` → `C8WalletProvider.connect()` + `getAccounts()` for the party.\n * - `disconnect` → `C8WalletProvider.disconnect()`.\n * - `submitTransaction` → `signAndExecute()` (the SDK fuses sign + execute).\n * - tx status → the SDK's `txChanged` event, surfaced as the `events` capability\n * via `on('txStatus')`.\n * - `signMessage` has NO counterpart in the SDK. It is NOT simulated, NOT routed\n * through a transaction method, and does NOT silently succeed. It fails with\n * `CapabilityNotSupportedError`.\n * - The SDK's networks are `devnet` and `mainnet` only.\n *\n * Reference: `@cantor8/wallet-connect-sdk` (npm, 0.4.0); docs at cantor8.mintlify.app.\n */\n\nimport type {\n WalletAdapter,\n AdapterContext,\n AdapterDetectResult,\n AdapterConnectResult,\n AdapterEventName,\n SignMessageParams,\n SubmitTransactionParams,\n Session,\n SignedMessage,\n TxReceipt,\n} from '@partylayer/core';\nimport {\n toWalletId,\n toPartyId,\n toTransactionHash,\n CapabilityNotSupportedError,\n mapUnknownErrorToPartyLayerError,\n type CapabilityKey,\n} from '@partylayer/core';\n\n// Lazy, SSR-safe access to the wallet's own SDK: the provider touches `window`\n// (it opens a popup), so the import is deferred to first use, mirroring the\n// Console adapter. `@cantor8/wallet-connect-sdk` has zero dependencies.\ntype C8Module = typeof import('@cantor8/wallet-connect-sdk');\ntype C8WalletProviderInstance = InstanceType<C8Module['C8WalletProvider']>;\n\nlet c8ModulePromise: Promise<C8Module> | null = null;\nfunction loadC8(): Promise<C8Module> {\n if (!c8ModulePromise) {\n c8ModulePromise = import('@cantor8/wallet-connect-sdk');\n }\n return c8ModulePromise;\n}\n\n/** The only networks the Cantor8 SDK constructor accepts. */\ntype C8Network = 'devnet' | 'mainnet';\n\n/**\n * The shape `submitTransaction` expects in `params.signedTx`: the Cantor8 SDK's\n * `signAndExecute` input. The caller owns this shape (passthrough).\n */\nexport interface Cantor8SubmitPayload {\n note?: string;\n partyId: string;\n commandId: string;\n commandsJson: string;\n disclosedContracts?: string;\n}\n\n/**\n * Cantor8 adapter configuration.\n */\nexport interface Cantor8AdapterConfig {\n /** dApp URL passed to the Cantor8 SDK popup handshake. Defaults to `ctx.origin`. */\n dappUrl?: string;\n}\n\n/**\n * Cantor8 Wallet Adapter.\n */\nexport class Cantor8Adapter implements WalletAdapter {\n readonly walletId = toWalletId('cantor8');\n readonly name = 'Cantor8';\n\n private readonly config: Cantor8AdapterConfig;\n private provider: C8WalletProviderInstance | null = null;\n\n constructor(config: Cantor8AdapterConfig = {}) {\n this.config = config;\n }\n\n getCapabilities(): CapabilityKey[] {\n // No 'signMessage' (the SDK has none) and no 'signTransaction' (the SDK only\n // fuses sign + execute via signAndExecute). Tx status is delivered as events.\n return ['connect', 'disconnect', 'submitTransaction', 'events', 'popup'];\n }\n\n /** The SDK supports devnet and mainnet only; anything else fails at connect. */\n private mapNetwork(network: string): C8Network {\n if (network === 'devnet') return 'devnet';\n if (network === 'mainnet') return 'mainnet';\n throw new Error(\n `Cantor8 does not support the \"${network}\" network (supported: devnet, mainnet).`,\n );\n }\n\n /**\n * Cantor8 is a HOSTED POPUP wallet: connect() opens the wallet at\n * wallet.dev.digik.cantor8.tech (devnet) or wallet.cantor8.tech (mainnet)\n * through the SDK's C8WalletProvider. Nothing of Cantor8 runs in THIS page, so\n * there is nothing here to detect.\n *\n * The SDK's discovery protocol has two sides. `announceC8Provider(get)` is the\n * DISPATCH side, called by a wallet that lives in the page to advertise a\n * provider factory; `onC8ProviderDiscovered(cb)` is the SUBSCRIBE side, called\n * by a dApp to hear such an announcement. A hosted popup on another origin never\n * calls `announceC8Provider` in our page, so subscribing and waiting for the\n * `c8#provider_discovery` event can only ever time out. That is precisely what\n * the previous strategy did, and it reported Cantor8 permanently unavailable,\n * which hid it from the picker.\n *\n * The right model is the gateway contract the official ProviderAdapter path uses\n * (see GenericDiscoveryAdapter.detectInstalled): a gateway is reachable wherever\n * it can be opened, so detect reports available and the REAL availability check\n * happens at connect(), when the popup opens. Walley, a popup wallet on that\n * generic path, works in our demos for exactly this reason.\n *\n * This is the SECOND detection strategy to be wrong here, both from the same\n * root cause: inferring the wallet's shape without the wallet in hand. First a\n * user-agent sniff mislabeled this desktop popup as a mobile wallet; then the\n * announcement wait assumed an in-page announcer that a hosted popup never\n * provides. Do not reintroduce a probe that depends on something being in the\n * page. If a future in-page Cantor8 ever announces over `c8#provider_discovery`,\n * it may be adopted as a fast path, but it must NEVER be the condition for\n * availability.\n *\n * detect still LOADS the SDK (the pre-fix detect did too; only its announcement\n * wait was wrong). This is deliberate warming, not detection: a bespoke adapter\n * does not get the SDK's popup-safe fast-path (that is for GenericDiscoveryAdapter),\n * so connect() reaches window.open only after awaits. detect runs at picker\n * render, off the click gesture, so the dynamic import resolves before the user\n * clicks; connect()'s `await loadC8()` is then a resolved microtask instead of a\n * cold import that would defer window.open past the gesture and let the browser\n * block the popup.\n */\n async detectInstalled(): Promise<AdapterDetectResult> {\n if (typeof window === 'undefined') {\n return { installed: false, reason: 'Browser environment required' };\n }\n // Warm the SDK module (see the warming note above). A load failure is not\n // fatal to availability; it surfaces at connect() where the real work happens.\n try {\n await loadC8();\n } catch {\n /* ignore; connect() surfaces a genuine SDK load failure */\n }\n return { installed: true };\n }\n\n async connect(\n ctx: AdapterContext,\n _opts?: { timeoutMs?: number },\n ): Promise<AdapterConnectResult> {\n try {\n const { C8WalletProvider } = await loadC8();\n const provider = new C8WalletProvider({\n dappName: ctx.appName,\n dappUrl: this.config.dappUrl ?? ctx.origin,\n network: this.mapNetwork(ctx.network),\n });\n // Cantor8's connect() resolves void; the party is read from getAccounts().\n await provider.connect();\n const { accounts } = await provider.getAccounts();\n const partyId = accounts?.[0]?.partyId;\n if (!partyId) {\n throw new Error('Cantor8 returned no account after connect');\n }\n this.provider = provider;\n return {\n partyId: toPartyId(partyId),\n session: {\n walletId: this.walletId,\n // Cantor8's connect carries no network → echo the requested one.\n network: ctx.network,\n createdAt: Date.now(),\n },\n capabilities: this.getCapabilities(),\n };\n } catch (err) {\n throw mapUnknownErrorToPartyLayerError(err, {\n walletId: this.walletId,\n phase: 'connect',\n transport: 'popup',\n });\n }\n }\n\n async disconnect(_ctx: AdapterContext, _session: Session): Promise<void> {\n if (!this.provider) return;\n try {\n await this.provider.disconnect();\n } finally {\n this.provider = null;\n }\n }\n\n /**\n * Cantor8 has NO arbitrary-message signing. Per the honesty rule: do not\n * simulate it, do not route it through `signAndExecute` (which is for\n * transactions), and do not silently succeed. Fail with the typed capability\n * error. `signMessage` is also absent from `getCapabilities()`, so the SDK's\n * capability guard blocks it first; this is the belt-and-suspenders backstop.\n */\n signMessage(\n _ctx: AdapterContext,\n _session: Session,\n _params: SignMessageParams,\n ): Promise<SignedMessage> {\n return Promise.reject(\n new CapabilityNotSupportedError(String(this.walletId), 'signMessage'),\n );\n }\n\n /**\n * Submit a transaction through the SDK's `signAndExecute` (sign + execute\n * fused; the wallet has no separate sign step). `params.signedTx` is passed\n * through: the caller owns its shape, which must be a {@link Cantor8SubmitPayload}.\n *\n * The SDK's `signAndExecute` resolves with no id; Cantor8 delivers the\n * on-ledger txId and status asynchronously via the `txChanged` event (surfaced\n * through `on('txStatus')`). The receipt is therefore keyed on the\n * caller-supplied `commandId`; the confirmed ledger id arrives on the event\n * stream. A live run against the real wallet is required to confirm\n * end-to-end execution.\n */\n async submitTransaction(\n _ctx: AdapterContext,\n _session: Session,\n params: SubmitTransactionParams,\n ): Promise<TxReceipt> {\n try {\n if (!this.provider) {\n throw new Error('Not connected to Cantor8');\n }\n const input = params.signedTx as Cantor8SubmitPayload;\n if (\n !input ||\n typeof input.commandId !== 'string' ||\n typeof input.commandsJson !== 'string' ||\n typeof input.partyId !== 'string'\n ) {\n throw new Error(\n 'Cantor8 submitTransaction requires signedTx to be the SDK signAndExecute '\n + 'input { note?, partyId, commandId, commandsJson, disclosedContracts? }.',\n );\n }\n await this.provider.signAndExecute({\n note: input.note ?? '',\n partyId: input.partyId,\n commandId: input.commandId,\n commandsJson: input.commandsJson,\n disclosedContracts: input.disclosedContracts ?? '',\n });\n // signAndExecute returns no id; key the receipt on the caller's commandId.\n // The confirmed ledger txId + status arrive via the txChanged event.\n return {\n transactionHash: toTransactionHash(input.commandId),\n submittedAt: Date.now(),\n commandId: input.commandId,\n };\n } catch (err) {\n throw mapUnknownErrorToPartyLayerError(err, {\n walletId: this.walletId,\n phase: 'submitTransaction',\n transport: 'popup',\n });\n }\n }\n\n /**\n * Forward the wallet's transaction-status stream. Cantor8 emits `txChanged`\n * ({ txId, status }); PartyLayer surfaces this as the `events` capability under\n * the `txStatus` event. Any other event name is a no-op. Returns an\n * unsubscribe function.\n */\n on(event: AdapterEventName, handler: (payload: unknown) => void): () => void {\n if (event !== 'txStatus' || !this.provider) {\n return () => {\n /* nothing subscribed */\n };\n }\n const off = this.provider.on('txChanged', (e) => {\n handler({ txId: e.txId, status: e.status });\n });\n return () => {\n try {\n off();\n } catch {\n /* ignore */\n }\n };\n }\n}\n"]}