decane-connect-kit 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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # decane-connect-kit
2
2
 
3
- Wallet connection for EVM and Solana dApps. Detects installed wallets via EIP-6963 and the Solana Wallet Standard, manages sessions with CAIP-25, and ships a polished wallet selector UI. Works with React 18+ in any framework.
3
+ Wallet connection for EVM, Solana, Tron, and Bitcoin dApps. Detects installed wallets via EIP-6963, the Solana Wallet Standard, TronLink, and Unisat/OKX/MetaMask Snap. Manages sessions with CAIP-25 and ships a polished wallet selector UI. Works with React 18+ in any framework.
4
4
 
5
5
  ---
6
6
 
@@ -137,7 +137,7 @@ All options are optional.
137
137
  ```tsx
138
138
  <DecaneKit config={{
139
139
  // Which wallet types to show and connect
140
- // "evm" | "solana" | "all" — default: "all"
140
+ // "evm" | "solana" | "tron" | "bitcoin" | "all" — default: "all"
141
141
  networks: "all",
142
142
 
143
143
  // Restrict which built-in chains are available
@@ -167,16 +167,22 @@ All options are optional.
167
167
 
168
168
  ### `networks`
169
169
 
170
- Controls which wallets appear in the selector and how multi-chain wallets connect.
170
+ Controls which wallets appear in the selector and which chain types are offered.
171
171
 
172
172
  ```tsx
173
- // EVM-only dApp — Solana wallets hidden, chain picker skipped
173
+ // EVM-only dApp — all other wallets hidden
174
174
  <DecaneKit config={{ networks: "evm" }}>
175
175
 
176
176
  // Solana-only dApp
177
177
  <DecaneKit config={{ networks: "solana" }}>
178
178
 
179
- // Both (default) — all wallets shown, chain picker appears for Phantom/Backpack
179
+ // Tron-only dApp
180
+ <DecaneKit config={{ networks: "tron" }}>
181
+
182
+ // Bitcoin-only dApp
183
+ <DecaneKit config={{ networks: "bitcoin" }}>
184
+
185
+ // All chains (default)
180
186
  <DecaneKit config={{ networks: "all" }}>
181
187
  ```
182
188
 
@@ -188,7 +194,7 @@ Restricts the chain list used throughout the SDK — the wallet selector, `provi
188
194
  import { CHAINS } from "decane-connect-kit";
189
195
 
190
196
  <DecaneKit config={{
191
- supportedChainIds: [CHAINS.ETHEREUM, CHAINS.BASE, CHAINS.SOLANA],
197
+ supportedChainIds: [CHAINS.ETHEREUM, CHAINS.BASE, CHAINS.SOLANA, CHAINS.TRON, CHAINS.BITCOIN],
192
198
  }}>
193
199
  ```
194
200
 
@@ -204,9 +210,11 @@ Reactive state hook. Re-renders on any wallet or session change.
204
210
  const {
205
211
  // Detected wallets
206
212
  wallets, // all wallets
207
- evmWallets, // EVM-only + multi-chain
208
- solanaWallets, // Solana-only + multi-chain
209
- multiChainWallets, // wallets with both EVM + Solana
213
+ evmWallets, // EVM wallets
214
+ solanaWallets, // Solana wallets
215
+ tronWallets, // Tron wallets (TronLink)
216
+ bitcoinWallets, // Bitcoin wallets (Unisat, OKX, MetaMask Snap)
217
+ multiChainWallets, // wallets supporting 2+ chains
210
218
 
211
219
  // Connection state
212
220
  isConnected,
@@ -241,6 +249,8 @@ const { isOpen, open, close } = useWalletSelector();
241
249
 
242
250
  ### `useSignMessage()`
243
251
 
252
+ Works on EVM, Solana, Tron, and Bitcoin. The active chain determines the signing method used.
253
+
244
254
  ```tsx
245
255
  const { signMessage, signature, loading, error, reset } = useSignMessage();
246
256
 
@@ -249,27 +259,38 @@ await signMessage("Hello world");
249
259
 
250
260
  // Override chain or account
251
261
  await signMessage("Hello world", { chainId: CHAINS.POLYGON, account: "0xAbc…" });
262
+ await signMessage("Hello world", { chainId: CHAINS.TRON });
263
+ await signMessage("Hello world", { chainId: CHAINS.BITCOIN });
252
264
  ```
253
265
 
254
266
  ### `useSendTransaction()`
255
267
 
268
+ Works on EVM, Solana, Tron, and Bitcoin.
269
+
256
270
  ```tsx
257
271
  const { sendTransaction, txHash, loading, error, reset } = useSendTransaction();
258
272
 
