decane-connect-kit 2.2.2 → 2.2.3

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.cts CHANGED
@@ -283,6 +283,17 @@ interface PortableBackup {
283
283
  createdAt: string;
284
284
  }
285
285
 
286
+ interface BalanceResult {
287
+ /** Smallest unit — wei for EVM, lamports for Solana. */
288
+ raw: bigint;
289
+ /** Human-readable amount in whole native units, e.g. "1.2345". */
290
+ formatted: string;
291
+ /** Native currency symbol, e.g. "ETH", "MATIC", "SOL". */
292
+ symbol: string;
293
+ /** Decimals of the native currency (18 for EVM, 9 for Solana). */
294
+ decimals: number;
295
+ }
296
+
286
297
  type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}`;
287
298
  interface DecaneConnectConfig {
288
299
  /** Dashboard project ID (public). Sent with every request so the backend can
@@ -369,6 +380,13 @@ interface SendTransactionRequest {
369
380
  gasLimit?: bigint;
370
381
  gasPrice?: bigint;
371
382
  }
383
+ interface SignTypedDataRequest {
384
+ chain: Chain;
385
+ domain: Record<string, unknown>;
386
+ types: Record<string, unknown>;
387
+ message: Record<string, unknown>;
388
+ primaryType?: string;
389
+ }
372
390
  type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started';
373
391
  interface DecaneConnect {
374
392
  connect(): Promise<ConnectResult>;
@@ -419,12 +437,26 @@ interface DecaneConnect {
419
437
  sessionExpiresAt(): number | null;
420
438
  signMessage(req: SignMessageRequest): Promise<string>;
421
439
  sendTransaction(req: SendTransactionRequest): Promise<string>;
440
+ /** Sign EIP-712 typed data (EVM only). Throws on a Solana chain. */
441
+ signTypedData(req: SignTypedDataRequest): Promise<string>;
422
442
  signSolanaTransaction(tx: Uint8Array): Promise<Uint8Array>;
423
443
  disconnect(): Promise<void>;
424
444
  getAddresses(): {
425
445
  evm?: string;
426
446
  solana?: string;
427
447
  } | null;
448
+ /**
449
+ * Native-token balance for a chain (defaults to the first configured EVM
450
+ * chain, else Solana). Read-only public data — works even while the wallet
451
+ * is locked. Returns raw (wei/lamports), a human `formatted` string, the
452
+ * native `symbol`, and `decimals`.
453
+ */
454
+ getBalance(chain?: string): Promise<BalanceResult>;
455
+ /** Native balances across every configured chain the wallet has an address for. */
456
+ getBalances(): Promise<Array<{
457
+ chain: string;
458
+ balance: BalanceResult | null;
459
+ }>>;
428
460
  /**
429
461
  * Voluntary share rotation — after a suspected device compromise, or just
430
462
  * to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
@@ -733,6 +765,19 @@ interface SocialWalletModalProps {
733
765
  }
734
766
  declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
735
767
 
768
+ declare function DecaneMark({ size, muted }: {
769
+ size?: number;
770
+ muted?: boolean;
771
+ }): React.JSX.Element;
772
+ declare function DecaneWordmark({ size, muted }: {
773
+ size?: number;
774
+ muted?: boolean;
775
+ }): React.JSX.Element;
776
+ declare function DecaneLogo({ size, muted }: {
777
+ size?: number;
778
+ muted?: boolean;
779
+ }): React.JSX.Element;
780
+
736
781
  declare function useSocialAuth(): {
737
782
  isConnected: boolean;
738
783
  isUnlocked: boolean;
@@ -787,6 +832,18 @@ declare function useSocialWallet(): {
787
832
  gasPrice?: bigint;
788
833
  }) => Promise<string>;
789
834
  signSolanaTransaction: (tx: Uint8Array) => Promise<Uint8Array<ArrayBufferLike>>;
835
+ signTypedData: (req: {
836
+ chain: string;
837
+ domain: Record<string, unknown>;
838
+ types: Record<string, unknown>;
839
+ message: Record<string, unknown>;
840
+ primaryType?: string;
841
+ }) => Promise<string>;
842
+ getBalance: (chain?: string) => Promise<BalanceResult>;
843
+ getBalances: () => Promise<{
844
+ chain: string;
845
+ balance: BalanceResult | null;
846
+ }[]>;
790
847
  rotateShares: (opts: {
791
848
  password: string;
792
849
  passwordHint?: string;
@@ -802,6 +859,12 @@ declare function useSocialWallet(): {
802
859
  error: string | null;
803
860
  clearError: () => void;
804
861
  };
862
+ declare function useSocialBalance(chain?: string): {
863
+ balance: BalanceResult | null;
864
+ loading: boolean;
865
+ error: string | null;
866
+ refetch: () => Promise<void>;
867
+ };
805
868
  declare function useDecane(): {
806
869
  provider: DecaneProvider;
807
870
  wallets: UnifiedWalletAccount[];
@@ -874,4 +937,4 @@ declare function useSignTypedData(): {
874
937
  reset: () => void;
875
938
  };
876
939
 
877
- export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, type DecaneProvider, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialWallet, useWalletSelector, verifyEmailCode };
940
+ export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type BalanceResult, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, DecaneLogo, DecaneMark, type DecaneProvider, DecaneWordmark, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SignTypedDataRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialBalance, useSocialWallet, useWalletSelector, verifyEmailCode };
package/dist/index.d.ts CHANGED
@@ -283,6 +283,17 @@ interface PortableBackup {
283
283
  createdAt: string;
284
284
  }
285
285
 
286
+ interface BalanceResult {
287
+ /** Smallest unit — wei for EVM, lamports for Solana. */
288
+ raw: bigint;
289
+ /** Human-readable amount in whole native units, e.g. "1.2345". */
290
+ formatted: string;
291
+ /** Native currency symbol, e.g. "ETH", "MATIC", "SOL". */
292
+ symbol: string;
293
+ /** Decimals of the native currency (18 for EVM, 9 for Solana). */
294
+ decimals: number;
295
+ }
296
+
286
297
  type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}`;
