@column-org/wallet-sdk 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -33,8 +33,210 @@ var ColumnCrypto = class {
33
33
  sharedSecret
34
34
  );
35
35
  if (!decrypted) return null;
36
- const jsonString = Buffer.from(decrypted).toString("utf8");
37
- return JSON.parse(jsonString);
36
+ try {
37
+ const jsonString = Buffer.from(decrypted).toString("utf8");
38
+ return JSON.parse(jsonString);
39
+ } catch (e) {
40
+ console.error("ColumnCrypto: Failed to parse decrypted payload", e);
41
+ return null;
42
+ }
43
+ }
44
+ };
45
+
46
+ // src/ColumnWalletSDK.ts
47
+ import * as nacl2 from "tweetnacl";
48
+ var ColumnWalletSDK = class {
49
+ config;
50
+ sessionKeyPair;
51
+ walletEncryptionPublicKey = null;
52
+ constructor(config) {
53
+ this.config = config;
54
+ if (config.sessionSecretKey) {
55
+ try {
56
+ const secretKey = bs582.decode(config.sessionSecretKey);
57
+ this.sessionKeyPair = nacl2.box.keyPair.fromSecretKey(secretKey);
58
+ } catch (e) {
59
+ console.warn("ColumnWalletSDK: Invalid sessionSecretKey provided, generating new one.");
60
+ this.sessionKeyPair = ColumnCrypto.generateKeyPair();
61
+ }
62
+ } else {
63
+ this.sessionKeyPair = ColumnCrypto.generateKeyPair();
64
+ }
65
+ }
66
+ /**
67
+ * Returns the current session's secret key (bs58) so it can be persisted
68
+ */
69
+ getSessionSecretKey() {
70
+ return bs582.encode(this.sessionKeyPair.secretKey);
71
+ }
72
+ /**
73
+ * Internal: Used by Web/Native adapters to get the config
74
+ */
75
+ getConfig() {
76
+ return this.config;
77
+ }
78
+ setWalletEncryptionPublicKey(key) {
79
+ this.walletEncryptionPublicKey = key;
80
+ }
81
+ /**
82
+ * Alias for setWalletEncryptionPublicKey to match docs
83
+ */
84
+ importWalletKey(key) {
85
+ this.setWalletEncryptionPublicKey(key);
86
+ }
87
+ /**
88
+ * Step 1: Connect to the wallet
89
+ */
90
+ connect() {
91
+ const params = new URLSearchParams({
92
+ request: "connect",
93
+ app_url: this.config.appUrl || "",
94
+ app_name: this.config.appName || "",
95
+ dapp_encryption_public_key: bs582.encode(this.sessionKeyPair.publicKey),
96
+ redirect_link: this.config.redirectLink || ""
97
+ });
98
+ if (this.config.appIcon) {
99
+ params.append("app_icon", this.config.appIcon);
100
+ }
101
+ if (this.config.network) {
102
+ params.append("network", this.config.network);
103
+ }
104
+ if (this.config.appDescription) {
105
+ params.append("app_description", this.config.appDescription);
106
+ }
107
+ const scheme = this.config.walletScheme || "column";
108
+ const base = scheme.includes("://") ? scheme : `${scheme}://`;
109
+ return `${base}${base.endsWith("/") ? "" : "/"}?${params.toString()}`;
110
+ }
111
+ /**
112
+ * Step 2: Sign and Submit a transaction
113
+ */
114
+ signAndSubmitTransaction(transaction) {
115
+ if (!this.walletEncryptionPublicKey) {
116
+ throw new Error("Wallet not connected. Call connect() first and store the wallet_encryption_public_key.");
117
+ }
118
+ const payload = {
119
+ transaction
120
+ };
121
+ const encrypted = ColumnCrypto.encryptPayload(
122
+ payload,
123
+ this.walletEncryptionPublicKey,
124
+ this.sessionKeyPair.secretKey
125
+ );
126
+ const params = new URLSearchParams({
127
+ request: "signAndSubmitTransaction",
128
+ app_url: this.config.appUrl || "",
129
+ app_name: this.config.appName || "",
130
+ dapp_encryption_public_key: bs582.encode(this.sessionKeyPair.publicKey),
131
+ data: encrypted.data,
132
+ nonce: encrypted.nonce,
133
+ redirect_link: this.config.redirectLink || ""
134
+ });
135
+ if (this.config.appIcon) {
136
+ params.append("app_icon", this.config.appIcon);
137
+ }
138
+ if (this.config.network) {
139
+ params.append("network", this.config.network);
140
+ }
141
+ if (this.config.appDescription) {
142
+ params.append("app_description", this.config.appDescription);
143
+ }
144
+ const scheme = this.config.walletScheme || "column";
145
+ const base = scheme.includes("://") ? scheme : `${scheme}://`;
146
+ return `${base}${base.endsWith("/") ? "" : "/"}?${params.toString()}`;
147
+ }
148
+ /**
149
+ * Step 3: Sign a simple message
150
+ */
151
+ signMessage(message) {
152
+ if (!this.walletEncryptionPublicKey) {
153
+ throw new Error("Wallet not connected.");
154
+ }
155
+ const payload = {
156
+ message
157
+ };
158
+ const encrypted = ColumnCrypto.encryptPayload(
159
+ payload,
160
+ this.walletEncryptionPublicKey,
161
+ this.sessionKeyPair.secretKey
162
+ );
163
+ const params = new URLSearchParams({
164
+ request: "signMessage",
165
+ app_url: this.config.appUrl || "",
166
+ app_name: this.config.appName || "",
167
+ dapp_encryption_public_key: bs582.encode(this.sessionKeyPair.publicKey),
168
+ data: encrypted.data,
169
+ nonce: encrypted.nonce,
170
+ redirect_link: this.config.redirectLink || ""
171
+ });
172
+ if (this.config.appIcon) {
173
+ params.append("app_icon", this.config.appIcon);
174
+ }
175
+ if (this.config.network) {
176
+ params.append("network", this.config.network);
177
+ }
178
+ if (this.config.appDescription) {
179
+ params.append("app_description", this.config.appDescription);
180
+ }
181
+ const scheme = this.config.walletScheme || "column";
182
+ const base = scheme.includes("://") ? scheme : `${scheme}://`;
183
+ return `${base}${base.endsWith("/") ? "" : "/"}?${params.toString()}`;
184
+ }
185
+ /**
186
+ * Utility: Parse the response from Column
187
+ */
188
+ handleResponse(url) {
189
+ let params;
190
+ try {
191
+ const urlObj = new URL(url.includes("://") ? url : `http://localhost/${url}`);
192
+ const hashStr = urlObj.hash.substring(1);
193
+ const searchStr = urlObj.search.substring(1);
194
+ if (hashStr) {
195
+ params = new URLSearchParams(hashStr);
196
+ } else {
197
+ params = new URLSearchParams(searchStr);
198
+ }
199
+ } catch (e) {
200
+ const hashMatch = url.match(/#(.*)$/);
201
+ const queryMatch = url.match(/\?(.*)$/);
202
+ params = new URLSearchParams(hashMatch ? hashMatch[1] : queryMatch ? queryMatch[1] : url);
203
+ }
204
+ const data = params.get("data");
205
+ const nonce = params.get("nonce");
206
+ const error = params.get("error");
207
+ if (error) {
208
+ throw new Error(`Column Wallet Error: ${error}`);
209
+ }
210
+ if (data && nonce && this.walletEncryptionPublicKey) {
211
+ const decrypted = ColumnCrypto.decryptPayload(
212
+ data,
213
+ nonce,
214
+ this.walletEncryptionPublicKey,
215
+ this.sessionKeyPair.secretKey
216
+ );
217
+ if (!decrypted) {
218
+ throw new Error("Column Wallet Error: Decryption failed. The response may have been tampered with or keys do not match.");
219
+ }
220
+ return {
221
+ ...decrypted,
222
+ network: params.get("network")
223
+ // Always include network from URL
224
+ };
225
+ }
226
+ const response = {};
227
+ params.forEach((value, key) => {
228
+ response[key] = value;
229
+ });
230
+ if (response.column_encryption_public_key) {
231
+ this.walletEncryptionPublicKey = response.column_encryption_public_key;
232
+ }
233
+ return response;
234
+ }
235
+ /**
236
+ * Sets the wallet's public key if known from a previous session
237
+ */
238
+ setWalletPublicKey(publicKeyB58) {
239
+ this.walletEncryptionPublicKey = publicKeyB58;
38
240
  }
39
241
  };
40
242
 
@@ -173,7 +375,7 @@ var ColumnConnectModal = class {
173
375
  document.head.appendChild(styleTag);
174
376
  }
175
377
  open() {
176
- if (typeof document === "undefined") return;
378
+ if (typeof document === "undefined" || this.overlay) return;
177
379
  this.overlay = document.createElement("div");
178
380
  this.overlay.className = "column-modal-overlay";
179
381
  this.overlay.innerHTML = `
@@ -220,217 +422,30 @@ var ColumnConnectModal = class {
220
422
  }
221
423
  };
222
424
 
223
- // src/ColumnWalletSDK.ts
224
- var ColumnWalletSDK = class {
225
- config;
226
- sessionKeyPair;
227
- walletEncryptionPublicKey = null;
425
+ // src/ui/ColumnWalletWeb.ts
426
+ var ColumnWalletWeb = class _ColumnWalletWeb extends ColumnWalletSDK {
228
427
  modal;
229
428
  constructor(config) {
230
- this.config = this.autoDetectMetadata(config);
231
- this.sessionKeyPair = ColumnCrypto.generateKeyPair();
429
+ const detectedConfig = _ColumnWalletWeb.autoDetectMetadata(config);
430
+ super(detectedConfig);
232
431
  this.modal = new ColumnConnectModal(() => {
233
432
  window.location.href = this.connect();
234
433
  });
235
434
  }
236
- autoDetectMetadata(config) {
435
+ static autoDetectMetadata(config) {
237
436
  if (typeof document === "undefined") return config;
238
437
  const detectedConfig = { ...config };
239
- if (!detectedConfig.appUrl) {
240
- detectedConfig.appUrl = window.location.origin;
241
- }
242
- if (!detectedConfig.appName) {
243
- detectedConfig.appName = document.title || "Unknown dApp";
244
- }
245
- if (!detectedConfig.appIcon) {
246
- const iconLink = document.querySelector('link[rel~="icon"]') || document.querySelector('link[rel="apple-touch-icon"]');
247
- if (iconLink) {
248
- try {
249
- detectedConfig.appIcon = new URL(iconLink.getAttribute("href") || "", window.location.origin).href;
250
- } catch (e) {
251
- detectedConfig.appIcon = iconLink.href;
252
- }
253
- }
254
- }
255
- if (!detectedConfig.appDescription) {
256
- const metaDescription = document.querySelector('meta[name="description"]') || document.querySelector('meta[property="og:description"]');
257
- if (metaDescription?.content) {
258
- detectedConfig.appDescription = metaDescription.content;
259
- }
260
- }
261
- if (!detectedConfig.redirectLink) {
262
- detectedConfig.redirectLink = window.location.href.split("?")[0];
263
- }
438
+ if (!detectedConfig.appUrl) detectedConfig.appUrl = window.location.origin;
439
+ if (!detectedConfig.appName) detectedConfig.appName = document.title || "Unknown dApp";
440
+ if (!detectedConfig.redirectLink) detectedConfig.redirectLink = window.location.href.split("?")[0];
264
441
  return detectedConfig;
265
442
  }
266
- /**
267
- * Opens the premium Column selection modal
268
- */
269
443
  openConnectModal() {
270
444
  this.modal.open();
271
445
  }
272
- setWalletEncryptionPublicKey(key) {
273
- this.walletEncryptionPublicKey = key;
274
- }
275
- /**
276
- * Alias for setWalletEncryptionPublicKey to match docs
277
- */
278
- importWalletKey(key) {
279
- this.setWalletEncryptionPublicKey(key);
280
- }
281
- /**
282
- * Step 1: Connect to the wallet
283
- */
284
- connect() {
285
- const params = new URLSearchParams({
286
- request: "connect",
287
- app_url: this.config.appUrl || "",
288
- app_name: this.config.appName || "",
289
- dapp_encryption_public_key: bs582.encode(this.sessionKeyPair.publicKey),
290
- redirect_link: this.config.redirectLink || ""
291
- });
292
- if (this.config.appIcon) {
293
- params.append("app_icon", this.config.appIcon);
294
- }
295
- if (this.config.network) {
296
- params.append("network", this.config.network);
297
- }
298
- if (this.config.appDescription) {
299
- params.append("app_description", this.config.appDescription);
300
- }
301
- const scheme = this.config.walletScheme || "column";
302
- const base = scheme.includes("://") ? scheme : `${scheme}://`;
303
- return `${base}${base.endsWith("/") ? "" : "/"}?${params.toString()}`;
304
- }
305
- /**
306
- * Step 2: Sign and Submit a transaction
307
- */
308
- signAndSubmitTransaction(transaction) {
309
- if (!this.walletEncryptionPublicKey) {
310
- throw new Error("Wallet not connected. Call connect() first and store the wallet_encryption_public_key.");
311
- }
312
- const payload = {
313
- transaction
314
- };
315
- const encrypted = ColumnCrypto.encryptPayload(
316
- payload,
317
- this.walletEncryptionPublicKey,
318
- this.sessionKeyPair.secretKey
319
- );
320
- const params = new URLSearchParams({
321
- request: "signAndSubmitTransaction",
322
- app_url: this.config.appUrl || "",
323
- app_name: this.config.appName || "",
324
- dapp_encryption_public_key: bs582.encode(this.sessionKeyPair.publicKey),
325
- data: encrypted.data,
326
- nonce: encrypted.nonce,
327
- redirect_link: this.config.redirectLink || ""
328
- });
329
- if (this.config.appIcon) {
330
- params.append("app_icon", this.config.appIcon);
331
- }
332
- if (this.config.network) {
333
- params.append("network", this.config.network);
334
- }
335
- if (this.config.appDescription) {
336
- params.append("app_description", this.config.appDescription);
337
- }
338
- const scheme = this.config.walletScheme || "column";
339
- const base = scheme.includes("://") ? scheme : `${scheme}://`;
340
- return `${base}${base.endsWith("/") ? "" : "/"}?${params.toString()}`;
341
- }
342
- /**
343
- * Step 3: Sign a simple message
344
- */
345
- signMessage(message) {
346
- if (!this.walletEncryptionPublicKey) {
347
- throw new Error("Wallet not connected.");
348
- }
349
- const payload = {
350
- message
351
- };
352
- const encrypted = ColumnCrypto.encryptPayload(
353
- payload,
354
- this.walletEncryptionPublicKey,
355
- this.sessionKeyPair.secretKey
356
- );
357
- const params = new URLSearchParams({
358
- request: "signMessage",
359
- app_url: this.config.appUrl || "",
360
- app_name: this.config.appName || "",
361
- dapp_encryption_public_key: bs582.encode(this.sessionKeyPair.publicKey),
362
- data: encrypted.data,
363
- nonce: encrypted.nonce,
364
- redirect_link: this.config.redirectLink || ""
365
- });
366
- if (this.config.appIcon) {
367
- params.append("app_icon", this.config.appIcon);
368
- }
369
- if (this.config.network) {
370
- params.append("network", this.config.network);
371
- }
372
- if (this.config.appDescription) {
373
- params.append("app_description", this.config.appDescription);
374
- }
375
- const scheme = this.config.walletScheme || "column";
376
- const base = scheme.includes("://") ? scheme : `${scheme}://`;
377
- return `${base}${base.endsWith("/") ? "" : "/"}?${params.toString()}`;
378
- }
379
- /**
380
- * Utility: Parse the response from Column
381
- */
382
- handleResponse(url) {
383
- let params;
384
- try {
385
- const urlObj = new URL(url.includes("://") ? url : `http://localhost/${url}`);
386
- const hashStr = urlObj.hash.substring(1);
387
- const searchStr = urlObj.search.substring(1);
388
- if (hashStr) {
389
- params = new URLSearchParams(hashStr);
390
- } else {
391
- params = new URLSearchParams(searchStr);
392
- }
393
- } catch (e) {
394
- const hashMatch = url.match(/#(.*)$/);
395
- const queryMatch = url.match(/\?(.*)$/);
396
- params = new URLSearchParams(hashMatch ? hashMatch[1] : queryMatch ? queryMatch[1] : url);
397
- }
398
- const data = params.get("data");
399
- const nonce = params.get("nonce");
400
- const error = params.get("error");
401
- if (error) {
402
- throw new Error(`Column Wallet Error: ${error}`);
403
- }
404
- if (data && nonce && this.walletEncryptionPublicKey) {
405
- const decrypted = ColumnCrypto.decryptPayload(
406
- data,
407
- nonce,
408
- this.walletEncryptionPublicKey,
409
- this.sessionKeyPair.secretKey
410
- );
411
- return {
412
- ...decrypted,
413
- network: params.get("network")
414
- // Always include network from URL
415
- };
416
- }
417
- const response = {};
418
- params.forEach((value, key) => {
419
- response[key] = value;
420
- });
421
- if (response.column_encryption_public_key) {
422
- this.walletEncryptionPublicKey = response.column_encryption_public_key;
423
- }
424
- return response;
425
- }
426
- /**
427
- * Sets the wallet's public key if known from a previous session
428
- */
429
- setWalletPublicKey(publicKeyB58) {
430
- this.walletEncryptionPublicKey = publicKeyB58;
431
- }
432
446
  };
433
447
  export {
434
448
  ColumnCrypto,
435
- ColumnWalletSDK
449
+ ColumnWalletSDK,
450
+ ColumnWalletWeb
436
451
  };
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@column-org/wallet-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Column Wallet Mobile Deep-Link SDK for Movement/Aptos ecosystem",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
+ "react-native": "src/index.native.ts",
7
8
  "types": "dist/index.d.ts",
8
9
  "files": [
9
- "dist"
10
+ "dist",
11
+ "src"
10
12
  ],
11
13
  "publishConfig": {
12
14
  "access": "public"
@@ -30,6 +32,7 @@
30
32
  "dependencies": {
31
33
  "bs58": "^6.0.0",
32
34
  "buffer": "^6.0.3",
35
+ "react-native-get-random-values": "^1.11.0",
33
36
  "tweetnacl": "^1.0.3"
34
37
  },
35
38
  "devDependencies": {
@@ -0,0 +1,69 @@
1
+ import * as nacl from 'tweetnacl'
2
+ import bs58 from 'bs58'
3
+ import { Buffer } from 'buffer'
4
+
5
+ export interface ColumnSDKConfig {
6
+ appUrl?: string
7
+ appName?: string
8
+ redirectLink?: string
9
+ appIcon?: string
10
+ appDescription?: string
11
+ walletScheme?: string
12
+ network?: string
13
+ sessionSecretKey?: string
14
+ }
15
+
16
+ export class ColumnCrypto {
17
+ static generateKeyPair(): nacl.BoxKeyPair {
18
+ return nacl.box.keyPair()
19
+ }
20
+
21
+ static encryptPayload(
22
+ payload: any,
23
+ targetPublicKeyB58: string,
24
+ mySecretKey: Uint8Array
25
+ ): { data: string; nonce: string } {
26
+ const sharedSecret = nacl.box.before(
27
+ bs58.decode(targetPublicKeyB58),
28
+ mySecretKey
29
+ )
30
+
31
+ const nonce = nacl.randomBytes(nacl.box.nonceLength)
32
+ const message = Buffer.from(JSON.stringify(payload), 'utf8')
33
+
34
+ const encrypted = nacl.box.after(message, nonce, sharedSecret)
35
+
36
+ return {
37
+ data: bs58.encode(encrypted),
38
+ nonce: bs58.encode(nonce)
39
+ }
40
+ }
41
+
42
+ static decryptPayload(
43
+ encryptedDataB58: string,
44
+ nonceB58: string,
45
+ sourcePublicKeyB58: string,
46
+ mySecretKey: Uint8Array
47
+ ): any {
48
+ const sharedSecret = nacl.box.before(
49
+ bs58.decode(sourcePublicKeyB58),
50
+ mySecretKey
51
+ )
52
+
53
+ const decrypted = nacl.box.open.after(
54
+ bs58.decode(encryptedDataB58),
55
+ bs58.decode(nonceB58),
56
+ sharedSecret
57
+ )
58
+
59
+ if (!decrypted) return null
60
+
61
+ try {
62
+ const jsonString = Buffer.from(decrypted).toString('utf8')
63
+ return JSON.parse(jsonString)
64
+ } catch (e) {
65
+ console.error('ColumnCrypto: Failed to parse decrypted payload', e)
66
+ return null
67
+ }
68
+ }
69
+ }