273
+ // EVM — value in wei
259
274
  await sendTransaction({
260
275
  to: "0xRecipient",
261
- value: "1000000000000000", // wei as decimal string or hex
276
+ value: "1000000000000000", // decimal string or hex
262
277
  data: "0x", // optional calldata
263
278
  gas: "21000", // optional
264
279
  });
265
280
 
281
+ // Tron — value in SUN (1 TRX = 1,000,000 SUN)
282
+ await sendTransaction({ to: "TRecipient…", value: "1000000" }, { chainId: CHAINS.TRON });
283
+
284
+ // Bitcoin — value in satoshis
285
+ await sendTransaction({ to: "bc1q…", value: "10000" }, { chainId: CHAINS.BITCOIN });
286
+
266
287
  // Override chain
267
288
  await sendTransaction({ to: "0xRecipient" }, { chainId: CHAINS.ARBITRUM });
268
289
  ```
269
290
 
270
291
  ### `useSignTypedData()`
271
292
 
272
- EVM only. Throws `DecaneErrorCode.EVM_ONLY_METHOD` if called on a Solana chain.
293
+ EVM only. Throws `DecaneErrorCode.EVM_ONLY_METHOD` if called on a non-EVM chain.
273
294
 
274
295
  ```tsx
275
296
  const { signTypedData, signature, loading, error, reset } = useSignTypedData();
@@ -289,12 +310,35 @@ Switch the active chain within the current session.
289
310
  const { switchChain, activeChain, grantedChains, loading, error } = useChainSwitch();
290
311
 
291
312
  // grantedChains — CAIP-2 IDs granted in the current session
292
- // switchChain requests permissions for the new chain
293
313
  await switchChain(CHAINS.POLYGON);
314
+ await switchChain(CHAINS.TRON);
315
+ await switchChain(CHAINS.BITCOIN);
294
316
  ```
295
317
 
296
318
  ---
297
319
 
320
+ ## Wallet detection
321
+
322
+ ### EVM
323
+ Detected via **EIP-6963** — standard event-based discovery. All compliant wallets (MetaMask, Rabby, Coinbase Wallet, Brave, OKX, and others) are discovered automatically. `window.ethereum` is never used.
324
+
325
+ ### Solana
326
+ Detected via the **Wallet Standard** (`@wallet-standard/app`). Wallets must expose `standard:connect`, `solana:signTransaction`, and `solana:signMessage`. Multi-chain wallets like Phantom and Backpack are merged into a single entry when detected on both EVM and Solana.
327
+
328
+ ### Tron
329
+ Detected via **TronLink's injected provider** (`window.tronLink`). The SDK listens for `tronLink#initialized` and polls for up to 3 seconds to handle extensions that load after the page. Transactions are built via `window.tronWeb.transactionBuilder` when available, falling back to the TronGrid HTTP API.
330
+
331
+ ### Bitcoin
332
+ Three detection paths run in parallel:
333
+
334
+ | Wallet | Detection |
335
+ |--------|-----------|
336
+ | **Unisat** | `window.unisat` injected provider |
337
+ | **OKX Wallet** | `window.okxwallet.bitcoin` — shares `rdns` with OKX EVM, auto-merges to give EVM + Bitcoin support |
338
+ | **MetaMask Bitcoin Snap** | After each EVM wallet is discovered, `wallet_getSnaps` is called silently. If `npm:@metamask/bitcoin-snap` or `npm:@consensys/btc-snap` is enabled, the wallet gains Bitcoin support with no user interaction |
339
+
340
+ ---
341
+
298
342
  ## Chain constants
299
343
 
300
344
  Use `CHAINS` instead of bare strings to get autocomplete and catch typos at compile time.
@@ -302,7 +346,7 @@ Use `CHAINS` instead of bare strings to get autocomplete and catch typos at comp
302
346
  ```tsx
303
347
  import { CHAINS, type ChainId } from "decane-connect-kit";
304
348
 
305
- // Tier-1
349
+ // Tier-1 EVM
306
350
  CHAINS.ETHEREUM // "eip155:1"
307
351
  CHAINS.BASE // "eip155:8453"
308
352
  CHAINS.ARBITRUM // "eip155:42161"
@@ -335,6 +379,13 @@ CHAINS.LOCALHOST // "eip155:31337"
335
379
  // Solana
336
380
  CHAINS.SOLANA // "solana:mainnet"
337
381
  CHAINS.SOLANA_DEVNET // "solana:devnet"
382
+
383
+ // Tron
384
+ CHAINS.TRON // "tron:mainnet"
385
+ CHAINS.TRON_SHASTA // "tron:shasta"
386
+
387
+ // Bitcoin
388
+ CHAINS.BITCOIN // "bip122:mainnet"
338
389
  ```
339
390
 
340
391
  `ChainId` is the union type of all built-in chain ID strings.
