@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.mjs CHANGED
@@ -1,311 +1,197 @@
1
- import { toWalletId, MockTransport, DeepLinkTransport, UserRejectedError, mapUnknownErrorToPartyLayerError, toSignature, toTransactionHash, toPartyId } from '@partylayer/core';
1
+ import { toWalletId, toPartyId, mapUnknownErrorToPartyLayerError, CapabilityNotSupportedError, toTransactionHash } from '@partylayer/core';
2
2
 
3
3
  // src/cantor8-adapter.ts
4
- var StubCantor8VendorModule = class {
5
- createConnectUrl(request, config) {
6
- if (config.universalLinkBase) {
7
- const endpoint = config.connectEndpoint || "/connect";
8
- const url = new URL(endpoint, config.universalLinkBase);
9
- Object.entries(request).forEach(([key, value]) => {
10
- if (value !== void 0) {
11
- url.searchParams.set(key, typeof value === "string" ? value : JSON.stringify(value));
12
- }
13
- });
14
- return url.toString();
15
- }
16
- if (config.deepLinkScheme) {
17
- const params = new URLSearchParams();
18
- Object.entries(request).forEach(([key, value]) => {
19
- if (value !== void 0) {
20
- params.set(key, typeof value === "string" ? value : JSON.stringify(value));
21
- }
22
- });
23
- return `${config.deepLinkScheme}://connect?${params.toString()}`;
24
- }
25
- throw new Error(
26
- "Cantor8 vendor not configured. Please set deepLinkScheme or universalLinkBase in adapter config."
27
- );
28
- }
29
- parseConnectCallback(callbackData, expectedState) {
30
- const data = callbackData;
31
- if (data.state !== expectedState) {
32
- throw new Error("State mismatch in callback");
33
- }
34
- return {
35
- state: String(data.state),
36
- partyId: data.partyId ? toPartyId(String(data.partyId)) : void 0,
37
- sessionToken: data.sessionToken ? String(data.sessionToken) : void 0,
38
- expiresAt: data.expiresAt ? Number(data.expiresAt) : void 0,
39
- capabilities: data.capabilities ? Array.isArray(data.capabilities) ? data.capabilities.map(String) : [] : void 0,
40
- error: data.error ? {
41
- code: String(data.error.code || "UNKNOWN"),
42
- message: String(data.error.message || "Unknown error")
43
- } : void 0
44
- };
45
- }
46
- createSignUrl(request, config) {
47
- if (config.universalLinkBase) {
48
- const endpoint = config.signEndpoint || "/sign";
49
- const url = new URL(endpoint, config.universalLinkBase);
50
- Object.entries(request).forEach(([key, value]) => {
51
- if (value !== void 0) {
52
- url.searchParams.set(key, typeof value === "string" ? value : JSON.stringify(value));
53
- }
54
- });
55
- return url.toString();
56
- }
57
- if (config.deepLinkScheme) {
58
- const params = new URLSearchParams();
59
- Object.entries(request).forEach(([key, value]) => {
60
- if (value !== void 0) {
61
- params.set(key, typeof value === "string" ? value : JSON.stringify(value));
62
- }
63
- });
64
- return `${config.deepLinkScheme}://sign?${params.toString()}`;
65
- }
66
- throw new Error("Cantor8 vendor not configured");
67
- }
68
- parseSignCallback(callbackData, expectedState) {
69
- const data = callbackData;
70
- if (data.state !== expectedState) {
71
- throw new Error("State mismatch in callback");
72
- }
73
- return {
74
- state: String(data.state),
75
- signature: data.signature ? String(data.signature) : void 0,
76
- transactionHash: data.transactionHash ? String(data.transactionHash) : void 0,
77
- jobId: data.jobId ? String(data.jobId) : void 0,
78
- error: data.error ? {
79
- code: String(data.error.code || "UNKNOWN"),
80
- message: String(data.error.message || "Unknown error")
81
- } : void 0
82
- };
4
+ var c8ModulePromise = null;
5
+ function loadC8() {
6
+ if (!c8ModulePromise) {
7
+ c8ModulePromise = import('@cantor8/wallet-connect-sdk');
83
8
  }
84
- };
85
-
86
- // src/cantor8-adapter.ts
9
+ return c8ModulePromise;
10
+ }
87
11
  var Cantor8Adapter = class {
88
12
  walletId = toWalletId("cantor8");
89
13
  name = "Cantor8";
90
- vendorModule;
91
- vendorConfig;
92
- transport;
14
+ config;
15
+ provider = null;
93
16
  constructor(config = {}) {
94
- this.vendorModule = config.vendorModule || new StubCantor8VendorModule();
95
- this.vendorConfig = config.vendorConfig || {};
96
- if (config.useMockTransport || process.env.NODE_ENV === "development") {
97
- this.transport = new MockTransport();
98
- } else {
99
- this.transport = new DeepLinkTransport();
100
- }
17
+ this.config = config;
101
18
  }
102
19
  getCapabilities() {
103
- return [
104
- "connect",
105
- "disconnect",
106
- "restore",
107
- "deeplink",
108
- "signMessage",
109
- "signTransaction"
110
- ];
20
+ return ["connect", "disconnect", "submitTransaction", "events", "popup"];
21
+ }
22
+ /** The SDK supports devnet and mainnet only; anything else fails at connect. */
23
+ mapNetwork(network) {
24
+ if (network === "devnet") return "devnet";
25
+ if (network === "mainnet") return "mainnet";
26
+ throw new Error(
27
+ `Cantor8 does not support the "${network}" network (supported: devnet, mainnet).`
28
+ );
111
29
  }
112
- detectInstalled() {
30
+ /**
31
+ * Cantor8 is a HOSTED POPUP wallet: connect() opens the wallet at
32
+ * wallet.dev.digik.cantor8.tech (devnet) or wallet.cantor8.tech (mainnet)
33
+ * through the SDK's C8WalletProvider. Nothing of Cantor8 runs in THIS page, so
34
+ * there is nothing here to detect.
35
+ *
36
+ * The SDK's discovery protocol has two sides. `announceC8Provider(get)` is the
37
+ * DISPATCH side, called by a wallet that lives in the page to advertise a
38
+ * provider factory; `onC8ProviderDiscovered(cb)` is the SUBSCRIBE side, called
39
+ * by a dApp to hear such an announcement. A hosted popup on another origin never
40
+ * calls `announceC8Provider` in our page, so subscribing and waiting for the
41
+ * `c8#provider_discovery` event can only ever time out. That is precisely what
42
+ * the previous strategy did, and it reported Cantor8 permanently unavailable,
43
+ * which hid it from the picker.
44
+ *
45
+ * The right model is the gateway contract the official ProviderAdapter path uses
46
+ * (see GenericDiscoveryAdapter.detectInstalled): a gateway is reachable wherever
47
+ * it can be opened, so detect reports available and the REAL availability check
48
+ * happens at connect(), when the popup opens. Walley, a popup wallet on that
49
+ * generic path, works in our demos for exactly this reason.
50
+ *
51
+ * This is the SECOND detection strategy to be wrong here, both from the same
52
+ * root cause: inferring the wallet's shape without the wallet in hand. First a
53
+ * user-agent sniff mislabeled this desktop popup as a mobile wallet; then the
54
+ * announcement wait assumed an in-page announcer that a hosted popup never
55
+ * provides. Do not reintroduce a probe that depends on something being in the
56
+ * page. If a future in-page Cantor8 ever announces over `c8#provider_discovery`,
57
+ * it may be adopted as a fast path, but it must NEVER be the condition for
58
+ * availability.
59
+ *
60
+ * detect still LOADS the SDK (the pre-fix detect did too; only its announcement
61
+ * wait was wrong). This is deliberate warming, not detection: a bespoke adapter
62
+ * does not get the SDK's popup-safe fast-path (that is for GenericDiscoveryAdapter),
63
+ * so connect() reaches window.open only after awaits. detect runs at picker
64
+ * render, off the click gesture, so the dynamic import resolves before the user
65
+ * clicks; connect()'s `await loadC8()` is then a resolved microtask instead of a
66
+ * cold import that would defer window.open past the gesture and let the browser
67
+ * block the popup.
68
+ */
69
+ async detectInstalled() {
113
70
  if (typeof window === "undefined") {
114
- return Promise.resolve({ installed: false, reason: "Browser environment required" });
71
+ return { installed: false, reason: "Browser environment required" };
115
72
  }
116
- const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
117
- return Promise.resolve({
118
- installed: isMobile,
119
- reason: isMobile ? void 0 : "Cantor8 is a mobile wallet"
120
- });
73
+ try {
74
+ await loadC8();
75
+ } catch {
76
+ }
77
+ return { installed: true };
121
78
  }
122
- async connect(ctx, opts) {
79
+ async connect(ctx, _opts) {
123
80
  try {
124
- const state = this.generateState();
125
- const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;
126
- const request = {
127
- appName: ctx.appName,
128
- origin: ctx.origin,
129
- network: ctx.network,
130
- requestedCapabilities: void 0,
131
- // Cantor8 doesn't support capability filtering yet
132
- state,
133
- redirectUri
134
- };
135
- const connectUrl = this.vendorModule.createConnectUrl(request, this.vendorConfig);
136
- const transportOptions = {
137
- timeoutMs: opts?.timeoutMs || 6e4,
138
- allowedOrigins: [ctx.origin],
139
- origin: ctx.origin
140
- };
141
- const response = await this.transport.openConnectRequest(
142
- connectUrl,
143
- request,
144
- transportOptions
145
- );
146
- if (response.error) {
147
- if (response.error.code === "USER_REJECTED" || response.error.code === "CANCELLED") {
148
- throw new UserRejectedError("User rejected connection");
149
- }
150
- throw new Error(response.error.message || "Connection failed");
151
- }
152
- if (!response.partyId) {
153
- throw new Error("No partyId in response");
81
+ const { C8WalletProvider } = await loadC8();
82
+ const provider = new C8WalletProvider({
83
+ dappName: ctx.appName,
84
+ dappUrl: this.config.dappUrl ?? ctx.origin,
85
+ network: this.mapNetwork(ctx.network)
86
+ });
87
+ await provider.connect();
88
+ const { accounts } = await provider.getAccounts();
89
+ const partyId = accounts?.[0]?.partyId;
90
+ if (!partyId) {
91
+ throw new Error("Cantor8 returned no account after connect");
154
92
  }
93
+ this.provider = provider;
155
94
  return {
156
- partyId: response.partyId,
95
+ partyId: toPartyId(partyId),
157
96
  session: {
158
97
  walletId: this.walletId,
159
- // Cantor8's connect response carries no network → not wallet-reported,
160
- // so network-mismatch detection is limited for this adapter (echoes
161
- // the requested ctx.network).
98
+ // Cantor8's connect carries no network → echo the requested one.
162
99
  network: ctx.network,
163
- createdAt: Date.now(),
164
- expiresAt: response.expiresAt,
165
- capabilitiesSnapshot: response.capabilities || ["connect", "signMessage"],
166
- metadata: {
167
- sessionToken: response.sessionToken || ""
168
- }
100
+ createdAt: Date.now()
169
101
  },
170
- capabilities: response.capabilities || ["connect", "signMessage"]
102
+ capabilities: this.getCapabilities()
171
103
  };
172
104
  } catch (err) {
173
105
  throw mapUnknownErrorToPartyLayerError(err, {
174
106
  walletId: this.walletId,
175
107
  phase: "connect",
176
- transport: "deeplink"
108
+ transport: "popup"
177
109
  });
178
110
  }
179
111
  }
180
- disconnect(_ctx, _session) {
181
- if (_session.metadata?.sessionToken) ;
182
- return Promise.resolve();
183
- }
184
- restore(_ctx, persisted) {
185
- const sessionToken = persisted.metadata?.sessionToken;
186
- if (typeof sessionToken !== "string") {
187
- return Promise.resolve(null);
188
- }
189
- if (persisted.expiresAt && Date.now() >= persisted.expiresAt) {
190
- return Promise.resolve(null);
191
- }
192
- return Promise.resolve({
193
- ...persisted,
194
- walletId: this.walletId
195
- });
196
- }
197
- async signMessage(ctx, session, params) {
112
+ async disconnect(_ctx, _session) {
113
+ if (!this.provider) return;
198
114
  try {
199
- const state = this.generateState();
200
- const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;
201
- const request = {
202
- message: params.message,
203
- state,
204
- redirectUri
205
- };
206
- const signUrl = this.vendorModule.createSignUrl(request, this.vendorConfig);
207
- const transportOptions = {
208
- timeoutMs: 6e4,
209
- allowedOrigins: [ctx.origin],
210
- origin: ctx.origin
211
- };
212
- const response = await this.transport.openSignRequest(
213
- signUrl,
214
- request,
215
- transportOptions
216
- );
217
- if (response.error) {
218
- if (response.error.code === "USER_REJECTED") {
219
- throw new UserRejectedError("User rejected signing");
220
- }
221
- throw new Error(response.error.message || "Signing failed");
222
- }
223
- if (!response.signature) {
224
- throw new Error("No signature in response");
225
- }
226
- return {
227
- message: params.message,
228
- signature: toSignature(response.signature),
229
- partyId: session.partyId
230
- };
231
- } catch (err) {
232
- throw mapUnknownErrorToPartyLayerError(err, {
233
- walletId: this.walletId,
234
- phase: "signMessage",
235
- transport: "deeplink"
236
- });
115
+ await this.provider.disconnect();
116
+ } finally {
117
+ this.provider = null;
237
118
  }
238
119
  }
239
- async signTransaction(ctx, session, params) {
120
+ /**
121
+ * Cantor8 has NO arbitrary-message signing. Per the honesty rule: do not
122
+ * simulate it, do not route it through `signAndExecute` (which is for
123
+ * transactions), and do not silently succeed. Fail with the typed capability
124
+ * error. `signMessage` is also absent from `getCapabilities()`, so the SDK's
125
+ * capability guard blocks it first; this is the belt-and-suspenders backstop.
126
+ */
127
+ signMessage(_ctx, _session, _params) {
128
+ return Promise.reject(
129
+ new CapabilityNotSupportedError(String(this.walletId), "signMessage")
130
+ );
131
+ }
132
+ /**
133
+ * Submit a transaction through the SDK's `signAndExecute` (sign + execute
134
+ * fused; the wallet has no separate sign step). `params.signedTx` is passed
135
+ * through: the caller owns its shape, which must be a {@link Cantor8SubmitPayload}.
136
+ *
137
+ * The SDK's `signAndExecute` resolves with no id; Cantor8 delivers the
138
+ * on-ledger txId and status asynchronously via the `txChanged` event (surfaced
139
+ * through `on('txStatus')`). The receipt is therefore keyed on the
140
+ * caller-supplied `commandId`; the confirmed ledger id arrives on the event
141
+ * stream. A live run against the real wallet is required to confirm
142
+ * end-to-end execution.
143
+ */
144
+ async submitTransaction(_ctx, _session, params) {
240
145
  try {
241
- const state = this.generateState();
242
- const redirectUri = this.vendorConfig.redirectUri || `${ctx.origin}/callback`;
243
- const request = {
244
- transaction: params.tx,
245
- state,
246
- redirectUri
247
- };
248
- const signUrl = this.vendorModule.createSignUrl(request, this.vendorConfig);
249
- const transportOptions = {
250
- timeoutMs: 6e4,
251
- allowedOrigins: [ctx.origin],
252
- origin: ctx.origin
253
- };
254
- const response = await this.transport.openSignRequest(
255
- signUrl,
256
- request,
257
- transportOptions
258
- );
259
- if (response.error) {
260
- if (response.error.code === "USER_REJECTED") {
261
- throw new UserRejectedError("User rejected signing");
262
- }
263
- throw new Error(response.error.message || "Signing failed");
146
+ if (!this.provider) {
147
+ throw new Error("Not connected to Cantor8");
264
148
  }
265
- if (response.jobId && this.vendorModule.pollJobStatus) {
266
- const statusUrl = this.vendorConfig.statusEndpoint || "";
267
- const jobStatus = await this.vendorModule.pollJobStatus(
268
- response.jobId,
269
- statusUrl,
270
- 6e4
149
+ const input = params.signedTx;
150
+ if (!input || typeof input.commandId !== "string" || typeof input.commandsJson !== "string" || typeof input.partyId !== "string") {
151
+ throw new Error(
152
+ "Cantor8 submitTransaction requires signedTx to be the SDK signAndExecute input { note?, partyId, commandId, commandsJson, disclosedContracts? }."
271
153
  );
272
- if (jobStatus.status === "denied") {
273
- throw new UserRejectedError("Transaction signing denied");
274
- }
275
- if (jobStatus.status === "approved" && jobStatus.result?.signature) {
276
- const signedTx2 = typeof params.tx === "object" && params.tx !== null ? { ...params.tx, signature: jobStatus.result.signature } : { tx: params.tx, signature: jobStatus.result.signature };
277
- return {
278
- signedTx: signedTx2,
279
- transactionHash: jobStatus.result.transactionHash ? toTransactionHash(jobStatus.result.transactionHash) : toTransactionHash("pending"),
280
- partyId: session.partyId
281
- };
282
- }
283
- throw new Error("Job not completed");
284
154
  }
285
- if (!response.signature) {
286
- throw new Error("No signature in response");
287
- }
288
- const signedTx = typeof params.tx === "object" && params.tx !== null ? { ...params.tx, signature: response.signature } : { tx: params.tx, signature: response.signature };
155
+ await this.provider.signAndExecute({
156
+ note: input.note ?? "",
157
+ partyId: input.partyId,
158
+ commandId: input.commandId,
159
+ commandsJson: input.commandsJson,
160
+ disclosedContracts: input.disclosedContracts ?? ""
161
+ });
289
162
  return {
290
- signedTx,
291
- transactionHash: response.transactionHash ? toTransactionHash(response.transactionHash) : toTransactionHash("pending"),
292
- partyId: session.partyId
163
+ transactionHash: toTransactionHash(input.commandId),
164
+ submittedAt: Date.now(),
165
+ commandId: input.commandId
293
166
  };
294
167
  } catch (err) {
295
168
  throw mapUnknownErrorToPartyLayerError(err, {
296
169
  walletId: this.walletId,
297
- phase: "signTransaction",
298
- transport: "deeplink"
170
+ phase: "submitTransaction",
171
+ transport: "popup"
299
172
  });
300
173
  }
301
174
  }
302
175
  /**
303
- * Generate random state nonce
176
+ * Forward the wallet's transaction-status stream. Cantor8 emits `txChanged`
177
+ * ({ txId, status }); PartyLayer surfaces this as the `events` capability under
178
+ * the `txStatus` event. Any other event name is a no-op. Returns an
179
+ * unsubscribe function.
304
180
  */
305
- generateState() {
306
- const array = new Uint8Array(32);
307
- crypto.getRandomValues(array);
308
- return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
181
+ on(event, handler) {
182
+ if (event !== "txStatus" || !this.provider) {
183
+ return () => {
184
+ };
185
+ }
186
+ const off = this.provider.on("txChanged", (e) => {
187
+ handler({ txId: e.txId, status: e.status });
188
+ });
189
+ return () => {
190
+ try {
191
+ off();
192
+ } catch {
193
+ }
194
+ };
309
195
  }
310
196
  };
311
197
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vendor.ts","../src/cantor8-adapter.ts"],"names":["signedTx"],"mappings":"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","file":"index.mjs","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":[],"mappings":"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","file":"index.mjs","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@partylayer/adapter-cantor8",
3
- "version": "0.2.20",
3
+ "version": "0.4.0",
4
4
  "sideEffects": false,
5
5
  "description": "Cantor8 (C8) Wallet adapter for PartyLayer",
6
6
  "main": "./dist/index.js",
@@ -33,6 +33,7 @@
33
33
  "url": "https://github.com/PartyLayer/PartyLayer/issues"
34
34
  },
35
35
  "dependencies": {
36
+ "@cantor8/wallet-connect-sdk": "^0.4.0",
36
37
  "@partylayer/core": "^0.12.1"
37
38
  },
38
39
  "devDependencies": {