@talken/talkenkit 2.4.10 → 2.4.12

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.
Files changed (29) hide show
  1. package/dist/abcWallet-DRHY5BUW.js +184 -0
  2. package/dist/abcWallet-YOPLVWVE.js +184 -0
  3. package/dist/chunk-5NAGRWKN.js +6991 -0
  4. package/dist/chunk-KZZG24U5.js +7015 -0
  5. package/dist/components/RainbowKitProvider/InternalPinProvider.d.ts +2 -1
  6. package/dist/contexts/MultiChainContext.d.ts +2 -0
  7. package/dist/index.js +159 -161
  8. package/dist/themes/baseTheme.js +1 -1
  9. package/dist/themes/darkTheme.js +1 -1
  10. package/dist/themes/lightTheme.js +1 -1
  11. package/dist/themes/midnightTheme.js +1 -1
  12. package/dist/wallets/walletConnectors/abcWallet/abcApi.d.ts +11 -0
  13. package/dist/wallets/walletConnectors/abcWallet/abcApi.js +1 -1
  14. package/dist/wallets/walletConnectors/abcWallet/abcConnector.js +3 -3
  15. package/dist/wallets/walletConnectors/abcWallet/abcProvider.d.ts +5 -0
  16. package/dist/wallets/walletConnectors/abcWallet/abcProvider.js +1 -1
  17. package/dist/wallets/walletConnectors/abcWallet/abcSolanaWalletAdapter.js +1 -1
  18. package/dist/wallets/walletConnectors/abcWallet/abcWallet.js +4 -4
  19. package/dist/wallets/walletConnectors/abcWallet/index.js +4 -4
  20. package/dist/wallets/walletConnectors/chunk-6KJQ2GQZ.js +47 -0
  21. package/dist/wallets/walletConnectors/chunk-JW3YCANT.js +464 -0
  22. package/dist/wallets/walletConnectors/chunk-LVODOLXE.js +47 -0
  23. package/dist/wallets/walletConnectors/chunk-NY2MBDVO.js +276 -0
  24. package/dist/wallets/walletConnectors/chunk-PPTSQD3J.js +276 -0
  25. package/dist/wallets/walletConnectors/chunk-UTRMKLD5.js +1683 -0
  26. package/dist/wallets/walletConnectors/chunk-UYFNIOUH.js +475 -0
  27. package/dist/wallets/walletConnectors/chunk-VDVBAWB4.js +1670 -0
  28. package/dist/wallets/walletConnectors/index.js +54 -54
  29. package/package.json +2 -2