287
298
  interface DecaneConnectConfig {
288
299
  /** Dashboard project ID (public). Sent with every request so the backend can
@@ -369,6 +380,13 @@ interface SendTransactionRequest {
369
380
  gasLimit?: bigint;
370
381
  gasPrice?: bigint;
371
382
  }
383
+ interface SignTypedDataRequest {
384
+ chain: Chain;
385
+ domain: Record<string, unknown>;
386
+ types: Record<string, unknown>;
387
+ message: Record<string, unknown>;
388
+ primaryType?: string;
389
+ }
372
390
  type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started';
373
391
  interface DecaneConnect {
374
392
  connect(): Promise<ConnectResult>;
@@ -419,12 +437,26 @@ interface DecaneConnect {
419
437
  sessionExpiresAt(): number | null;
420
438
  signMessage(req: SignMessageRequest): Promise<string>;
421
439
  sendTransaction(req: SendTransactionRequest): Promise<string>;
440
+ /** Sign EIP-712 typed data (EVM only). Throws on a Solana chain. */
441
+ signTypedData(req: SignTypedDataRequest): Promise<string>;
422
442
  signSolanaTransaction(tx: Uint8Array): Promise<Uint8Array>;
423
443
  disconnect(): Promise<void>;
424
444
  getAddresses(): {
425
445
  evm?: string;
426
446
  solana?: string;
427
447
  } | null;
448
+ /**
449
+ * Native-token balance for a chain (defaults to the first configured EVM
450
+ * chain, else Solana). Read-only public data — works even while the wallet
451
+ * is locked. Returns raw (wei/lamports), a human `formatted` string, the
452
+ * native `symbol`, and `decimals`.
453
+ */
454
+ getBalance(chain?: string): Promise<BalanceResult>;
455
+ /** Native balances across every configured chain the wallet has an address for. */
456
+ getBalances(): Promise<Array<{
457
+ chain: string;
458
+ balance: BalanceResult | null;
459
+ }>>;
428
460
  /**
429
461
  * Voluntary share rotation — after a suspected device compromise, or just
430
462
  * to invalidate an old recovery file. Requires a fresh WebAuthn assertion.
@@ -733,6 +765,19 @@ interface SocialWalletModalProps {
733
765
  }
734
766
  declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
735
767
 
768
+ declare function DecaneMark({ size, muted }: {
769
+ size?: number;
770
+ muted?: boolean;
771
+ }): React.JSX.Element;
772
+ declare function DecaneWordmark({ size, muted }: {
773
+ size?: number;
774
+ muted?: boolean;
775
+ }): React.JSX.Element;
776
+ declare function DecaneLogo({ size, muted }: {
777
+ size?: number;
778
+ muted?: boolean;
779
+ }): React.JSX.Element;
780
+
736
781
  declare function useSocialAuth(): {
737
782
  isConnected: boolean;
738
783
  isUnlocked: boolean;
@@ -787,6 +832,18 @@ declare function useSocialWallet(): {
787
832
  gasPrice?: bigint;
788
833
  }) => Promise<string>;
789
834
  signSolanaTransaction: (tx: Uint8Array) => Promise<Uint8Array<ArrayBufferLike>>;
835
+ signTypedData: (req: {
836
+ chain: string;
837
+ domain: Record<string, unknown>;
838
+ types: Record<string, unknown>;
839
+ message: Record<string, unknown>;
840
+ primaryType?: string;
841
+ }) => Promise<string>;
842
+ getBalance: (chain?: string) => Promise<BalanceResult>;
843
+ getBalances: () => Promise<{
844
+ chain: string;
845
+ balance: BalanceResult | null;
846
+ }[]>;
790
847
  rotateShares: (opts: {
791
848
  password: string;
792
849
  passwordHint?: string;
@@ -802,6 +859,12 @@ declare function useSocialWallet(): {
802
859
  error: string | null;
803
860
  clearError: () => void;
804
861
  };
862
+ declare function useSocialBalance(chain?: string): {
863
+ balance: BalanceResult | null;
864
+ loading: boolean;
865
+ error: string | null;
866
+ refetch: () => Promise<void>;
867
+ };
805
868
  declare function useDecane(): {
806
869
  provider: DecaneProvider;
807
870
  wallets: UnifiedWalletAccount[];
@@ -874,4 +937,4 @@ declare function useSignTypedData(): {
874
937
  reset: () => void;
875
938
  };
876
939
 
877
- export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, type DecaneProvider, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialWallet, useWalletSelector, verifyEmailCode };
940
+ export { type ActiveSession, AttestationError, type AuthResult, BUILT_IN_CHAINS, type BalanceResult, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, DecaneLogo, DecaneMark, type DecaneProvider, DecaneWordmark, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SignTypedDataRequest, type SocialAuthState, type SocialConfig, SocialWalletModal, type Theme, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, startEmailAuth, useChainSwitch, useDecane, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialBalance, useSocialWallet, useWalletSelector, verifyEmailCode };
package/dist/index.js CHANGED
@@ -2668,6 +2668,13 @@ async function signEvmTransaction(tee, chain, tx, jwt) {
2668
2668
  async function signEvmMessage(tee, chain, message, jwt) {
2669
2669
  return tee.sign({ chain, kind: "message", payload: message }, jwt);
2670
2670
  }
2671
+ async function signEvmTypedData(tee, chain, typedData, jwt) {
2672
+ const payload = JSON.stringify(
2673
+ typedData,
2674
+ (_k, v) => typeof v === "bigint" ? v.toString() : v
2675
+ );
2676
+ return tee.sign({ chain, kind: "typedData", payload }, jwt);
2677
+ }
2671
2678
 
2672
2679
  // src/social/signers/solana.ts
2673
2680
  import { VersionedTransaction as VTx } from "@solana/web3.js";
@@ -2906,10 +2913,7 @@ async function buildPortableBackup(deviceShare, recoveryShare, password, address
2906
2913
  };
2907
2914
  }
2908
2915
 
2909
- // src/social/client.ts
2910
- var DEFAULT_API_BASE = "https://backend.decane.app";
2911
- var DEFAULT_TEE_URL = "https://4405742df08d19abc549a05ade2a7154d98bd87f-8443.dstack-pha-prod9.phala.network";
2912
- var DEFAULT_SESSION_MINUTES = 30;
2916
+ // src/social/balance.ts
2913
2917
  var DEFAULT_RPCS = {
2914
2918
  "evm:1": "https://ethereum.publicnode.com",
2915
2919
  "evm:137": "https://polygon.publicnode.com",
@@ -2921,6 +2925,50 @@ var DEFAULT_RPCS = {
2921
2925
  "evm:84532": "https://base-sepolia.publicnode.com",
2922
2926
  "evm:31337": "http://localhost:8545"
2923
2927
  };
2928
+ var EVM_SYMBOLS = {
2929
+ "evm:137": "MATIC",
2930
+ "evm:56": "BNB"
2931
+ };
2932
+ function solanaEndpoint(chain, rpcUrls) {
2933
+ if (rpcUrls?.[chain]) return rpcUrls[chain];
2934
+ const cluster = chain.slice("solana:".length);
2935
+ return cluster === "devnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com";
2936
+ }
2937
+ async function fetchBalance(opts) {
2938
+ const { kind, address, chain, rpcUrls } = opts;
2939
+ if (kind === "evm") {
2940
+ const rpcUrl = rpcUrls?.[chain] ?? DEFAULT_RPCS[chain];
2941
+ if (!rpcUrl) {
2942
+ throw new Error(
2943
+ `No RPC URL for ${chain}. Pass rpcUrls: { '${chain}': 'https://...' } in social config.`
2944
+ );
2945
+ }
2946
+ const { JsonRpcProvider, formatEther } = await import("ethers");
2947
+ const wei = await new JsonRpcProvider(rpcUrl).getBalance(address);
2948
+ return { raw: wei, formatted: formatEther(wei), symbol: EVM_SYMBOLS[chain] ?? "ETH", decimals: 18 };
2949
+ }
2950
+ const { Connection, PublicKey, LAMPORTS_PER_SOL } = await import("@solana/web3.js");
2951
+ const lamports = await new Connection(solanaEndpoint(chain, rpcUrls)).getBalance(
2952
+ new PublicKey(address)
2953
+ );
2954
+ return {
2955
+ raw: BigInt(lamports),
2956
+ formatted: String(lamports / LAMPORTS_PER_SOL),
2957
+ symbol: "SOL",
2958
+ decimals: 9
2959
+ };
2960
+ }
2961
+ function formatBalanceDisplay(value) {
2962
+ if (value == null) return "\u2014";
2963
+ const n = Number(value);
2964
+ if (!Number.isFinite(n)) return value;
2965
+ return n === 0 ? "0" : n < 1e-4 ? "<0.0001" : n.toFixed(4).replace(/\.?0+$/, "");
2966
+ }
2967
+
2968
+ // src/social/client.ts
2969
+ var DEFAULT_API_BASE = "https://backend.decane.app";
2970
+ var DEFAULT_TEE_URL = "https://4405742df08d19abc549a05ade2a7154d98bd87f-8443.dstack-pha-prod9.phala.network";
2971
+ var DEFAULT_SESSION_MINUTES = 30;
2924
2972
  function makePersistKey(apiKey) {
2925
2973
  return `decane:social:${apiKey.slice(0, 20)}`;
2926
2974
  }
@@ -3418,6 +3466,23 @@ async function createDecaneConnect(config) {
3418
3466
  const txResponse = await provider.broadcastTransaction(signedTx);
3419
3467
  return txResponse.hash;
3420
3468
  },
3469
+ async signTypedData(req) {
3470
+ const tee = session.requireActive();
3471
+ if (!req.chain.startsWith("evm:")) {
3472
+ throw new CallSequenceError("signTypedData is EVM-only (EIP-712).");
3473
+ }
3474
+ return signEvmTypedData(
3475
+ tee,
3476
+ req.chain,
3477
+ {
3478
+ domain: req.domain,
3479
+ types: req.types,
3480
+ message: req.message,
3481
+ primaryType: req.primaryType
3482
+ },
3483
+ auth?.jwt
3484
+ );
3485
+ },
3421
3486
  async signSolanaTransaction(serializedTx) {
3422
3487
  const tee = session.requireActive();
3423
3488
  let tx;
@@ -3443,6 +3508,42 @@ async function createDecaneConnect(config) {
3443
3508
  getAddresses() {
3444
3509
  return auth?.addresses ?? persisted?.addresses ?? null;
3445
3510
  },
3511
+ // Native-token balance. Read-only public chain data, so it works whenever an
3512
+ // address is known — including while the wallet is locked (no TEE session
3513
+ // needed). `chain` defaults to the first configured EVM chain, else the
3514
+ // first Solana chain.
3515
+ async getBalance(chain) {
3516
+ const addresses = auth?.addresses ?? persisted?.addresses;
3517
+ if (!addresses) throw new CallSequenceError("Not connected \u2014 no wallet address known");
3518
+ const resolvedChain = chain ?? config.chains.find((c) => c.startsWith("evm:")) ?? config.chains.find((c) => c.startsWith("solana:"));
3519
+ if (!resolvedChain) throw new CallSequenceError("No chain configured");
3520
+ const kind = resolvedChain.startsWith("solana:") ? "solana" : "evm";
3521
+ const address = kind === "evm" ? addresses.evm : addresses.solana;
3522
+ if (!address) throw new CallSequenceError(`This wallet has no ${kind} address`);
3523
+ return fetchBalance({ kind, address, chain: resolvedChain, rpcUrls: config.rpcUrls });
3524
+ },
3525
+ // Balances across every configured chain the wallet has an address for.
3526
+ // Per-chain failures resolve to null rather than rejecting the whole call,
3527
+ // so one dead RPC doesn't blank the entire list.
3528
+ async getBalances() {
3529
+ const addresses = auth?.addresses ?? persisted?.addresses;
3530
+ if (!addresses) throw new CallSequenceError("Not connected \u2014 no wallet address known");
3531
+ const targets = config.chains.filter((c) => {
3532
+ const kind = c.startsWith("solana:") ? "solana" : "evm";
3533
+ return kind === "evm" ? !!addresses.evm : !!addresses.solana;
3534
+ });
3535
+ return Promise.all(
3536
+ targets.map(async (chain) => {
3537
+ const kind = chain.startsWith("solana:") ? "solana" : "evm";
3538
+ const address = kind === "evm" ? addresses.evm : addresses.solana;
3539
+ try {
3540
+ return { chain, balance: await fetchBalance({ kind, address, chain, rpcUrls: config.rpcUrls }) };
3541
+ } catch {
3542
+ return { chain, balance: null };
3543
+ }
3544
+ })
3545
+ );
3546
+ },
3446
3547
  async rotateShares(opts) {
3447
3548
  if (!auth) throw new CallSequenceError("Not connected \u2014 call connect() first");
3448
3549
  const tee = session.requireActive();
@@ -3689,6 +3790,31 @@ var CSS_VARS = {
3689
3790
 
3690
3791
  // src/ui/icons.tsx
3691
3792
  import React from "react";
3793
+ var DECAGON_OUTER = "12,2 17.88,3.91 21.51,8.91 21.51,15.09 17.88,20.09 12,22 6.12,20.09 2.49,15.09 2.49,8.91 6.12,3.91";
3794
+ var DECAGON_INNER = "12,7.4 14.7,8.28 16.37,10.58 16.37,13.42 14.7,15.72 12,16.6 9.3,15.72 7.63,13.42 7.63,10.58 9.3,8.28";
3795
+ function DecaneMark({ size = 24, muted = false }) {
3796
+ const color = muted ? "var(--dc-text-muted)" : "var(--dc-accent)";
3797
+ return /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, style: { flexShrink: 0, display: "block" } }, /* @__PURE__ */ React.createElement("polygon", { points: DECAGON_OUTER, fill: color, fillOpacity: muted ? 0.08 : 0.1, stroke: color, strokeWidth: "1.4", strokeLinejoin: "round" }), /* @__PURE__ */ React.createElement("polygon", { points: DECAGON_INNER, fill: color, fillOpacity: muted ? 0.55 : 0.95 }));
3798
+ }
3799
+ function DecaneWordmark({ size = 15, muted = false }) {
3800
+ return /* @__PURE__ */ React.createElement(
3801
+ "span",
3802
+ {
3803
+ style: {
3804
+ fontFamily: "var(--dc-font-sans, system-ui, sans-serif)",
3805
+ fontWeight: 800,
3806
+ fontSize: size,
3807
+ letterSpacing: "-0.03em",
3808
+ color: muted ? "var(--dc-text-muted)" : "var(--dc-text)",
3809
+ lineHeight: 1
3810
+ }
3811
+ },
3812
+ "decane"
3813
+ );
3814
+ }
3815
+ function DecaneLogo({ size = 20, muted = false }) {
3816
+ return /* @__PURE__ */ React.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: size * 0.34 } }, /* @__PURE__ */ React.createElement(DecaneMark, { size, muted }), /* @__PURE__ */ React.createElement(DecaneWordmark, { size: size * 0.78, muted }));
3817
+ }
3692
3818
  function WalletIcon({ src, name, size = 36 }) {
3693
3819
  if (src) {
3694
3820
  return /* @__PURE__ */ React.createElement(
@@ -4642,40 +4768,17 @@ function useBalance(kind, address, chain, rpcUrls) {
4642
4768
  if (!address || !chain) return;
4643
4769
  let cancelled = false;
4644
4770
  setState((s) => ({ ...s, loading: true }));
4645
- (async () => {
4646
- try {
4647
- if (kind === "evm") {
4648
- const rpcUrl = rpcUrls?.[chain] ?? DEFAULT_RPCS[chain];
4649
- if (!rpcUrl) {
4650
- if (!cancelled) setState({ loading: false, value: null, symbol: "ETH" });
4651
- return;
4652
- }
4653
- const { JsonRpcProvider, formatEther } = await import("ethers");
4654
- const wei = await new JsonRpcProvider(rpcUrl).getBalance(address);
4655
- if (!cancelled) setState({ loading: false, value: formatEther(wei), symbol: "ETH" });
4656
- } else {
4657
- const cluster = chain.slice("solana:".length);
4658
- const endpoint = rpcUrls?.[chain] ?? (cluster === "devnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com");
4659
- const { Connection, PublicKey, LAMPORTS_PER_SOL } = await import("@solana/web3.js");
4660
- const lamports = await new Connection(endpoint).getBalance(new PublicKey(address));
4661
- if (!cancelled) setState({ loading: false, value: String(lamports / LAMPORTS_PER_SOL), symbol: "SOL" });
4662
- }
4663
- } catch {
4664
- if (!cancelled) setState({ loading: false, value: null, symbol: kind === "evm" ? "ETH" : "SOL" });
4665
- }
4666
- })();
4771
+ fetchBalance({ kind, address, chain, rpcUrls }).then((b) => {
4772
+ if (!cancelled) setState({ loading: false, value: b.formatted, symbol: b.symbol });
4773
+ }).catch(() => {
4774
+ if (!cancelled) setState({ loading: false, value: null, symbol: kind === "evm" ? "ETH" : "SOL" });
4775
+ });
4667
4776
  return () => {
4668
4777
  cancelled = true;
4669
4778
  };
4670
4779
  }, [kind, address, chain, rpcUrls]);
4671
4780
  return state;
4672
4781
  }
4673
- function formatBalance(value) {
4674
- if (value == null) return "\u2014";
4675
- const n = Number(value);
4676
- if (!Number.isFinite(n)) return value;
4677
- return n === 0 ? "0" : n < 1e-4 ? "<0.0001" : n.toFixed(4).replace(/\.?0+$/, "");
4678
- }
4679
4782
  function AddressRow({
4680
4783
  label,
4681
4784
  address,
@@ -4691,7 +4794,7 @@ function AddressRow({
4691
4794
  borderRadius: 10,
4692
4795
  padding: "10px 14px",
4693
4796
  marginBottom: 8
4694
- } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 5 } }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 10, fontWeight: 700, color: "var(--dc-text-muted)", letterSpacing: "0.06em", textTransform: "uppercase" } }, label), /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 11, fontFamily: "monospace", color: "var(--dc-text)" } }, balance.loading ? "\u2026" : `${formatBalance(balance.value)} ${balance.symbol}`)), /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 } }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 13, fontFamily: "monospace", color: "var(--dc-text)", letterSpacing: "0.02em" } }, short), /* @__PURE__ */ React3.createElement(CopyButton, { value: address })));
4797
+ } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 5 } }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 10, fontWeight: 700, color: "var(--dc-text-muted)", letterSpacing: "0.06em", textTransform: "uppercase" } }, label), /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 11, fontFamily: "monospace", color: "var(--dc-text)" } }, balance.loading ? "\u2026" : `${formatBalanceDisplay(balance.value)} ${balance.symbol}`)), /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 } }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 13, fontFamily: "monospace", color: "var(--dc-text)", letterSpacing: "0.02em" } }, short), /* @__PURE__ */ React3.createElement(CopyButton, { value: address })));
4695
4798
  }
