@column-org/wallet-sdk 1.0.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/README.md +84 -0
- package/dist/index.d.mts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +474 -0
- package/dist/index.mjs +436 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @column-org/wallet-sdk
|
|
2
|
+
|
|
3
|
+
The official SDK for integrating Column Wallet's deep-linking protocol into your Web3 applications on the Movement/Aptos ecosystem.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Direct Deep-Linking**: Low-latency communication between dApps and Column Wallet.
|
|
8
|
+
- **End-to-End Encryption**: Secure handshakes using X25519 and TweetNaCl.
|
|
9
|
+
- **Environment Agnostic**: Works seamlessly for both mobile websites and native applications.
|
|
10
|
+
- **Aptos Compatible**: Built-in support for BCS transaction payloads and message signing.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @column-org/wallet-sdk
|
|
16
|
+
# or
|
|
17
|
+
yarn add @column-org/wallet-sdk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Initialize the SDK
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { ColumnWalletSDK } from '@column-org/wallet-sdk';
|
|
26
|
+
|
|
27
|
+
const sdk = new ColumnWalletSDK({
|
|
28
|
+
appName: 'My Awesome DApp',
|
|
29
|
+
appUrl: 'https://mydapp.io',
|
|
30
|
+
redirectLink: 'https://mydapp.io/callback', // Use custom scheme for native apps
|
|
31
|
+
appIcon: 'https://mydapp.io/logo.png'
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Connect Wallet
|
|
36
|
+
|
|
37
|
+
You can either generate a link manually or use the **built-in premium modal**:
|
|
38
|
+
|
|
39
|
+
#### Method A: Using the built-in Modal (Recommended)
|
|
40
|
+
```typescript
|
|
41
|
+
// This will open a glassmorphic selection modal
|
|
42
|
+
sdk.openConnectModal();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
#### Method B: Manual Redirect
|
|
46
|
+
```typescript
|
|
47
|
+
// Generate the connection URL
|
|
48
|
+
const connectUrl = sdk.connect();
|
|
49
|
+
|
|
50
|
+
// Redirect the user to Column Wallet
|
|
51
|
+
window.location.href = connectUrl;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Handle the Response
|
|
55
|
+
|
|
56
|
+
After approval, Column redirects back to your `redirectLink`.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// On your callback page
|
|
60
|
+
const response = sdk.handleResponse(window.location.href);
|
|
61
|
+
|
|
62
|
+
if (response.address) {
|
|
63
|
+
console.log('Connected to:', response.address);
|
|
64
|
+
// Store the wallet's encryption public key for future signing requests
|
|
65
|
+
localStorage.setItem('wallet_pk', response.column_encryption_public_key);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 4. Sign and Submit Transaction
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
sdk.setWalletPublicKey(localStorage.getItem('wallet_pk'));
|
|
73
|
+
|
|
74
|
+
const txUrl = sdk.signAndSubmitTransaction(base58TransactionPayload);
|
|
75
|
+
window.location.href = txUrl;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Security
|
|
79
|
+
|
|
80
|
+
Column SDK uses the **Nacl protocol** for authenticated encryption. Every request generates an ephemeral session key pair, ensuring that your user's transactions are private and secure from transit-level interceptions.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as nacl from 'tweetnacl';
|
|
2
|
+
|
|
3
|
+
interface ColumnSDKConfig {
|
|
4
|
+
appUrl?: string;
|
|
5
|
+
appName?: string;
|
|
6
|
+
redirectLink?: string;
|
|
7
|
+
appIcon?: string;
|
|
8
|
+
appDescription?: string;
|
|
9
|
+
walletScheme?: string;
|
|
10
|
+
network?: string;
|
|
11
|
+
}
|
|
12
|
+
declare class ColumnCrypto {
|
|
13
|
+
static generateKeyPair(): nacl.BoxKeyPair;
|
|
14
|
+
static encryptPayload(payload: any, targetPublicKeyB58: string, mySecretKey: Uint8Array): {
|
|
15
|
+
data: string;
|
|
16
|
+
nonce: string;
|
|
17
|
+
};
|
|
18
|
+
static decryptPayload(encryptedDataB58: string, nonceB58: string, sourcePublicKeyB58: string, mySecretKey: Uint8Array): any;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare class ColumnWalletSDK {
|
|
22
|
+
private config;
|
|
23
|
+
private sessionKeyPair;
|
|
24
|
+
private walletEncryptionPublicKey;
|
|
25
|
+
private modal;
|
|
26
|
+
constructor(config: ColumnSDKConfig);
|
|
27
|
+
private autoDetectMetadata;
|
|
28
|
+
/**
|
|
29
|
+
* Opens the premium Column selection modal
|
|
30
|
+
*/
|
|
31
|
+
openConnectModal(): void;
|
|
32
|
+
setWalletEncryptionPublicKey(key: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Alias for setWalletEncryptionPublicKey to match docs
|
|
35
|
+
*/
|
|
36
|
+
importWalletKey(key: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Step 1: Connect to the wallet
|
|
39
|
+
*/
|
|
40
|
+
connect(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Step 2: Sign and Submit a transaction
|
|
43
|
+
*/
|
|
44
|
+
signAndSubmitTransaction(transaction: any): string;
|
|
45
|
+
/**
|
|
46
|
+
* Step 3: Sign a simple message
|
|
47
|
+
*/
|
|
48
|
+
signMessage(message: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Utility: Parse the response from Column
|
|
51
|
+
*/
|
|
52
|
+
handleResponse(url: string): any;
|
|
53
|
+
/**
|
|
54
|
+
* Sets the wallet's public key if known from a previous session
|
|
55
|
+
*/
|
|
56
|
+
setWalletPublicKey(publicKeyB58: string): void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { ColumnCrypto, type ColumnSDKConfig, ColumnWalletSDK };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as nacl from 'tweetnacl';
|
|
2
|
+
|
|
3
|
+
interface ColumnSDKConfig {
|
|
4
|
+
appUrl?: string;
|
|
5
|
+
appName?: string;
|
|
6
|
+
redirectLink?: string;
|
|
7
|
+
appIcon?: string;
|
|
8
|
+
appDescription?: string;
|
|
9
|
+
walletScheme?: string;
|
|
10
|
+
network?: string;
|
|
11
|
+
}
|
|
12
|
+
declare class ColumnCrypto {
|
|
13
|
+
static generateKeyPair(): nacl.BoxKeyPair;
|
|
14
|
+
static encryptPayload(payload: any, targetPublicKeyB58: string, mySecretKey: Uint8Array): {
|
|
15
|
+
data: string;
|
|
16
|
+
nonce: string;
|
|
17
|
+
};
|
|
18
|
+
static decryptPayload(encryptedDataB58: string, nonceB58: string, sourcePublicKeyB58: string, mySecretKey: Uint8Array): any;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare class ColumnWalletSDK {
|
|
22
|
+
private config;
|
|
23
|
+
private sessionKeyPair;
|
|
24
|
+
private walletEncryptionPublicKey;
|
|
25
|
+
private modal;
|
|
26
|
+
constructor(config: ColumnSDKConfig);
|
|
27
|
+
private autoDetectMetadata;
|
|
28
|
+
/**
|
|
29
|
+
* Opens the premium Column selection modal
|
|
30
|
+
*/
|
|
31
|
+
openConnectModal(): void;
|
|
32
|
+
setWalletEncryptionPublicKey(key: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Alias for setWalletEncryptionPublicKey to match docs
|
|
35
|
+
*/
|
|
36
|
+
importWalletKey(key: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Step 1: Connect to the wallet
|
|
39
|
+
*/
|
|
40
|
+
connect(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Step 2: Sign and Submit a transaction
|
|
43
|
+
*/
|
|
44
|
+
signAndSubmitTransaction(transaction: any): string;
|
|
45
|
+
/**
|
|
46
|
+
* Step 3: Sign a simple message
|
|
47
|
+
*/
|
|
48
|
+
signMessage(message: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Utility: Parse the response from Column
|
|
51
|
+
*/
|
|
52
|
+
handleResponse(url: string): any;
|
|
53
|
+
/**
|
|
54
|
+
* Sets the wallet's public key if known from a previous session
|
|
55
|
+
*/
|
|
56
|
+
setWalletPublicKey(publicKeyB58: string): void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { ColumnCrypto, type ColumnSDKConfig, ColumnWalletSDK };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ColumnCrypto: () => ColumnCrypto,
|
|
34
|
+
ColumnWalletSDK: () => ColumnWalletSDK
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/ColumnWalletSDK.ts
|
|
39
|
+
var import_bs582 = __toESM(require("bs58"));
|
|
40
|
+
|
|
41
|
+
// src/ColumnShared.ts
|
|
42
|
+
var nacl = __toESM(require("tweetnacl"));
|
|
43
|
+
var import_bs58 = __toESM(require("bs58"));
|
|
44
|
+
var import_buffer = require("buffer");
|
|
45
|
+
var ColumnCrypto = class {
|
|
46
|
+
static generateKeyPair() {
|
|
47
|
+
return nacl.box.keyPair();
|
|
48
|
+
}
|
|
49
|
+
static encryptPayload(payload, targetPublicKeyB58, mySecretKey) {
|
|
50
|
+
const sharedSecret = nacl.box.before(
|
|
51
|
+
import_bs58.default.decode(targetPublicKeyB58),
|
|
52
|
+
mySecretKey
|
|
53
|
+
);
|
|
54
|
+
const nonce = nacl.randomBytes(nacl.box.nonceLength);
|
|
55
|
+
const message = import_buffer.Buffer.from(JSON.stringify(payload), "utf8");
|
|
56
|
+
const encrypted = nacl.box.after(message, nonce, sharedSecret);
|
|
57
|
+
return {
|
|
58
|
+
data: import_bs58.default.encode(encrypted),
|
|
59
|
+
nonce: import_bs58.default.encode(nonce)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
static decryptPayload(encryptedDataB58, nonceB58, sourcePublicKeyB58, mySecretKey) {
|
|
63
|
+
const sharedSecret = nacl.box.before(
|
|
64
|
+
import_bs58.default.decode(sourcePublicKeyB58),
|
|
65
|
+
mySecretKey
|
|
66
|
+
);
|
|
67
|
+
const decrypted = nacl.box.open.after(
|
|
68
|
+
import_bs58.default.decode(encryptedDataB58),
|
|
69
|
+
import_bs58.default.decode(nonceB58),
|
|
70
|
+
sharedSecret
|
|
71
|
+
);
|
|
72
|
+
if (!decrypted) return null;
|
|
73
|
+
const jsonString = import_buffer.Buffer.from(decrypted).toString("utf8");
|
|
74
|
+
return JSON.parse(jsonString);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/ui/ColumnConnectModal.ts
|
|
79
|
+
var ColumnModalStyles = `
|
|
80
|
+
.column-modal-overlay {
|
|
81
|
+
position: fixed;
|
|
82
|
+
top: 0;
|
|
83
|
+
left: 0;
|
|
84
|
+
width: 100%;
|
|
85
|
+
height: 100%;
|
|
86
|
+
background: rgba(0, 0, 0, 0.75);
|
|
87
|
+
backdrop-filter: blur(8px);
|
|
88
|
+
-webkit-backdrop-filter: blur(8px);
|
|
89
|
+
display: flex;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
align-items: center;
|
|
92
|
+
z-index: 999999;
|
|
93
|
+
opacity: 0;
|
|
94
|
+
transition: opacity 0.3s ease;
|
|
95
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.column-modal-overlay.active {
|
|
99
|
+
opacity: 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.column-modal-card {
|
|
103
|
+
background: #1C1F2E;
|
|
104
|
+
width: 100%;
|
|
105
|
+
max-width: 420px;
|
|
106
|
+
border-radius: 20px;
|
|
107
|
+
padding: 28px;
|
|
108
|
+
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
|
|
109
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
110
|
+
transform: scale(0.95);
|
|
111
|
+
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
112
|
+
position: relative;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.column-modal-overlay.active .column-modal-card {
|
|
116
|
+
transform: scale(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.column-modal-close {
|
|
120
|
+
position: absolute;
|
|
121
|
+
top: 16px;
|
|
122
|
+
right: 16px;
|
|
123
|
+
background: transparent;
|
|
124
|
+
border: none;
|
|
125
|
+
color: #8B98A5;
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
font-size: 28px;
|
|
128
|
+
padding: 4px;
|
|
129
|
+
line-height: 1;
|
|
130
|
+
transition: color 0.2s ease;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.column-modal-close:hover {
|
|
134
|
+
color: #fff;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.column-modal-title {
|
|
138
|
+
color: #fff;
|
|
139
|
+
font-size: 20px;
|
|
140
|
+
font-weight: 600;
|
|
141
|
+
margin: 0 0 24px;
|
|
142
|
+
text-align: center;
|
|
143
|
+
line-height: 1.4;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.column-modal-wallet-option {
|
|
147
|
+
background: #252938;
|
|
148
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
149
|
+
border-radius: 14px;
|
|
150
|
+
padding: 16px 20px;
|
|
151
|
+
width: 100%;
|
|
152
|
+
display: flex;
|
|
153
|
+
justify-content: space-between;
|
|
154
|
+
align-items: center;
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
transition: all 0.2s ease;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.column-modal-wallet-option:hover {
|
|
160
|
+
background: #2D3142;
|
|
161
|
+
border-color: rgba(255, 218, 52, 0.3);
|
|
162
|
+
transform: translateY(-2px);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.column-modal-wallet-left {
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
gap: 14px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.column-modal-wallet-icon {
|
|
172
|
+
width: 40px;
|
|
173
|
+
height: 40px;
|
|
174
|
+
background: rgba(255, 218, 52, 0.12);
|
|
175
|
+
border-radius: 10px;
|
|
176
|
+
display: flex;
|
|
177
|
+
justify-content: center;
|
|
178
|
+
align-items: center;
|
|
179
|
+
flex-shrink: 0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.column-modal-wallet-name {
|
|
183
|
+
color: #fff;
|
|
184
|
+
font-size: 16px;
|
|
185
|
+
font-weight: 600;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.column-modal-wallet-badge {
|
|
189
|
+
background: rgba(76, 175, 80, 0.15);
|
|
190
|
+
color: #4CAF50;
|
|
191
|
+
font-size: 13px;
|
|
192
|
+
font-weight: 600;
|
|
193
|
+
padding: 6px 12px;
|
|
194
|
+
border-radius: 8px;
|
|
195
|
+
}
|
|
196
|
+
`;
|
|
197
|
+
var ColumnConnectModal = class {
|
|
198
|
+
overlay = null;
|
|
199
|
+
onConnect;
|
|
200
|
+
constructor(onConnect) {
|
|
201
|
+
this.onConnect = onConnect;
|
|
202
|
+
this.injectStyles();
|
|
203
|
+
}
|
|
204
|
+
injectStyles() {
|
|
205
|
+
if (typeof document === "undefined") return;
|
|
206
|
+
if (document.getElementById("column-modal-styles")) return;
|
|
207
|
+
const styleTag = document.createElement("style");
|
|
208
|
+
styleTag.id = "column-modal-styles";
|
|
209
|
+
styleTag.innerHTML = ColumnModalStyles;
|
|
210
|
+
document.head.appendChild(styleTag);
|
|
211
|
+
}
|
|
212
|
+
open() {
|
|
213
|
+
if (typeof document === "undefined") return;
|
|
214
|
+
this.overlay = document.createElement("div");
|
|
215
|
+
this.overlay.className = "column-modal-overlay";
|
|
216
|
+
this.overlay.innerHTML = `
|
|
217
|
+
<div class="column-modal-card">
|
|
218
|
+
<button class="column-modal-close" id="column-modal-close">×</button>
|
|
219
|
+
<h2 class="column-modal-title">Connect a wallet on<br>Movement to continue</h2>
|
|
220
|
+
|
|
221
|
+
<button class="column-modal-wallet-option" id="column-modal-connect-btn">
|
|
222
|
+
<div class="column-modal-wallet-left">
|
|
223
|
+
<div class="column-modal-wallet-icon">
|
|
224
|
+
<svg width="28" height="28" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
225
|
+
<path d="M16 4L28 28H4L16 4Z" stroke="#ffda34" stroke-width="2.5" stroke-linejoin="round"/>
|
|
226
|
+
<circle cx="16" cy="18" r="4" fill="#ffda34"/>
|
|
227
|
+
</svg>
|
|
228
|
+
</div>
|
|
229
|
+
<span class="column-modal-wallet-name">Column Wallet</span>
|
|
230
|
+
</div>
|
|
231
|
+
<span class="column-modal-wallet-badge">Mobile</span>
|
|
232
|
+
</button>
|
|
233
|
+
</div>
|
|
234
|
+
`;
|
|
235
|
+
document.body.appendChild(this.overlay);
|
|
236
|
+
setTimeout(() => {
|
|
237
|
+
this.overlay?.classList.add("active");
|
|
238
|
+
}, 10);
|
|
239
|
+
document.getElementById("column-modal-close")?.addEventListener("click", () => this.close());
|
|
240
|
+
document.getElementById("column-modal-connect-btn")?.addEventListener("click", () => {
|
|
241
|
+
this.onConnect();
|
|
242
|
+
this.close();
|
|
243
|
+
});
|
|
244
|
+
this.overlay.addEventListener("click", (e) => {
|
|
245
|
+
if (e.target === this.overlay) this.close();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
close() {
|
|
249
|
+
if (!this.overlay) return;
|
|
250
|
+
this.overlay.classList.remove("active");
|
|
251
|
+
setTimeout(() => {
|
|
252
|
+
if (this.overlay && this.overlay.parentNode) {
|
|
253
|
+
this.overlay.parentNode.removeChild(this.overlay);
|
|
254
|
+
}
|
|
255
|
+
this.overlay = null;
|
|
256
|
+
}, 300);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// src/ColumnWalletSDK.ts
|
|
261
|
+
var ColumnWalletSDK = class {
|
|
262
|
+
config;
|
|
263
|
+
sessionKeyPair;
|
|
264
|
+
walletEncryptionPublicKey = null;
|
|
265
|
+
modal;
|
|
266
|
+
constructor(config) {
|
|
267
|
+
this.config = this.autoDetectMetadata(config);
|
|
268
|
+
this.sessionKeyPair = ColumnCrypto.generateKeyPair();
|
|
269
|
+
this.modal = new ColumnConnectModal(() => {
|
|
270
|
+
window.location.href = this.connect();
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
autoDetectMetadata(config) {
|
|
274
|
+
if (typeof document === "undefined") return config;
|
|
275
|
+
const detectedConfig = { ...config };
|
|
276
|
+
if (!detectedConfig.appUrl) {
|
|
277
|
+
detectedConfig.appUrl = window.location.origin;
|
|
278
|
+
}
|
|
279
|
+
if (!detectedConfig.appName) {
|
|
280
|
+
detectedConfig.appName = document.title || "Unknown dApp";
|
|
281
|
+
}
|
|
282
|
+
if (!detectedConfig.appIcon) {
|
|
283
|
+
const iconLink = document.querySelector('link[rel~="icon"]') || document.querySelector('link[rel="apple-touch-icon"]');
|
|
284
|
+
if (iconLink) {
|
|
285
|
+
try {
|
|
286
|
+
detectedConfig.appIcon = new URL(iconLink.getAttribute("href") || "", window.location.origin).href;
|
|
287
|
+
} catch (e) {
|
|
288
|
+
detectedConfig.appIcon = iconLink.href;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (!detectedConfig.appDescription) {
|
|
293
|
+
const metaDescription = document.querySelector('meta[name="description"]') || document.querySelector('meta[property="og:description"]');
|
|
294
|
+
if (metaDescription?.content) {
|
|
295
|
+
detectedConfig.appDescription = metaDescription.content;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (!detectedConfig.redirectLink) {
|
|
299
|
+
detectedConfig.redirectLink = window.location.href.split("?")[0];
|
|
300
|
+
}
|
|
301
|
+
return detectedConfig;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Opens the premium Column selection modal
|
|
305
|
+
*/
|
|
306
|
+
openConnectModal() {
|
|
307
|
+
this.modal.open();
|
|
308
|
+
}
|
|
309
|
+
setWalletEncryptionPublicKey(key) {
|
|
310
|
+
this.walletEncryptionPublicKey = key;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Alias for setWalletEncryptionPublicKey to match docs
|
|
314
|
+
*/
|
|
315
|
+
importWalletKey(key) {
|
|
316
|
+
this.setWalletEncryptionPublicKey(key);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Step 1: Connect to the wallet
|
|
320
|
+
*/
|
|
321
|
+
connect() {
|
|
322
|
+
const params = new URLSearchParams({
|
|
323
|
+
request: "connect",
|
|
324
|
+
app_url: this.config.appUrl || "",
|
|
325
|
+
app_name: this.config.appName || "",
|
|
326
|
+
dapp_encryption_public_key: import_bs582.default.encode(this.sessionKeyPair.publicKey),
|
|
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 2: Sign and Submit a transaction
|
|
344
|
+
*/
|
|
345
|
+
signAndSubmitTransaction(transaction) {
|
|
346
|
+
if (!this.walletEncryptionPublicKey) {
|
|
347
|
+
throw new Error("Wallet not connected. Call connect() first and store the wallet_encryption_public_key.");
|
|
348
|
+
}
|
|
349
|
+
const payload = {
|
|
350
|
+
transaction
|
|
351
|
+
};
|
|
352
|
+
const encrypted = ColumnCrypto.encryptPayload(
|
|
353
|
+
payload,
|
|
354
|
+
this.walletEncryptionPublicKey,
|
|
355
|
+
this.sessionKeyPair.secretKey
|
|
356
|
+
);
|
|
357
|
+
const params = new URLSearchParams({
|
|
358
|
+
request: "signAndSubmitTransaction",
|
|
359
|
+
app_url: this.config.appUrl || "",
|
|
360
|
+
app_name: this.config.appName || "",
|
|
361
|
+
dapp_encryption_public_key: import_bs582.default.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
|
+
* Step 3: Sign a simple message
|
|
381
|
+
*/
|
|
382
|
+
signMessage(message) {
|
|
383
|
+
if (!this.walletEncryptionPublicKey) {
|
|
384
|
+
throw new Error("Wallet not connected.");
|
|
385
|
+
}
|
|
386
|
+
const payload = {
|
|
387
|
+
message
|
|
388
|
+
};
|
|
389
|
+
const encrypted = ColumnCrypto.encryptPayload(
|
|
390
|
+
payload,
|
|
391
|
+
this.walletEncryptionPublicKey,
|
|
392
|
+
this.sessionKeyPair.secretKey
|
|
393
|
+
);
|
|
394
|
+
const params = new URLSearchParams({
|
|
395
|
+
request: "signMessage",
|
|
396
|
+
app_url: this.config.appUrl || "",
|
|
397
|
+
app_name: this.config.appName || "",
|
|
398
|
+
dapp_encryption_public_key: import_bs582.default.encode(this.sessionKeyPair.publicKey),
|
|
399
|
+
data: encrypted.data,
|
|
400
|
+
nonce: encrypted.nonce,
|
|
401
|
+
redirect_link: this.config.redirectLink || ""
|
|
402
|
+
});
|
|
403
|
+
if (this.config.appIcon) {
|
|
404
|
+
params.append("app_icon", this.config.appIcon);
|
|
405
|
+
}
|
|
406
|
+
if (this.config.network) {
|
|
407
|
+
params.append("network", this.config.network);
|
|
408
|
+
}
|
|
409
|
+
if (this.config.appDescription) {
|
|
410
|
+
params.append("app_description", this.config.appDescription);
|
|
411
|
+
}
|
|
412
|
+
const scheme = this.config.walletScheme || "column";
|
|
413
|
+
const base = scheme.includes("://") ? scheme : `${scheme}://`;
|
|
414
|
+
return `${base}${base.endsWith("/") ? "" : "/"}?${params.toString()}`;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Utility: Parse the response from Column
|
|
418
|
+
*/
|
|
419
|
+
handleResponse(url) {
|
|
420
|
+
let params;
|
|
421
|
+
try {
|
|
422
|
+
const urlObj = new URL(url.includes("://") ? url : `http://localhost/${url}`);
|
|
423
|
+
const hashStr = urlObj.hash.substring(1);
|
|
424
|
+
const searchStr = urlObj.search.substring(1);
|
|
425
|
+
if (hashStr) {
|
|
426
|
+
params = new URLSearchParams(hashStr);
|
|
427
|
+
} else {
|
|
428
|
+
params = new URLSearchParams(searchStr);
|
|
429
|
+
}
|
|
430
|
+
} catch (e) {
|
|
431
|
+
const hashMatch = url.match(/#(.*)$/);
|
|
432
|
+
const queryMatch = url.match(/\?(.*)$/);
|
|
433
|
+
params = new URLSearchParams(hashMatch ? hashMatch[1] : queryMatch ? queryMatch[1] : url);
|
|
434
|
+
}
|
|
435
|
+
const data = params.get("data");
|
|
436
|
+
const nonce = params.get("nonce");
|
|
437
|
+
const error = params.get("error");
|
|
438
|
+
if (error) {
|
|
439
|
+
throw new Error(`Column Wallet Error: ${error}`);
|
|
440
|
+
}
|
|
441
|
+
if (data && nonce && this.walletEncryptionPublicKey) {
|
|
442
|
+
const decrypted = ColumnCrypto.decryptPayload(
|
|
443
|
+
data,
|
|
444
|
+
nonce,
|
|
445
|
+
this.walletEncryptionPublicKey,
|
|
446
|
+
this.sessionKeyPair.secretKey
|
|
447
|
+
);
|
|
448
|
+
return {
|
|
449
|
+
...decrypted,
|
|
450
|
+
network: params.get("network")
|
|
451
|
+
// Always include network from URL
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
const response = {};
|
|
455
|
+
params.forEach((value, key) => {
|
|
456
|
+
response[key] = value;
|
|
457
|
+
});
|
|
458
|
+
if (response.column_encryption_public_key) {
|
|
459
|
+
this.walletEncryptionPublicKey = response.column_encryption_public_key;
|
|
460
|
+
}
|
|
461
|
+
return response;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Sets the wallet's public key if known from a previous session
|
|
465
|
+
*/
|
|
466
|
+
setWalletPublicKey(publicKeyB58) {
|
|
467
|
+
this.walletEncryptionPublicKey = publicKeyB58;
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
471
|
+
0 && (module.exports = {
|
|
472
|
+
ColumnCrypto,
|
|
473
|
+
ColumnWalletSDK
|
|
474
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// src/ColumnWalletSDK.ts
|
|
2
|
+
import bs582 from "bs58";
|
|
3
|
+
|
|
4
|
+
// src/ColumnShared.ts
|
|
5
|
+
import * as nacl from "tweetnacl";
|
|
6
|
+
import bs58 from "bs58";
|
|
7
|
+
import { Buffer } from "buffer";
|
|
8
|
+
var ColumnCrypto = class {
|
|
9
|
+
static generateKeyPair() {
|
|
10
|
+
return nacl.box.keyPair();
|
|
11
|
+
}
|
|
12
|
+
static encryptPayload(payload, targetPublicKeyB58, mySecretKey) {
|
|
13
|
+
const sharedSecret = nacl.box.before(
|
|
14
|
+
bs58.decode(targetPublicKeyB58),
|
|
15
|
+
mySecretKey
|
|
16
|
+
);
|
|
17
|
+
const nonce = nacl.randomBytes(nacl.box.nonceLength);
|
|
18
|
+
const message = Buffer.from(JSON.stringify(payload), "utf8");
|
|
19
|
+
const encrypted = nacl.box.after(message, nonce, sharedSecret);
|
|
20
|
+
return {
|
|
21
|
+
data: bs58.encode(encrypted),
|
|
22
|
+
nonce: bs58.encode(nonce)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
static decryptPayload(encryptedDataB58, nonceB58, sourcePublicKeyB58, mySecretKey) {
|
|
26
|
+
const sharedSecret = nacl.box.before(
|
|
27
|
+
bs58.decode(sourcePublicKeyB58),
|
|
28
|
+
mySecretKey
|
|
29
|
+
);
|
|
30
|
+
const decrypted = nacl.box.open.after(
|
|
31
|
+
bs58.decode(encryptedDataB58),
|
|
32
|
+
bs58.decode(nonceB58),
|
|
33
|
+
sharedSecret
|
|
34
|
+
);
|
|
35
|
+
if (!decrypted) return null;
|
|
36
|
+
const jsonString = Buffer.from(decrypted).toString("utf8");
|
|
37
|
+
return JSON.parse(jsonString);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/ui/ColumnConnectModal.ts
|
|
42
|
+
var ColumnModalStyles = `
|
|
43
|
+
.column-modal-overlay {
|
|
44
|
+
position: fixed;
|
|
45
|
+
top: 0;
|
|
46
|
+
left: 0;
|
|
47
|
+
width: 100%;
|
|
48
|
+
height: 100%;
|
|
49
|
+
background: rgba(0, 0, 0, 0.75);
|
|
50
|
+
backdrop-filter: blur(8px);
|
|
51
|
+
-webkit-backdrop-filter: blur(8px);
|
|
52
|
+
display: flex;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
align-items: center;
|
|
55
|
+
z-index: 999999;
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transition: opacity 0.3s ease;
|
|
58
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.column-modal-overlay.active {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.column-modal-card {
|
|
66
|
+
background: #1C1F2E;
|
|
67
|
+
width: 100%;
|
|
68
|
+
max-width: 420px;
|
|
69
|
+
border-radius: 20px;
|
|
70
|
+
padding: 28px;
|
|
71
|
+
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
|
|
72
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
73
|
+
transform: scale(0.95);
|
|
74
|
+
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
75
|
+
position: relative;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.column-modal-overlay.active .column-modal-card {
|
|
79
|
+
transform: scale(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.column-modal-close {
|
|
83
|
+
position: absolute;
|
|
84
|
+
top: 16px;
|
|
85
|
+
right: 16px;
|
|
86
|
+
background: transparent;
|
|
87
|
+
border: none;
|
|
88
|
+
color: #8B98A5;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
font-size: 28px;
|
|
91
|
+
padding: 4px;
|
|
92
|
+
line-height: 1;
|
|
93
|
+
transition: color 0.2s ease;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.column-modal-close:hover {
|
|
97
|
+
color: #fff;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.column-modal-title {
|
|
101
|
+
color: #fff;
|
|
102
|
+
font-size: 20px;
|
|
103
|
+
font-weight: 600;
|
|
104
|
+
margin: 0 0 24px;
|
|
105
|
+
text-align: center;
|
|
106
|
+
line-height: 1.4;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.column-modal-wallet-option {
|
|
110
|
+
background: #252938;
|
|
111
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
112
|
+
border-radius: 14px;
|
|
113
|
+
padding: 16px 20px;
|
|
114
|
+
width: 100%;
|
|
115
|
+
display: flex;
|
|
116
|
+
justify-content: space-between;
|
|
117
|
+
align-items: center;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
transition: all 0.2s ease;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.column-modal-wallet-option:hover {
|
|
123
|
+
background: #2D3142;
|
|
124
|
+
border-color: rgba(255, 218, 52, 0.3);
|
|
125
|
+
transform: translateY(-2px);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.column-modal-wallet-left {
|
|
129
|
+
display: flex;
|
|
130
|
+
align-items: center;
|
|
131
|
+
gap: 14px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.column-modal-wallet-icon {
|
|
135
|
+
width: 40px;
|
|
136
|
+
height: 40px;
|
|
137
|
+
background: rgba(255, 218, 52, 0.12);
|
|
138
|
+
border-radius: 10px;
|
|
139
|
+
display: flex;
|
|
140
|
+
justify-content: center;
|
|
141
|
+
align-items: center;
|
|
142
|
+
flex-shrink: 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.column-modal-wallet-name {
|
|
146
|
+
color: #fff;
|
|
147
|
+
font-size: 16px;
|
|
148
|
+
font-weight: 600;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.column-modal-wallet-badge {
|
|
152
|
+
background: rgba(76, 175, 80, 0.15);
|
|
153
|
+
color: #4CAF50;
|
|
154
|
+
font-size: 13px;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
padding: 6px 12px;
|
|
157
|
+
border-radius: 8px;
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
var ColumnConnectModal = class {
|
|
161
|
+
overlay = null;
|
|
162
|
+
onConnect;
|
|
163
|
+
constructor(onConnect) {
|
|
164
|
+
this.onConnect = onConnect;
|
|
165
|
+
this.injectStyles();
|
|
166
|
+
}
|
|
167
|
+
injectStyles() {
|
|
168
|
+
if (typeof document === "undefined") return;
|
|
169
|
+
if (document.getElementById("column-modal-styles")) return;
|
|
170
|
+
const styleTag = document.createElement("style");
|
|
171
|
+
styleTag.id = "column-modal-styles";
|
|
172
|
+
styleTag.innerHTML = ColumnModalStyles;
|
|
173
|
+
document.head.appendChild(styleTag);
|
|
174
|
+
}
|
|
175
|
+
open() {
|
|
176
|
+
if (typeof document === "undefined") return;
|
|
177
|
+
this.overlay = document.createElement("div");
|
|
178
|
+
this.overlay.className = "column-modal-overlay";
|
|
179
|
+
this.overlay.innerHTML = `
|
|
180
|
+
<div class="column-modal-card">
|
|
181
|
+
<button class="column-modal-close" id="column-modal-close">×</button>
|
|
182
|
+
<h2 class="column-modal-title">Connect a wallet on<br>Movement to continue</h2>
|
|
183
|
+
|
|
184
|
+
<button class="column-modal-wallet-option" id="column-modal-connect-btn">
|
|
185
|
+
<div class="column-modal-wallet-left">
|
|
186
|
+
<div class="column-modal-wallet-icon">
|
|
187
|
+
<svg width="28" height="28" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
188
|
+
<path d="M16 4L28 28H4L16 4Z" stroke="#ffda34" stroke-width="2.5" stroke-linejoin="round"/>
|
|
189
|
+
<circle cx="16" cy="18" r="4" fill="#ffda34"/>
|
|
190
|
+
</svg>
|
|
191
|
+
</div>
|
|
192
|
+
<span class="column-modal-wallet-name">Column Wallet</span>
|
|
193
|
+
</div>
|
|
194
|
+
<span class="column-modal-wallet-badge">Mobile</span>
|
|
195
|
+
</button>
|
|
196
|
+
</div>
|
|
197
|
+
`;
|
|
198
|
+
document.body.appendChild(this.overlay);
|
|
199
|
+
setTimeout(() => {
|
|
200
|
+
this.overlay?.classList.add("active");
|
|
201
|
+
}, 10);
|
|
202
|
+
document.getElementById("column-modal-close")?.addEventListener("click", () => this.close());
|
|
203
|
+
document.getElementById("column-modal-connect-btn")?.addEventListener("click", () => {
|
|
204
|
+
this.onConnect();
|
|
205
|
+
this.close();
|
|
206
|
+
});
|
|
207
|
+
this.overlay.addEventListener("click", (e) => {
|
|
208
|
+
if (e.target === this.overlay) this.close();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
close() {
|
|
212
|
+
if (!this.overlay) return;
|
|
213
|
+
this.overlay.classList.remove("active");
|
|
214
|
+
setTimeout(() => {
|
|
215
|
+
if (this.overlay && this.overlay.parentNode) {
|
|
216
|
+
this.overlay.parentNode.removeChild(this.overlay);
|
|
217
|
+
}
|
|
218
|
+
this.overlay = null;
|
|
219
|
+
}, 300);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/ColumnWalletSDK.ts
|
|
224
|
+
var ColumnWalletSDK = class {
|
|
225
|
+
config;
|
|
226
|
+
sessionKeyPair;
|
|
227
|
+
walletEncryptionPublicKey = null;
|
|
228
|
+
modal;
|
|
229
|
+
constructor(config) {
|
|
230
|
+
this.config = this.autoDetectMetadata(config);
|
|
231
|
+
this.sessionKeyPair = ColumnCrypto.generateKeyPair();
|
|
232
|
+
this.modal = new ColumnConnectModal(() => {
|
|
233
|
+
window.location.href = this.connect();
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
autoDetectMetadata(config) {
|
|
237
|
+
if (typeof document === "undefined") return config;
|
|
238
|
+
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
|
+
}
|
|
264
|
+
return detectedConfig;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Opens the premium Column selection modal
|
|
268
|
+
*/
|
|
269
|
+
openConnectModal() {
|
|
270
|
+
this.modal.open();
|
|
271
|
+
}
|
|
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
|
+
};
|
|
433
|
+
export {
|
|
434
|
+
ColumnCrypto,
|
|
435
|
+
ColumnWalletSDK
|
|
436
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@column-org/wallet-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Column Wallet Mobile Deep-Link SDK for Movement/Aptos ecosystem",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
16
|
+
"lint": "eslint src/**/*.ts",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"column",
|
|
21
|
+
"wallet",
|
|
22
|
+
"aptos",
|
|
23
|
+
"movement",
|
|
24
|
+
"deeplink",
|
|
25
|
+
"sdk",
|
|
26
|
+
"web3"
|
|
27
|
+
],
|
|
28
|
+
"author": "Column Labs",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"bs58": "^6.0.0",
|
|
32
|
+
"buffer": "^6.0.3",
|
|
33
|
+
"tweetnacl": "^1.0.3"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"tsup": "^8.0.2",
|
|
37
|
+
"typescript": "^5.3.3"
|
|
38
|
+
}
|
|
39
|
+
}
|