@@ -0,0 +1,1670 @@
1
+ "use client";
2
+ import {
3
+ getNetworkName
4
+ } from "./chunk-4PCVQBFZ.js";
5
+ import {
6
+ secure_default
7
+ } from "./chunk-NDYGTKP5.js";
8
+ import {
9
+ getSolanaNetwork
10
+ } from "./chunk-A7FIBI6X.js";
11
+ import {
12
+ calculateExpiryTimestamp,
13
+ createAbcError,
14
+ isTokenExpired,
15
+ loadFromStorage,
16
+ parseApiError,
17
+ removeFromStorage,
18
+ saveToStorage
19
+ } from "./chunk-5RSS3SRV.js";
20
+ import {
21
+ ABC_AUDIENCE,
22
+ ABC_ENDPOINTS,
23
+ DEFAULT_HEADERS,
24
+ REQUEST_TIMEOUT
25
+ } from "./chunk-VETRBBA2.js";
26
+
27
+ // src/wallets/walletConnectors/abcWallet/abcApi.ts
28
+ import { getAddress } from "viem";
29
+ var AbcWaasClient = class {
30
+ constructor(config) {
31
+ this.accessToken = null;
32
+ this.refreshToken = null;
33
+ this.expiresAt = null;
34
+ this.isRefreshing = false;
35
+ this.refreshPromise = null;
36
+ this.config = config;
37
+ this.baseURL = config.waasUrl;
38
+ this.secure = new secure_default(this.baseURL);
39
+ this.loadTokens();
40
+ }
41
+ /**
42
+ * Load tokens from storage
43
+ */
44
+ loadTokens() {
45
+ this.accessToken = loadFromStorage("access_token" /* ACCESS_TOKEN */);
46
+ this.refreshToken = loadFromStorage("refresh_token" /* REFRESH_TOKEN */);
47
+ this.expiresAt = loadFromStorage("expires_at" /* EXPIRES_AT */);
48
+ }
49
+ /**
50
+ * Save tokens to storage
51
+ */
52
+ saveTokens(accessToken, refreshToken, expiresIn) {
53
+ this.accessToken = accessToken;
54
+ this.refreshToken = refreshToken;
55
+ this.expiresAt = calculateExpiryTimestamp(expiresIn);
56
+ saveToStorage("access_token" /* ACCESS_TOKEN */, accessToken);
57
+ saveToStorage("refresh_token" /* REFRESH_TOKEN */, refreshToken);
58
+ saveToStorage("expires_at" /* EXPIRES_AT */, this.expiresAt);
59
+ }
60
+ /**
61
+ * Clear tokens
62
+ */
63
+ clearTokens() {
64
+ this.accessToken = null;
65
+ this.refreshToken = null;
66
+ this.expiresAt = null;
67
+ removeFromStorage("access_token" /* ACCESS_TOKEN */);
68
+ removeFromStorage("refresh_token" /* REFRESH_TOKEN */);
69
+ removeFromStorage("expires_at" /* EXPIRES_AT */);
70
+ }
71
+ /**
72
+ * Check if access token is expired
73
+ */
74
+ isTokenExpired() {
75
+ return isTokenExpired(this.expiresAt);
76
+ }
77
+ /**
78
+ * Get current access token
79
+ */
80
+ getAccessToken() {
81
+ return this.accessToken;
82
+ }
83
+ /**
84
+ * Refresh access token
85
+ */
86
+ async refreshAccessToken() {
87
+ if (this.isRefreshing) {
88
+ if (this.refreshPromise) {
89
+ await this.refreshPromise;
90
+ }
91
+ return;
92
+ }
93
+ if (!this.refreshToken) {
94
+ throw createAbcError(
95
+ "TOKEN_EXPIRED" /* TOKEN_EXPIRED */,
96
+ "No refresh token available"
97
+ );
98
+ }
99
+ this.isRefreshing = true;
100
+ this.refreshPromise = (async () => {
101
+ try {
102
+ const isIframe = typeof window !== "undefined" && window.self !== window.top;
103
+ const talkenApiUrl = process.env.NEXT_PUBLIC_API_SERVER || "https://dev.walletapi.talken.io";
104
+ const response = await fetch(
105
+ `${talkenApiUrl}${ABC_ENDPOINTS.REFRESH_TOKEN}`,
106
+ {
107
+ method: "POST",
108
+ headers: DEFAULT_HEADERS,
109
+ credentials: "include",
110
+ // Important: withCredentials from reference
111
+ body: new URLSearchParams({
112
+ refresh_token: this.refreshToken || "",
113
+ // Use snake_case (reference pattern)
114
+ isIframe: String(isIframe)
115
+ }).toString()
116
+ }
117
+ );
118
+ const text = await response.text();
119
+ const data = text ? JSON.parse(text) : {};
120
+ if (response.ok && data.access_token) {
121
+ const expiresIn = data.expire_in || 3600;
122
+ this.saveTokens(
123
+ data.access_token,
124
+ data.refresh_token || this.refreshToken,
125
+ // Keep old if not provided
126
+ expiresIn
127
+ );
128
+ } else {
129
+ throw createAbcError(
130
+ "TOKEN_EXPIRED" /* TOKEN_EXPIRED */,
131
+ "Failed to refresh token"
132
+ );
133
+ }
134
+ } finally {
135
+ this.isRefreshing = false;
136
+ this.refreshPromise = null;
137
+ }
138
+ })();
139
+ await this.refreshPromise;
140
+ }
141
+ /**
142
+ * Make HTTP request
143
+ */
144
+ async request(endpoint, options = {}) {
145
+ const {
146
+ method = "GET",
147
+ body,
148
+ headers = {},
149
+ skipAuth = false,
150
+ isRetry = false
151
+ } = options;
152
+ if (!skipAuth && this.isTokenExpired() && this.refreshToken) {
153
+ await this.refreshAccessToken();
154
+ }
155
+ const url = `${this.baseURL}${endpoint}`;
156
+ const requestHeaders = {
157
+ ...DEFAULT_HEADERS,
158
+ ...headers
159
+ };
160
+ if (!skipAuth && this.accessToken) {
161
+ requestHeaders.Authorization = `Bearer ${this.accessToken}`;
162
+ }
163
+ if (this.config.apiKey) {
164
+ requestHeaders["X-API-Key"] = this.config.apiKey;
165
+ }
166
+ try {
167
+ const controller = new AbortController();
168
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
169
+ const requestBody = body ? new URLSearchParams(body).toString() : void 0;
170
+ const response = await fetch(url, {
171
+ method,
172
+ headers: requestHeaders,
173
+ body: requestBody,
174
+ signal: controller.signal
175
+ });
176
+ clearTimeout(timeoutId);
177
+ const text = await response.text();
178
+ let data;
179
+ if (text) {
180
+ try {
181
+ data = JSON.parse(text);
182
+ } catch (parseError) {
183
+ console.error("[AbcWaasClient] \u274C JSON parse error:", {
184
+ url,
185
+ method,
186
+ status: response.status,
187
+ responseText: text.substring(0, 200)
188
+ // Log first 200 chars
189
+ });
190
+ throw createAbcError(
191
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
192
+ "Invalid JSON response from server",
193
+ { text, parseError }
194
+ );
195
+ }
196
+ } else {
197
+ console.log("[AbcWaasClient] \u2139\uFE0F Empty response received:", {
198
+ url,
199
+ method,
200
+ status: response.status
201
+ });
202
+ data = { status: "success" };
203
+ }
204
+ if (response.status === 401 && !skipAuth && this.refreshToken && !isRetry) {
205
+ try {
206
+ await this.refreshAccessToken();
207
+ return await this.request(endpoint, {
208
+ ...options,
209
+ isRetry: true
210
+ // Prevent infinite retry loop
211
+ });
212
+ } catch (refreshError) {
213
+ console.error("Token refresh failed:", refreshError);
214
+ throw parseApiError({
215
+ response: {
216
+ status: response.status,
217
+ data
218
+ }
219
+ });
220
+ }
221
+ }
222
+ if (!response.ok) {
223
+ console.error("[AbcWaasClient] \u274C API Error:", {
224
+ url,
225
+ method,
226
+ status: response.status,
227
+ statusText: response.statusText,
228
+ data
229
+ });
230
+ throw parseApiError({
231
+ response: {
232
+ status: response.status,
233
+ data
234
+ }
235
+ });
236
+ }
237
+ return data;
238
+ } catch (error) {
239
+ console.error("[AbcWaasClient] \u274C Request failed:", {
240
+ url,
241
+ method,
242
+ error: error.message,
243
+ errorName: error.name,
244
+ errorType: error.constructor.name,
245
+ stack: error.stack
246
+ });
247
+ if (error.name === "AbortError") {
248
+ throw createAbcError(
249
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
250
+ "Request timeout",
251
+ error
252
+ );
253
+ }
254
+ if (error.code && error.message) {
255
+ throw error;
256
+ }
257
+ throw parseApiError(error);
258
+ }
259
+ }
260
+ /**
261
+ * Login with email and password
262
+ * NOTE: Tokens are stored in memory only. Call persistTokens() after PIN verification to save to localStorage.
263
+ */
264
+ async loginWithEmail(email, password) {
265
+ const response = await this.request(
266
+ ABC_ENDPOINTS.SNS_LOGIN,
267
+ {
268
+ method: "POST",
269
+ body: {
270
+ email,
271
+ token: password,
272
+ // Use password as token
273
+ service: "email",
274
+ audience: ABC_AUDIENCE
275
+ },
276
+ skipAuth: true
277
+ }
278
+ );
279
+ if (response.status === "success" && response.data) {
280
+ this.accessToken = response.data.accessToken;
281
+ this.refreshToken = response.data.refreshToken;
282
+ this.expiresAt = calculateExpiryTimestamp(response.data.expiresIn);
283
+ console.log(
284
+ "[AbcWaasClient] \u2705 Login successful, tokens stored in memory (not persisted yet)"
285
+ );
286
+ return response.data;
287
+ }
288
+ throw createAbcError(
289
+ "INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */,
290
+ "Email login failed",
291
+ response
292
+ );
293
+ }
294
+ /**
295
+ * Login with email and OTP (for PIN recovery)
296
+ * NOTE: Tokens are stored in memory only. Call persistTokens() after PIN verification to save to localStorage.
297
+ */
298
+ async loginWithOtp(email, otpCode) {
299
+ const response = await this.request(
300
+ ABC_ENDPOINTS.SNS_LOGIN,
301
+ {
302
+ method: "POST",
303
+ body: {
304
+ email,
305
+ token: otpCode,
306
+ service: "email",
307
+ audience: ABC_AUDIENCE
308
+ },
309
+ skipAuth: true
310
+ }
311
+ );
312
+ if (response.status === "success" && response.data) {
313
+ this.accessToken = response.data.accessToken;
314
+ this.refreshToken = response.data.refreshToken;
315
+ this.expiresAt = calculateExpiryTimestamp(response.data.expiresIn);
316
+ console.log(
317
+ "[AbcWaasClient] \u2705 OTP login successful, tokens stored in memory (not persisted yet)"
318
+ );
319
+ return response.data;
320
+ }
321
+ throw createAbcError(
322
+ "INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */,
323
+ "OTP login failed",
324
+ response
325
+ );
326
+ }
327
+ /**
328
+ * Login with social provider
329
+ * NOTE: Tokens are stored in memory only. Call persistTokens() after PIN verification to save to localStorage.
330
+ */
331
+ async loginWithSocial(provider, token, email) {
332
+ const response = await this.request(
333
+ ABC_ENDPOINTS.SNS_LOGIN,
334
+ {
335
+ method: "POST",
336
+ body: {
337
+ token,
338
+ service: provider,
339
+ audience: ABC_AUDIENCE,
340
+ email
341
+ },
342
+ skipAuth: true
343
+ }
344
+ );
345
+ if (response.status === "success" && response.data) {
346
+ this.accessToken = response.data.accessToken;
347
+ this.refreshToken = response.data.refreshToken;
348
+ this.expiresAt = calculateExpiryTimestamp(response.data.expiresIn);
349
+ console.log(
350
+ "[AbcWaasClient] \u2705 Social login successful, tokens stored in memory (not persisted yet)"
351
+ );
352
+ return response.data;
353
+ }
354
+ throw createAbcError(
355
+ "INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */,
356
+ "Social login failed",
357
+ response
358
+ );
359
+ }
360
+ /**
361
+ * Refresh access token using refresh token (public wrapper)
362
+ */
363
+ async refreshTokens(refreshTokenValue) {
364
+ const oldRefreshToken = this.refreshToken;
365
+ this.refreshToken = refreshTokenValue;
366
+ try {
367
+ await this.refreshAccessToken();
368
+ return {
369
+ accessToken: this.accessToken,
370
+ refreshToken: this.refreshToken,
371
+ expiresIn: Math.floor((this.expiresAt - Date.now()) / 1e3)
372
+ };
373
+ } catch (error) {
374
+ this.refreshToken = oldRefreshToken;
375
+ throw error;
376
+ }
377
+ }
378
+ /**
379
+ * Request OTP code for email
380
+ */
381
+ async requestOtpCode(email) {
382
+ const response = await this.request(
383
+ ABC_ENDPOINTS.SEND_OTP,
384
+ {
385
+ method: "POST",
386
+ body: { email },
387
+ skipAuth: true
388
+ }
389
+ );
390
+ if (response.status === "success" && response.data) {
391
+ return response.data;
392
+ }
393
+ throw createAbcError(
394
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
395
+ "Failed to request OTP",
396
+ response
397
+ );
398
+ }
399
+ /**
400
+ * Verify OTP code
401
+ */
402
+ async verifyOtpCode(email, code) {
403
+ const response = await this.request(ABC_ENDPOINTS.VERIFY_OTP, {
404
+ method: "POST",
405
+ body: { email, code },
406
+ skipAuth: true
407
+ });
408
+ return response.status === "success";
409
+ }
410
+ /**
411
+ * Check if email already exists
412
+ * Reference: tg-wallet-frontend src/features/user/api/index.ts:63-69
413
+ */
414
+ async emailCheck(email) {
415
+ const talkenApiUrl = process.env.NEXT_PUBLIC_API_SERVER || "https://dev.walletapi.talken.io";
416
+ const url = `${talkenApiUrl}/abc/emailCheck?email=${encodeURIComponent(email)}`;
417
+ const method = "GET";
418
+ console.log("[AbcWaasClient] \u{1F4EC} Checking email");
419
+ try {
420
+ const response = await fetch(url, {
421
+ method,
422
+ headers: DEFAULT_HEADERS
423
+ });
424
+ const text = await response.text();
425
+ const data = text ? JSON.parse(text) : { status: "success" };
426
+ if (!response.ok) {
427
+ console.error("[AbcWaasClient] \u274C API Error:", {
428
+ url,
429
+ method,
430
+ status: response.status,
431
+ statusText: response.statusText,
432
+ data
433
+ });
434
+ throw parseApiError({
435
+ response: {
436
+ status: response.status,
437
+ data
438
+ }
439
+ });
440
+ }
441
+ return data;
442
+ } catch (error) {
443
+ if (error.name === "AbortError") {
444
+ throw createAbcError(
445
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
446
+ "Request timeout",
447
+ error
448
+ );
449
+ }
450
+ throw parseApiError(error);
451
+ }
452
+ }
453
+ /**
454
+ * Send OTP code to email for signup
455
+ * Reference: tg-wallet-frontend src/features/user/api/index.ts:105-110
456
+ * Note: Uses ABC WaaS endpoint, not Talken API
457
+ */
458
+ async sendOtpCode(email) {
459
+ const endpoint = `/member/mail-service/${encodeURIComponent(email)}/sendcode`;
460
+ console.log("[AbcWaasClient] \u{1F4E7} Sending OTP code");
461
+ const response = await this.request(endpoint, {
462
+ method: "GET",
463
+ skipAuth: true
464
+ });
465
+ if (response.status === "success") {
466
+ return response;
467
+ }
468
+ throw createAbcError(
469
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
470
+ "Failed to send OTP code",
471
+ response
472
+ );
473
+ }
474
+ /**
475
+ * Verify OTP code for signup
476
+ * Reference: tg-wallet-frontend src/features/user/api/index.ts:112-123
477
+ * Note: Uses ABC WaaS endpoint, not Talken API
478
+ */
479
+ async verifyOtpCodeSignup(email, code) {
480
+ const endpoint = `/member/mail-service/${encodeURIComponent(email)}/verifycode`;
481
+ const requestBody = {
482
+ email,
483
+ code,
484
+ serviceid: ABC_AUDIENCE
485
+ // 'https://mw.myabcwallet.com'
486
+ };
487
+ console.log("[AbcWaasClient] \u{1F510} Verifying OTP code");
488
+ const response = await this.request(endpoint, {
489
+ method: "POST",
490
+ body: requestBody,
491
+ skipAuth: true
492
+ });
493
+ console.log("[verifyOtpCodeSignup] Response:", response);
494
+ const isSuccess = response.status === "success" || response.status === 200 || response.message === "success";
495
+ if (isSuccess) {
496
+ return response;
497
+ }
498
+ console.error("[verifyOtpCodeSignup] Failed:", response);
499
+ throw createAbcError(
500
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
501
+ "Invalid or expired OTP code",
502
+ response
503
+ );
504
+ }
505
+ /**
506
+ * SNS (Social) Login via Google/Apple/Kakao
507
+ * Reference: tg-wallet-frontend src/features/user/api/index.ts:43-51
508
+ * Note: Uses Talken API server, not ABC WaaS
509
+ */
510
+ async snsLogin(params) {
511
+ const talkenApiUrl = process.env.NEXT_PUBLIC_API_SERVER || "https://dev.walletapi.talken.io";
512
+ const url = `${talkenApiUrl}/abc/snsLogin`;
513
+ console.log("[AbcWaasClient] \u{1F510} SNS Login");
514
+ try {
515
+ const response = await fetch(url, {
516
+ method: "POST",
517
+ headers: {
518
+ ...DEFAULT_HEADERS
519
+ },
520
+ credentials: "include",
521
+ // withCredentials from reference
522
+ body: new URLSearchParams(params).toString()
523
+ });
524
+ const data = await response.json();
525
+ if (!response.ok) {
526
+ console.error("[AbcWaasClient] \u274C SNS Login failed:", {
527
+ url,
528
+ status: response.status,
529
+ statusText: response.statusText,
530
+ data
531
+ });
532
+ throw parseApiError({
533
+ response: {
534
+ status: response.status,
535
+ data
536
+ }
537
+ });
538
+ }
539
+ if (!data.access_token) {
540
+ throw createAbcError(
541
+ "INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */,
542
+ "SNS login failed: Invalid response from server",
543
+ { service: params.service, response: data }
544
+ );
545
+ }
546
+ console.log("[AbcWaasClient] \u2705 SNS login successful");
547
+ return {
548
+ uid: "",
549
+ // Will be fetched later via info() API
550
+ email: params.email,
551
+ access_token: data.access_token,
552
+ refresh_token: data.refresh_token,
553
+ user_type: "existing"
554
+ // Default to existing, will be determined later
555
+ };
556
+ } catch (error) {
557
+ if (error.name === "AbortError") {
558
+ throw createAbcError(
559
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
560
+ "SNS login request timeout",
561
+ error
562
+ );
563
+ }
564
+ throw error;
565
+ }
566
+ }
567
+ /**
568
+ * Register new user
569
+ * Reference: tg-wallet-frontend src/features/user/api/index.ts:71-78
570
+ * Note: Uses Talken API server, not ABC WaaS
571
+ */
572
+ async registerUser(params) {
573
+ const talkenApiUrl = process.env.NEXT_PUBLIC_API_SERVER || "https://dev.walletapi.talken.io";
574
+ const url = `${talkenApiUrl}/abc/adduser`;
575
+ const method = "POST";
576
+ console.log("[AbcWaasClient] \u{1F4DD} Registering user");
577
+ try {
578
+ const response = await fetch(url, {
579
+ method,
580
+ headers: {
581
+ ...DEFAULT_HEADERS
582
+ },
583
+ credentials: "include",
584
+ // withCredentials from reference
585
+ body: new URLSearchParams(params).toString()
586
+ });
587
+ const text = await response.text();
588
+ const data = text ? JSON.parse(text) : { status: "success" };
589
+ if (!response.ok) {
590
+ console.error("[AbcWaasClient] \u274C API Error:", {
591
+ url,
592
+ method,
593
+ status: response.status,
594
+ statusText: response.statusText,
595
+ data
596
+ });
597
+ throw parseApiError({
598
+ response: {
599
+ status: response.status,
600
+ data
601
+ }
602
+ });
603
+ }
604
+ return data;
605
+ } catch (error) {
606
+ if (error.name === "AbortError") {
607
+ throw createAbcError(
608
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
609
+ "Request timeout",
610
+ error
611
+ );
612
+ }
613
+ throw parseApiError(error);
614
+ }
615
+ }
616
+ /**
617
+ * Register new SNS user (email-based registration with OTP)
618
+ * Reference: tg-wallet-frontend src/features/user/api/index.ts:80-87
619
+ * Note: Uses Talken API server, not ABC WaaS
620
+ */
621
+ async registerSnsUser(params) {
622
+ const talkenApiUrl = process.env.NEXT_PUBLIC_API_SERVER || "https://dev.walletapi.talken.io";
623
+ const url = `${talkenApiUrl}/abc/snsAdduser`;
624
+ const method = "POST";
625
+ console.log("[AbcWaasClient] \u{1F4DD} Registering SNS user");
626
+ try {
627
+ const response = await fetch(url, {
628
+ method,
629
+ headers: {
630
+ ...DEFAULT_HEADERS
631
+ },
632
+ credentials: "include",
633
+ // withCredentials from reference
634
+ body: new URLSearchParams(params).toString()
635
+ });
636
+ const text = await response.text();
637
+ const data = text ? JSON.parse(text) : { status: "success" };
638
+ if (!response.ok) {
639
+ console.error("[AbcWaasClient] \u274C API Error:", {
640
+ url,
641
+ method,
642
+ status: response.status,
643
+ statusText: response.statusText,
644
+ data
645
+ });
646
+ throw parseApiError({
647
+ response: {
648
+ status: response.status,
649
+ data
650
+ }
651
+ });
652
+ }
653
+ console.log("[AbcWaasClient] \u2705 SNS user registered successfully");
654
+ return data;
655
+ } catch (error) {
656
+ if (error.name === "AbortError") {
657
+ throw createAbcError(
658
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
659
+ "Request timeout",
660
+ error
661
+ );
662
+ }
663
+ throw parseApiError(error);
664
+ }
665
+ }
666
+ /**
667
+ * Reset/Set password for existing user
668
+ * Reference: tg-wallet-frontend src/features/user/api/index.ts:89-95
669
+ * Note: Uses Talken API server, not ABC WaaS
670
+ */
671
+ async resetPassword(params) {
672
+ const talkenApiUrl = process.env.NEXT_PUBLIC_API_SERVER || "https://dev.walletapi.talken.io";
673
+ const url = `${talkenApiUrl}/abc/initpassword`;
674
+ const method = "POST";
675
+ console.log("[AbcWaasClient] \u{1F510} Setting password");
676
+ try {
677
+ const response = await fetch(url, {
678
+ method,
679
+ headers: {
680
+ ...DEFAULT_HEADERS
681
+ },
682
+ credentials: "include",
683
+ body: new URLSearchParams(params).toString()
684
+ });
685
+ const text = await response.text();
686
+ const data = text ? JSON.parse(text) : { status: "success" };
687
+ if (!response.ok) {
688
+ console.error("[AbcWaasClient] \u274C API Error:", {
689
+ url,
690
+ method,
691
+ status: response.status,
692
+ statusText: response.statusText,
693
+ data
694
+ });
695
+ throw parseApiError({
696
+ response: {
697
+ status: response.status,
698
+ data
699
+ }
700
+ });
701
+ }
702
+ console.log("[AbcWaasClient] \u2705 Password set successfully");
703
+ return data;
704
+ } catch (error) {
705
+ if (error.name === "AbortError") {
706
+ throw createAbcError(
707
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
708
+ "Request timeout",
709
+ error
710
+ );
711
+ }
712
+ throw parseApiError(error);
713
+ }
714
+ }
715
+ /**
716
+ * Generate or recover wallet
717
+ */
718
+ async generateOrRecoverWallet(params) {
719
+ const response = await this.request(
720
+ // Use 'any' to get raw API response
721
+ ABC_ENDPOINTS.MPC_WALLETS,
722
+ // Use correct EVM endpoint, not WALLETS_V3 (Solana only)
723
+ {
724
+ method: "POST",
725
+ body: {
726
+ uid: params.uid,
727
+ pin: params.pin,
728
+ chainId: params.chainId,
729
+ network: params.network || "mainnet"
730
+ }
731
+ }
732
+ );
733
+ if (response.status === "success" && response.data) {
734
+ const raw = response.data;
735
+ console.log("[AbcWaasClient] Raw wallet API response:", {
736
+ hasUid: !!raw.uid,
737
+ hasSid: !!raw.sid,
738
+ hasKeyId: !!raw.key_id,
739
+ hasWid: !!raw.wid,
740
+ hasPvencstr: !!raw.pvencstr,
741
+ hasEncryptDevicePassword: !!raw.encryptDevicePassword,
742
+ hasEncryptedShare: !!raw.encrypted_share,
743
+ hasPubkey: !!raw.pubkey,
744
+ allKeys: Object.keys(raw)
745
+ });
746
+ return {
747
+ address: raw.sid || "",
748
+ // FIX: sid is the EVM address
749
+ keyId: raw.key_id || raw.keyId || raw.sid,
750
+ encryptedShare: raw.encrypted_share || raw.key_id || "",
751
+ // Use key_id as fallback
752
+ uid: raw.uid,
753
+ sid: raw.sid,
754
+ wid: raw.wid,
755
+ // Preserve for Bitcoin signing
756
+ pvencstr: raw.pvencstr,
757
+ // Preserve for Bitcoin signing
758
+ encryptDevicePassword: raw.encryptDevicePassword,
759
+ // Preserve for Bitcoin signing
760
+ pubkey: raw.pubkey || null
761
+ };
762
+ }
763
+ throw createAbcError(
764
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
765
+ "Failed to generate wallet",
766
+ response
767
+ );
768
+ }
769
+ /**
770
+ * Sign EVM transaction (EIP-1559)
771
+ * Based on ABC WaaS official documentation
772
+ */
773
+ async signTransaction(params) {
774
+ this.loadTokens();
775
+ const wallet = loadFromStorage("wallet" /* WALLET */);
776
+ if (!wallet) {
777
+ throw createAbcError(
778
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
779
+ "Wallet information not found in storage"
780
+ );
781
+ }
782
+ const encryptedDevicePassword = await this.secure.getEncryptPlain(
783
+ wallet.encryptDevicePassword
784
+ );
785
+ const encryptedPvencstr = await this.secure.getEncryptPlain(
786
+ wallet.pvencstr
787
+ );
788
+ const encryptedWid = await this.secure.getEncryptPlain(wallet.wid);
789
+ const secureChannelId = await this.secure.getSecureChannelId();
790
+ const network = getNetworkName(params.chainId);
791
+ const tx = params.transaction;
792
+ if (!tx.maxFeePerGas || !tx.maxPriorityFeePerGas || !tx.gasLimit || !tx.gas) {
793
+ try {
794
+ if (!tx.maxFeePerGas || !tx.maxPriorityFeePerGas) {
795
+ const gasFeeUrl = `${this.baseURL}${ABC_ENDPOINTS.GAS_SUGGESTED_FEES}?network=${network}`;
796
+ const gasFeeResponse = await fetch(gasFeeUrl, {
797
+ method: "GET",
798
+ headers: {
799
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
800
+ }
801
+ });
802
+ if (gasFeeResponse.ok) {
803
+ const gasData = await gasFeeResponse.json();
804
+ if (gasData.medium) {
805
+ if (!tx.maxFeePerGas) {
806
+ const maxFeeGwei = Number.parseFloat(
807
+ gasData.medium.suggestedMaxFeePerGas
808
+ );
809
+ tx.maxFeePerGas = Math.floor(maxFeeGwei * 1e9).toString();
810
+ }
811
+ if (!tx.maxPriorityFeePerGas) {
812
+ const priorityFeeGwei = Number.parseFloat(
813
+ gasData.medium.suggestedMaxPriorityFeePerGas
814
+ );
815
+ tx.maxPriorityFeePerGas = Math.floor(
816
+ priorityFeeGwei * 1e9
817
+ ).toString();
818
+ }
819
+ }
820
+ }
821
+ }
822
+ if (!tx.gasLimit && !tx.gas) {
823
+ const estimateBody = new URLSearchParams({
824
+ network,
825
+ to: tx.to || "0x",
826
+ // Use '0x' for contract deployment
827
+ ...tx.from ? { from: tx.from } : {},
828
+ ...tx.value ? { value: tx.value } : {},
829
+ ...tx.data && tx.data !== "0x" ? { data: tx.data } : {},
830
+ ...tx.maxFeePerGas ? { maxFeePerGas: tx.maxFeePerGas } : {},
831
+ ...tx.maxPriorityFeePerGas ? { maxPriorityFeePerGas: tx.maxPriorityFeePerGas } : {}
832
+ });
833
+ const estimateUrl = `${this.baseURL}${ABC_ENDPOINTS.GAS_ESTIMATE_EIP1559}`;
834
+ const estimateResponse = await fetch(estimateUrl, {
835
+ method: "POST",
836
+ headers: {
837
+ "Content-Type": "application/x-www-form-urlencoded",
838
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
839
+ },
840
+ body: estimateBody.toString()
841
+ });
842
+ const estimateData = await estimateResponse.json();
843
+ if (estimateResponse.ok && estimateData.result) {
844
+ tx.gasLimit = estimateData.result;
845
+ } else {
846
+ const fallbackGasLimit = "0x2dc6c0";
847
+ tx.gasLimit = fallbackGasLimit;
848
+ }
849
+ }
850
+ } catch (error) {
851
+ console.warn("Failed to fetch gas parameters from ABC WaaS:", error);
852
+ }
853
+ }
854
+ const bodyData = {
855
+ network,
856
+ encryptDevicePassword: encryptedDevicePassword,
857
+ pvencstr: encryptedPvencstr,
858
+ uid: wallet.uid,
859
+ wid: encryptedWid,
860
+ sid: getAddress(wallet.address),
861
+ // EIP-55 checksum format
862
+ // For contract deployment, 'to' is '0x' (ABC WaaS convention)
863
+ // Reference: talken-nft-drops/src/libs/abc-waas-provider/index.ts:800 - to: to ?? '0x'
864
+ to: tx.to === "0x" ? "0x" : getAddress(tx.to),
865
+ // Required by ABC WaaS API
866
+ // Always use EIP1559 type (ABC WaaS handles network-specific conversion)
867
+ type: "EIP1559",
868
+ // ourpubkey and ucpubkey are required by ABC WaaS (empty strings for now)
869
+ // Based on successful tg-wallet payload analysis
870
+ ourpubkey: "",
871
+ ucpubkey: ""
872
+ };
873
+ bodyData.from = getAddress(tx.from || wallet.address);
874
+ if (tx.value) {
875
+ let hexValue = tx.value.startsWith("0x") ? tx.value.slice(2) : tx.value;
876
+ if (hexValue.length % 2 !== 0) {
877
+ hexValue = `0${hexValue}`;
878
+ }
879
+ bodyData.value = `0x${hexValue}`;
880
+ } else {
881
+ bodyData.value = "0x0";
882
+ }
883
+ if (tx.gasLimit || tx.gas) {
884
+ const gasLimitValue = tx.gasLimit || tx.gas;
885
+ if (gasLimitValue.toString().startsWith("0x")) {
886
+ bodyData.gasLimit = gasLimitValue.toString();
887
+ } else {
888
+ bodyData.gasLimit = `0x${BigInt(gasLimitValue).toString(16)}`;
889
+ }
890
+ }
891
+ if (tx.maxFeePerGas) {
892
+ if (tx.maxFeePerGas.toString().startsWith("0x")) {
893
+ bodyData.maxFeePerGas = tx.maxFeePerGas.toString();
894
+ } else {
895
+ bodyData.maxFeePerGas = `0x${BigInt(tx.maxFeePerGas).toString(16)}`;
896
+ }
897
+ }
898
+ if (tx.maxPriorityFeePerGas) {
899
+ if (tx.maxPriorityFeePerGas.toString().startsWith("0x")) {
900
+ bodyData.maxPriorityFeePerGas = tx.maxPriorityFeePerGas.toString();
901
+ } else {
902
+ bodyData.maxPriorityFeePerGas = `0x${BigInt(tx.maxPriorityFeePerGas).toString(16)}`;
903
+ }
904
+ }
905
+ if (tx.gasPrice) {
906
+ if (tx.gasPrice.toString().startsWith("0x")) {
907
+ bodyData.gasPrice = tx.gasPrice.toString();
908
+ } else {
909
+ bodyData.gasPrice = `0x${BigInt(tx.gasPrice).toString(16)}`;
910
+ }
911
+ }
912
+ if (tx.data !== void 0 && tx.data !== null) {
913
+ bodyData.data = tx.data || "0x";
914
+ } else {
915
+ bodyData.data = "0x";
916
+ }
917
+ const bodyString = new URLSearchParams(bodyData).toString();
918
+ const url = `${this.baseURL}${ABC_ENDPOINTS.SIGN_TRANSACTION}`;
919
+ const response = await fetch(url, {
920
+ method: "POST",
921
+ headers: {
922
+ "Content-Type": "application/x-www-form-urlencoded",
923
+ "Secure-Channel": secureChannelId,
924
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
925
+ },
926
+ body: bodyString
927
+ });
928
+ const res = await response.json();
929
+ console.log("\u{1F4E1} ABC WaaS /sign response:", {
930
+ status: response.status,
931
+ ok: response.ok,
932
+ response: res
933
+ });
934
+ if (!response.ok) {
935
+ console.error("\u274C ABC WaaS API error details:", {
936
+ status: response.status,
937
+ statusText: response.statusText,
938
+ errorCode: res.code,
939
+ errorMessage: res.msg || res.message || res.detail,
940
+ fullResponse: res
941
+ });
942
+ }
943
+ if (response.status === 401 && this.refreshToken) {
944
+ try {
945
+ await this.refreshAccessToken();
946
+ const retryResponse = await fetch(url, {
947
+ method: "POST",
948
+ headers: {
949
+ "Content-Type": "application/x-www-form-urlencoded",
950
+ "Secure-Channel": secureChannelId,
951
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
952
+ },
953
+ body: bodyString
954
+ });
955
+ const retryRes = await retryResponse.json();
956
+ if (retryRes.serializedTx && retryRes.rawTx) {
957
+ return {
958
+ signature: retryRes.serializedTx,
959
+ txHash: retryRes.rawTx
960
+ };
961
+ }
962
+ throw createAbcError(
963
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
964
+ "Failed to sign transaction after token refresh",
965
+ retryRes
966
+ );
967
+ } catch (refreshError) {
968
+ console.error("Token refresh failed in signTransaction:", refreshError);
969
+ throw refreshError;
970
+ }
971
+ }
972
+ if (res.serializedTx && res.rawTx) {
973
+ return {
974
+ signature: res.serializedTx,
975
+ txHash: res.rawTx
976
+ };
977
+ }
978
+ throw createAbcError(
979
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
980
+ "Failed to sign transaction",
981
+ res
982
+ );
983
+ }
984
+ /**
985
+ * Sign message (Personal Sign - EIP-191)
986
+ * Based on tg-wallet-frontend reference implementation
987
+ */
988
+ async signMessage(params) {
989
+ this.loadTokens();
990
+ const wallet = loadFromStorage("wallet" /* WALLET */);
991
+ if (!wallet) {
992
+ throw createAbcError(
993
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
994
+ "Wallet information not found in storage"
995
+ );
996
+ }
997
+ const ethers = await import("ethers");
998
+ const messageHash = ethers.utils.hashMessage(params.message);
999
+ const hash = messageHash.startsWith("0x") ? messageHash.slice(2) : messageHash;
1000
+ const encryptedDevicePassword = await this.secure.getEncryptPlain(
1001
+ wallet.encryptDevicePassword
1002
+ );
1003
+ const encryptedPvencstr = await this.secure.getEncryptPlain(
1004
+ wallet.pvencstr
1005
+ );
1006
+ const encryptedWid = await this.secure.getEncryptPlain(wallet.wid);
1007
+ const secureChannelId = await this.secure.getSecureChannelId();
1008
+ const bodyData = {
1009
+ hash,
1010
+ encryptDevicePassword: encryptedDevicePassword,
1011
+ pvencstr: encryptedPvencstr,
1012
+ wid: encryptedWid,
1013
+ uid: wallet.uid,
1014
+ sid: wallet.sid
1015
+ };
1016
+ const bodyString = new URLSearchParams(bodyData).toString();
1017
+ const url = `${this.baseURL}${ABC_ENDPOINTS.SIGN_HASH}`;
1018
+ const response = await fetch(url, {
1019
+ method: "POST",
1020
+ headers: {
1021
+ "Content-Type": "application/x-www-form-urlencoded",
1022
+ "Secure-Channel": secureChannelId,
1023
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1024
+ },
1025
+ body: bodyString
1026
+ });
1027
+ const text = await response.text();
1028
+ if (!text) {
1029
+ throw createAbcError(
1030
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1031
+ `Empty response from sign API (status: ${response.status})`,
1032
+ { status: response.status }
1033
+ );
1034
+ }
1035
+ const res = JSON.parse(text);
1036
+ if (response.status === 401) {
1037
+ if (!this.refreshToken) {
1038
+ this.loadTokens();
1039
+ }
1040
+ if (this.refreshToken) {
1041
+ try {
1042
+ await this.refreshAccessToken();
1043
+ const retryResponse = await fetch(url, {
1044
+ method: "POST",
1045
+ headers: {
1046
+ "Content-Type": "application/x-www-form-urlencoded",
1047
+ "Secure-Channel": secureChannelId,
1048
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1049
+ },
1050
+ body: bodyString
1051
+ });
1052
+ const retryText = await retryResponse.text();
1053
+ if (!retryText) {
1054
+ throw createAbcError(
1055
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1056
+ `Empty response from retry (status: ${retryResponse.status})`,
1057
+ { status: retryResponse.status }
1058
+ );
1059
+ }
1060
+ const retryRes = JSON.parse(retryText);
1061
+ if (retryRes.status === "success" && retryRes.result?.signstr) {
1062
+ return { signature: retryRes.result.signstr };
1063
+ }
1064
+ throw createAbcError(
1065
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1066
+ "Failed to sign message after token refresh",
1067
+ retryRes
1068
+ );
1069
+ } catch (refreshError) {
1070
+ console.error("Token refresh failed in signMessage:", refreshError);
1071
+ throw refreshError;
1072
+ }
1073
+ }
1074
+ }
1075
+ if (res.signstr) {
1076
+ const signData = JSON.parse(res.signstr);
1077
+ const sigList = signData.sig_list?.[0];
1078
+ if (!sigList || !sigList.r || !sigList.s) {
1079
+ throw createAbcError(
1080
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1081
+ "Invalid signature format",
1082
+ res
1083
+ );
1084
+ }
1085
+ const v = (sigList.vsource || 0) + 27;
1086
+ const signature = sigList.r + sigList.s.slice(2) + v.toString(16).padStart(2, "0");
1087
+ return { signature };
1088
+ }
1089
+ throw createAbcError(
1090
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1091
+ "Failed to sign message",
1092
+ res
1093
+ );
1094
+ }
1095
+ /**
1096
+ * Sign typed data (EIP-712)
1097
+ * Reference: tg-wallet-frontend src/features/wallet/api/index.ts sign_typed_data()
1098
+ */
1099
+ async signTypedData(params) {
1100
+ this.loadTokens();
1101
+ const wallet = loadFromStorage("wallet" /* WALLET */);
1102
+ if (!wallet) {
1103
+ throw createAbcError(
1104
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
1105
+ "Wallet information not found in storage"
1106
+ );
1107
+ }
1108
+ const encryptedDevicePassword = await this.secure.getEncryptPlain(
1109
+ wallet.encryptDevicePassword
1110
+ );
1111
+ const encryptedPvencstr = await this.secure.getEncryptPlain(
1112
+ wallet.pvencstr
1113
+ );
1114
+ const encryptedWid = await this.secure.getEncryptPlain(wallet.wid);
1115
+ const secureChannelId = await this.secure.getSecureChannelId();
1116
+ const network = getNetworkName(wallet.chainId);
1117
+ const bodyData = {
1118
+ messageJson: params.typedData,
1119
+ // Server expects "messageJson" field
1120
+ version: "v4",
1121
+ // EIP-712 version
1122
+ network,
1123
+ // Network name (required for JSON-RPC)
1124
+ encryptDevicePassword: encryptedDevicePassword,
1125
+ pvencstr: encryptedPvencstr,
1126
+ wid: encryptedWid,
1127
+ uid: wallet.uid,
1128
+ sid: wallet.sid
1129
+ };
1130
+ const bodyString = new URLSearchParams(bodyData).toString();
1131
+ const url = `${this.baseURL}${ABC_ENDPOINTS.SIGN_TYPED}`;
1132
+ const response = await fetch(url, {
1133
+ method: "POST",
1134
+ headers: {
1135
+ "Content-Type": "application/x-www-form-urlencoded",
1136
+ "Secure-Channel": secureChannelId,
1137
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1138
+ },
1139
+ body: bodyString
1140
+ });
1141
+ const text = await response.text();
1142
+ if (!text) {
1143
+ throw createAbcError(
1144
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1145
+ `Empty response from signTypedData API (status: ${response.status})`,
1146
+ { status: response.status }
1147
+ );
1148
+ }
1149
+ const res = JSON.parse(text);
1150
+ if (response.status === 401 && this.refreshToken) {
1151
+ try {
1152
+ await this.refreshAccessToken();
1153
+ const retryResponse = await fetch(url, {
1154
+ method: "POST",
1155
+ headers: {
1156
+ "Content-Type": "application/x-www-form-urlencoded",
1157
+ "Secure-Channel": secureChannelId,
1158
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1159
+ },
1160
+ body: bodyString
1161
+ });
1162
+ const retryText = await retryResponse.text();
1163
+ if (!retryText) {
1164
+ throw createAbcError(
1165
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1166
+ `Empty response after retry (status: ${retryResponse.status})`,
1167
+ { status: retryResponse.status }
1168
+ );
1169
+ }
1170
+ const retryRes = JSON.parse(retryText);
1171
+ if (retryRes.serializedTx) {
1172
+ return { signature: retryRes.serializedTx };
1173
+ }
1174
+ throw createAbcError(
1175
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1176
+ "Failed to sign typed data after token refresh",
1177
+ retryRes
1178
+ );
1179
+ } catch (refreshError) {
1180
+ console.error("Token refresh failed in signTypedData:", refreshError);
1181
+ throw refreshError;
1182
+ }
1183
+ }
1184
+ if (res.serializedTx) {
1185
+ return { signature: res.serializedTx };
1186
+ }
1187
+ throw createAbcError(
1188
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1189
+ "Failed to sign typed data",
1190
+ res
1191
+ );
1192
+ }
1193
+ /**
1194
+ * Send raw transaction
1195
+ * Reference: tg-wallet-frontend src/features/wallet/api/index.ts sendRawTx()
1196
+ */
1197
+ async sendRawTransaction(chainId, signedTransaction) {
1198
+ const wallet = loadFromStorage("wallet" /* WALLET */);
1199
+ if (!wallet) {
1200
+ throw createAbcError(
1201
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
1202
+ "Wallet information not found in storage"
1203
+ );
1204
+ }
1205
+ const network = getNetworkName(chainId);
1206
+ const secureChannelId = await this.secure.getSecureChannelId();
1207
+ const bodyData = {
1208
+ signedSerializeTx: signedTransaction,
1209
+ network
1210
+ // rpc parameter
1211
+ };
1212
+ const bodyString = new URLSearchParams(bodyData).toString();
1213
+ const url = `${this.baseURL}${ABC_ENDPOINTS.SEND_RAW_TX}`;
1214
+ const response = await fetch(url, {
1215
+ method: "POST",
1216
+ headers: {
1217
+ "Content-Type": "application/x-www-form-urlencoded",
1218
+ "Secure-Channel": secureChannelId,
1219
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1220
+ },
1221
+ body: bodyString
1222
+ });
1223
+ const text = await response.text();
1224
+ if (!text) {
1225
+ throw createAbcError(
1226
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1227
+ `Empty response from sendRawTx API (status: ${response.status})`,
1228
+ { status: response.status }
1229
+ );
1230
+ }
1231
+ const res = JSON.parse(text);
1232
+ console.log("\u{1F4E1} sendRawTransaction response:", {
1233
+ status: response.status,
1234
+ ok: response.ok,
1235
+ response: res
1236
+ });
1237
+ if (!response.ok) {
1238
+ console.error("\u274C sendRawTransaction error:", {
1239
+ status: response.status,
1240
+ errorMessage: res.errorMessage || res.msg || res.message,
1241
+ fullResponse: res
1242
+ });
1243
+ }
1244
+ if (response.status === 401 && this.refreshToken) {
1245
+ try {
1246
+ await this.refreshAccessToken();
1247
+ const retryResponse = await fetch(url, {
1248
+ method: "POST",
1249
+ headers: {
1250
+ "Content-Type": "application/x-www-form-urlencoded",
1251
+ "Secure-Channel": secureChannelId,
1252
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1253
+ },
1254
+ body: bodyString
1255
+ });
1256
+ const retryText = await retryResponse.text();
1257
+ if (!retryText) {
1258
+ throw createAbcError(
1259
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1260
+ `Empty response after retry (status: ${retryResponse.status})`,
1261
+ { status: retryResponse.status }
1262
+ );
1263
+ }
1264
+ const retryRes = JSON.parse(retryText);
1265
+ if (retryRes.item || retryRes.txHash || retryRes.result?.txHash) {
1266
+ const txHash = retryRes.item || retryRes.txHash || retryRes.result.txHash;
1267
+ return { txHash };
1268
+ }
1269
+ throw createAbcError(
1270
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1271
+ "Failed to send transaction after token refresh",
1272
+ retryRes
1273
+ );
1274
+ } catch (refreshError) {
1275
+ console.error(
1276
+ "Token refresh failed in sendRawTransaction:",
1277
+ refreshError
1278
+ );
1279
+ throw refreshError;
1280
+ }
1281
+ }
1282
+ if (res.result) {
1283
+ if (typeof res.result === "string") {
1284
+ console.log("\u2705 Transaction hash from result:", res.result);
1285
+ return { txHash: res.result };
1286
+ }
1287
+ if (res.result.txHash) {
1288
+ return { txHash: res.result.txHash };
1289
+ }
1290
+ }
1291
+ if (res.item) {
1292
+ return { txHash: res.item };
1293
+ }
1294
+ if (res.txHash) {
1295
+ return { txHash: res.txHash };
1296
+ }
1297
+ throw createAbcError(
1298
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1299
+ "Failed to send transaction",
1300
+ res
1301
+ );
1302
+ }
1303
+ /**
1304
+ * Estimate gas for EIP-1559 transaction
1305
+ * Reference: ABC WaaS API docs - /wapi/v2/gas/estimate/eip1559
1306
+ */
1307
+ async estimateGas(params) {
1308
+ const network = getNetworkName(params.chainId);
1309
+ const secureChannelId = await this.secure.getSecureChannelId();
1310
+ const bodyData = {
1311
+ network,
1312
+ to: params.to
1313
+ };
1314
+ if (params.from)
1315
+ bodyData.from = params.from;
1316
+ if (params.value)
1317
+ bodyData.value = params.value;
1318
+ if (params.data)
1319
+ bodyData.data = params.data;
1320
+ const bodyString = new URLSearchParams(bodyData).toString();
1321
+ const url = `${this.baseURL}${ABC_ENDPOINTS.GAS_ESTIMATE_EIP1559}`;
1322
+ const response = await fetch(url, {
1323
+ method: "POST",
1324
+ headers: {
1325
+ "Content-Type": "application/x-www-form-urlencoded",
1326
+ "Secure-Channel": secureChannelId,
1327
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1328
+ },
1329
+ body: bodyString
1330
+ });
1331
+ const text = await response.text();
1332
+ if (!text) {
1333
+ throw createAbcError(
1334
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1335
+ `Empty response from gas estimation API (status: ${response.status})`,
1336
+ { status: response.status }
1337
+ );
1338
+ }
1339
+ const res = JSON.parse(text);
1340
+ if (response.status === 401 && this.refreshToken) {
1341
+ try {
1342
+ await this.refreshAccessToken();
1343
+ const retryResponse = await fetch(url, {
1344
+ method: "POST",
1345
+ headers: {
1346
+ "Content-Type": "application/x-www-form-urlencoded",
1347
+ "Secure-Channel": secureChannelId,
1348
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1349
+ },
1350
+ body: bodyString
1351
+ });
1352
+ const retryText = await retryResponse.text();
1353
+ if (!retryText) {
1354
+ throw createAbcError(
1355
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1356
+ `Empty response after retry (status: ${retryResponse.status})`,
1357
+ { status: retryResponse.status }
1358
+ );
1359
+ }
1360
+ const retryRes = JSON.parse(retryText);
1361
+ if (retryRes.result) {
1362
+ return retryRes.result;
1363
+ }
1364
+ throw createAbcError(
1365
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1366
+ "Failed to estimate gas after token refresh",
1367
+ retryRes
1368
+ );
1369
+ } catch (refreshError) {
1370
+ console.error("Token refresh failed in estimateGas:", refreshError);
1371
+ throw refreshError;
1372
+ }
1373
+ }
1374
+ if (res.result) {
1375
+ return res.result;
1376
+ }
1377
+ throw createAbcError(
1378
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1379
+ res.errorMessage || res.msg || "Failed to estimate gas",
1380
+ res
1381
+ );
1382
+ }
1383
+ /**
1384
+ * Get wallet info
1385
+ */
1386
+ async getWalletInfo(uid) {
1387
+ const response = await this.request(ABC_ENDPOINTS.INFO, {
1388
+ method: "POST",
1389
+ body: { uid }
1390
+ });
1391
+ if (response.status === "success") {
1392
+ return response.data;
1393
+ }
1394
+ throw createAbcError(
1395
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
1396
+ "Failed to get wallet info",
1397
+ response
1398
+ );
1399
+ }
1400
+ /**
1401
+ * Logout (clear tokens)
1402
+ */
1403
+ async logout() {
1404
+ this.clearTokens();
1405
+ }
1406
+ /**
1407
+ * Check if user is authenticated
1408
+ */
1409
+ isAuthenticated() {
1410
+ return !!this.accessToken && !this.isTokenExpired();
1411
+ }
1412
+ /**
1413
+ * Set tokens in client instance (useful when restoring from storage or state)
1414
+ * Call this before persistTokens() if you need to set tokens from external source
1415
+ */
1416
+ setTokens(accessToken, refreshToken, expiresAt) {
1417
+ this.accessToken = accessToken;
1418
+ this.refreshToken = refreshToken;
1419
+ this.expiresAt = expiresAt;
1420
+ }
1421
+ /**
1422
+ * Persist tokens to localStorage
1423
+ * Call this after PIN verification is complete
1424
+ *
1425
+ * SECURITY NOTE: This should only be called after full authentication (password + PIN)
1426
+ * to ensure tokens are only persisted after the user has proven ownership of both credentials.
1427
+ */
1428
+ persistTokens() {
1429
+ if (!this.accessToken || !this.refreshToken || !this.expiresAt) {
1430
+ console.warn(
1431
+ "[AbcWaasClient] \u26A0\uFE0F No tokens to persist - ensure login was successful first"
1432
+ );
1433
+ return;
1434
+ }
1435
+ console.log(
1436
+ "[AbcWaasClient] \u{1F4BE} Persisting tokens to localStorage after PIN verification"
1437
+ );
1438
+ saveToStorage("access_token" /* ACCESS_TOKEN */, this.accessToken);
1439
+ saveToStorage("refresh_token" /* REFRESH_TOKEN */, this.refreshToken);
1440
+ saveToStorage("expires_at" /* EXPIRES_AT */, this.expiresAt);
1441
+ }
1442
+ // ==========================================================================
1443
+ // Solana Methods
1444
+ // ==========================================================================
1445
+ /**
1446
+ * Generate or recover Solana wallet (Ed25519)
1447
+ * Reference: SigningService.ts:56-72, wallet/api/index.ts:56-72
1448
+ */
1449
+ async generateSolanaWallet(pin, isRecover = false) {
1450
+ const url = `${this.baseURL}/v3/wallet/${isRecover ? "recover" : "generate"}`;
1451
+ const response = await fetch(url, {
1452
+ method: "POST",
1453
+ headers: {
1454
+ "Content-Type": "application/json",
1455
+ // V3 API requires JSON, not form-urlencoded
1456
+ Accept: "application/json",
1457
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1458
+ },
1459
+ body: JSON.stringify({
1460
+ curve: "ed25519",
1461
+ password: pin
1462
+ // PIN will be encrypted by Secure Channel
1463
+ // Note: network parameter not needed for v3 API
1464
+ })
1465
+ });
1466
+ let data;
1467
+ try {
1468
+ data = await response.json();
1469
+ } catch (e) {
1470
+ console.error(`[AbcWaasClient] Failed to parse response from ${url}:`, e);
1471
+ throw createAbcError(
1472
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
1473
+ `Failed to ${isRecover ? "recover" : "generate"} Solana wallet - invalid response`,
1474
+ { status: response.status }
1475
+ );
1476
+ }
1477
+ if (!response.ok) {
1478
+ if (!isRecover && response.status === 409) {
1479
+ const error = createAbcError(
1480
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
1481
+ "Wallet already exists",
1482
+ data
1483
+ );
1484
+ error.response = { status: 409 };
1485
+ throw error;
1486
+ }
1487
+ console.error(
1488
+ `[AbcWaasClient] ${isRecover ? "Recover" : "Generate"} failed:`,
1489
+ data
1490
+ );
1491
+ throw createAbcError(
1492
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
1493
+ `Failed to ${isRecover ? "recover" : "generate"} Solana wallet`,
1494
+ data
1495
+ );
1496
+ }
1497
+ const result = data.result || data;
1498
+ if (!result.key_id || !result.public_key || !result.encrypted_share) {
1499
+ console.error(
1500
+ "[AbcWaasClient] Invalid wallet response, missing required fields:",
1501
+ result
1502
+ );
1503
+ throw createAbcError(
1504
+ "WALLET_NOT_FOUND" /* WALLET_NOT_FOUND */,
1505
+ `Invalid wallet response from ${isRecover ? "recover" : "generate"}`,
1506
+ data
1507
+ );
1508
+ }
1509
+ const solanaNetwork = getSolanaNetwork();
1510
+ const addressResult = await this.getSolanaAddress(
1511
+ result.public_key,
1512
+ solanaNetwork
1513
+ );
1514
+ return {
1515
+ uid: result.uid || "",
1516
+ sessionId: result.share_id || result.key_id,
1517
+ shareId: result.share_id || result.key_id,
1518
+ publicKey: result.public_key,
1519
+ address: addressResult,
1520
+ keyId: result.key_id,
1521
+ encryptedShare: result.encrypted_share || "",
1522
+ secretStore: result.secret_store || "",
1523
+ network: solanaNetwork
1524
+ };
1525
+ }
1526
+ /**
1527
+ * Get Solana address from Ed25519 public key
1528
+ * Reference: wallet/api/index.ts:74-82
1529
+ */
1530
+ async getSolanaAddress(publicKey, network) {
1531
+ const url = `${this.baseURL}/wapi/v2/solana/wallet/getAddress`;
1532
+ const body = new URLSearchParams({
1533
+ network,
1534
+ publicKey
1535
+ }).toString();
1536
+ const response = await fetch(url, {
1537
+ method: "POST",
1538
+ headers: {
1539
+ ...DEFAULT_HEADERS,
1540
+ // Contains 'Content-Type': 'application/x-www-form-urlencoded'
1541
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1542
+ },
1543
+ body
1544
+ });
1545
+ const data = await response.json();
1546
+ if (!response.ok || data.status !== "success") {
1547
+ throw createAbcError(
1548
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1549
+ "Failed to get Solana address",
1550
+ data
1551
+ );
1552
+ }
1553
+ return data.result?.data?.address;
1554
+ }
1555
+ /**
1556
+ * Sign Solana transaction with Ed25519
1557
+ * Reference: SigningService.ts:587-626, wallet/api/index.ts:114-122
1558
+ */
1559
+ async signSolanaTransaction(params) {
1560
+ const url = `${this.baseURL}/v3/wallet/sign`;
1561
+ const response = await fetch(url, {
1562
+ method: "POST",
1563
+ headers: {
1564
+ "Content-Type": "application/json",
1565
+ Accept: "application/json",
1566
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1567
+ },
1568
+ body: JSON.stringify({
1569
+ uid: params.uid,
1570
+ share_id: params.shareId,
1571
+ key_id: params.keyId,
1572
+ encrypted_share: params.encryptedShare,
1573
+ public_key: params.publicKey,
1574
+ message: params.message,
1575
+ // Hex-encoded transaction
1576
+ password: params.pin,
1577
+ // PIN will be encrypted
1578
+ network: params.network
1579
+ // Required: 'solana' or 'solana_devnet'
1580
+ })
1581
+ });
1582
+ const data = await response.json();
1583
+ if (!response.ok || data.status !== "success") {
1584
+ throw createAbcError(
1585
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1586
+ "Failed to sign Solana transaction",
1587
+ data
1588
+ );
1589
+ }
1590
+ return {
1591
+ signature: data.result?.signature
1592
+ };
1593
+ }
1594
+ /**
1595
+ * Sign Solana message with Ed25519
1596
+ * Reference: SigningService.ts:546-579
1597
+ */
1598
+ async signSolanaMessage(params) {
1599
+ const url = `${this.baseURL}/v3/wallet/sign`;
1600
+ const response = await fetch(url, {
1601
+ method: "POST",
1602
+ headers: {
1603
+ "Content-Type": "application/json",
1604
+ Accept: "application/json",
1605
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1606
+ },
1607
+ body: JSON.stringify({
1608
+ uid: params.uid,
1609
+ share_id: params.shareId,
1610
+ key_id: params.keyId,
1611
+ encrypted_share: params.encryptedShare,
1612
+ public_key: params.publicKey,
1613
+ message: params.message,
1614
+ // Hex-encoded message
1615
+ password: params.pin,
1616
+ network: params.network
1617
+ // Required: 'solana' or 'solana_devnet'
1618
+ })
1619
+ });
1620
+ const data = await response.json();
1621
+ if (!response.ok || data.status !== "success") {
1622
+ throw createAbcError(
1623
+ "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1624
+ "Failed to sign Solana message",
1625
+ data
1626
+ );
1627
+ }
1628
+ return {
1629
+ signature: data.result?.signature
1630
+ };
1631
+ }
1632
+ /**
1633
+ * Send Solana transaction
1634
+ * Reference: SigningService.ts:418, wallet/api/index.ts:124-132
1635
+ */
1636
+ async sendSolanaTransaction(params) {
1637
+ const url = `${this.baseURL}/wapi/v2/solana/tx/sendTransaction`;
1638
+ const response = await fetch(url, {
1639
+ method: "POST",
1640
+ headers: {
1641
+ ...DEFAULT_HEADERS,
1642
+ ...this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}
1643
+ },
1644
+ body: JSON.stringify({
1645
+ network: params.network,
1646
+ serializedTX: params.serializedTX,
1647
+ signatures: params.signatures
1648
+ })
1649
+ });
1650
+ const data = await response.json();
1651
+ if (!response.ok || data.status !== "success") {
1652
+ throw createAbcError(
1653
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
1654
+ "Failed to send Solana transaction",
1655
+ data
1656
+ );
1657
+ }
1658
+ return {
1659
+ txHash: data.result?.data
1660
+ };
1661
+ }
1662
+ };
1663
+ function createAbcWaasClient(config) {
1664
+ return new AbcWaasClient(config);
1665
+ }
1666
+
1667
+ export {
1668
+ AbcWaasClient,
1669
+ createAbcWaasClient
1670
+ };