@column-org/wallet-sdk 1.1.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/package.json +5 -2
- package/src/ColumnShared.ts +69 -0
- package/src/ColumnWalletSDK.ts +236 -0
- package/src/assets/Column.png +0 -0
- package/src/index.native.ts +10 -0
- package/src/index.ts +3 -0
- package/src/ui/ColumnConnectModal.ts +199 -0
- package/src/ui/ColumnWalletWeb.ts +34 -0
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@column-org/wallet-sdk",
|
|
3
|
-
"version": "1.1.
|
|
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
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import bs58 from 'bs58'
|
|
2
|
+
import { ColumnCrypto, ColumnSDKConfig } from './ColumnShared'
|
|
3
|
+
import * as nacl from 'tweetnacl'
|
|
4
|
+
import { ColumnConnectModal } from './ui/ColumnConnectModal'
|
|
5
|
+
|
|
6
|
+
export class ColumnWalletSDK {
|
|
7
|
+
private config: ColumnSDKConfig
|
|
8
|
+
private sessionKeyPair: nacl.BoxKeyPair
|
|
9
|
+
private walletEncryptionPublicKey: string | null = null
|
|
10
|
+
|
|
11
|
+
constructor(config: ColumnSDKConfig) {
|
|
12
|
+
this.config = config
|
|
13
|
+
|
|
14
|
+
if (config.sessionSecretKey) {
|
|
15
|
+
try {
|
|
16
|
+
const secretKey = bs58.decode(config.sessionSecretKey)
|
|
17
|
+
this.sessionKeyPair = nacl.box.keyPair.fromSecretKey(secretKey)
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.warn('ColumnWalletSDK: Invalid sessionSecretKey provided, generating new one.')
|
|
20
|
+
this.sessionKeyPair = ColumnCrypto.generateKeyPair()
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
this.sessionKeyPair = ColumnCrypto.generateKeyPair()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns the current session's secret key (bs58) so it can be persisted
|
|
29
|
+
*/
|
|
30
|
+
public getSessionSecretKey(): string {
|
|
31
|
+
return bs58.encode(this.sessionKeyPair.secretKey)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Internal: Used by Web/Native adapters to get the config
|
|
36
|
+
*/
|
|
37
|
+
public getConfig(): ColumnSDKConfig {
|
|
38
|
+
return this.config
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public setWalletEncryptionPublicKey(key: string) {
|
|
42
|
+
this.walletEncryptionPublicKey = key
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Alias for setWalletEncryptionPublicKey to match docs
|
|
47
|
+
*/
|
|
48
|
+
public importWalletKey(key: string) {
|
|
49
|
+
this.setWalletEncryptionPublicKey(key)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Step 1: Connect to the wallet
|
|
54
|
+
*/
|
|
55
|
+
public connect(): string {
|
|
56
|
+
const params = new URLSearchParams({
|
|
57
|
+
request: 'connect',
|
|
58
|
+
app_url: this.config.appUrl || '',
|
|
59
|
+
app_name: this.config.appName || '',
|
|
60
|
+
dapp_encryption_public_key: bs58.encode(this.sessionKeyPair.publicKey),
|
|
61
|
+
redirect_link: this.config.redirectLink || ''
|
|
62
|
+
} as Record<string, string>)
|
|
63
|
+
|
|
64
|
+
if (this.config.appIcon) {
|
|
65
|
+
params.append('app_icon', this.config.appIcon)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this.config.network) {
|
|
69
|
+
params.append('network', this.config.network)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.config.appDescription) {
|
|
73
|
+
params.append('app_description', this.config.appDescription)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const scheme = this.config.walletScheme || 'column'
|
|
77
|
+
const base = scheme.includes('://') ? scheme : `${scheme}://`
|
|
78
|
+
return `${base}${base.endsWith('/') ? '' : '/'}?${params.toString()}`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Step 2: Sign and Submit a transaction
|
|
83
|
+
*/
|
|
84
|
+
public signAndSubmitTransaction(transaction: any): string {
|
|
85
|
+
if (!this.walletEncryptionPublicKey) {
|
|
86
|
+
throw new Error('Wallet not connected. Call connect() first and store the wallet_encryption_public_key.')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const payload = {
|
|
90
|
+
transaction: transaction,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const encrypted = ColumnCrypto.encryptPayload(
|
|
94
|
+
payload,
|
|
95
|
+
this.walletEncryptionPublicKey,
|
|
96
|
+
this.sessionKeyPair.secretKey
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const params = new URLSearchParams({
|
|
100
|
+
request: 'signAndSubmitTransaction',
|
|
101
|
+
app_url: this.config.appUrl || '',
|
|
102
|
+
app_name: this.config.appName || '',
|
|
103
|
+
dapp_encryption_public_key: bs58.encode(this.sessionKeyPair.publicKey),
|
|
104
|
+
data: encrypted.data,
|
|
105
|
+
nonce: encrypted.nonce,
|
|
106
|
+
redirect_link: this.config.redirectLink || ''
|
|
107
|
+
} as Record<string, string>)
|
|
108
|
+
|
|
109
|
+
if (this.config.appIcon) {
|
|
110
|
+
params.append('app_icon', this.config.appIcon)
|
|
111
|
+
}
|
|
112
|
+
if (this.config.network) {
|
|
113
|
+
params.append('network', this.config.network)
|
|
114
|
+
}
|
|
115
|
+
if (this.config.appDescription) {
|
|
116
|
+
params.append('app_description', this.config.appDescription)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const scheme = this.config.walletScheme || 'column'
|
|
120
|
+
const base = scheme.includes('://') ? scheme : `${scheme}://`
|
|
121
|
+
return `${base}${base.endsWith('/') ? '' : '/'}?${params.toString()}`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Step 3: Sign a simple message
|
|
126
|
+
*/
|
|
127
|
+
public signMessage(message: string): string {
|
|
128
|
+
if (!this.walletEncryptionPublicKey) {
|
|
129
|
+
throw new Error('Wallet not connected.')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const payload = {
|
|
133
|
+
message: message,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const encrypted = ColumnCrypto.encryptPayload(
|
|
137
|
+
payload,
|
|
138
|
+
this.walletEncryptionPublicKey,
|
|
139
|
+
this.sessionKeyPair.secretKey
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
const params = new URLSearchParams({
|
|
143
|
+
request: 'signMessage',
|
|
144
|
+
app_url: this.config.appUrl || '',
|
|
145
|
+
app_name: this.config.appName || '',
|
|
146
|
+
dapp_encryption_public_key: bs58.encode(this.sessionKeyPair.publicKey),
|
|
147
|
+
data: encrypted.data,
|
|
148
|
+
nonce: encrypted.nonce,
|
|
149
|
+
redirect_link: this.config.redirectLink || ''
|
|
150
|
+
} as Record<string, string>)
|
|
151
|
+
|
|
152
|
+
if (this.config.appIcon) {
|
|
153
|
+
params.append('app_icon', this.config.appIcon)
|
|
154
|
+
}
|
|
155
|
+
if (this.config.network) {
|
|
156
|
+
params.append('network', this.config.network)
|
|
157
|
+
}
|
|
158
|
+
if (this.config.appDescription) {
|
|
159
|
+
params.append('app_description', this.config.appDescription)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const scheme = this.config.walletScheme || 'column'
|
|
163
|
+
const base = scheme.includes('://') ? scheme : `${scheme}://`
|
|
164
|
+
return `${base}${base.endsWith('/') ? '' : '/'}?${params.toString()}`
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Utility: Parse the response from Column
|
|
169
|
+
*/
|
|
170
|
+
public handleResponse(url: string): any {
|
|
171
|
+
let params: URLSearchParams;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const urlObj = new URL(url.includes('://') ? url : `http://localhost/${url}`);
|
|
175
|
+
// Prioritize Hash params if they exist (used for tab burial redirection)
|
|
176
|
+
const hashStr = urlObj.hash.substring(1);
|
|
177
|
+
const searchStr = urlObj.search.substring(1);
|
|
178
|
+
|
|
179
|
+
if (hashStr) {
|
|
180
|
+
params = new URLSearchParams(hashStr);
|
|
181
|
+
} else {
|
|
182
|
+
params = new URLSearchParams(searchStr);
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
// Fallback for native schemes or malformed URLs
|
|
186
|
+
const hashMatch = url.match(/#(.*)$/);
|
|
187
|
+
const queryMatch = url.match(/\?(.*)$/);
|
|
188
|
+
params = new URLSearchParams(hashMatch ? hashMatch[1] : (queryMatch ? queryMatch[1] : url));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const data = params.get('data')
|
|
192
|
+
const nonce = params.get('nonce')
|
|
193
|
+
const error = params.get('error')
|
|
194
|
+
|
|
195
|
+
if (error) {
|
|
196
|
+
throw new Error(`Column Wallet Error: ${error}`)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (data && nonce && this.walletEncryptionPublicKey) {
|
|
200
|
+
const decrypted = ColumnCrypto.decryptPayload(
|
|
201
|
+
data,
|
|
202
|
+
nonce,
|
|
203
|
+
this.walletEncryptionPublicKey,
|
|
204
|
+
this.sessionKeyPair.secretKey
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
if (!decrypted) {
|
|
208
|
+
throw new Error("Column Wallet Error: Decryption failed. The response may have been tampered with or keys do not match.")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
...decrypted,
|
|
213
|
+
network: params.get('network') // Always include network from URL
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Plaintext fallback for initial connect
|
|
218
|
+
const response: Record<string, string> = {}
|
|
219
|
+
params.forEach((value, key) => {
|
|
220
|
+
response[key] = value
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
if (response.column_encryption_public_key) {
|
|
224
|
+
this.walletEncryptionPublicKey = response.column_encryption_public_key
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return response
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Sets the wallet's public key if known from a previous session
|
|
232
|
+
*/
|
|
233
|
+
public setWalletPublicKey(publicKeyB58: string) {
|
|
234
|
+
this.walletEncryptionPublicKey = publicKeyB58
|
|
235
|
+
}
|
|
236
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import 'react-native-get-random-values';
|
|
2
|
+
import { Buffer } from 'buffer';
|
|
3
|
+
|
|
4
|
+
// Polyfill Buffer for the native environment
|
|
5
|
+
if (typeof global.Buffer === 'undefined') {
|
|
6
|
+
global.Buffer = Buffer;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export * from './ColumnWalletSDK';
|
|
10
|
+
export * from './ColumnShared';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
export const ColumnModalStyles = `
|
|
2
|
+
.column-modal-overlay {
|
|
3
|
+
position: fixed;
|
|
4
|
+
top: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
background: rgba(0, 0, 0, 0.75);
|
|
9
|
+
backdrop-filter: blur(8px);
|
|
10
|
+
-webkit-backdrop-filter: blur(8px);
|
|
11
|
+
display: flex;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
align-items: center;
|
|
14
|
+
z-index: 999999;
|
|
15
|
+
opacity: 0;
|
|
16
|
+
transition: opacity 0.3s ease;
|
|
17
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.column-modal-overlay.active {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.column-modal-card {
|
|
25
|
+
background: #1C1F2E;
|
|
26
|
+
width: 100%;
|
|
27
|
+
max-width: 420px;
|
|
28
|
+
border-radius: 20px;
|
|
29
|
+
padding: 28px;
|
|
30
|
+
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
|
|
31
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
32
|
+
transform: scale(0.95);
|
|
33
|
+
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
34
|
+
position: relative;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.column-modal-overlay.active .column-modal-card {
|
|
38
|
+
transform: scale(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.column-modal-close {
|
|
42
|
+
position: absolute;
|
|
43
|
+
top: 16px;
|
|
44
|
+
right: 16px;
|
|
45
|
+
background: transparent;
|
|
46
|
+
border: none;
|
|
47
|
+
color: #8B98A5;
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
font-size: 28px;
|
|
50
|
+
padding: 4px;
|
|
51
|
+
line-height: 1;
|
|
52
|
+
transition: color 0.2s ease;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.column-modal-close:hover {
|
|
56
|
+
color: #fff;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.column-modal-title {
|
|
60
|
+
color: #fff;
|
|
61
|
+
font-size: 20px;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
margin: 0 0 24px;
|
|
64
|
+
text-align: center;
|
|
65
|
+
line-height: 1.4;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.column-modal-wallet-option {
|
|
69
|
+
background: #252938;
|
|
70
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
71
|
+
border-radius: 14px;
|
|
72
|
+
padding: 16px 20px;
|
|
73
|
+
width: 100%;
|
|
74
|
+
display: flex;
|
|
75
|
+
justify-content: space-between;
|
|
76
|
+
align-items: center;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
transition: all 0.2s ease;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.column-modal-wallet-option:hover {
|
|
82
|
+
background: #2D3142;
|
|
83
|
+
border-color: rgba(255, 218, 52, 0.3);
|
|
84
|
+
transform: translateY(-2px);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.column-modal-wallet-left {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: 14px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.column-modal-wallet-icon {
|
|
94
|
+
width: 40px;
|
|
95
|
+
height: 40px;
|
|
96
|
+
background: rgba(255, 218, 52, 0.12);
|
|
97
|
+
border-radius: 10px;
|
|
98
|
+
display: flex;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
align-items: center;
|
|
101
|
+
flex-shrink: 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.column-modal-wallet-name {
|
|
105
|
+
color: #fff;
|
|
106
|
+
font-size: 16px;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.column-modal-wallet-badge {
|
|
111
|
+
background: rgba(76, 175, 80, 0.15);
|
|
112
|
+
color: #4CAF50;
|
|
113
|
+
font-size: 13px;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
padding: 6px 12px;
|
|
116
|
+
border-radius: 8px;
|
|
117
|
+
}
|
|
118
|
+
`;
|
|
119
|
+
|
|
120
|
+
export class ColumnConnectModal {
|
|
121
|
+
private overlay: HTMLDivElement | null = null;
|
|
122
|
+
private onConnect: () => void;
|
|
123
|
+
|
|
124
|
+
constructor(onConnect: () => void) {
|
|
125
|
+
this.onConnect = onConnect;
|
|
126
|
+
this.injectStyles();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private injectStyles() {
|
|
130
|
+
if (typeof document === 'undefined') return;
|
|
131
|
+
if (document.getElementById('column-modal-styles')) return;
|
|
132
|
+
|
|
133
|
+
const styleTag = document.createElement('style');
|
|
134
|
+
styleTag.id = 'column-modal-styles';
|
|
135
|
+
styleTag.innerHTML = ColumnModalStyles;
|
|
136
|
+
document.head.appendChild(styleTag);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public open() {
|
|
140
|
+
if (typeof document === 'undefined' || this.overlay) return;
|
|
141
|
+
|
|
142
|
+
// Create overlay
|
|
143
|
+
this.overlay = document.createElement('div');
|
|
144
|
+
this.overlay.className = 'column-modal-overlay';
|
|
145
|
+
|
|
146
|
+
// Create card with new design
|
|
147
|
+
this.overlay.innerHTML = `
|
|
148
|
+
<div class="column-modal-card">
|
|
149
|
+
<button class="column-modal-close" id="column-modal-close">×</button>
|
|
150
|
+
<h2 class="column-modal-title">Connect a wallet on<br>Movement to continue</h2>
|
|
151
|
+
|
|
152
|
+
<button class="column-modal-wallet-option" id="column-modal-connect-btn">
|
|
153
|
+
<div class="column-modal-wallet-left">
|
|
154
|
+
<div class="column-modal-wallet-icon">
|
|
155
|
+
<svg width="28" height="28" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
156
|
+
<path d="M16 4L28 28H4L16 4Z" stroke="#ffda34" stroke-width="2.5" stroke-linejoin="round"/>
|
|
157
|
+
<circle cx="16" cy="18" r="4" fill="#ffda34"/>
|
|
158
|
+
</svg>
|
|
159
|
+
</div>
|
|
160
|
+
<span class="column-modal-wallet-name">Column Wallet</span>
|
|
161
|
+
</div>
|
|
162
|
+
<span class="column-modal-wallet-badge">Mobile</span>
|
|
163
|
+
</button>
|
|
164
|
+
</div>
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
document.body.appendChild(this.overlay);
|
|
168
|
+
|
|
169
|
+
// Animate in
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
this.overlay?.classList.add('active');
|
|
172
|
+
}, 10);
|
|
173
|
+
|
|
174
|
+
// Bind events
|
|
175
|
+
document.getElementById('column-modal-close')?.addEventListener('click', () => this.close());
|
|
176
|
+
|
|
177
|
+
document.getElementById('column-modal-connect-btn')?.addEventListener('click', () => {
|
|
178
|
+
this.onConnect();
|
|
179
|
+
this.close();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Close on click outside
|
|
183
|
+
this.overlay.addEventListener('click', (e) => {
|
|
184
|
+
if (e.target === this.overlay) this.close();
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public close() {
|
|
189
|
+
if (!this.overlay) return;
|
|
190
|
+
|
|
191
|
+
this.overlay.classList.remove('active');
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
if (this.overlay && this.overlay.parentNode) {
|
|
194
|
+
this.overlay.parentNode.removeChild(this.overlay);
|
|
195
|
+
}
|
|
196
|
+
this.overlay = null;
|
|
197
|
+
}, 300);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ColumnWalletSDK } from '../ColumnWalletSDK';
|
|
2
|
+
import { ColumnConnectModal } from './ColumnConnectModal';
|
|
3
|
+
import { ColumnSDKConfig } from '../ColumnShared';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enhanced SDK Class specifically for Web environments
|
|
7
|
+
*/
|
|
8
|
+
export class ColumnWalletWeb extends ColumnWalletSDK {
|
|
9
|
+
private modal: ColumnConnectModal;
|
|
10
|
+
|
|
11
|
+
constructor(config: ColumnSDKConfig) {
|
|
12
|
+
const detectedConfig = ColumnWalletWeb.autoDetectMetadata(config);
|
|
13
|
+
super(detectedConfig);
|
|
14
|
+
|
|
15
|
+
this.modal = new ColumnConnectModal(() => {
|
|
16
|
+
window.location.href = this.connect();
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private static autoDetectMetadata(config: ColumnSDKConfig): ColumnSDKConfig {
|
|
21
|
+
if (typeof document === 'undefined') return config;
|
|
22
|
+
const detectedConfig = { ...config };
|
|
23
|
+
|
|
24
|
+
if (!detectedConfig.appUrl) detectedConfig.appUrl = window.location.origin;
|
|
25
|
+
if (!detectedConfig.appName) detectedConfig.appName = document.title || 'Unknown dApp';
|
|
26
|
+
if (!detectedConfig.redirectLink) detectedConfig.redirectLink = window.location.href.split('?')[0];
|
|
27
|
+
|
|
28
|
+
return detectedConfig;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public openConnectModal() {
|
|
32
|
+
this.modal.open();
|
|
33
|
+
}
|
|
34
|
+
}
|