@enclave-hq/wallet-sdk 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +62 -37
- package/dist/index.d.ts +62 -37
- package/dist/index.js +360 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +360 -63
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +33 -8
- package/dist/react/index.d.ts +33 -8
- package/dist/react/index.js +369 -34
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +369 -35
- package/dist/react/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/react/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { createContext, useState, useCallback, useEffect, useContext } from 'react';
|
|
2
2
|
import EventEmitter from 'eventemitter3';
|
|
3
|
+
import { ChainType as ChainType$1 } from '@enclave-hq/chain-utils';
|
|
3
4
|
import { createWalletClient, http, createPublicClient, custom, isAddress, getAddress } from 'viem';
|
|
4
5
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
5
6
|
|
|
@@ -32,6 +33,7 @@ var TypedEventEmitter = class {
|
|
|
32
33
|
return this;
|
|
33
34
|
}
|
|
34
35
|
};
|
|
36
|
+
var ChainType = ChainType$1;
|
|
35
37
|
|
|
36
38
|
// src/core/errors.ts
|
|
37
39
|
var WalletSDKError = class _WalletSDKError extends Error {
|
|
@@ -212,14 +214,15 @@ function formatEVMAddress(address) {
|
|
|
212
214
|
}
|
|
213
215
|
return getAddress(address);
|
|
214
216
|
}
|
|
215
|
-
|
|
216
|
-
// src/utils/chain-info.ts
|
|
217
217
|
var CHAIN_INFO = {
|
|
218
218
|
// EVM Mainnet
|
|
219
219
|
1: {
|
|
220
220
|
id: 1,
|
|
221
|
+
slip44: 60,
|
|
222
|
+
// Ethereum SLIP-44
|
|
221
223
|
name: "Ethereum Mainnet",
|
|
222
|
-
chainType:
|
|
224
|
+
chainType: ChainType$1.EVM,
|
|
225
|
+
symbol: "ETH",
|
|
223
226
|
nativeCurrency: {
|
|
224
227
|
name: "Ether",
|
|
225
228
|
symbol: "ETH",
|
|
@@ -232,7 +235,8 @@ var CHAIN_INFO = {
|
|
|
232
235
|
11155111: {
|
|
233
236
|
id: 11155111,
|
|
234
237
|
name: "Sepolia Testnet",
|
|
235
|
-
chainType:
|
|
238
|
+
chainType: ChainType$1.EVM,
|
|
239
|
+
symbol: "ETH",
|
|
236
240
|
nativeCurrency: {
|
|
237
241
|
name: "Sepolia Ether",
|
|
238
242
|
symbol: "ETH",
|
|
@@ -244,8 +248,11 @@ var CHAIN_INFO = {
|
|
|
244
248
|
// Binance Smart Chain
|
|
245
249
|
56: {
|
|
246
250
|
id: 56,
|
|
251
|
+
slip44: 714,
|
|
252
|
+
// BSC SLIP-44
|
|
247
253
|
name: "BNB Smart Chain",
|
|
248
|
-
chainType:
|
|
254
|
+
chainType: ChainType$1.EVM,
|
|
255
|
+
symbol: "BNB",
|
|
249
256
|
nativeCurrency: {
|
|
250
257
|
name: "BNB",
|
|
251
258
|
symbol: "BNB",
|
|
@@ -257,7 +264,8 @@ var CHAIN_INFO = {
|
|
|
257
264
|
97: {
|
|
258
265
|
id: 97,
|
|
259
266
|
name: "BNB Smart Chain Testnet",
|
|
260
|
-
chainType:
|
|
267
|
+
chainType: ChainType$1.EVM,
|
|
268
|
+
symbol: "BNB",
|
|
261
269
|
nativeCurrency: {
|
|
262
270
|
name: "BNB",
|
|
263
271
|
symbol: "BNB",
|
|
@@ -269,8 +277,11 @@ var CHAIN_INFO = {
|
|
|
269
277
|
// Polygon
|
|
270
278
|
137: {
|
|
271
279
|
id: 137,
|
|
280
|
+
slip44: 966,
|
|
281
|
+
// Polygon SLIP-44
|
|
272
282
|
name: "Polygon Mainnet",
|
|
273
|
-
chainType:
|
|
283
|
+
chainType: ChainType$1.EVM,
|
|
284
|
+
symbol: "MATIC",
|
|
274
285
|
nativeCurrency: {
|
|
275
286
|
name: "MATIC",
|
|
276
287
|
symbol: "MATIC",
|
|
@@ -282,7 +293,8 @@ var CHAIN_INFO = {
|
|
|
282
293
|
80002: {
|
|
283
294
|
id: 80002,
|
|
284
295
|
name: "Polygon Amoy Testnet",
|
|
285
|
-
chainType:
|
|
296
|
+
chainType: ChainType$1.EVM,
|
|
297
|
+
symbol: "MATIC",
|
|
286
298
|
nativeCurrency: {
|
|
287
299
|
name: "MATIC",
|
|
288
300
|
symbol: "MATIC",
|
|
@@ -294,8 +306,11 @@ var CHAIN_INFO = {
|
|
|
294
306
|
// Tron
|
|
295
307
|
195: {
|
|
296
308
|
id: 195,
|
|
309
|
+
slip44: 195,
|
|
310
|
+
// Tron SLIP-44
|
|
297
311
|
name: "Tron Mainnet",
|
|
298
|
-
chainType:
|
|
312
|
+
chainType: ChainType$1.TRON,
|
|
313
|
+
symbol: "TRX",
|
|
299
314
|
nativeCurrency: {
|
|
300
315
|
name: "TRX",
|
|
301
316
|
symbol: "TRX",
|
|
@@ -307,8 +322,11 @@ var CHAIN_INFO = {
|
|
|
307
322
|
// Arbitrum
|
|
308
323
|
42161: {
|
|
309
324
|
id: 42161,
|
|
325
|
+
slip44: 1042161,
|
|
326
|
+
// Custom SLIP-44 (1000000 + 42161)
|
|
310
327
|
name: "Arbitrum One",
|
|
311
|
-
chainType:
|
|
328
|
+
chainType: ChainType$1.EVM,
|
|
329
|
+
symbol: "ETH",
|
|
312
330
|
nativeCurrency: {
|
|
313
331
|
name: "Ether",
|
|
314
332
|
symbol: "ETH",
|
|
@@ -320,8 +338,11 @@ var CHAIN_INFO = {
|
|
|
320
338
|
// Optimism
|
|
321
339
|
10: {
|
|
322
340
|
id: 10,
|
|
341
|
+
slip44: 1000010,
|
|
342
|
+
// Custom SLIP-44 (1000000 + 10)
|
|
323
343
|
name: "Optimism",
|
|
324
|
-
chainType:
|
|
344
|
+
chainType: ChainType$1.EVM,
|
|
345
|
+
symbol: "ETH",
|
|
325
346
|
nativeCurrency: {
|
|
326
347
|
name: "Ether",
|
|
327
348
|
symbol: "ETH",
|
|
@@ -333,8 +354,11 @@ var CHAIN_INFO = {
|
|
|
333
354
|
// Avalanche
|
|
334
355
|
43114: {
|
|
335
356
|
id: 43114,
|
|
357
|
+
slip44: 9e3,
|
|
358
|
+
// Avalanche SLIP-44
|
|
336
359
|
name: "Avalanche C-Chain",
|
|
337
|
-
chainType:
|
|
360
|
+
chainType: ChainType$1.EVM,
|
|
361
|
+
symbol: "AVAX",
|
|
338
362
|
nativeCurrency: {
|
|
339
363
|
name: "AVAX",
|
|
340
364
|
symbol: "AVAX",
|
|
@@ -353,26 +377,34 @@ var MetaMaskAdapter = class extends BrowserWalletAdapter {
|
|
|
353
377
|
constructor() {
|
|
354
378
|
super(...arguments);
|
|
355
379
|
this.type = "metamask" /* METAMASK */;
|
|
356
|
-
this.chainType =
|
|
380
|
+
this.chainType = ChainType.EVM;
|
|
357
381
|
this.name = "MetaMask";
|
|
358
382
|
this.icon = "https://upload.wikimedia.org/wikipedia/commons/3/36/MetaMask_Fox.svg";
|
|
359
383
|
this.walletClient = null;
|
|
360
384
|
this.publicClient = null;
|
|
361
385
|
/**
|
|
362
386
|
* 处理账户变化
|
|
387
|
+
*
|
|
388
|
+
* 注意:MetaMask 的行为
|
|
389
|
+
* - 切换到已连接的账户:触发事件,返回新账户 ['0xNewAddress']
|
|
390
|
+
* - 切换到未连接的账户:不触发事件(用户需要手动断开和重新连接)
|
|
391
|
+
* - 锁定钱包:触发事件,返回空数组 []
|
|
363
392
|
*/
|
|
364
393
|
this.handleAccountsChanged = (accounts) => {
|
|
394
|
+
console.log("[MetaMask] accountsChanged event triggered:", accounts);
|
|
365
395
|
if (accounts.length === 0) {
|
|
396
|
+
console.log("[MetaMask] Disconnecting: wallet locked or manually disconnected");
|
|
366
397
|
this.setState("disconnected" /* DISCONNECTED */);
|
|
367
398
|
this.setAccount(null);
|
|
368
399
|
this.emitAccountChanged(null);
|
|
369
400
|
} else {
|
|
370
401
|
const address = formatEVMAddress(accounts[0]);
|
|
402
|
+
console.log("[MetaMask] Account changed to:", address);
|
|
371
403
|
const account = {
|
|
372
404
|
universalAddress: createUniversalAddress(this.currentAccount.chainId, address),
|
|
373
405
|
nativeAddress: address,
|
|
374
406
|
chainId: this.currentAccount.chainId,
|
|
375
|
-
chainType:
|
|
407
|
+
chainType: ChainType.EVM,
|
|
376
408
|
isActive: true
|
|
377
409
|
};
|
|
378
410
|
this.setAccount(account);
|
|
@@ -424,21 +456,23 @@ var MetaMaskAdapter = class extends BrowserWalletAdapter {
|
|
|
424
456
|
if (chainId && chainId !== parsedChainId) {
|
|
425
457
|
await this.switchChain(chainId);
|
|
426
458
|
}
|
|
459
|
+
const finalChainId = chainId || parsedChainId;
|
|
460
|
+
const viemChain = this.getViemChain(finalChainId);
|
|
427
461
|
this.walletClient = createWalletClient({
|
|
428
462
|
account: accounts[0],
|
|
463
|
+
chain: viemChain,
|
|
429
464
|
transport: custom(provider)
|
|
430
465
|
});
|
|
431
|
-
const finalChainId = chainId || parsedChainId;
|
|
432
466
|
this.publicClient = createPublicClient({
|
|
433
|
-
chain:
|
|
434
|
-
transport:
|
|
467
|
+
chain: viemChain,
|
|
468
|
+
transport: custom(provider)
|
|
435
469
|
});
|
|
436
470
|
const address = formatEVMAddress(accounts[0]);
|
|
437
471
|
const account = {
|
|
438
472
|
universalAddress: createUniversalAddress(finalChainId, address),
|
|
439
473
|
nativeAddress: address,
|
|
440
474
|
chainId: finalChainId,
|
|
441
|
-
chainType:
|
|
475
|
+
chainType: ChainType.EVM,
|
|
442
476
|
isActive: true
|
|
443
477
|
};
|
|
444
478
|
this.setState("connected" /* CONNECTED */);
|
|
@@ -492,6 +526,40 @@ var MetaMaskAdapter = class extends BrowserWalletAdapter {
|
|
|
492
526
|
throw error;
|
|
493
527
|
}
|
|
494
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* 签名交易
|
|
531
|
+
*
|
|
532
|
+
* Note: This signs a raw transaction without sending it.
|
|
533
|
+
* The transaction can be broadcast later using the returned signature.
|
|
534
|
+
*/
|
|
535
|
+
async signTransaction(transaction) {
|
|
536
|
+
this.ensureConnected();
|
|
537
|
+
try {
|
|
538
|
+
const provider = this.getBrowserProvider();
|
|
539
|
+
const tx = {
|
|
540
|
+
from: this.currentAccount.nativeAddress,
|
|
541
|
+
to: transaction.to,
|
|
542
|
+
value: transaction.value ? `0x${BigInt(transaction.value).toString(16)}` : void 0,
|
|
543
|
+
data: transaction.data || "0x",
|
|
544
|
+
gas: transaction.gas ? `0x${BigInt(transaction.gas).toString(16)}` : void 0,
|
|
545
|
+
gasPrice: transaction.gasPrice && transaction.gasPrice !== "auto" ? `0x${BigInt(transaction.gasPrice).toString(16)}` : void 0,
|
|
546
|
+
maxFeePerGas: transaction.maxFeePerGas ? `0x${BigInt(transaction.maxFeePerGas).toString(16)}` : void 0,
|
|
547
|
+
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas ? `0x${BigInt(transaction.maxPriorityFeePerGas).toString(16)}` : void 0,
|
|
548
|
+
nonce: transaction.nonce !== void 0 ? `0x${transaction.nonce.toString(16)}` : void 0,
|
|
549
|
+
chainId: transaction.chainId || this.currentAccount.chainId
|
|
550
|
+
};
|
|
551
|
+
const signature = await provider.request({
|
|
552
|
+
method: "eth_signTransaction",
|
|
553
|
+
params: [tx]
|
|
554
|
+
});
|
|
555
|
+
return signature;
|
|
556
|
+
} catch (error) {
|
|
557
|
+
if (error.code === 4001) {
|
|
558
|
+
throw new SignatureRejectedError("Transaction signature was rejected by user");
|
|
559
|
+
}
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
495
563
|
/**
|
|
496
564
|
* 切换链
|
|
497
565
|
*/
|
|
@@ -572,6 +640,16 @@ var MetaMaskAdapter = class extends BrowserWalletAdapter {
|
|
|
572
640
|
throw new Error("Wallet client not initialized");
|
|
573
641
|
}
|
|
574
642
|
try {
|
|
643
|
+
console.log("\u{1F50D} [MetaMask writeContract] params.gasPrice:", params.gasPrice, "type:", typeof params.gasPrice);
|
|
644
|
+
let processedGasPrice;
|
|
645
|
+
if (!params.gasPrice) {
|
|
646
|
+
processedGasPrice = void 0;
|
|
647
|
+
} else if (params.gasPrice === "auto") {
|
|
648
|
+
processedGasPrice = void 0;
|
|
649
|
+
} else {
|
|
650
|
+
processedGasPrice = BigInt(params.gasPrice);
|
|
651
|
+
}
|
|
652
|
+
console.log("\u{1F50D} [MetaMask writeContract] processedGasPrice:", processedGasPrice);
|
|
575
653
|
const txHash = await this.walletClient.writeContract({
|
|
576
654
|
address: params.address,
|
|
577
655
|
abi: params.abi,
|
|
@@ -579,7 +657,7 @@ var MetaMaskAdapter = class extends BrowserWalletAdapter {
|
|
|
579
657
|
...params.args ? { args: params.args } : {},
|
|
580
658
|
value: params.value ? BigInt(params.value) : void 0,
|
|
581
659
|
gas: params.gas ? BigInt(params.gas) : void 0,
|
|
582
|
-
gasPrice:
|
|
660
|
+
gasPrice: processedGasPrice
|
|
583
661
|
});
|
|
584
662
|
return txHash;
|
|
585
663
|
} catch (error) {
|
|
@@ -722,9 +800,14 @@ var _TronLinkAdapter = class _TronLinkAdapter extends BrowserWalletAdapter {
|
|
|
722
800
|
constructor() {
|
|
723
801
|
super(...arguments);
|
|
724
802
|
this.type = "tronlink" /* TRONLINK */;
|
|
725
|
-
this.chainType =
|
|
803
|
+
this.chainType = ChainType.TRON;
|
|
726
804
|
this.name = "TronLink";
|
|
727
805
|
this.icon = "https://www.tronlink.org/static/logoIcon.svg";
|
|
806
|
+
/**
|
|
807
|
+
* 轮询检测账户变化(备用方案)
|
|
808
|
+
*/
|
|
809
|
+
this.pollingInterval = null;
|
|
810
|
+
this.lastKnownAddress = null;
|
|
728
811
|
/**
|
|
729
812
|
* 处理账户变化
|
|
730
813
|
*/
|
|
@@ -739,7 +822,7 @@ var _TronLinkAdapter = class _TronLinkAdapter extends BrowserWalletAdapter {
|
|
|
739
822
|
universalAddress: createUniversalAddress(this.currentAccount.chainId, address),
|
|
740
823
|
nativeAddress: address,
|
|
741
824
|
chainId: this.currentAccount.chainId,
|
|
742
|
-
chainType:
|
|
825
|
+
chainType: ChainType.TRON,
|
|
743
826
|
isActive: true
|
|
744
827
|
};
|
|
745
828
|
this.setAccount(account);
|
|
@@ -778,7 +861,7 @@ var _TronLinkAdapter = class _TronLinkAdapter extends BrowserWalletAdapter {
|
|
|
778
861
|
universalAddress: createUniversalAddress(tronChainId, address),
|
|
779
862
|
nativeAddress: address,
|
|
780
863
|
chainId: tronChainId,
|
|
781
|
-
chainType:
|
|
864
|
+
chainType: ChainType.TRON,
|
|
782
865
|
isActive: true
|
|
783
866
|
};
|
|
784
867
|
this.setState("connected" /* CONNECTED */);
|
|
@@ -796,20 +879,177 @@ var _TronLinkAdapter = class _TronLinkAdapter extends BrowserWalletAdapter {
|
|
|
796
879
|
}
|
|
797
880
|
/**
|
|
798
881
|
* 签名消息
|
|
882
|
+
*
|
|
883
|
+
* Note: TronLink supports two signing methods:
|
|
884
|
+
* - trx.sign(): Signs a transaction object
|
|
885
|
+
* - trx.signMessageV2(): Signs a plain text message (what we use here)
|
|
799
886
|
*/
|
|
800
887
|
async signMessage(message) {
|
|
801
888
|
this.ensureConnected();
|
|
802
889
|
try {
|
|
803
890
|
const tronWeb = this.getTronWeb();
|
|
804
|
-
|
|
805
|
-
|
|
891
|
+
if (typeof tronWeb.trx.signMessageV2 === "function") {
|
|
892
|
+
const signature = await tronWeb.trx.signMessageV2(message);
|
|
893
|
+
return signature;
|
|
894
|
+
} else {
|
|
895
|
+
console.warn("[TronLink] signMessageV2 not available, falling back to sign()");
|
|
896
|
+
const signature = await tronWeb.trx.sign(message);
|
|
897
|
+
return signature;
|
|
898
|
+
}
|
|
806
899
|
} catch (error) {
|
|
807
900
|
if (error.message?.includes("User rejected") || error.message?.includes("Confirmation declined")) {
|
|
808
901
|
throw new SignatureRejectedError();
|
|
809
902
|
}
|
|
903
|
+
if (error.message?.includes("Invalid transaction")) {
|
|
904
|
+
throw new Error("Invalid message format. For transaction signing, use signTransaction() instead.");
|
|
905
|
+
}
|
|
906
|
+
throw error;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* 签名交易
|
|
911
|
+
*
|
|
912
|
+
* Note: This uses trx.sign() which is specifically for signing transaction objects.
|
|
913
|
+
* For plain text message signing, use signMessage() instead.
|
|
914
|
+
*/
|
|
915
|
+
async signTransaction(transaction) {
|
|
916
|
+
this.ensureConnected();
|
|
917
|
+
try {
|
|
918
|
+
const tronWeb = this.getTronWeb();
|
|
919
|
+
const signature = await tronWeb.trx.sign(transaction);
|
|
920
|
+
return signature;
|
|
921
|
+
} catch (error) {
|
|
922
|
+
if (error.message?.includes("User rejected") || error.message?.includes("Confirmation declined")) {
|
|
923
|
+
throw new SignatureRejectedError("Transaction signature was rejected by user");
|
|
924
|
+
}
|
|
925
|
+
if (error.message?.includes("Invalid transaction")) {
|
|
926
|
+
throw new Error("Invalid transaction format. Please provide a properly formatted Tron transaction object.");
|
|
927
|
+
}
|
|
810
928
|
throw error;
|
|
811
929
|
}
|
|
812
930
|
}
|
|
931
|
+
/**
|
|
932
|
+
* 读取合约
|
|
933
|
+
*/
|
|
934
|
+
async readContract(params) {
|
|
935
|
+
this.ensureConnected();
|
|
936
|
+
try {
|
|
937
|
+
const tronWeb = this.getTronWeb();
|
|
938
|
+
const contract = await tronWeb.contract(params.abi, params.address);
|
|
939
|
+
const result = await contract[params.functionName](...params.args || []).call();
|
|
940
|
+
return result;
|
|
941
|
+
} catch (error) {
|
|
942
|
+
console.error("Read contract error:", error);
|
|
943
|
+
throw new Error(`Failed to read contract: ${error.message}`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* 写入合约
|
|
948
|
+
*/
|
|
949
|
+
async writeContract(params) {
|
|
950
|
+
this.ensureConnected();
|
|
951
|
+
try {
|
|
952
|
+
const tronWeb = this.getTronWeb();
|
|
953
|
+
console.log("[TronLink] writeContract params:", {
|
|
954
|
+
address: params.address,
|
|
955
|
+
functionName: params.functionName,
|
|
956
|
+
args: params.args,
|
|
957
|
+
value: params.value,
|
|
958
|
+
gas: params.gas
|
|
959
|
+
});
|
|
960
|
+
if (!params.args || params.args.length === 0) {
|
|
961
|
+
throw new Error("Contract function arguments are required");
|
|
962
|
+
}
|
|
963
|
+
const hasUndefined = params.args.some((arg) => arg === void 0 || arg === null);
|
|
964
|
+
if (hasUndefined) {
|
|
965
|
+
console.error("[TronLink] Invalid args detected:", params.args);
|
|
966
|
+
throw new Error(`Invalid contract arguments: some arguments are undefined or null`);
|
|
967
|
+
}
|
|
968
|
+
const functionAbi = params.abi.find(
|
|
969
|
+
(item) => item.name === params.functionName && item.type === "function"
|
|
970
|
+
);
|
|
971
|
+
if (!functionAbi) {
|
|
972
|
+
throw new Error(`Function ${params.functionName} not found in ABI`);
|
|
973
|
+
}
|
|
974
|
+
console.log("[TronLink] Function ABI:", functionAbi);
|
|
975
|
+
console.log("[TronLink] Calling with args:", params.args);
|
|
976
|
+
const options = {
|
|
977
|
+
feeLimit: params.gas || 1e8,
|
|
978
|
+
// 默认 100 TRX 的能量限制
|
|
979
|
+
callValue: params.value || 0
|
|
980
|
+
// 发送的 TRX 数量(单位:SUN)
|
|
981
|
+
};
|
|
982
|
+
const parameter = functionAbi.inputs.map((input, index) => ({
|
|
983
|
+
type: input.type,
|
|
984
|
+
value: params.args[index]
|
|
985
|
+
}));
|
|
986
|
+
console.log("[TronLink] Transaction options:", options);
|
|
987
|
+
console.log("[TronLink] Parameters:", parameter);
|
|
988
|
+
const transaction = await tronWeb.transactionBuilder.triggerSmartContract(
|
|
989
|
+
params.address,
|
|
990
|
+
params.functionName + "(" + functionAbi.inputs.map((i) => i.type).join(",") + ")",
|
|
991
|
+
options,
|
|
992
|
+
parameter,
|
|
993
|
+
this.currentAccount.nativeAddress
|
|
994
|
+
);
|
|
995
|
+
console.log("[TronLink] Transaction built:", transaction);
|
|
996
|
+
if (!transaction || !transaction.transaction) {
|
|
997
|
+
throw new Error("Failed to build transaction");
|
|
998
|
+
}
|
|
999
|
+
const signedTx = await tronWeb.trx.sign(transaction.transaction);
|
|
1000
|
+
const broadcast = await tronWeb.trx.sendRawTransaction(signedTx);
|
|
1001
|
+
console.log("[TronLink] Broadcast result:", broadcast);
|
|
1002
|
+
if (!broadcast.result) {
|
|
1003
|
+
throw new Error(broadcast.message || "Transaction broadcast failed");
|
|
1004
|
+
}
|
|
1005
|
+
const txHash = broadcast.txid || broadcast.transaction?.txID;
|
|
1006
|
+
console.log("[TronLink] Transaction hash:", txHash);
|
|
1007
|
+
return txHash || "";
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
console.error("Write contract error:", error);
|
|
1010
|
+
if (error.message?.includes("User rejected") || error.message?.includes("Confirmation declined")) {
|
|
1011
|
+
throw new SignatureRejectedError("Transaction was rejected by user");
|
|
1012
|
+
}
|
|
1013
|
+
throw new Error(`Failed to write contract: ${error.message}`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* 等待交易确认
|
|
1018
|
+
*/
|
|
1019
|
+
async waitForTransaction(txHash, _confirmations = 1) {
|
|
1020
|
+
try {
|
|
1021
|
+
const tronWeb = this.getTronWeb();
|
|
1022
|
+
let attempts = 0;
|
|
1023
|
+
const maxAttempts = 60;
|
|
1024
|
+
while (attempts < maxAttempts) {
|
|
1025
|
+
try {
|
|
1026
|
+
const txInfo = await tronWeb.trx.getTransactionInfo(txHash);
|
|
1027
|
+
if (txInfo && txInfo.id) {
|
|
1028
|
+
const receipt = {
|
|
1029
|
+
transactionHash: txHash,
|
|
1030
|
+
blockNumber: txInfo.blockNumber || 0,
|
|
1031
|
+
blockHash: txInfo.blockHash || "",
|
|
1032
|
+
from: this.currentAccount.nativeAddress,
|
|
1033
|
+
to: txInfo.contract_address || "",
|
|
1034
|
+
status: txInfo.receipt?.result === "SUCCESS" ? "success" : "failed",
|
|
1035
|
+
gasUsed: (txInfo.receipt?.energy_usage_total || 0).toString(),
|
|
1036
|
+
logs: txInfo.log || []
|
|
1037
|
+
};
|
|
1038
|
+
if (receipt.status === "failed") {
|
|
1039
|
+
throw new TransactionFailedError(txHash, "Transaction failed on Tron network");
|
|
1040
|
+
}
|
|
1041
|
+
return receipt;
|
|
1042
|
+
}
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
}
|
|
1045
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1046
|
+
attempts++;
|
|
1047
|
+
}
|
|
1048
|
+
throw new Error("Transaction confirmation timeout");
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
throw new Error(`Failed to wait for transaction: ${error.message}`);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
813
1053
|
/**
|
|
814
1054
|
* 获取 Provider
|
|
815
1055
|
*/
|
|
@@ -848,9 +1088,16 @@ var _TronLinkAdapter = class _TronLinkAdapter extends BrowserWalletAdapter {
|
|
|
848
1088
|
setupEventListeners() {
|
|
849
1089
|
if (typeof window === "undefined") return;
|
|
850
1090
|
const w = window;
|
|
851
|
-
|
|
852
|
-
w.tronLink.on
|
|
853
|
-
|
|
1091
|
+
try {
|
|
1092
|
+
if (w.tronLink && typeof w.tronLink.on === "function") {
|
|
1093
|
+
w.tronLink.on("accountsChanged", this.handleAccountsChanged);
|
|
1094
|
+
w.tronLink.on("disconnect", this.handleDisconnect);
|
|
1095
|
+
} else if (w.tronWeb && w.tronWeb.eventServer) {
|
|
1096
|
+
this.startPolling();
|
|
1097
|
+
}
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
console.warn("TronLink event listener setup failed:", error);
|
|
1100
|
+
this.startPolling();
|
|
854
1101
|
}
|
|
855
1102
|
}
|
|
856
1103
|
/**
|
|
@@ -859,9 +1106,38 @@ var _TronLinkAdapter = class _TronLinkAdapter extends BrowserWalletAdapter {
|
|
|
859
1106
|
removeEventListeners() {
|
|
860
1107
|
if (typeof window === "undefined") return;
|
|
861
1108
|
const w = window;
|
|
862
|
-
|
|
863
|
-
w.tronLink.off
|
|
864
|
-
|
|
1109
|
+
try {
|
|
1110
|
+
if (w.tronLink && typeof w.tronLink.off === "function") {
|
|
1111
|
+
w.tronLink.off("accountsChanged", this.handleAccountsChanged);
|
|
1112
|
+
w.tronLink.off("disconnect", this.handleDisconnect);
|
|
1113
|
+
}
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
console.warn("TronLink event listener removal failed:", error);
|
|
1116
|
+
}
|
|
1117
|
+
this.stopPolling();
|
|
1118
|
+
}
|
|
1119
|
+
startPolling() {
|
|
1120
|
+
if (this.pollingInterval) return;
|
|
1121
|
+
this.lastKnownAddress = this.currentAccount?.nativeAddress || null;
|
|
1122
|
+
this.pollingInterval = setInterval(async () => {
|
|
1123
|
+
try {
|
|
1124
|
+
const tronWeb = this.getTronWeb();
|
|
1125
|
+
const currentAddress = tronWeb.defaultAddress?.base58;
|
|
1126
|
+
if (currentAddress && currentAddress !== this.lastKnownAddress) {
|
|
1127
|
+
this.lastKnownAddress = currentAddress;
|
|
1128
|
+
this.handleAccountsChanged({ address: { base58: currentAddress } });
|
|
1129
|
+
} else if (!currentAddress && this.lastKnownAddress) {
|
|
1130
|
+
this.lastKnownAddress = null;
|
|
1131
|
+
this.handleAccountsChanged(null);
|
|
1132
|
+
}
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
}
|
|
1135
|
+
}, 2e3);
|
|
1136
|
+
}
|
|
1137
|
+
stopPolling() {
|
|
1138
|
+
if (this.pollingInterval) {
|
|
1139
|
+
clearInterval(this.pollingInterval);
|
|
1140
|
+
this.pollingInterval = null;
|
|
865
1141
|
}
|
|
866
1142
|
}
|
|
867
1143
|
};
|
|
@@ -872,7 +1148,7 @@ var EVMPrivateKeyAdapter = class extends WalletAdapter {
|
|
|
872
1148
|
constructor() {
|
|
873
1149
|
super(...arguments);
|
|
874
1150
|
this.type = "private-key" /* PRIVATE_KEY */;
|
|
875
|
-
this.chainType =
|
|
1151
|
+
this.chainType = ChainType.EVM;
|
|
876
1152
|
this.name = "Private Key (EVM)";
|
|
877
1153
|
this.privateKey = null;
|
|
878
1154
|
this.walletClient = null;
|
|
@@ -902,7 +1178,7 @@ var EVMPrivateKeyAdapter = class extends WalletAdapter {
|
|
|
902
1178
|
universalAddress: createUniversalAddress(chainId, address),
|
|
903
1179
|
nativeAddress: address,
|
|
904
1180
|
chainId,
|
|
905
|
-
chainType:
|
|
1181
|
+
chainType: ChainType.EVM,
|
|
906
1182
|
isActive: true
|
|
907
1183
|
};
|
|
908
1184
|
this.setState("connected" /* CONNECTED */);
|
|
@@ -1252,9 +1528,10 @@ var WalletManager = class extends TypedEventEmitter {
|
|
|
1252
1528
|
if (!this.primaryWallet) {
|
|
1253
1529
|
return;
|
|
1254
1530
|
}
|
|
1531
|
+
const chainType = this.primaryWallet.chainType;
|
|
1255
1532
|
await this.primaryWallet.disconnect();
|
|
1256
1533
|
this.removeAdapterListeners(this.primaryWallet);
|
|
1257
|
-
this.connectedWallets.delete(
|
|
1534
|
+
this.connectedWallets.delete(chainType);
|
|
1258
1535
|
this.primaryWallet = null;
|
|
1259
1536
|
if (this.config.enableStorage) {
|
|
1260
1537
|
this.saveToStorage();
|
|
@@ -1359,6 +1636,34 @@ var WalletManager = class extends TypedEventEmitter {
|
|
|
1359
1636
|
}
|
|
1360
1637
|
return adapter.signTypedData(typedData);
|
|
1361
1638
|
}
|
|
1639
|
+
/**
|
|
1640
|
+
* 签名交易(使用主钱包)
|
|
1641
|
+
*/
|
|
1642
|
+
async signTransaction(transaction) {
|
|
1643
|
+
if (!this.primaryWallet) {
|
|
1644
|
+
throw new WalletNotConnectedError();
|
|
1645
|
+
}
|
|
1646
|
+
if (!this.primaryWallet.signTransaction) {
|
|
1647
|
+
throw new Error(`signTransaction not supported by ${this.primaryWallet.type}`);
|
|
1648
|
+
}
|
|
1649
|
+
return this.primaryWallet.signTransaction(transaction);
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* 使用指定链类型的钱包签名交易
|
|
1653
|
+
*/
|
|
1654
|
+
async signTransactionWithChainType(transaction, chainType) {
|
|
1655
|
+
if (!chainType) {
|
|
1656
|
+
return this.signTransaction(transaction);
|
|
1657
|
+
}
|
|
1658
|
+
const adapter = this.connectedWallets.get(chainType);
|
|
1659
|
+
if (!adapter) {
|
|
1660
|
+
throw new WalletNotConnectedError(`Wallet for chain type ${chainType}`);
|
|
1661
|
+
}
|
|
1662
|
+
if (!adapter.signTransaction) {
|
|
1663
|
+
throw new Error(`signTransaction not supported by ${adapter.type}`);
|
|
1664
|
+
}
|
|
1665
|
+
return adapter.signTransaction(transaction);
|
|
1666
|
+
}
|
|
1362
1667
|
// ===== 链切换 =====
|
|
1363
1668
|
/**
|
|
1364
1669
|
* 请求切换链(仅 EVM)
|
|
@@ -1519,6 +1824,7 @@ var WalletManager = class extends TypedEventEmitter {
|
|
|
1519
1824
|
* 移除适配器事件监听
|
|
1520
1825
|
*/
|
|
1521
1826
|
removeAdapterListeners(adapter) {
|
|
1827
|
+
if (!adapter) return;
|
|
1522
1828
|
adapter.removeAllListeners();
|
|
1523
1829
|
}
|
|
1524
1830
|
// ===== 存储 =====
|
|
@@ -1616,6 +1922,9 @@ function WalletProvider({ children, walletManager: externalWalletManager }) {
|
|
|
1616
1922
|
const signMessage = useCallback(async (message) => {
|
|
1617
1923
|
return walletManager.signMessage(message);
|
|
1618
1924
|
}, [walletManager]);
|
|
1925
|
+
const signTransaction = useCallback(async (transaction) => {
|
|
1926
|
+
return walletManager.signTransaction(transaction);
|
|
1927
|
+
}, [walletManager]);
|
|
1619
1928
|
useEffect(() => {
|
|
1620
1929
|
const handleAccountChanged = (newAccount) => {
|
|
1621
1930
|
setAccount(newAccount);
|
|
@@ -1655,7 +1964,8 @@ function WalletProvider({ children, walletManager: externalWalletManager }) {
|
|
|
1655
1964
|
connectAdditional,
|
|
1656
1965
|
disconnect,
|
|
1657
1966
|
switchPrimaryWallet,
|
|
1658
|
-
signMessage
|
|
1967
|
+
signMessage,
|
|
1968
|
+
signTransaction
|
|
1659
1969
|
};
|
|
1660
1970
|
return /* @__PURE__ */ React.createElement(WalletContext.Provider, { value }, children);
|
|
1661
1971
|
}
|
|
@@ -1764,7 +2074,31 @@ function useSignMessage() {
|
|
|
1764
2074
|
error
|
|
1765
2075
|
};
|
|
1766
2076
|
}
|
|
2077
|
+
function useSignTransaction() {
|
|
2078
|
+
const { signTransaction: contextSignTransaction } = useWallet();
|
|
2079
|
+
const [isSigning, setIsSigning] = useState(false);
|
|
2080
|
+
const [error, setError] = useState(null);
|
|
2081
|
+
const signTransaction = async (transaction) => {
|
|
2082
|
+
setIsSigning(true);
|
|
2083
|
+
setError(null);
|
|
2084
|
+
try {
|
|
2085
|
+
const signature = await contextSignTransaction(transaction);
|
|
2086
|
+
return signature;
|
|
2087
|
+
} catch (err) {
|
|
2088
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
2089
|
+
setError(error2);
|
|
2090
|
+
throw error2;
|
|
2091
|
+
} finally {
|
|
2092
|
+
setIsSigning(false);
|
|
2093
|
+
}
|
|
2094
|
+
};
|
|
2095
|
+
return {
|
|
2096
|
+
signTransaction,
|
|
2097
|
+
isSigning,
|
|
2098
|
+
error
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
1767
2101
|
|
|
1768
|
-
export { WalletProvider, useAccount, useConnect, useDisconnect, useSignMessage, useWallet };
|
|
2102
|
+
export { WalletProvider, useAccount, useConnect, useDisconnect, useSignMessage, useSignTransaction, useWallet };
|
|
1769
2103
|
//# sourceMappingURL=index.mjs.map
|
|
1770
2104
|
//# sourceMappingURL=index.mjs.map
|