@@ -422,7 +473,7 @@ if (error) {
422
473
  | `WALLET_DISCONNECTED` | 5021 | Wallet disconnected mid-session |
423
474
  | `PROVIDER_ERROR` | 5022 | Underlying wallet threw an error |
424
475
  | `METHOD_NOT_SUPPORTED` | 5030 | Method not recognised |
425
- | `EVM_ONLY_METHOD` | 5031 | `wallet_signTypedData` on Solana chain |
476
+ | `EVM_ONLY_METHOD` | 5031 | `wallet_signTypedData` on a non-EVM chain |
426
477
 
427
478
  ---
428
479
 
@@ -477,7 +528,7 @@ import type {
477
528
  ChainDescriptor, // chain definition object
478
529
  ActiveSession, // CAIP-25 session state
479
530
  ChainScope, // methods + accounts for one chain
480
- ChainType, // "evm" | "solana"
531
+ ChainType, // "evm" | "solana" | "tron" | "bitcoin"
481
532
  ChainId, // union of all built-in CAIP-2 chain IDs
482
533
  Theme, // "light" | "dark" | "auto"
483
534
  TxParams, // params for useSendTransaction
@@ -490,7 +541,7 @@ import type {
490
541
  ## What is not included
491
542
 
492
543
  - Social sign-in or email login
493
- - Any backend, server, or API calls
544
+ - Any backend, server, or API calls (except the TronGrid fallback when TronWeb is unavailable)
494
545
  - `window.ethereum` — detection uses EIP-6963 exclusively
495
546
  - Private key or seed phrase management
496
547
  - State management libraries (Redux, Zustand) — pure React context
@@ -581,3 +632,16 @@ The following chains are available out of the box. Pass `supportedChainIds` in c
581
632
  |------|-----------|-------------------|
582
633
  | Solana | `solana:mainnet` | `CHAINS.SOLANA` |
583
634
  | Solana Devnet | `solana:devnet` | `CHAINS.SOLANA_DEVNET` |
635
+
636
+ ### Tron
637
+
638
+ | Name | CAIP-2 ID | `CHAINS` constant |
639
+ |------|-----------|-------------------|
640
+ | Tron | `tron:mainnet` | `CHAINS.TRON` |
641
+ | Tron Shasta Testnet | `tron:shasta` | `CHAINS.TRON_SHASTA` |
642
+
643
+ ### Bitcoin
644
+
645
+ | Name | CAIP-2 ID | `CHAINS` constant |
646
+ |------|-----------|-------------------|
647
+ | Bitcoin | `bip122:mainnet` | `CHAINS.BITCOIN` |
package/dist/index.cjs CHANGED
@@ -1104,7 +1104,14 @@ async function getSolanaAddress(wallet) {
1104
1104
  }
1105
1105
 
1106
1106
  // src/tron/adapter.ts
1107
+ function getTronWeb() {
1108
+ if (typeof window === "undefined") return void 0;
1109
+ const win = window;
1110
+ return win.tronLink?.tronWeb ?? win.tronWeb;
1111
+ }
1107
1112
  async function getTronAddress(provider) {
1113
+ const addr = getTronWeb()?.defaultAddress?.base58;
1114
+ if (addr) return addr;
1108
1115
  try {
1109
1116
  const result = await provider.request({ method: "tron_requestAccounts" });
1110
1117
  const accounts = result;
@@ -1123,11 +1130,80 @@ async function tronRequest(provider, method, payload, account) {
1123
1130
  method: "tron_signMessage",
1124
1131
  params: { message: payload }
1125
1132
  });
1126
- case "wallet_sendTransaction":
1127
- return provider.request({
1128
- method: "tron_sendTransaction",
1129
- params: { ...payload, from: account }
1130
- });
1133
+ case "wallet_sendTransaction": {
1134
+ const tronWeb = getTronWeb();
1135
+ const p = payload;
1136
+ const toAddress = p.to;
1137
+ const sunAmount = typeof p.value === "string" ? parseInt(p.value, 10) : p.value ?? 0;
1138
+ let unsignedTx;
1139
+ if (tronWeb?.transactionBuilder?.sendTrx) {
1140
+ if (p.data && p.data !== "0x") {
1141
+ const res = await tronWeb.transactionBuilder.triggerSmartContract(
1142
+ toAddress,
1143
+ "transfer(address,uint256)",
1144
+ { callValue: 0, feeLimit: 1e8 },
1145
+ [],
1146
+ account
1147
+ );
1148
+ unsignedTx = res.transaction;
1149
+ } else {
1150
+ unsignedTx = await tronWeb.transactionBuilder.sendTrx(toAddress, sunAmount, account);
1151
+ }
1152
+ } else {
1153
+ const host = tronWeb?.fullNode?.host ?? "https://api.trongrid.io";
1154
+ const endpoint = p.data && p.data !== "0x" ? `${host}/wallet/triggersmartcontract` : `${host}/wallet/createtransaction`;
1155
+ const body = p.data && p.data !== "0x" ? {
1156
+ owner_address: account,
1157
+ contract_address: toAddress,
1158
+ function_selector: "transfer(address,uint256)",
1159
+ fee_limit: 1e8,
1160
+ call_value: 0,
1161
+ visible: true
1162
+ } : {
1163
+ owner_address: account,
1164
+ to_address: toAddress,
1165
+ amount: sunAmount,
1166
+ visible: true
1167
+ };
1168
+ const res = await fetch(endpoint, {
1169
+ method: "POST",
1170
+ headers: { "Content-Type": "application/json" },
1171
+ body: JSON.stringify(body)
1172
+ });
1173
+ if (!res.ok) {
1174
+ throw new DecaneError(
1175
+ 5022 /* PROVIDER_ERROR */,
1176
+ `Failed to build Tron transaction: ${res.statusText}`
1177
+ );
1178
+ }
1179
+ const json = await res.json();
1180
+ unsignedTx = json.transaction ?? json;
1181
+ }
1182
+ let signedTx;
1183
+ if (tronWeb?.trx?.sign) {
1184
+ signedTx = await tronWeb.trx.sign(unsignedTx);
1185
+ } else {
1186
+ signedTx = await provider.request({
1187
+ method: "tron_signTransaction",
1188
+ params: unsignedTx
1189
+ });
1190
+ }
1191
+ let txid;
1192
+ if (tronWeb?.trx?.sendRawTransaction) {
1193
+ const result = await tronWeb.trx.sendRawTransaction(signedTx);
1194
+ txid = result?.txid;
1195
+ } else {
1196
+ const result = await provider.request({
1197
+ method: "tron_sendRawTransaction",
1198
+ params: signedTx
1199
+ });
1200
+ txid = result?.txid;
1201
+ }
1202
+ if (!txid) {
1203
+ throw new DecaneError(5022 /* PROVIDER_ERROR */, "Tron transaction broadcast failed");
1204
+ }
1205
+ return txid;
1206
+ }
1131
1207
  default:
1132
1208
  throw new DecaneError(
1133
1209
  5030 /* METHOD_NOT_SUPPORTED */,
@@ -2425,6 +2501,11 @@ function useDecaneContext() {
2425
2501
 
2426
2502
  // src/ui/hooks.ts
2427
2503
  var import_react4 = require("react");
2504
+ function toDecaneError(err) {
2505
+ if (err instanceof DecaneError) return err;
2506
+ const msg = err instanceof Error ? err.message : err?.message != null ? String(err.message) : String(err);
2507
+ return new DecaneError(5022 /* PROVIDER_ERROR */, msg);
2508
+ }
2428
2509
  function useDecane() {
2429
2510
  const { provider, theme, setTheme } = useDecaneContext();
2430
2511
  const [, forceUpdate] = (0, import_react4.useState)(0);
@@ -2502,7 +2583,7 @@ function useChainSwitch() {
2502
2583
  });
2503
2584
  forceUpdate((n) => n + 1);
2504
2585
  } catch (err) {
2505
- const e = err instanceof DecaneError ? err : new DecaneError(5022 /* PROVIDER_ERROR */, String(err));
2586
+ const e = toDecaneError(err);
2506
2587
  setError(e);
2507
2588
  throw err;
2508
2589
  } finally {
@@ -2553,7 +2634,7 @@ function useSignMessage() {
2553
2634
  setSignature(sig);
2554
2635
  return sig;
2555
2636
  } catch (err) {
2556
- const e = err instanceof DecaneError ? err : new DecaneError(5022 /* PROVIDER_ERROR */, String(err));
2637
+ const e = toDecaneError(err);
2557
2638
  setError(e);
2558
2639
  throw err;
2559
2640
  } finally {
@@ -2591,7 +2672,7 @@ function useSendTransaction() {
2591
2672
  setTxHash(hash);
2592
2673
  return hash;
2593
2674
  } catch (err) {
2594
- const e = err instanceof DecaneError ? err : new DecaneError(5022 /* PROVIDER_ERROR */, String(err));
2675
+ const e = toDecaneError(err);
2595
2676
  setError(e);
2596
2677
  throw err;
2597
2678
  } finally {
@@ -2628,7 +2709,7 @@ function useSignTypedData() {
2628
2709
  setSignature(sig);
2629
2710
  return sig;
2630
2711
  } catch (err) {
2631
- const e = err instanceof DecaneError ? err : new DecaneError(5022 /* PROVIDER_ERROR */, String(err));
2712
+ const e = toDecaneError(err);
2632
2713
  setError(e);
2633
2714
  throw err;
2634
2715
  } finally {