4696
4799
  function SignInView({
4697
4800
  onGoogleSignIn,
@@ -5068,34 +5171,17 @@ function SocialWalletModal({
5068
5171
  top: 0,
5069
5172
  zIndex: 1,
5070
5173
  background: "linear-gradient(90deg, transparent, var(--dc-accent) 30%, var(--dc-accent) 70%, transparent)"
5071
- } }), /* @__PURE__ */ React3.createElement("div", { style: { padding: "18px 20px 16px", display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React3.createElement("div", { style: {
5072
- width: 34,
5073
- height: 34,
5074
- borderRadius: 10,
5174
+ } }), /* @__PURE__ */ React3.createElement("div", { style: { padding: "18px 20px 16px", display: "flex", alignItems: "center", gap: 11 } }, /* @__PURE__ */ React3.createElement("div", { style: {
5175
+ width: 38,
5176
+ height: 38,
5177
+ borderRadius: 11,
5075
5178
  background: "var(--dc-surface)",
5076
5179
  border: "1px solid var(--dc-border)",
5077
5180
  display: "flex",
5078
5181
  alignItems: "center",
5079
5182
  justifyContent: "center",
5080
5183
  flexShrink: 0
5081
- } }, /* @__PURE__ */ React3.createElement("svg", { width: "16", height: "18", viewBox: "0 0 16 18", fill: "none" }, /* @__PURE__ */ React3.createElement(
5082
- "path",
5083
- {
5084
- d: "M8 1L1.5 3.5V8.5C1.5 12.09 4.35 15.4 8 16.5C11.65 15.4 14.5 12.09 14.5 8.5V3.5L8 1Z",
5085
- stroke: "var(--dc-accent)",
5086
- strokeWidth: "1.4",
5087
- strokeLinejoin: "round"
5088
- }
5089
- ), /* @__PURE__ */ React3.createElement(
5090
- "path",
5091
- {
5092
- d: "M5.5 9L7 10.5L10.5 7",
5093
- stroke: "var(--dc-accent)",
5094
- strokeWidth: "1.4",
5095
- strokeLinecap: "round",
5096
- strokeLinejoin: "round"
5097
- }
5098
- ))), /* @__PURE__ */ React3.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 15, fontWeight: 800, color: "var(--dc-text)", letterSpacing: "-0.02em" } }, "Social Wallet"), /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 11, color: "var(--dc-text-muted)", marginTop: 1 } }, "Decane non-custodial \xB7 key split three ways")), /* @__PURE__ */ React3.createElement(
5184
+ } }, /* @__PURE__ */ React3.createElement(DecaneMark, { size: 22 })), /* @__PURE__ */ React3.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React3.createElement("div", { style: { display: "flex", alignItems: "baseline", gap: 6 } }, /* @__PURE__ */ React3.createElement(DecaneWordmark, { size: 15 }), /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 13, fontWeight: 700, color: "var(--dc-text-muted)", letterSpacing: "-0.01em" } }, "wallet")), /* @__PURE__ */ React3.createElement("div", { style: { fontSize: 11, color: "var(--dc-text-muted)", marginTop: 2 } }, "Non-custodial \xB7 key split three ways")), /* @__PURE__ */ React3.createElement(
5099
5185
  "button",
5100
5186
  {
5101
5187
  onClick: onClose,
@@ -5198,7 +5284,14 @@ function SocialWalletModal({
5198
5284
  }
5199
5285
  },
5200
5286
  disconnecting ? "Signing out\u2026" : "Sign out"
5201
- ))))
5287
+ ))), /* @__PURE__ */ React3.createElement("div", { style: {
5288
+ padding: "11px 16px",
5289
+ borderTop: "1px solid var(--dc-border)",
5290
+ display: "flex",
5291
+ alignItems: "center",
5292
+ justifyContent: "center",
5293
+ gap: 6
5294
+ } }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: 10.5, color: "var(--dc-text-muted)", letterSpacing: "0.02em" } }, "Secured by"), /* @__PURE__ */ React3.createElement(DecaneMark, { size: 13, muted: true }), /* @__PURE__ */ React3.createElement(DecaneWordmark, { size: 11, muted: true })))
5202
5295
  );
