@tomo-inc/cubist-wallet-sdk 0.0.4

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.
@@ -0,0 +1,494 @@
1
+ import { CubeAccountService } from "./cube-account";
2
+ import { CubeSignerClient } from "./cube-libs";
3
+
4
+ import { ApiClient, BizType, CubeRes, MfaAnswer, MfaConfig, MfaInfo, MfaReceipt, MfaType, TotpInfo } from "./types";
5
+
6
+ import { isEmail } from "@tomo-inc/wallet-utils";
7
+ import { getMfaInfoConfig } from "const";
8
+ import { addPasskey, closeWindow, CubeRelayConfig, verifyPasskey } from "./passkey";
9
+ import { applyLocalDevFixes } from "./utils";
10
+
11
+ export class CubeMfaService {
12
+ private static instance: CubeMfaService;
13
+
14
+ private cubeAccountService: CubeAccountService | null;
15
+ public cubeSignerClient: CubeSignerClient | null;
16
+ public apiClient: ApiClient;
17
+
18
+ private mfaType: MfaType;
19
+ private mfaAnswer: MfaAnswer | null;
20
+ private mfaChallenge: any;
21
+ private registerChallenge: any;
22
+
23
+ public constructor() {
24
+ this.mfaType = "fido";
25
+ this.cubeAccountService = null;
26
+ this.cubeSignerClient = null;
27
+ this.apiClient = null;
28
+ this.mfaAnswer = null;
29
+ this.mfaChallenge = null;
30
+ this.registerChallenge = null;
31
+ }
32
+
33
+ public static getInstance() {
34
+ if (!CubeMfaService.instance) {
35
+ CubeMfaService.instance = new CubeMfaService();
36
+ }
37
+ return CubeMfaService.instance;
38
+ }
39
+
40
+ public init(cubeAccountService: CubeAccountService) {
41
+ this.cubeSignerClient = cubeAccountService.cubeSignerClient;
42
+ this.apiClient = cubeAccountService.apiClient;
43
+ this.cubeAccountService = cubeAccountService;
44
+ return this;
45
+ }
46
+
47
+ public setMfaType(mfaType: MfaType): void {
48
+ if (!mfaType) {
49
+ throw new Error("mfaType is required");
50
+ }
51
+ this.mfaType = mfaType;
52
+ }
53
+
54
+ public setMfaAnswer(mfaAnswer: MfaAnswer): void {
55
+ this.mfaAnswer = mfaAnswer;
56
+ }
57
+
58
+ public async getMfaInfo(bizType?: BizType): Promise<MfaInfo | null> {
59
+ if (!bizType) {
60
+ throw new Error("mfaRequestType is required");
61
+ }
62
+
63
+ const MfaInfoConfig = getMfaInfoConfig(this.cubeAccountService?.config?.cubeStage || "gamma");
64
+ const config = MfaInfoConfig[bizType];
65
+ if (!config) {
66
+ throw new Error("mfaRequestType is not supported");
67
+ }
68
+
69
+ //['Totp', 'EmailOtp#172800', 'Fido']
70
+ const { method, path } = config;
71
+
72
+ const mfaList: MfaInfo[] = await this.apiClient.mfaList();
73
+ const mfaInfo = mfaList.find((mfa) => {
74
+ const _method = mfa?.request.method;
75
+ const _path = mfa?.request.path + "/";
76
+ return _method === method && _path.indexOf(path) > -1;
77
+ });
78
+ if (!mfaInfo) {
79
+ return null;
80
+ }
81
+
82
+ let mfaRequired: any = null;
83
+ const allowed_mfa_types: string[] = mfaInfo?.status?.allowed_mfa_types || [];
84
+ for (const type of allowed_mfa_types) {
85
+ mfaRequired = mfaRequired || {};
86
+ const types = type.toLowerCase().split("#");
87
+ mfaRequired[types[0]] = types[1] ? Number(types[1]) : true;
88
+ }
89
+
90
+ const { receipt, created_at } = mfaInfo;
91
+ let emailOtpRemainTime = 0;
92
+ if (typeof mfaRequired.emailOtp === "number") {
93
+ const remainTime = mfaRequired.emailOtp - (Date.now() / 1000 - created_at);
94
+ emailOtpRemainTime = Math.max(0, Math.ceil(remainTime));
95
+ }
96
+ if (mfaRequired.emailOtp) {
97
+ mfaRequired.emailOtpRemainTime = emailOtpRemainTime;
98
+ }
99
+
100
+ const verifyStatus = receipt !== null ? "approved" : "pending";
101
+
102
+ return { ...mfaInfo, verifyStatus, mfaRequired };
103
+ }
104
+
105
+ public async answerRegister(code: string): Promise<CubeRes> {
106
+ const challenge = this.registerChallenge;
107
+ if (!challenge) {
108
+ throw new Error("challenge is required");
109
+ }
110
+ try {
111
+ const receipt = (await challenge.answer(code)) || null;
112
+ this.registerChallenge = null;
113
+ return {
114
+ success: true,
115
+ data: receipt,
116
+ };
117
+ } catch (error: any) {
118
+ return {
119
+ success: false,
120
+ message: error?.message || "answer error",
121
+ error,
122
+ };
123
+ }
124
+ }
125
+
126
+ public async executeBizWithMfa(bizType: BizType, mfaInfo?: MfaInfo | null): Promise<CubeRes> {
127
+ if (!bizType) {
128
+ throw new Error("bizType is required");
129
+ }
130
+ if (!mfaInfo) {
131
+ mfaInfo = (await this.getMfaInfo(bizType)) || null;
132
+ }
133
+ if (!mfaInfo?.id) {
134
+ throw new Error("mfa not exists, please create mfa first");
135
+ }
136
+
137
+ const { success, data: receipt } = (await this.approvalMfa(mfaInfo?.id || "")) || {};
138
+ if (!success) {
139
+ return {
140
+ success: false,
141
+ message: `approval with ${this.mfaType} error.`,
142
+ };
143
+ }
144
+
145
+ if (bizType === "createSession") {
146
+ const result = await this.cubeAccountService?.createSession(receipt);
147
+ return {
148
+ success: !!result,
149
+ message: !result ? "create session error" : "",
150
+ data: result,
151
+ };
152
+ }
153
+
154
+ if (bizType === "addFido") {
155
+ const name = mfaInfo.request?.body?.name;
156
+ const res = await this.addFido(name, receipt);
157
+ return {
158
+ success: !!res,
159
+ message: !res ? "addFido error" : "",
160
+ data: res,
161
+ };
162
+ }
163
+
164
+ if (bizType === "deleteFido") {
165
+ const path = mfaInfo?.request?.path || "";
166
+ const fidoId = path.split("/user/me/fido/")[1] || "";
167
+ if (!fidoId) {
168
+ throw new Error("fidoId is required");
169
+ }
170
+ const res = await this.deleteFido(decodeURIComponent(fidoId), receipt);
171
+ return {
172
+ success: !!res,
173
+ message: !res ? "deleteFido error" : "",
174
+ data: res,
175
+ };
176
+ }
177
+
178
+ if (bizType === "registerTotp") {
179
+ const issuer = mfaInfo.request?.body?.issuer;
180
+ const res = await this.registerTotp(issuer, receipt);
181
+ return {
182
+ success: !!res,
183
+ message: !res ? "registerTotp error" : "",
184
+ data: res,
185
+ };
186
+ }
187
+
188
+ if (bizType === "deleteTotp") {
189
+ const res = await this.deleteTotp(receipt);
190
+ return {
191
+ success: !!res,
192
+ message: !res ? "deleteTotp error" : "",
193
+ data: res,
194
+ };
195
+ }
196
+
197
+ if (bizType === "registerEmailOtp") {
198
+ const email = mfaInfo.request?.body?.email;
199
+ const res = await this.registerEmailOtp(email, receipt);
200
+ return {
201
+ success: !!res,
202
+ message: !res ? "registerEmailOtp error" : "",
203
+ data: res,
204
+ };
205
+ }
206
+
207
+ const cubeExportService = this.cubeAccountService?.cubeExportService;
208
+ if (bizType === "initExport") {
209
+ const res = await cubeExportService?.initExport(receipt);
210
+ return {
211
+ success: !!res,
212
+ message: !res ? "init export error" : "",
213
+ data: res,
214
+ };
215
+ }
216
+
217
+ if (bizType === "completeExport") {
218
+ const res = await cubeExportService?.completeExport(receipt);
219
+ return {
220
+ success: !!res,
221
+ message: !res ? "complete export error" : "",
222
+ data: res,
223
+ };
224
+ }
225
+
226
+ return {
227
+ success: false,
228
+ message: "bizType is not supported",
229
+ };
230
+ }
231
+
232
+ public async approvalMfa(mfaId: string): Promise<CubeRes> {
233
+ const apis = {
234
+ totp: "approvalTotp",
235
+ emailOtp: "approvalEmailOtp",
236
+ fido: "approvalFido",
237
+ };
238
+ const mfaType: MfaType = this.mfaType;
239
+
240
+ if (!mfaType || !apis[mfaType]) {
241
+ return {
242
+ success: false,
243
+ message: "mfaType is required",
244
+ };
245
+ }
246
+ let receipt: MfaReceipt | null = null;
247
+ try {
248
+ switch (mfaType) {
249
+ case "totp":
250
+ receipt = await this.approvalTotp(mfaId);
251
+ break;
252
+ case "emailOtp":
253
+ receipt = await this.approvalEmailOtp(mfaId);
254
+ break;
255
+ case "fido":
256
+ receipt = await this.approvalFido(mfaId);
257
+ break;
258
+ default:
259
+ return {
260
+ success: false,
261
+ message: "Unsupported MFA type",
262
+ };
263
+ }
264
+ } catch (error) {
265
+ return {
266
+ success: false,
267
+ message: `approval ${mfaType} error: ${error instanceof Error ? error.message : "Unknown error"}`,
268
+ };
269
+ }
270
+ if (!receipt) {
271
+ return {
272
+ success: false,
273
+ message: `approval ${mfaType} error.`,
274
+ };
275
+ }
276
+ return {
277
+ success: true,
278
+ data: receipt,
279
+ };
280
+ }
281
+
282
+ private getPasskeyConfig(): CubeRelayConfig {
283
+ const { config } = this.cubeAccountService || {};
284
+ delete config?.cubeSalt;
285
+ delete config?.jwtToken;
286
+ return config as CubeRelayConfig;
287
+ }
288
+
289
+ public passkeyClear(): void {
290
+ closeWindow();
291
+ }
292
+
293
+ public async addFido(fidoName: string, receipt?: MfaReceipt): Promise<any> {
294
+ const config = this.getPasskeyConfig();
295
+ return await addPasskey({ fidoName, receipt }, config);
296
+ }
297
+
298
+ public async addFidoLocal(fidoName: string, receipt?: MfaReceipt): Promise<MfaInfo | null | boolean> {
299
+ if (!fidoName || typeof fidoName !== "string") {
300
+ throw new Error("name is required");
301
+ }
302
+
303
+ try {
304
+ const mfaConfig = (await this.cubeAccountService?.getMfaConfig()) as MfaConfig;
305
+ const fidos = mfaConfig?.fido?.data || [];
306
+ const existingFido = fidos.find((fido) => fido?.name === fidoName);
307
+ if (existingFido) {
308
+ throw new Error(`FIDO key with name "${fidoName}" already exists`);
309
+ }
310
+
311
+ const cubeSignerClient = this.cubeSignerClient;
312
+ const addFidoResp: any = await cubeSignerClient?.addFido(fidoName, receipt);
313
+ if (addFidoResp.requiresMfa()) {
314
+ return await this.getMfaInfo("addFido");
315
+ }
316
+
317
+ const config = {
318
+ userId: mfaConfig?.account?.userId,
319
+ rpId: this.cubeAccountService?.config?.rpId || window.location.hostname,
320
+ fidoName,
321
+ };
322
+ const challenge = applyLocalDevFixes("add", addFidoResp.data(), config);
323
+ await challenge.createCredentialAndAnswer();
324
+ return true;
325
+ } catch (error) {
326
+ console.error("addFido error", error);
327
+ return false;
328
+ }
329
+ }
330
+
331
+ public async deleteFido(fidoId: string, receipt?: MfaReceipt): Promise<MfaInfo | null> {
332
+ try {
333
+ const cubeSignerClient = this.cubeSignerClient;
334
+ const deleteFidoResp: any = await cubeSignerClient?.deleteFido(fidoId, receipt);
335
+ if (deleteFidoResp.requiresMfa()) {
336
+ return await this.getMfaInfo("deleteFido");
337
+ }
338
+ return deleteFidoResp.data();
339
+ } catch (error) {
340
+ return null;
341
+ }
342
+ }
343
+
344
+ public async approvalFido(mfaId: string): Promise<MfaReceipt> {
345
+ const config = this.getPasskeyConfig();
346
+ const receipt = await verifyPasskey({ mfaId }, config);
347
+ return receipt;
348
+ }
349
+
350
+ public async approvalFidoLocal(mfaId: string): Promise<MfaReceipt> {
351
+ const apiClient = this.apiClient;
352
+
353
+ const mfaConfig = (await this.cubeAccountService?.getMfaConfig()) as MfaConfig;
354
+ //mfaVoteFidoComplete
355
+ let challenge = await apiClient.mfaFidoInit(mfaId);
356
+
357
+ const config = {
358
+ userId: mfaConfig?.account?.userId,
359
+ rpId: this.cubeAccountService?.config?.rpId || window.location.hostname,
360
+ fidoName: "",
361
+ };
362
+ challenge = applyLocalDevFixes("approval", challenge, config);
363
+
364
+ const mfa = await challenge.createCredentialAndAnswer("approve");
365
+ const receipt = await mfa.receipt();
366
+ return receipt;
367
+ }
368
+
369
+ public async registerTotp(issuer: string, receipt?: MfaReceipt): Promise<MfaInfo | TotpInfo | null> {
370
+ if (issuer === "") {
371
+ throw new Error("issuer are required");
372
+ }
373
+
374
+ const totpResp = await this.apiClient.userTotpResetInit(issuer, receipt);
375
+ if (totpResp.requiresMfa()) {
376
+ return await this.getMfaInfo("registerTotp");
377
+ }
378
+
379
+ const challenge = totpResp.data();
380
+ this.registerChallenge = challenge;
381
+
382
+ const searchParams = new URL(challenge?.url).searchParams;
383
+
384
+ const totpInfo: TotpInfo = {
385
+ id: challenge?.id,
386
+ url: challenge?.url,
387
+ secret: searchParams.get("secret") || "",
388
+ issuer: searchParams.get("issuer") || issuer,
389
+ };
390
+ return totpInfo;
391
+ }
392
+
393
+ public async deleteTotp(receipt?: MfaReceipt): Promise<MfaInfo | null> {
394
+ const deleteTotpResp = await this.apiClient.userTotpDelete(receipt);
395
+
396
+ if (deleteTotpResp.requiresMfa()) {
397
+ return await this.getMfaInfo("deleteTotp");
398
+ }
399
+
400
+ return deleteTotpResp.data();
401
+ }
402
+
403
+ public async approvalTotp(mfaId: string): Promise<MfaReceipt> {
404
+ if (!mfaId) {
405
+ throw new Error("MFA ID not found");
406
+ }
407
+
408
+ const totpCode = this.mfaAnswer?.totpCode;
409
+ if (!totpCode || !/^\d{6}$/.test(totpCode)) {
410
+ console.error("TOTP code is required");
411
+ throw new Error("TOTP code is required");
412
+ }
413
+
414
+ const mfa = await this.apiClient.mfaVoteTotp(mfaId, totpCode, "approve");
415
+ const mfaOrgId = this.cubeSignerClient?.org()?.id || this.cubeAccountService?.cubeOrgId || "";
416
+ const receipt = {
417
+ mfaId: mfa.id,
418
+ mfaConf: mfa?.receipt?.confirmation,
419
+ mfaOrgId,
420
+ };
421
+ return receipt;
422
+ }
423
+
424
+ public async registerEmailOtp(email: string, receipt?: MfaReceipt): Promise<MfaInfo | null> {
425
+ if (!email || !isEmail(email)) {
426
+ throw new Error("email is required");
427
+ }
428
+
429
+ const cubeSignerClient = await this.cubeAccountService?.getOidcClient();
430
+ const apiClient = cubeSignerClient.apiClient;
431
+
432
+ const params = {
433
+ email,
434
+ allow_otp_login: true,
435
+ };
436
+ const registerEmailOtpResp = await apiClient.userEmailResetInit(params, receipt);
437
+ if (registerEmailOtpResp.requiresMfa()) {
438
+ return await this.getMfaInfo("registerEmailOtp");
439
+ }
440
+
441
+ this.registerChallenge = registerEmailOtpResp.data();
442
+ return this.registerChallenge;
443
+ }
444
+
445
+ //48hour countdown
446
+ //mfaVoteEmailComplete
447
+ public async approvalEmailOtp(mfaId: string): Promise<MfaReceipt | null> {
448
+ if (!mfaId) {
449
+ throw new Error("MFA ID not found");
450
+ }
451
+
452
+ if (!this.mfaChallenge) {
453
+ const mfaChallenge = await this.apiClient.mfaVoteEmailInit(mfaId, "approve");
454
+ this.mfaChallenge = mfaChallenge;
455
+ return null;
456
+ }
457
+
458
+ //answer with code by user input
459
+ let emailCode = this.mfaAnswer?.emailCode || "";
460
+ emailCode = emailCode.split(/\s/).join("");
461
+
462
+ if (!emailCode) {
463
+ throw new Error("emailCode is required");
464
+ }
465
+ try {
466
+ const mfa = await this.mfaChallenge.answer(emailCode);
467
+ const mfaOrgId = this.cubeSignerClient?.org()?.id || this.cubeAccountService?.cubeOrgId || "";
468
+ const receipt = {
469
+ mfaId: mfa.id,
470
+ mfaConf: mfa?.receipt?.confirmation,
471
+ mfaOrgId,
472
+ };
473
+ this.mfaChallenge = null;
474
+
475
+ return receipt;
476
+ } catch (error) {
477
+ console.warn("approvalEmailOtp error", error);
478
+ return null;
479
+ }
480
+ }
481
+
482
+ public async sendEmailCode(mfaId: string): Promise<boolean> {
483
+ if (!mfaId) {
484
+ throw new Error("mfaId is required");
485
+ }
486
+ try {
487
+ const mfaChallenge = await this.apiClient.mfaVoteEmailInit(mfaId, "approve");
488
+ this.mfaChallenge = mfaChallenge;
489
+ return true;
490
+ } catch (error) {
491
+ return false;
492
+ }
493
+ }
494
+ }
@@ -0,0 +1,210 @@
1
+ import { ChainTypes } from "@tomo-inc/wallet-utils";
2
+ import {
3
+ getKeys,
4
+ signDogeMessage,
5
+ signDogePsbt,
6
+ signEvmMessage,
7
+ signEvmTransaction,
8
+ signEvmTypedData,
9
+ signSolanaMessage,
10
+ signSolanaTransaction,
11
+ signTronMessage,
12
+ signTronRawTransaction,
13
+ signTronTypeDaya,
14
+ TypedData,
15
+ } from "./cube-libs";
16
+
17
+ export const recoverEVMTxData = (unsignedTx: string, numberType?: "bigint" | "string") => {
18
+ try {
19
+ const { data, types } = JSON.parse(unsignedTx);
20
+
21
+ // https://viem.sh/docs/actions/wallet/signTransaction#value-optional
22
+
23
+ for (const prop in types) {
24
+ if (types[prop] === "bigint") {
25
+ data[prop] = BigInt(data[prop]);
26
+ if (numberType === "string") {
27
+ data[prop] = data[prop].toString();
28
+ }
29
+ }
30
+ }
31
+ return data;
32
+ } catch (error) {
33
+ throw new Error("Invalid unsigned transaction");
34
+ return {};
35
+ }
36
+ };
37
+
38
+ export class CubeSignService {
39
+ private static instance: CubeSignService;
40
+ private cubeSession: any;
41
+ private services: any;
42
+
43
+ public async init({ cubeSession }: { cubeSession: any }) {
44
+ this.cubeSession = cubeSession;
45
+ if (!this.services) {
46
+ // return the services map for sign
47
+ this.services = await getKeys(cubeSession);
48
+ }
49
+ }
50
+
51
+ public static getInstance() {
52
+ if (!CubeSignService.instance) {
53
+ CubeSignService.instance = new CubeSignService();
54
+ }
55
+ return CubeSignService.instance;
56
+ }
57
+
58
+ /**
59
+ * Get specific services by chainType and address
60
+ * @param chainType Chain type (EVM, TRON, SOL, DOGE)
61
+ * @param address Wallet address
62
+ * @returns The matching services object or null if not found
63
+ */
64
+ public async getServiceByChainAndAddress(chainType: string, address: string): Promise<any> {
65
+ if (!this.services) {
66
+ this.services = await getKeys(this.cubeSession);
67
+ }
68
+
69
+ // Create the expected key ID based on chainType and address
70
+ const keyId = this.buildKeyId(chainType, address);
71
+
72
+ // Find the sign service with matching ID
73
+ const service = this.services.find((k: any) => k.id === keyId);
74
+
75
+ if (!service) {
76
+ console.warn(`service not found for chainType: ${chainType}`);
77
+ return null;
78
+ }
79
+
80
+ return service;
81
+ }
82
+
83
+ /**
84
+ * Build key ID from chainType and address following the pattern in the services data
85
+ * @param chainType Chain type
86
+ * @param address Wallet address
87
+ * @returns Key ID string
88
+ */
89
+ private buildKeyId(chainType: string, address: string): string {
90
+ switch (chainType) {
91
+ case ChainTypes.EVM:
92
+ return `Key#${address}`;
93
+ case ChainTypes.TRON:
94
+ return `Key#Tron_${address}`;
95
+ case ChainTypes.SOL:
96
+ return `Key#Solana_${address}`;
97
+ case ChainTypes.DOGE:
98
+ return `Key#Doge_${address}`;
99
+ default:
100
+ return `Key#${address}`;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Unified transaction signing method for all supported chains
106
+ * @param chainType Chain type (EVM, SOL, DOGE, TRON, BTC)
107
+ * @param address User's wallet address
108
+ * @param params Signing parameters specific to each chain (always JSON stringified from social account)
109
+ * @returns Signed transaction
110
+ */
111
+ public async signTransaction(chainType: string, address: string, params: any): Promise<string> {
112
+ const sessionInfo = this?.cubeSession;
113
+ const service = await this.getServiceByChainAndAddress(chainType, address);
114
+
115
+ if (!service) {
116
+ throw new Error(`service not found for chain ${chainType}`);
117
+ }
118
+
119
+ // Parse the JSON stringified params from social account
120
+ const parsedParams = typeof params === "string" ? JSON.parse(params) : params;
121
+
122
+ switch (chainType) {
123
+ case ChainTypes.EVM: {
124
+ // EVM expects: { data: preparedTx, types: preparedTxPropsType }
125
+ const transaction = recoverEVMTxData(params, "string");
126
+ return await signEvmTransaction(service, transaction, transaction.chainId, sessionInfo);
127
+ }
128
+
129
+ case ChainTypes.SOL: {
130
+ // Solana expects: { rawTransaction: string, walletId: -1, rpcUrl: string }
131
+ return await signSolanaTransaction(service, parsedParams.rawTransaction, sessionInfo);
132
+ }
133
+
134
+ case ChainTypes.DOGE: {
135
+ // DOGE expects: { psbtBase64: string }
136
+ return await signDogePsbt(service, parsedParams.psbtBase64, sessionInfo);
137
+ }
138
+
139
+ case ChainTypes.TRON: {
140
+ // TRON expects: { type: "signRawTx", data: { rawTransaction: string, walletId: -1 }}
141
+ const rawTransaction = parsedParams.data?.rawTransaction || parsedParams.rawTransaction;
142
+ return await signTronRawTransaction(service, rawTransaction, sessionInfo);
143
+ }
144
+ default:
145
+ throw new Error(`Unsupported chain type: ${chainType}`);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Unified message signing method for all supported chains
151
+ * @param chainType Chain type (EVM, SOL, DOGE, TRON)
152
+ * @param address User's wallet address
153
+ * @param message Message to be signed
154
+ * @returns Message signature
155
+ */
156
+ public async signMessage(chainType: string, address: string, message: string): Promise<string> {
157
+ const sessionInfo = this?.cubeSession;
158
+ const service = await this.getServiceByChainAndAddress(chainType, address);
159
+
160
+ if (!service) {
161
+ throw new Error(`service not found for chain ${chainType}`);
162
+ }
163
+
164
+ switch (chainType) {
165
+ case ChainTypes.EVM:
166
+ return await signEvmMessage(service, message, sessionInfo);
167
+
168
+ case ChainTypes.SOL:
169
+ return await signSolanaMessage(service, message, sessionInfo);
170
+
171
+ case ChainTypes.DOGE:
172
+ return (await signDogeMessage(service, message, sessionInfo)).signature;
173
+
174
+ case ChainTypes.TRON:
175
+ return await signTronMessage(service, message, sessionInfo);
176
+
177
+ default:
178
+ throw new Error(`Unsupported chain type: ${chainType}`);
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Unified typed data signing method (supports EVM and TRON)
184
+ * @param chainType Chain type (EVM, TRON)
185
+ * @param address User's wallet address
186
+ * @param typedData Typed data to be signed
187
+ * @returns Typed data signature
188
+ */
189
+ public async signTypedData(chainType: string, address: string, typedData: TypedData): Promise<string> {
190
+ const sessionInfo = this?.cubeSession;
191
+ const service = await this.getServiceByChainAndAddress(chainType, address);
192
+
193
+ if (!service) {
194
+ throw new Error(`service not found for chain ${chainType}`);
195
+ }
196
+
197
+ switch (chainType) {
198
+ case ChainTypes.EVM:
199
+ return await signEvmTypedData(service, typedData as TypedData, sessionInfo);
200
+
201
+ case ChainTypes.TRON: {
202
+ // For Tron EIP712 signing, we convert TypedData to JSON string and use signTronMessage
203
+ return await signTronTypeDaya(service, typedData.message as string, sessionInfo);
204
+ }
205
+
206
+ default:
207
+ throw new Error(`Unsupported chain type: ${chainType}`);
208
+ }
209
+ }
210
+ }