decane-connect-kit 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +386 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -4
- package/dist/index.d.ts +31 -4
- package/dist/index.js +386 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32,8 +32,12 @@ function createRegistry(emitter) {
|
|
|
32
32
|
};
|
|
33
33
|
if (incoming.chain === "evm") {
|
|
34
34
|
account.evmProvider = incoming.provider;
|
|
35
|
-
} else {
|
|
35
|
+
} else if (incoming.chain === "solana") {
|
|
36
36
|
account.solanaProvider = incoming.provider;
|
|
37
|
+
} else if (incoming.chain === "tron") {
|
|
38
|
+
account.tronProvider = incoming.provider;
|
|
39
|
+
} else if (incoming.chain === "bitcoin") {
|
|
40
|
+
account.bitcoinProvider = incoming.provider;
|
|
37
41
|
}
|
|
38
42
|
return account;
|
|
39
43
|
}
|
|
@@ -44,12 +48,10 @@ function createRegistry(emitter) {
|
|
|
44
48
|
incoming.chain
|
|
45
49
|
].sort();
|
|
46
50
|
}
|
|
47
|
-
if (incoming.chain === "evm")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (incoming.chain === "
|
|
51
|
-
existing.solanaProvider = incoming.provider;
|
|
52
|
-
}
|
|
51
|
+
if (incoming.chain === "evm") existing.evmProvider = incoming.provider;
|
|
52
|
+
if (incoming.chain === "solana") existing.solanaProvider = incoming.provider;
|
|
53
|
+
if (incoming.chain === "tron") existing.tronProvider = incoming.provider;
|
|
54
|
+
if (incoming.chain === "bitcoin") existing.bitcoinProvider = incoming.provider;
|
|
53
55
|
if (!existing.icon && incoming.icon) existing.icon = incoming.icon;
|
|
54
56
|
}
|
|
55
57
|
function registerOrMerge(incoming) {
|
|
@@ -96,9 +98,9 @@ function createRegistry(emitter) {
|
|
|
96
98
|
all: () => [...wallets],
|
|
97
99
|
evm: () => wallets.filter((w) => w.chainSupport.includes("evm")),
|
|
98
100
|
solana: () => wallets.filter((w) => w.chainSupport.includes("solana")),
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
),
|
|
101
|
+
tron: () => wallets.filter((w) => w.chainSupport.includes("tron")),
|
|
102
|
+
bitcoin: () => wallets.filter((w) => w.chainSupport.includes("bitcoin")),
|
|
103
|
+
multiChain: () => wallets.filter((w) => w.chainSupport.length >= 2),
|
|
102
104
|
byId: (id) => wallets.find((w) => w.id === id)
|
|
103
105
|
}
|
|
104
106
|
};
|
|
@@ -681,11 +683,37 @@ var BUILT_IN_CHAINS = [
|
|
|
681
683
|
name: "Solana Devnet",
|
|
682
684
|
nativeCurrency: SOL,
|
|
683
685
|
blockExplorerUrl: "https://solscan.io/?cluster=devnet"
|
|
686
|
+
},
|
|
687
|
+
// ── Tron ─────────────────────────────────────────────────────────────────────
|
|
688
|
+
{
|
|
689
|
+
id: "tron:mainnet",
|
|
690
|
+
type: "tron",
|
|
691
|
+
name: "Tron",
|
|
692
|
+
nativeCurrency: { name: "Tronix", symbol: "TRX", decimals: 6 },
|
|
693
|
+
blockExplorerUrl: "https://tronscan.org"
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
id: "tron:shasta",
|
|
697
|
+
type: "tron",
|
|
698
|
+
name: "Tron Shasta Testnet",
|
|
699
|
+
nativeCurrency: { name: "Tronix", symbol: "TRX", decimals: 6 },
|
|
700
|
+
blockExplorerUrl: "https://shasta.tronscan.org",
|
|
701
|
+
isCustom: true
|
|
702
|
+
},
|
|
703
|
+
// ── Bitcoin ───────────────────────────────────────────────────────────────────
|
|
704
|
+
{
|
|
705
|
+
id: "bip122:mainnet",
|
|
706
|
+
type: "bitcoin",
|
|
707
|
+
name: "Bitcoin",
|
|
708
|
+
nativeCurrency: { name: "Bitcoin", symbol: "BTC", decimals: 8 },
|
|
709
|
+
blockExplorerUrl: "https://blockstream.info"
|
|
684
710
|
}
|
|
685
711
|
];
|
|
686
712
|
function getChainType(caip2Id) {
|
|
687
713
|
if (caip2Id.startsWith("eip155:")) return "evm";
|
|
688
714
|
if (caip2Id.startsWith("solana:")) return "solana";
|
|
715
|
+
if (caip2Id.startsWith("tron:")) return "tron";
|
|
716
|
+
if (caip2Id.startsWith("bip122:")) return "bitcoin";
|
|
689
717
|
throw new Error(`Cannot determine chain type from: ${caip2Id}`);
|
|
690
718
|
}
|
|
691
719
|
function toCAIP10(chainId, address) {
|
|
@@ -701,6 +729,14 @@ var SOLANA_METHODS = /* @__PURE__ */ new Set([
|
|
|
701
729
|
"wallet_signMessage"
|
|
702
730
|
// wallet_signTypedData is EVM-only — never included here
|
|
703
731
|
]);
|
|
732
|
+
var TRON_METHODS = /* @__PURE__ */ new Set([
|
|
733
|
+
"wallet_sendTransaction",
|
|
734
|
+
"wallet_signMessage"
|
|
735
|
+
]);
|
|
736
|
+
var BITCOIN_METHODS = /* @__PURE__ */ new Set([
|
|
737
|
+
"wallet_sendTransaction",
|
|
738
|
+
"wallet_signMessage"
|
|
739
|
+
]);
|
|
704
740
|
var CHAINS = {
|
|
705
741
|
// Tier-1
|
|
706
742
|
ETHEREUM: "eip155:1",
|
|
@@ -766,13 +802,17 @@ var CHAINS = {
|
|
|
766
802
|
LOCALHOST: "eip155:31337",
|
|
767
803
|
// Solana
|
|
768
804
|
SOLANA: "solana:mainnet",
|
|
769
|
-
SOLANA_DEVNET: "solana:devnet"
|
|
805
|
+
SOLANA_DEVNET: "solana:devnet",
|
|
806
|
+
// Tron
|
|
807
|
+
TRON: "tron:mainnet",
|
|
808
|
+
// Bitcoin
|
|
809
|
+
BITCOIN: "bip122:mainnet"
|
|
770
810
|
};
|
|
771
811
|
function validateCustomChain(chain) {
|
|
772
812
|
if (!chain || typeof chain !== "object") return false;
|
|
773
813
|
const c = chain;
|
|
774
814
|
if (typeof c.id !== "string" || !c.id) return false;
|
|
775
|
-
if (c.type !== "evm" && c.type !== "solana") return false;
|
|
815
|
+
if (c.type !== "evm" && c.type !== "solana" && c.type !== "tron" && c.type !== "bitcoin") return false;
|
|
776
816
|
if (typeof c.name !== "string" || !c.name) return false;
|
|
777
817
|
if (!c.nativeCurrency || typeof c.nativeCurrency !== "object") return false;
|
|
778
818
|
if (c.type === "evm" && (typeof c.chainId !== "number" || !c.id.startsWith("eip155:")))
|
|
@@ -991,7 +1031,12 @@ async function walletStandardRequest(wallet, method, payload, account) {
|
|
|
991
1031
|
);
|
|
992
1032
|
}
|
|
993
1033
|
}
|
|
1034
|
+
function isSolanaAccount(a) {
|
|
1035
|
+
return !a.chains?.length || a.chains.some((c) => c.startsWith("solana:"));
|
|
1036
|
+
}
|
|
994
1037
|
async function getSolanaAddress(wallet) {
|
|
1038
|
+
const pre = wallet.accounts.find(isSolanaAccount);
|
|
1039
|
+
if (pre) return base58.encode(pre.publicKey);
|
|
995
1040
|
const connectFeature = wallet.features["standard:connect"];
|
|
996
1041
|
if (!connectFeature) {
|
|
997
1042
|
throw new DecaneError(
|
|
@@ -1000,13 +1045,81 @@ async function getSolanaAddress(wallet) {
|
|
|
1000
1045
|
);
|
|
1001
1046
|
}
|
|
1002
1047
|
const { accounts } = await connectFeature.connect();
|
|
1003
|
-
|
|
1048
|
+
const solanaAccount = accounts.find(isSolanaAccount);
|
|
1049
|
+
if (!solanaAccount) {
|
|
1004
1050
|
throw new DecaneError(
|
|
1005
1051
|
4001 /* USER_REJECTED */,
|
|
1006
1052
|
"No Solana accounts returned"
|
|
1007
1053
|
);
|
|
1008
1054
|
}
|
|
1009
|
-
return base58.encode(
|
|
1055
|
+
return base58.encode(solanaAccount.publicKey);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/tron/adapter.ts
|
|
1059
|
+
async function getTronAddress(provider) {
|
|
1060
|
+
try {
|
|
1061
|
+
const result = await provider.request({ method: "tron_requestAccounts" });
|
|
1062
|
+
const accounts = result;
|
|
1063
|
+
if (accounts?.[0]) return accounts[0];
|
|
1064
|
+
} catch {
|
|
1065
|
+
}
|
|
1066
|
+
const result2 = await provider.request({ method: "tron_accounts" }).catch(() => null);
|
|
1067
|
+
const accounts2 = result2;
|
|
1068
|
+
if (accounts2?.[0]) return accounts2[0];
|
|
1069
|
+
throw new DecaneError(4001 /* USER_REJECTED */, "No Tron accounts returned");
|
|
1070
|
+
}
|
|
1071
|
+
async function tronRequest(provider, method, payload, account) {
|
|
1072
|
+
switch (method) {
|
|
1073
|
+
case "wallet_signMessage":
|
|
1074
|
+
return provider.request({
|
|
1075
|
+
method: "tron_signMessage",
|
|
1076
|
+
params: { message: payload }
|
|
1077
|
+
});
|
|
1078
|
+
case "wallet_sendTransaction":
|
|
1079
|
+
return provider.request({
|
|
1080
|
+
method: "tron_sendTransaction",
|
|
1081
|
+
params: { ...payload, from: account }
|
|
1082
|
+
});
|
|
1083
|
+
default:
|
|
1084
|
+
throw new DecaneError(
|
|
1085
|
+
5030 /* METHOD_NOT_SUPPORTED */,
|
|
1086
|
+
`Tron adapter: unsupported method ${method}`
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// src/bitcoin/adapter.ts
|
|
1092
|
+
async function getBitcoinAddress(provider) {
|
|
1093
|
+
const result = await provider.requestAccounts();
|
|
1094
|
+
if (!result || result.length === 0) {
|
|
1095
|
+
throw new DecaneError(4001 /* USER_REJECTED */, "No Bitcoin accounts returned");
|
|
1096
|
+
}
|
|
1097
|
+
const first = result[0];
|
|
1098
|
+
return typeof first === "string" ? first : first.address;
|
|
1099
|
+
}
|
|
1100
|
+
async function bitcoinRequest(provider, method, payload, account) {
|
|
1101
|
+
switch (method) {
|
|
1102
|
+
case "wallet_signMessage":
|
|
1103
|
+
return provider.signMessage(payload);
|
|
1104
|
+
case "wallet_sendTransaction": {
|
|
1105
|
+
const { to, value } = payload;
|
|
1106
|
+
if (provider.sendBitcoin) {
|
|
1107
|
+
return provider.sendBitcoin(to, value);
|
|
1108
|
+
}
|
|
1109
|
+
if (provider.send) {
|
|
1110
|
+
return provider.send({ to, value, from: account });
|
|
1111
|
+
}
|
|
1112
|
+
throw new DecaneError(
|
|
1113
|
+
5030 /* METHOD_NOT_SUPPORTED */,
|
|
1114
|
+
"Bitcoin wallet does not support sendBitcoin"
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
default:
|
|
1118
|
+
throw new DecaneError(
|
|
1119
|
+
5030 /* METHOD_NOT_SUPPORTED */,
|
|
1120
|
+
`Bitcoin adapter: unsupported method ${method}`
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1010
1123
|
}
|
|
1011
1124
|
|
|
1012
1125
|
// src/core/router.ts
|
|
@@ -1057,20 +1170,23 @@ function createRouter(opts) {
|
|
|
1057
1170
|
const chainType = getChainType(chainId);
|
|
1058
1171
|
if (chainType === "evm" && !connectedWallet.evmProvider) continue;
|
|
1059
1172
|
if (chainType === "solana" && !connectedWallet.solanaProvider) continue;
|
|
1173
|
+
if (chainType === "tron" && !connectedWallet.tronProvider) continue;
|
|
1174
|
+
if (chainType === "bitcoin" && !connectedWallet.bitcoinProvider) continue;
|
|
1060
1175
|
let address;
|
|
1061
1176
|
if (chainType === "evm") {
|
|
1062
1177
|
address = await getEvmAddress(connectedWallet.evmProvider);
|
|
1063
1178
|
if (chain.isCustom) {
|
|
1064
|
-
await addEthereumChain(
|
|
1065
|
-
connectedWallet.evmProvider,
|
|
1066
|
-
chain
|
|
1067
|
-
).catch(() => {
|
|
1179
|
+
await addEthereumChain(connectedWallet.evmProvider, chain).catch(() => {
|
|
1068
1180
|
});
|
|
1069
1181
|
}
|
|
1070
|
-
} else {
|
|
1182
|
+
} else if (chainType === "solana") {
|
|
1071
1183
|
address = await getSolanaAddress(connectedWallet.solanaProvider);
|
|
1184
|
+
} else if (chainType === "tron") {
|
|
1185
|
+
address = await getTronAddress(connectedWallet.tronProvider);
|
|
1186
|
+
} else {
|
|
1187
|
+
address = await getBitcoinAddress(connectedWallet.bitcoinProvider);
|
|
1072
1188
|
}
|
|
1073
|
-
const methodSet = chainType === "evm" ? EVM_METHODS : SOLANA_METHODS;
|
|
1189
|
+
const methodSet = chainType === "evm" ? EVM_METHODS : chainType === "solana" ? SOLANA_METHODS : chainType === "tron" ? TRON_METHODS : BITCOIN_METHODS;
|
|
1074
1190
|
const grantedMethods = (scope?.methods ?? []).filter(
|
|
1075
1191
|
(m) => methodSet.has(m)
|
|
1076
1192
|
);
|
|
@@ -1149,19 +1265,13 @@ function createRouter(opts) {
|
|
|
1149
1265
|
);
|
|
1150
1266
|
}
|
|
1151
1267
|
if (chainType === "evm") {
|
|
1152
|
-
return eip1193Request(
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
);
|
|
1268
|
+
return eip1193Request(wallet.evmProvider, method, payload, account);
|
|
1269
|
+
} else if (chainType === "solana") {
|
|
1270
|
+
return walletStandardRequest(wallet.solanaProvider, method, payload, account);
|
|
1271
|
+
} else if (chainType === "tron") {
|
|
1272
|
+
return tronRequest(wallet.tronProvider, method, payload, account);
|
|
1158
1273
|
} else {
|
|
1159
|
-
return
|
|
1160
|
-
wallet.solanaProvider,
|
|
1161
|
-
method,
|
|
1162
|
-
payload,
|
|
1163
|
-
account
|
|
1164
|
-
);
|
|
1274
|
+
return bitcoinRequest(wallet.bitcoinProvider, method, payload, account);
|
|
1165
1275
|
}
|
|
1166
1276
|
}
|
|
1167
1277
|
return { request };
|
|
@@ -1216,6 +1326,12 @@ function createProvider(opts) {
|
|
|
1216
1326
|
get multiChainWallets() {
|
|
1217
1327
|
return registry.get.multiChain();
|
|
1218
1328
|
},
|
|
1329
|
+
get tronWallets() {
|
|
1330
|
+
return registry.get.tron();
|
|
1331
|
+
},
|
|
1332
|
+
get bitcoinWallets() {
|
|
1333
|
+
return registry.get.bitcoin();
|
|
1334
|
+
},
|
|
1219
1335
|
// ── Connection ──────────────────────────────────────────────────────────
|
|
1220
1336
|
async connect(wallet, preferredChain) {
|
|
1221
1337
|
if (session.get()) {
|
|
@@ -1224,13 +1340,19 @@ function createProvider(opts) {
|
|
|
1224
1340
|
}
|
|
1225
1341
|
_connectedWallet = wallet;
|
|
1226
1342
|
const support = wallet.chainSupport;
|
|
1227
|
-
|
|
1343
|
+
const hasEvm = support.includes("evm") && preferredChain !== "solana" && preferredChain !== "tron" && preferredChain !== "bitcoin";
|
|
1344
|
+
const hasSol = support.includes("solana") && preferredChain !== "evm" && preferredChain !== "tron" && preferredChain !== "bitcoin";
|
|
1345
|
+
const hasTron = support.includes("tron") && preferredChain !== "evm" && preferredChain !== "solana" && preferredChain !== "bitcoin";
|
|
1346
|
+
const hasBitcoin = support.includes("bitcoin") && preferredChain !== "evm" && preferredChain !== "solana" && preferredChain !== "tron";
|
|
1347
|
+
if (hasEvm) {
|
|
1228
1348
|
_activeChain = config.defaultChain?.startsWith("eip155:") ? config.defaultChain : "eip155:1";
|
|
1229
|
-
} else {
|
|
1349
|
+
} else if (hasSol) {
|
|
1230
1350
|
_activeChain = "solana:mainnet";
|
|
1351
|
+
} else if (hasTron) {
|
|
1352
|
+
_activeChain = "tron:mainnet";
|
|
1353
|
+
} else if (hasBitcoin) {
|
|
1354
|
+
_activeChain = "bip122:mainnet";
|
|
1231
1355
|
}
|
|
1232
|
-
const hasEvm = support.includes("evm") && preferredChain !== "solana";
|
|
1233
|
-
const hasSol = support.includes("solana") && preferredChain !== "evm";
|
|
1234
1356
|
const required = {};
|
|
1235
1357
|
const optional = {};
|
|
1236
1358
|
if (hasEvm) {
|
|
@@ -1254,6 +1376,34 @@ function createProvider(opts) {
|
|
|
1254
1376
|
_activeChain = solChain;
|
|
1255
1377
|
}
|
|
1256
1378
|
}
|
|
1379
|
+
if (hasTron) {
|
|
1380
|
+
const tronChain = "tron:mainnet";
|
|
1381
|
+
const scope = {
|
|
1382
|
+
methods: ["wallet_sendTransaction", "wallet_signMessage"],
|
|
1383
|
+
accounts: [],
|
|
1384
|
+
events: []
|
|
1385
|
+
};
|
|
1386
|
+
if (hasEvm || hasSol) {
|
|
1387
|
+
optional[tronChain] = scope;
|
|
1388
|
+
} else {
|
|
1389
|
+
required[tronChain] = scope;
|
|
1390
|
+
_activeChain = tronChain;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
if (hasBitcoin) {
|
|
1394
|
+
const btcChain = "bip122:mainnet";
|
|
1395
|
+
const scope = {
|
|
1396
|
+
methods: ["wallet_sendTransaction", "wallet_signMessage"],
|
|
1397
|
+
accounts: [],
|
|
1398
|
+
events: []
|
|
1399
|
+
};
|
|
1400
|
+
if (hasEvm || hasSol || hasTron) {
|
|
1401
|
+
optional[btcChain] = scope;
|
|
1402
|
+
} else {
|
|
1403
|
+
required[btcChain] = scope;
|
|
1404
|
+
_activeChain = btcChain;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1257
1407
|
await router.request({
|
|
1258
1408
|
method: "wallet_requestPermissions",
|
|
1259
1409
|
params: [{ requiredScopes: required, optionalScopes: optional }, wallet.id]
|
|
@@ -1321,6 +1471,58 @@ function createProvider(opts) {
|
|
|
1321
1471
|
return provider;
|
|
1322
1472
|
}
|
|
1323
1473
|
|
|
1474
|
+
// src/evm/bitcoinSnap.ts
|
|
1475
|
+
var BITCOIN_SNAP_IDS = [
|
|
1476
|
+
"npm:@metamask/bitcoin-snap",
|
|
1477
|
+
"npm:@consensys/btc-snap"
|
|
1478
|
+
];
|
|
1479
|
+
async function detectBitcoinSnap(provider) {
|
|
1480
|
+
try {
|
|
1481
|
+
const snaps = await provider.request({ method: "wallet_getSnaps" });
|
|
1482
|
+
if (!snaps) return null;
|
|
1483
|
+
for (const id of BITCOIN_SNAP_IDS) {
|
|
1484
|
+
const snap = snaps[id];
|
|
1485
|
+
if (snap && snap.blocked !== true) return id;
|
|
1486
|
+
}
|
|
1487
|
+
return null;
|
|
1488
|
+
} catch {
|
|
1489
|
+
return null;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
function makeBitcoinSnapProvider(snapId, evmProvider) {
|
|
1493
|
+
function invoke(method, params) {
|
|
1494
|
+
return evmProvider.request({
|
|
1495
|
+
method: "wallet_invokeSnap",
|
|
1496
|
+
params: [{ snapId, request: { method, params } }]
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
return {
|
|
1500
|
+
async requestAccounts() {
|
|
1501
|
+
const result = await invoke("btc_getAddress").catch(() => invoke("btc_getPublicKey"));
|
|
1502
|
+
if (typeof result === "string") return [result];
|
|
1503
|
+
if (Array.isArray(result)) return result;
|
|
1504
|
+
return [];
|
|
1505
|
+
},
|
|
1506
|
+
async signMessage(message) {
|
|
1507
|
+
return invoke("btc_signMessage", [message]);
|
|
1508
|
+
},
|
|
1509
|
+
async sendBitcoin(to, value) {
|
|
1510
|
+
return invoke("btc_sendBitcoin", [{ to, satoshis: value }]);
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
async function tryRegisterBitcoinSnap(registry, name, icon, rdns, evmProvider) {
|
|
1515
|
+
const snapId = await detectBitcoinSnap(evmProvider);
|
|
1516
|
+
if (!snapId) return;
|
|
1517
|
+
registry.registerOrMerge({
|
|
1518
|
+
chain: "bitcoin",
|
|
1519
|
+
name,
|
|
1520
|
+
icon,
|
|
1521
|
+
rdns,
|
|
1522
|
+
provider: makeBitcoinSnapProvider(snapId, evmProvider)
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1324
1526
|
// src/evm/discovery.ts
|
|
1325
1527
|
function startEvmDiscovery(registry) {
|
|
1326
1528
|
if (typeof window === "undefined") return;
|
|
@@ -1340,6 +1542,14 @@ function startEvmDiscovery(registry) {
|
|
|
1340
1542
|
uuid: info.uuid,
|
|
1341
1543
|
provider
|
|
1342
1544
|
});
|
|
1545
|
+
tryRegisterBitcoinSnap(
|
|
1546
|
+
registry,
|
|
1547
|
+
info.name,
|
|
1548
|
+
info.icon,
|
|
1549
|
+
info.rdns,
|
|
1550
|
+
provider
|
|
1551
|
+
).catch(() => {
|
|
1552
|
+
});
|
|
1343
1553
|
}
|
|
1344
1554
|
);
|
|
1345
1555
|
window.dispatchEvent(new CustomEvent("eip6963:requestProvider"));
|
|
@@ -1380,6 +1590,53 @@ function startSolanaDiscovery(registry) {
|
|
|
1380
1590
|
);
|
|
1381
1591
|
}
|
|
1382
1592
|
|
|
1593
|
+
// src/tron/discovery.ts
|
|
1594
|
+
var TRONLINK_RDNS = "link.tron.tronlink";
|
|
1595
|
+
var TRONLINK_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTYiIGZpbGw9IiNGRjAwMTMiLz48cG9seWdvbiBwb2ludHM9IjEyLDkgMjMsMTYgMTIsMjMiIGZpbGw9IndoaXRlIi8+PC9zdmc+";
|
|
1596
|
+
function startTronDiscovery(registry) {
|
|
1597
|
+
let registered = false;
|
|
1598
|
+
function tryRegister() {
|
|
1599
|
+
if (registered || !window.tronLink) return;
|
|
1600
|
+
registered = true;
|
|
1601
|
+
registry.registerOrMerge({
|
|
1602
|
+
chain: "tron",
|
|
1603
|
+
name: "TronLink",
|
|
1604
|
+
icon: TRONLINK_ICON,
|
|
1605
|
+
rdns: TRONLINK_RDNS,
|
|
1606
|
+
provider: window.tronLink
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
tryRegister();
|
|
1610
|
+
window.addEventListener("tronLink#initialized", tryRegister, { once: true });
|
|
1611
|
+
const start = Date.now();
|
|
1612
|
+
const poll = setInterval(() => {
|
|
1613
|
+
tryRegister();
|
|
1614
|
+
if (registered || Date.now() - start > 3e3) clearInterval(poll);
|
|
1615
|
+
}, 200);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// src/bitcoin/discovery.ts
|
|
1619
|
+
var BTC_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTYiIGZpbGw9IiNGNzkzMUEiLz48dGV4dCB4PSIxNi41IiB5PSIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0id2hpdGUiIGZvbnQtZmFtaWx5PSJBcmlhbCxzeXN0ZW0tdWkiPuKmojwvdGV4dD48L3N2Zz4=";
|
|
1620
|
+
function startBitcoinDiscovery(registry) {
|
|
1621
|
+
setTimeout(() => {
|
|
1622
|
+
const candidates = [
|
|
1623
|
+
{ name: "Unisat", rdns: "io.unisat.wallet", provider: window.unisat },
|
|
1624
|
+
// OKX shares rdns with its EVM provider — merges into the EVM entry automatically
|
|
1625
|
+
{ name: "OKX Wallet", rdns: "com.okex.okxwallet", provider: window.okxwallet?.bitcoin }
|
|
1626
|
+
];
|
|
1627
|
+
for (const { name, rdns, provider } of candidates) {
|
|
1628
|
+
if (!provider) continue;
|
|
1629
|
+
registry.registerOrMerge({
|
|
1630
|
+
chain: "bitcoin",
|
|
1631
|
+
name,
|
|
1632
|
+
icon: BTC_ICON,
|
|
1633
|
+
rdns,
|
|
1634
|
+
provider
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
}, 150);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1383
1640
|
// src/factory.ts
|
|
1384
1641
|
function createDecane(config = {}) {
|
|
1385
1642
|
const emitter = createEmitter();
|
|
@@ -1393,6 +1650,8 @@ function createDecane(config = {}) {
|
|
|
1393
1650
|
const router = createRouter({ registry, session, chainStore });
|
|
1394
1651
|
startEvmDiscovery(registry);
|
|
1395
1652
|
startSolanaDiscovery(registry);
|
|
1653
|
+
startTronDiscovery(registry);
|
|
1654
|
+
startBitcoinDiscovery(registry);
|
|
1396
1655
|
return createProvider({ config, registry, session, chainStore, router, emitter });
|
|
1397
1656
|
}
|
|
1398
1657
|
|
|
@@ -1560,6 +1819,67 @@ function WalletIcon({ src, name, size = 36 }) {
|
|
|
1560
1819
|
initial
|
|
1561
1820
|
);
|
|
1562
1821
|
}
|
|
1822
|
+
function EthCircle({ size }) {
|
|
1823
|
+
const s = size;
|
|
1824
|
+
return /* @__PURE__ */ React.createElement("svg", { width: s, height: s, viewBox: "0 0 16 16", fill: "none" }, /* @__PURE__ */ React.createElement("circle", { cx: "8", cy: "8", r: "8", fill: "#627EEA" }), /* @__PURE__ */ React.createElement("polygon", { points: "8,2.5 11.5,8 8,9.8 4.5,8", fill: "white", fillOpacity: "0.9" }), /* @__PURE__ */ React.createElement("polygon", { points: "8,9.8 11.5,8 8,13.5", fill: "white", fillOpacity: "0.6" }), /* @__PURE__ */ React.createElement("polygon", { points: "8,9.8 4.5,8 8,13.5", fill: "white", fillOpacity: "0.8" }), /* @__PURE__ */ React.createElement("polygon", { points: "8,2.5 8,9.8 4.5,8", fill: "white", fillOpacity: "0.6" }));
|
|
1825
|
+
}
|
|
1826
|
+
function SolCircle({ size }) {
|
|
1827
|
+
const s = size;
|
|
1828
|
+
return /* @__PURE__ */ React.createElement("svg", { width: s, height: s, viewBox: "0 0 16 16", fill: "none" }, /* @__PURE__ */ React.createElement("circle", { cx: "8", cy: "8", r: "8", fill: "#9945FF" }), /* @__PURE__ */ React.createElement("rect", { x: "4", y: "4.5", width: "7", height: "1.5", rx: "0.75", fill: "white", fillOpacity: "0.95" }), /* @__PURE__ */ React.createElement("rect", { x: "4", y: "7.25", width: "7", height: "1.5", rx: "0.75", fill: "white", fillOpacity: "0.95" }), /* @__PURE__ */ React.createElement("rect", { x: "4", y: "10", width: "5.5", height: "1.5", rx: "0.75", fill: "white", fillOpacity: "0.95" }));
|
|
1829
|
+
}
|
|
1830
|
+
function TronCircle({ size }) {
|
|
1831
|
+
const s = size;
|
|
1832
|
+
return /* @__PURE__ */ React.createElement("svg", { width: s, height: s, viewBox: "0 0 16 16", fill: "none" }, /* @__PURE__ */ React.createElement("circle", { cx: "8", cy: "8", r: "8", fill: "#FF0013" }), /* @__PURE__ */ React.createElement("polygon", { points: "6,4.5 12,8 6,11.5", fill: "white", fillOpacity: "0.95" }));
|
|
1833
|
+
}
|
|
1834
|
+
function BitcoinCircle({ size }) {
|
|
1835
|
+
const s = size;
|
|
1836
|
+
return /* @__PURE__ */ React.createElement("svg", { width: s, height: s, viewBox: "0 0 16 16", fill: "none" }, /* @__PURE__ */ React.createElement("circle", { cx: "8", cy: "8", r: "8", fill: "#F7931A" }), /* @__PURE__ */ React.createElement(
|
|
1837
|
+
"text",
|
|
1838
|
+
{
|
|
1839
|
+
x: "8.3",
|
|
1840
|
+
y: "11.5",
|
|
1841
|
+
textAnchor: "middle",
|
|
1842
|
+
fontSize: "8",
|
|
1843
|
+
fontWeight: "700",
|
|
1844
|
+
fill: "white",
|
|
1845
|
+
fontFamily: "Arial, system-ui"
|
|
1846
|
+
},
|
|
1847
|
+
"\u20BF"
|
|
1848
|
+
));
|
|
1849
|
+
}
|
|
1850
|
+
function ChainCircleIcon({ chain, size }) {
|
|
1851
|
+
switch (chain) {
|
|
1852
|
+
case "evm":
|
|
1853
|
+
return /* @__PURE__ */ React.createElement(EthCircle, { size });
|
|
1854
|
+
case "solana":
|
|
1855
|
+
return /* @__PURE__ */ React.createElement(SolCircle, { size });
|
|
1856
|
+
case "tron":
|
|
1857
|
+
return /* @__PURE__ */ React.createElement(TronCircle, { size });
|
|
1858
|
+
case "bitcoin":
|
|
1859
|
+
return /* @__PURE__ */ React.createElement(BitcoinCircle, { size });
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
var CHAIN_ORDER = ["evm", "solana", "tron", "bitcoin"];
|
|
1863
|
+
function ChainIconStack({ support, size = 16 }) {
|
|
1864
|
+
const ordered = CHAIN_ORDER.filter((c) => support.includes(c));
|
|
1865
|
+
if (ordered.length === 0) return null;
|
|
1866
|
+
const overlap = Math.round(size * 0.35);
|
|
1867
|
+
return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", flexShrink: 0 } }, ordered.map((chain, i) => /* @__PURE__ */ React.createElement(
|
|
1868
|
+
"div",
|
|
1869
|
+
{
|
|
1870
|
+
key: chain,
|
|
1871
|
+
style: {
|
|
1872
|
+
marginLeft: i === 0 ? 0 : `-${overlap}px`,
|
|
1873
|
+
zIndex: ordered.length - i,
|
|
1874
|
+
position: "relative",
|
|
1875
|
+
borderRadius: "50%",
|
|
1876
|
+
lineHeight: 0,
|
|
1877
|
+
boxShadow: i > 0 ? `0 0 0 1.5px var(--dc-bg, #fff)` : void 0
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
/* @__PURE__ */ React.createElement(ChainCircleIcon, { chain, size })
|
|
1881
|
+
)));
|
|
1882
|
+
}
|
|
1563
1883
|
|
|
1564
1884
|
// src/ui/WalletSelector.tsx
|
|
1565
1885
|
var FONT_URL = "https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap";
|
|
@@ -1586,21 +1906,6 @@ function injectFont() {
|
|
|
1586
1906
|
link.href = FONT_URL;
|
|
1587
1907
|
document.head.appendChild(link);
|
|
1588
1908
|
}
|
|
1589
|
-
var badgeBase = {
|
|
1590
|
-
display: "inline-flex",
|
|
1591
|
-
alignItems: "center",
|
|
1592
|
-
padding: "3px 8px",
|
|
1593
|
-
borderRadius: 20,
|
|
1594
|
-
fontSize: 10,
|
|
1595
|
-
fontWeight: 700,
|
|
1596
|
-
letterSpacing: "0.04em",
|
|
1597
|
-
textTransform: "uppercase"
|
|
1598
|
-
};
|
|
1599
|
-
function ChainBadges({ chainSupport }) {
|
|
1600
|
-
const hasEvm = chainSupport.includes("evm");
|
|
1601
|
-
const hasSol = chainSupport.includes("solana");
|
|
1602
|
-
return /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4 } }, hasEvm && /* @__PURE__ */ React2.createElement("span", { style: { ...badgeBase, background: "var(--dc-badge-evm)", color: "var(--dc-badge-evm-text)" } }, "EVM"), hasSol && /* @__PURE__ */ React2.createElement("span", { style: { ...badgeBase, background: "var(--dc-badge-sol)", color: "var(--dc-badge-sol-text)", border: "1px solid var(--dc-border)" } }, "SVM"));
|
|
1603
|
-
}
|
|
1604
1909
|
function WalletRow({ wallet, onClick }) {
|
|
1605
1910
|
const [hovered, setHovered] = useState(false);
|
|
1606
1911
|
const [focused, setFocused] = useState(false);
|
|
@@ -1630,7 +1935,7 @@ function WalletRow({ wallet, onClick }) {
|
|
|
1630
1935
|
},
|
|
1631
1936
|
/* @__PURE__ */ React2.createElement(WalletIcon, { src: wallet.icon || void 0, name: wallet.name, size: 38 }),
|
|
1632
1937
|
/* @__PURE__ */ React2.createElement("span", { style: { flex: 1, fontSize: 14, fontWeight: 600, color: "var(--dc-text)", letterSpacing: "-0.01em" } }, wallet.name),
|
|
1633
|
-
/* @__PURE__ */ React2.createElement(
|
|
1938
|
+
/* @__PURE__ */ React2.createElement(ChainIconStack, { support: wallet.chainSupport, size: 16 }),
|
|
1634
1939
|
/* @__PURE__ */ React2.createElement(
|
|
1635
1940
|
"svg",
|
|
1636
1941
|
{
|
|
@@ -1757,17 +2062,19 @@ function WalletSelector({
|
|
|
1757
2062
|
const cs = w.chainSupport;
|
|
1758
2063
|
if (networks === "evm") return cs.includes("evm");
|
|
1759
2064
|
if (networks === "solana") return cs.includes("solana");
|
|
2065
|
+
if (networks === "tron") return cs.includes("tron");
|
|
2066
|
+
if (networks === "bitcoin") return cs.includes("bitcoin");
|
|
1760
2067
|
return true;
|
|
1761
2068
|
});
|
|
1762
|
-
const baseWallets = filter === "evm" ? networkWallets.filter((w) => w.chainSupport.includes("evm")) : filter === "solana" ? networkWallets.filter((w) => w.chainSupport.includes("solana")) : networkWallets;
|
|
2069
|
+
const baseWallets = filter === "evm" ? networkWallets.filter((w) => w.chainSupport.includes("evm")) : filter === "solana" ? networkWallets.filter((w) => w.chainSupport.includes("solana")) : filter === "tron" ? networkWallets.filter((w) => w.chainSupport.includes("tron")) : filter === "bitcoin" ? networkWallets.filter((w) => w.chainSupport.includes("bitcoin")) : networkWallets;
|
|
1763
2070
|
const filtered = q ? baseWallets.filter((w) => w.name.toLowerCase().includes(q)) : baseWallets;
|
|
1764
2071
|
function handleWalletClick(wallet) {
|
|
1765
2072
|
const cs = wallet.chainSupport;
|
|
1766
|
-
const isMulti = cs.
|
|
2073
|
+
const isMulti = cs.length >= 2;
|
|
1767
2074
|
if (isMulti && networks === "all") {
|
|
1768
2075
|
setPendingWallet(wallet);
|
|
1769
2076
|
} else {
|
|
1770
|
-
const preference = networks === "evm" ? "evm" : networks === "solana" ? "solana" : void 0;
|
|
2077
|
+
const preference = networks === "evm" ? "evm" : networks === "solana" ? "solana" : networks === "tron" ? "tron" : networks === "bitcoin" ? "bitcoin" : void 0;
|
|
1771
2078
|
onConnect(wallet, preference);
|
|
1772
2079
|
onClose();
|
|
1773
2080
|
}
|
|
@@ -1902,28 +2209,44 @@ function WalletSelector({
|
|
|
1902
2209
|
},
|
|
1903
2210
|
/* @__PURE__ */ React2.createElement(WalletIcon, { src: pendingWallet.icon || void 0, name: pendingWallet.name, size: 36 }),
|
|
1904
2211
|
/* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("div", { style: { fontSize: 14, fontWeight: 700, color: "var(--dc-text)" } }, pendingWallet.name), /* @__PURE__ */ React2.createElement("div", { style: { fontSize: 11, color: "var(--dc-text-muted)", marginTop: 2 } }, "Multi-chain wallet detected"))
|
|
1905
|
-
), /* @__PURE__ */ React2.createElement("div", { style: { padding: "0 12px 20px", display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React2.createElement(
|
|
2212
|
+
), /* @__PURE__ */ React2.createElement("div", { style: { padding: "0 12px 20px", display: "flex", flexDirection: "column", gap: 8 } }, pendingWallet.chainSupport.includes("evm") && /* @__PURE__ */ React2.createElement(
|
|
1906
2213
|
ChainOption,
|
|
1907
2214
|
{
|
|
1908
2215
|
label: "EVM networks",
|
|
1909
2216
|
description: "Connect your Ethereum-compatible address only",
|
|
1910
|
-
badges: /* @__PURE__ */ React2.createElement(
|
|
2217
|
+
badges: /* @__PURE__ */ React2.createElement(ChainIconStack, { support: ["evm"], size: 18 }),
|
|
1911
2218
|
onClick: () => handleChainChoice("evm")
|
|
1912
2219
|
}
|
|
1913
|
-
), /* @__PURE__ */ React2.createElement(
|
|
2220
|
+
), pendingWallet.chainSupport.includes("solana") && /* @__PURE__ */ React2.createElement(
|
|
1914
2221
|
ChainOption,
|
|
1915
2222
|
{
|
|
1916
2223
|
label: "Solana network",
|
|
1917
2224
|
description: "Connect your Solana address only",
|
|
1918
|
-
badges: /* @__PURE__ */ React2.createElement(
|
|
2225
|
+
badges: /* @__PURE__ */ React2.createElement(ChainIconStack, { support: ["solana"], size: 18 }),
|
|
1919
2226
|
onClick: () => handleChainChoice("solana")
|
|
1920
2227
|
}
|
|
2228
|
+
), pendingWallet.chainSupport.includes("tron") && /* @__PURE__ */ React2.createElement(
|
|
2229
|
+
ChainOption,
|
|
2230
|
+
{
|
|
2231
|
+
label: "Tron network",
|
|
2232
|
+
description: "Connect your Tron address only",
|
|
2233
|
+
badges: /* @__PURE__ */ React2.createElement(ChainIconStack, { support: ["tron"], size: 18 }),
|
|
2234
|
+
onClick: () => handleChainChoice("tron")
|
|
2235
|
+
}
|
|
2236
|
+
), pendingWallet.chainSupport.includes("bitcoin") && /* @__PURE__ */ React2.createElement(
|
|
2237
|
+
ChainOption,
|
|
2238
|
+
{
|
|
2239
|
+
label: "Bitcoin network",
|
|
2240
|
+
description: "Connect your Bitcoin address only",
|
|
2241
|
+
badges: /* @__PURE__ */ React2.createElement(ChainIconStack, { support: ["bitcoin"], size: 18 }),
|
|
2242
|
+
onClick: () => handleChainChoice("bitcoin")
|
|
2243
|
+
}
|
|
1921
2244
|
), /* @__PURE__ */ React2.createElement(
|
|
1922
2245
|
ChainOption,
|
|
1923
2246
|
{
|
|
1924
|
-
label: "
|
|
1925
|
-
description: "Connect
|
|
1926
|
-
badges: /* @__PURE__ */ React2.createElement(
|
|
2247
|
+
label: "All networks",
|
|
2248
|
+
description: "Connect all supported addresses together",
|
|
2249
|
+
badges: /* @__PURE__ */ React2.createElement(ChainIconStack, { support: pendingWallet.chainSupport, size: 18 }),
|
|
1927
2250
|
onClick: () => handleChainChoice(void 0)
|
|
1928
2251
|
}
|
|
1929
2252
|
)))
|
|
@@ -1998,7 +2321,7 @@ function WalletSelector({
|
|
|
1998
2321
|
e.currentTarget.style.borderColor = "var(--dc-border)";
|
|
1999
2322
|
}
|
|
2000
2323
|
}
|
|
2001
|
-
)), networks === "all" && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 10 } }, /* @__PURE__ */ React2.createElement(FilterTab, { label: "All", active: filter === "all", onClick: () => setFilter("all") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "EVM", active: filter === "evm", onClick: () => setFilter("evm") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "Solana", active: filter === "solana", onClick: () => setFilter("solana") }))), /* @__PURE__ */ React2.createElement("div", { style: { overflowY: "auto", flex: 1, padding: "0 12px 20px" } }, filtered.length > 0 ? /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((w) => /* @__PURE__ */ React2.createElement(WalletRow, { key: w.id, wallet: w, onClick: () => handleWalletClick(w) }))) : /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center", padding: "32px 16px", color: "var(--dc-text-muted)", fontSize: 13 } }, q ? `No wallets matching "${q}"` : "No wallets detected")), /* @__PURE__ */ React2.createElement("div", { style: { padding: "10px 20px 14px", borderTop: "1px solid var(--dc-border)", textAlign: "center", fontSize: 11, color: "var(--dc-text-muted)" } }, "Don't see your wallet? Make sure it's installed and unlocked"))
|
|
2324
|
+
)), networks === "all" && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 10 } }, /* @__PURE__ */ React2.createElement(FilterTab, { label: "All", active: filter === "all", onClick: () => setFilter("all") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "EVM", active: filter === "evm", onClick: () => setFilter("evm") }), /* @__PURE__ */ React2.createElement(FilterTab, { label: "Solana", active: filter === "solana", onClick: () => setFilter("solana") }), provider.tronWallets && provider.tronWallets.length > 0 && /* @__PURE__ */ React2.createElement(FilterTab, { label: "Tron", active: filter === "tron", onClick: () => setFilter("tron") }), provider.bitcoinWallets && provider.bitcoinWallets.length > 0 && /* @__PURE__ */ React2.createElement(FilterTab, { label: "Bitcoin", active: filter === "bitcoin", onClick: () => setFilter("bitcoin") }))), /* @__PURE__ */ React2.createElement("div", { style: { overflowY: "auto", flex: 1, padding: "0 12px 20px" } }, filtered.length > 0 ? /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((w) => /* @__PURE__ */ React2.createElement(WalletRow, { key: w.id, wallet: w, onClick: () => handleWalletClick(w) }))) : /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center", padding: "32px 16px", color: "var(--dc-text-muted)", fontSize: 13 } }, q ? `No wallets matching "${q}"` : "No wallets detected")), /* @__PURE__ */ React2.createElement("div", { style: { padding: "10px 20px 14px", borderTop: "1px solid var(--dc-border)", textAlign: "center", fontSize: 11, color: "var(--dc-text-muted)" } }, "Don't see your wallet? Make sure it's installed and unlocked"))
|
|
2002
2325
|
)
|
|
2003
2326
|
)
|
|
2004
2327
|
);
|