5203
5296
  return createPortal2(modal, document.body);
5204
5297
  }
@@ -5391,6 +5484,21 @@ function DecaneKit({ config, children }) {
5391
5484
  if (!sdk) throw new Error("Social wallet not connected");
5392
5485
  return sdk.signSolanaTransaction(tx);
5393
5486
  }, []);
5487
+ const socialSignTypedData = useCallback3(async (req) => {
5488
+ const sdk = socialRef.current;
5489
+ if (!sdk) throw new Error("Social wallet not connected");
5490
+ return sdk.signTypedData({ ...req, chain: req.chain });
5491
+ }, []);
5492
+ const socialGetBalance = useCallback3(async (chain) => {
5493
+ const sdk = socialRef.current;
5494
+ if (!sdk) throw new Error("Social wallet not connected");
5495
+ return sdk.getBalance(chain);
5496
+ }, []);
5497
+ const socialGetBalances = useCallback3(async () => {
5498
+ const sdk = socialRef.current;
5499
+ if (!sdk) throw new Error("Social wallet not connected");
5500
+ return sdk.getBalances();
5501
+ }, []);
5394
5502
  const socialRotateShares = useCallback3(async (opts) => {
5395
5503
  const sdk = socialRef.current;
5396
5504
  if (!sdk) throw new Error("Social wallet not connected");
@@ -5446,7 +5554,10 @@ function DecaneKit({ config, children }) {
5446
5554
  socialDisconnect,
5447
5555
  socialSignMessage,
5448
5556
  socialSendTransaction,
5557
+ socialGetBalance,
5558
+ socialGetBalances,
5449
5559
  socialSignSolanaTransaction,
5560
+ socialSignTypedData,
5450
5561
  socialRotateShares,
5451
5562
  socialExportPortableBackup,
5452
5563
  socialHasRecoveryShare,
@@ -5644,6 +5755,9 @@ function useSocialWallet() {
5644
5755
  socialSignMessage,
5645
5756
  socialSendTransaction,
5646
5757
  socialSignSolanaTransaction,
5758
+ socialSignTypedData,
5759
+ socialGetBalance,
5760
+ socialGetBalances,
5647
5761
  socialRotateShares,
5648
5762
  socialExportPortableBackup,
5649
5763
  socialHasRecoveryShare,
@@ -5680,6 +5794,18 @@ function useSocialWallet() {
5680
5794
  (tx) => run(() => socialSignSolanaTransaction(tx)),
5681
5795
  [socialSignSolanaTransaction]
5682
5796
  );
5797
+ const signTypedData = useCallback4(
5798
+ (req) => run(() => socialSignTypedData(req)),
5799
+ [socialSignTypedData]
5800
+ );
5801
+ const getBalance = useCallback4(
5802
+ (chain) => run(() => socialGetBalance(chain)),
5803
+ [socialGetBalance]
5804
+ );
5805
+ const getBalances = useCallback4(
5806
+ () => run(() => socialGetBalances()),
5807
+ [socialGetBalances]
5808
+ );
5683
5809
  const unlock = useCallback4(async () => {
5684
5810
  setError(null);
5685
5811
  try {
@@ -5741,6 +5867,10 @@ function useSocialWallet() {
5741
5867
  signMessage,
5742
5868
  sendTransaction,
5743
5869
  signSolanaTransaction: signSolanaTransaction2,
5870
+ signTypedData,
5871
+ // Balances (native token; read-only, works while locked)
5872
+ getBalance,
5873
+ getBalances,
5744
5874
  // Recovery (2-of-3 Shamir share set)
5745
5875
  rotateShares,
5746
5876
  exportPortableBackup,
@@ -5752,6 +5882,52 @@ function useSocialWallet() {
5752
5882
  clearError: () => setError(null)
5753
5883
  };
5754
5884
  }
5885
+ function useSocialBalance(chain) {
5886
+ const { socialAuth, socialGetBalance } = useDecaneContext();
5887
+ const [balance, setBalance] = useState4(null);
5888
+ const [loading, setLoading] = useState4(false);
5889
+ const [error, setError] = useState4(null);
5890
+ const addrKey = `${socialAuth.addresses?.evm ?? ""}|${socialAuth.addresses?.solana ?? ""}`;
5891
+ const refetch = useCallback4(async () => {
5892
+ if (!socialAuth.addresses) {
5893
+ setBalance(null);
5894
+ return;
5895
+ }
5896
+ setLoading(true);
5897
+ setError(null);
5898
+ try {
5899
+ setBalance(await socialGetBalance(chain));
5900
+ } catch (e) {
5901
+ setError(e.message);
5902
+ setBalance(null);
5903
+ } finally {
5904
+ setLoading(false);
5905
+ }
5906
+ }, [socialGetBalance, chain, socialAuth.addresses]);
5907
+ useEffect4(() => {
5908
+ let cancelled = false;
5909
+ if (!socialAuth.addresses) {
5910
+ setBalance(null);
5911
+ return;
5912
+ }
5913
+ setLoading(true);
5914
+ setError(null);
5915
+ socialGetBalance(chain).then((b) => {
5916
+ if (!cancelled) setBalance(b);
5917
+ }).catch((e) => {
5918
+ if (!cancelled) {
5919
+ setError(e.message);
5920
+ setBalance(null);
5921
+ }
5922
+ }).finally(() => {
5923
+ if (!cancelled) setLoading(false);
5924
+ });
5925
+ return () => {
5926
+ cancelled = true;
5927
+ };
5928
+ }, [addrKey, chain]);
5929
+ return { balance, loading, error, refetch };
5930
+ }
5755
5931
  function toDecaneError(err) {
5756
5932
  if (err instanceof DecaneError) return err;
5757
5933
  const msg = err instanceof Error ? err.message : err?.message != null ? String(err.message) : String(err);
@@ -5984,6 +6160,9 @@ export {
5984
6160
  DecaneError,
5985
6161
  DecaneErrorCode,
5986
6162
  DecaneKit,
6163
+ DecaneLogo,
6164
+ DecaneMark,
6165
+ DecaneWordmark,
5987
6166
  ExportNotAuthorizedError,
5988
6167
  InvalidApiKeyError,
5989
6168
  InvalidPinError,
@@ -6012,6 +6191,7 @@ export {
6012
6191
  useSignMessage,
6013
6192
  useSignTypedData,
6014
6193
  useSocialAuth,
6194
+ useSocialBalance,
6015
6195
  useSocialWallet,
6016
6196
  useWalletSelector,
6017
6197
  verifyEmailCode