decane-connect-kit 2.2.8 → 2.3.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 CHANGED
@@ -207,9 +207,11 @@ __export(index_exports, {
207
207
  WrongPasswordError: () => WrongPasswordError,
208
208
  createDecane: () => createDecane,
209
209
  createDecaneConnect: () => createDecaneConnect,
210
+ createEthereumProvider: () => createEthereumProvider,
210
211
  startEmailAuth: () => startEmailAuth,
211
212
  useChainSwitch: () => useChainSwitch,
212
213
  useDecane: () => useDecane,
214
+ useEthereumProvider: () => useEthereumProvider,
213
215
  useSendTransaction: () => useSendTransaction,
214
216
  useSignMessage: () => useSignMessage,
215
217
  useSignTypedData: () => useSignTypedData,
@@ -2776,6 +2778,17 @@ async function signEvmTransaction(tee, chain, tx, jwt) {
2776
2778
  async function signEvmMessage(tee, chain, message, jwt) {
2777
2779
  return tee.sign({ chain, kind: "message", payload: message }, jwt);
2778
2780
  }
2781
+ async function signEvmAuthorization(tee, chainId, address, nonce, jwt) {
2782
+ const serialized = await tee.sign(
2783
+ { chain: `evm:${chainId}`, kind: "authorization", payload: JSON.stringify({ chainId, address, nonce }) },
2784
+ jwt
2785
+ );
2786
+ const hex = serialized.startsWith("0x") ? serialized.slice(2) : serialized;
2787
+ const r = "0x" + hex.slice(0, 64);
2788
+ const s = "0x" + hex.slice(64, 128);
2789
+ const v = parseInt(hex.slice(128, 130), 16);
2790
+ return { yParity: v >= 27 ? v - 27 : v, r, s };
2791
+ }
2779
2792
  async function signEvmTypedData(tee, chain, typedData, jwt) {
2780
2793
  const payload = JSON.stringify(
2781
2794
  typedData,
@@ -2784,6 +2797,194 @@ async function signEvmTypedData(tee, chain, typedData, jwt) {
2784
2797
  return tee.sign({ chain, kind: "typedData", payload }, jwt);
2785
2798
  }
2786
2799
 
2800
+ // src/social/balance.ts
2801
+ var DEFAULT_RPCS = {
2802
+ "evm:1": "https://ethereum.publicnode.com",
2803
+ "evm:137": "https://polygon.publicnode.com",
2804
+ "evm:8453": "https://base.publicnode.com",
2805
+ "evm:10": "https://optimism.publicnode.com",
2806
+ "evm:42161": "https://arbitrum-one.publicnode.com",
2807
+ "evm:56": "https://bsc.publicnode.com",
2808
+ "evm:11155111": "https://ethereum-sepolia.publicnode.com",
2809
+ "evm:84532": "https://base-sepolia.publicnode.com",
2810
+ "evm:31337": "http://localhost:8545"
2811
+ };
2812
+ var EVM_SYMBOLS = {
2813
+ "evm:137": "MATIC",
2814
+ "evm:56": "BNB"
2815
+ };
2816
+ function solanaEndpoint(chain, rpcUrls) {
2817
+ if (rpcUrls?.[chain]) return rpcUrls[chain];
2818
+ const cluster = chain.slice("solana:".length);
2819
+ return cluster === "devnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com";
2820
+ }
2821
+ async function fetchBalance(opts) {
2822
+ const { kind, address, chain, rpcUrls } = opts;
2823
+ if (kind === "evm") {
2824
+ const rpcUrl = rpcUrls?.[chain] ?? DEFAULT_RPCS[chain];
2825
+ if (!rpcUrl) {
2826
+ throw new Error(
2827
+ `No RPC URL for ${chain}. Pass rpcUrls: { '${chain}': 'https://...' } in social config.`
2828
+ );
2829
+ }
2830
+ const { JsonRpcProvider, formatEther } = await import("ethers");
2831
+ const wei = await new JsonRpcProvider(rpcUrl).getBalance(address);
2832
+ return { raw: wei, formatted: formatEther(wei), symbol: EVM_SYMBOLS[chain] ?? "ETH", decimals: 18 };
2833
+ }
2834
+ const { Connection, PublicKey, LAMPORTS_PER_SOL } = await import("@solana/web3.js");
2835
+ const lamports = await new Connection(solanaEndpoint(chain, rpcUrls)).getBalance(
2836
+ new PublicKey(address)
2837
+ );
2838
+ return {
2839
+ raw: BigInt(lamports),
2840
+ formatted: String(lamports / LAMPORTS_PER_SOL),
2841
+ symbol: "SOL",
2842
+ decimals: 9
2843
+ };
2844
+ }
2845
+ function formatBalanceDisplay(value) {
2846
+ if (value == null) return "\u2014";
2847
+ const n = Number(value);
2848
+ if (!Number.isFinite(n)) return value;
2849
+ return n === 0 ? "0" : n < 1e-4 ? "<0.0001" : n.toFixed(4).replace(/\.?0+$/, "");
2850
+ }
2851
+
2852
+ // src/social/eip1193.ts
2853
+ function toHex(n) {
2854
+ return "0x" + n.toString(16);
2855
+ }
2856
+ function hexToBytes2(hex) {
2857
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
2858
+ const bytes = new Uint8Array(clean.length / 2);
2859
+ for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(clean.substr(i * 2, 2), 16);
2860
+ return bytes;
2861
+ }
2862
+ function asBigInt(v) {
2863
+ if (v == null) return void 0;
2864
+ if (typeof v === "bigint") return v;
2865
+ if (typeof v === "number") return BigInt(v);
2866
+ if (typeof v === "string") return BigInt(v);
2867
+ return void 0;
2868
+ }
2869
+ function createEthereumProvider(client, opts = {}) {
2870
+ let chainId = opts.chainId ?? 1;
2871
+ const listeners = /* @__PURE__ */ new Map();
2872
+ function emit(event, ...args) {
2873
+ listeners.get(event)?.forEach((h) => {
2874
+ try {
2875
+ h(...args);
2876
+ } catch {
2877
+ }
2878
+ });
2879
+ }
2880
+ client.on("connected", () => {
2881
+ const evm = client.getAddresses()?.evm;
2882
+ emit("connect", { chainId: toHex(chainId) });
2883
+ if (evm) emit("accountsChanged", [evm]);
2884
+ });
2885
+ client.on("disconnected", () => {
2886
+ emit("accountsChanged", []);
2887
+ emit("disconnect", { code: 4900, message: "Wallet disconnected" });
2888
+ });
2889
+ const chainStr = () => `evm:${chainId}`;
2890
+ async function rpc(method, params) {
2891
+ const url = opts.rpcUrls?.[`evm:${chainId}`] ?? DEFAULT_RPCS[`evm:${chainId}`];
2892
+ if (!url) {
2893
+ throw Object.assign(new Error(`No RPC for chain ${chainId}. Pass rpcUrls.`), { code: 4900 });
2894
+ }
2895
+ let res;
2896
+ try {
2897
+ res = await fetch(url, {
2898
+ method: "POST",
2899
+ headers: { "Content-Type": "application/json" },
2900
+ body: JSON.stringify({ jsonrpc: "2.0", id: Date.now(), method, params })
2901
+ });
2902
+ } catch (e) {
2903
+ throw Object.assign(new Error(`RPC request failed: ${e.message}`), { code: 4900 });
2904
+ }
2905
+ const json = await res.json();
2906
+ if (json.error) throw Object.assign(new Error(json.error.message), { code: json.error.code });
2907
+ return json.result;
2908
+ }
2909
+ async function request(args) {
2910
+ const params = Array.isArray(args.params) ? args.params : args.params ? [args.params] : [];
2911
+ const addr = () => client.getAddresses()?.evm ?? null;
2912
+ switch (args.method) {
2913
+ case "eth_requestAccounts":
2914
+ case "eth_accounts": {
2915
+ const a = addr();
2916
+ return a ? [a] : [];
2917
+ }
2918
+ case "eth_chainId":
2919
+ return toHex(chainId);
2920
+ case "net_version":
2921
+ return String(chainId);
2922
+ case "personal_sign": {
2923
+ const message = params[0];
2924
+ const bytes = typeof message === "string" && message.startsWith("0x") ? hexToBytes2(message) : message;
2925
+ return client.signMessage({ chain: chainStr(), message: bytes });
2926
+ }
2927
+ case "eth_signTypedData_v4":
2928
+ case "eth_signTypedData": {
2929
+ const raw = params[1];
2930
+ const td = typeof raw === "string" ? JSON.parse(raw) : raw;
2931
+ return client.signTypedData({
2932
+ chain: chainStr(),
2933
+ domain: td.domain,
2934
+ types: td.types,
2935
+ message: td.message,
2936
+ primaryType: td.primaryType
2937
+ });
2938
+ }
2939
+ case "eth_sendTransaction": {
2940
+ const tx = params[0];
2941
+ return client.sendTransaction({
2942
+ chain: chainStr(),
2943
+ to: tx.to,
2944
+ value: asBigInt(tx.value),
2945
+ data: tx.data ?? tx.input,
2946
+ gasLimit: asBigInt(tx.gas ?? tx.gasLimit)
2947
+ });
2948
+ }
2949
+ case "eth_signAuthorization":
2950
+ case "wallet_sign7702Authorization": {
2951
+ if (!client.signAuthorization) {
2952
+ throw Object.assign(new Error("EIP-7702 is not supported by the connected signer."), { code: 4200 });
2953
+ }
2954
+ const p = params[0];
2955
+ const cid = p.chainId != null ? Number(asBigInt(p.chainId)) : chainId;
2956
+ const address = p.address ?? p.contractAddress;
2957
+ const nonce = p.nonce != null ? Number(asBigInt(p.nonce)) : 0;
2958
+ return client.signAuthorization({ chainId: cid, address, nonce });
2959
+ }
2960
+ case "wallet_switchEthereumChain": {
2961
+ const p = params[0];
2962
+ chainId = Number(asBigInt(p.chainId));
2963
+ emit("chainChanged", toHex(chainId));
2964
+ return null;
2965
+ }
2966
+ case "wallet_addEthereumChain":
2967
+ return null;
2968
+ default:
2969
+ return rpc(args.method, params);
2970
+ }
2971
+ }
2972
+ const provider = {
2973
+ request,
2974
+ on(event, handler) {
2975
+ if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
2976
+ listeners.get(event).add(handler);
2977
+ return this;
2978
+ },
2979
+ removeListener(event, handler) {
2980
+ listeners.get(event)?.delete(handler);
2981
+ return this;
2982
+ },
2983
+ isDecane: true
2984
+ };
2985
+ return provider;
2986
+ }
2987
+
2787
2988
  // src/social/signers/solana.ts
2788
2989
  var import_web3 = require("@solana/web3.js");
2789
2990
  function b64(bytes) {
@@ -3021,58 +3222,6 @@ async function buildPortableBackup(deviceShare, recoveryShare, password, address
3021
3222
  };
3022
3223
  }
3023
3224
 
3024
- // src/social/balance.ts
3025
- var DEFAULT_RPCS = {
3026
- "evm:1": "https://ethereum.publicnode.com",
3027
- "evm:137": "https://polygon.publicnode.com",
3028
- "evm:8453": "https://base.publicnode.com",
3029
- "evm:10": "https://optimism.publicnode.com",
3030
- "evm:42161": "https://arbitrum-one.publicnode.com",
3031
- "evm:56": "https://bsc.publicnode.com",
3032
- "evm:11155111": "https://ethereum-sepolia.publicnode.com",
3033
- "evm:84532": "https://base-sepolia.publicnode.com",
3034
- "evm:31337": "http://localhost:8545"
3035
- };
3036
- var EVM_SYMBOLS = {
3037
- "evm:137": "MATIC",
3038
- "evm:56": "BNB"
3039
- };
3040
- function solanaEndpoint(chain, rpcUrls) {
3041
- if (rpcUrls?.[chain]) return rpcUrls[chain];
3042
- const cluster = chain.slice("solana:".length);
3043
- return cluster === "devnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com";
3044
- }
3045
- async function fetchBalance(opts) {
3046
- const { kind, address, chain, rpcUrls } = opts;
3047
- if (kind === "evm") {
3048
- const rpcUrl = rpcUrls?.[chain] ?? DEFAULT_RPCS[chain];
3049
- if (!rpcUrl) {
3050
- throw new Error(
3051
- `No RPC URL for ${chain}. Pass rpcUrls: { '${chain}': 'https://...' } in social config.`
3052
- );
3053
- }
3054
- const { JsonRpcProvider, formatEther } = await import("ethers");
3055
- const wei = await new JsonRpcProvider(rpcUrl).getBalance(address);
3056
- return { raw: wei, formatted: formatEther(wei), symbol: EVM_SYMBOLS[chain] ?? "ETH", decimals: 18 };
3057
- }
3058
- const { Connection, PublicKey, LAMPORTS_PER_SOL } = await import("@solana/web3.js");
3059
- const lamports = await new Connection(solanaEndpoint(chain, rpcUrls)).getBalance(
3060
- new PublicKey(address)
3061
- );
3062
- return {
3063
- raw: BigInt(lamports),
3064
- formatted: String(lamports / LAMPORTS_PER_SOL),
3065
- symbol: "SOL",
3066
- decimals: 9
3067
- };
3068
- }
3069
- function formatBalanceDisplay(value) {
3070
- if (value == null) return "\u2014";
3071
- const n = Number(value);
3072
- if (!Number.isFinite(n)) return value;
3073
- return n === 0 ? "0" : n < 1e-4 ? "<0.0001" : n.toFixed(4).replace(/\.?0+$/, "");
3074
- }
3075
-
3076
3225
  // src/social/client.ts
3077
3226
  var DEFAULT_API_BASE = "https://backend.decane.app";
3078
3227
  var DEFAULT_TEE_URL = "https://4405742df08d19abc549a05ade2a7154d98bd87f-8443.dstack-pha-prod9.phala.network";
@@ -3145,6 +3294,7 @@ async function createDecaneConnect(config) {
3145
3294
  );
3146
3295
  let auth = null;
3147
3296
  let persisted = loadPersisted(PERSIST_KEY);
3297
+ let ethProvider = null;
3148
3298
  const persistSession = config.persistSession !== false;
3149
3299
  const SESSION_KEY = sessionPersistKey(config.apiKey);
3150
3300
  function persistLiveSession() {
@@ -3291,7 +3441,7 @@ async function createDecaneConnect(config) {
3291
3441
  let evmAddress;
3292
3442
  let solanaAddress;
3293
3443
  try {
3294
- evmAddress = new import_ethers2.Wallet(toHex(master)).address;
3444
+ evmAddress = new import_ethers2.Wallet(toHex2(master)).address;
3295
3445
  solanaAddress = import_web32.Keypair.fromSeed(master).publicKey.toBase58();
3296
3446
  shareSet = await splitMaster(master, 1);
3297
3447
  } finally {
@@ -3652,6 +3802,21 @@ async function createDecaneConnect(config) {
3652
3802
  auth?.jwt
3653
3803
  );
3654
3804
  },
3805
+ async signAuthorization(req) {
3806
+ const tee = session.requireActive();
3807
+ return signEvmAuthorization(tee, req.chainId, req.address, req.nonce, auth?.jwt);
3808
+ },
3809
+ getEthereumProvider(opts) {
3810
+ if (!ethProvider) {
3811
+ const firstEvm = config.chains.find((c) => c.startsWith("evm:"));
3812
+ const defaultChainId = firstEvm ? parseInt(firstEvm.slice(4), 10) : 1;
3813
+ ethProvider = createEthereumProvider(client, {
3814
+ chainId: opts?.chainId ?? defaultChainId,
3815
+ rpcUrls: config.rpcUrls
3816
+ });
3817
+ }
3818
+ return ethProvider;
3819
+ },
3655
3820
  async signSolanaTransaction(serializedTx) {
3656
3821
  const tee = session.requireActive();
3657
3822
  let tx;
@@ -3815,14 +3980,14 @@ function b64uToBuffer(b64u) {
3815
3980
  const b644 = b64u.replace(/-/g, "+").replace(/_/g, "/");
3816
3981
  return Uint8Array.from(atob(b644), (c) => c.charCodeAt(0));
3817
3982
  }
3818
- function toHex(bytes) {
3983
+ function toHex2(bytes) {
3819
3984
  return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
3820
3985
  }
3821
3986
  async function deriveAddressesFromShares(a, b) {
3822
3987
  const master = await combineShares(a, b);
3823
3988
  try {
3824
3989
  return {
3825
- evm: new import_ethers2.Wallet(toHex(master)).address,
3990
+ evm: new import_ethers2.Wallet(toHex2(master)).address,
3826
3991
  solana: import_web32.Keypair.fromSeed(master).publicKey.toBase58()
3827
3992
  };
3828
3993
  } finally {
@@ -5848,6 +6013,19 @@ function DecaneKit({ config, children }) {
5848
6013
  if (!sdk) throw new Error("Social wallet not connected");
5849
6014
  return sdk.getBalance(chain);
5850
6015
  }, []);
6016
+ const socialGetEthereumProvider = (0, import_react5.useCallback)((opts) => {
6017
+ const sdk = socialRef.current;
6018
+ if (!sdk) throw new Error("Social wallet not connected");
6019
+ return sdk.getEthereumProvider(opts);
6020
+ }, []);
6021
+ const socialSignAuthorization = (0, import_react5.useCallback)(
6022
+ async (req) => {
6023
+ const sdk = socialRef.current;
6024
+ if (!sdk) throw new Error("Social wallet not connected");
6025
+ return sdk.signAuthorization(req);
6026
+ },
6027
+ []
6028
+ );
5851
6029
  const socialGetBalances = (0, import_react5.useCallback)(async () => {
5852
6030
  const sdk = socialRef.current;
5853
6031
  if (!sdk) throw new Error("Social wallet not connected");
@@ -5910,6 +6088,8 @@ function DecaneKit({ config, children }) {
5910
6088
  socialSendTransaction,
5911
6089
  socialGetBalance,
5912
6090
  socialGetBalances,
6091
+ socialGetEthereumProvider,
6092
+ socialSignAuthorization,
5913
6093
  socialSignSolanaTransaction,
5914
6094
  socialSignTypedData,
5915
6095
  socialRotateShares,
@@ -6132,6 +6312,8 @@ function useSocialWallet() {
6132
6312
  socialSignTypedData,
6133
6313
  socialGetBalance,
6134
6314
  socialGetBalances,
6315
+ socialGetEthereumProvider,
6316
+ socialSignAuthorization,
6135
6317
  socialRotateShares,
6136
6318
  socialExportPortableBackup,
6137
6319
  socialHasRecoveryShare,
@@ -6176,6 +6358,10 @@ function useSocialWallet() {
6176
6358
  (chain) => run(() => socialGetBalance(chain)),
6177
6359
  [socialGetBalance]
6178
6360
  );
6361
+ const signAuthorization = (0, import_react6.useCallback)(
6362
+ (req) => run(() => socialSignAuthorization(req)),
6363
+ [socialSignAuthorization]
6364
+ );
6179
6365
  const getBalances = (0, import_react6.useCallback)(
6180
6366
  () => run(() => socialGetBalances()),
6181
6367
  [socialGetBalances]
@@ -6249,6 +6435,10 @@ function useSocialWallet() {
6249
6435
  // Balances (native token; read-only, works while locked)
6250
6436
  getBalance,
6251
6437
  getBalances,
6438
+ // EVM interop
6439
+ signAuthorization,
6440
+ // An EIP-1193 provider for viem/wagmi/ethers (memoized).
6441
+ getEthereumProvider: socialGetEthereumProvider,
6252
6442
  // Recovery (2-of-3 Shamir share set)
6253
6443
  rotateShares,
6254
6444
  exportPortableBackup,
@@ -6306,6 +6496,18 @@ function useSocialBalance(chain) {
6306
6496
  }, [addrKey, chain]);
6307
6497
  return { balance, loading, error, refetch };
6308
6498
  }
6499
+ function useEthereumProvider(opts) {
6500
+ const { socialAuth, socialGetEthereumProvider } = useDecaneContext();
6501
+ const chainId = opts?.chainId;
6502
+ return (0, import_react6.useMemo)(() => {
6503
+ if (!socialAuth.addresses?.evm) return null;
6504
+ try {
6505
+ return socialGetEthereumProvider(chainId ? { chainId } : void 0);
6506
+ } catch {
6507
+ return null;
6508
+ }
6509
+ }, [socialAuth.addresses?.evm, chainId]);
6510
+ }
6309
6511
  function toDecaneError(err) {
6310
6512
  if (err instanceof DecaneError) return err;
6311
6513
  const msg = err instanceof Error ? err.message : err?.message != null ? String(err.message) : String(err);
@@ -6563,9 +6765,11 @@ function useSignTypedData() {
6563
6765
  WrongPasswordError,
6564
6766
  createDecane,
6565
6767
  createDecaneConnect,
6768
+ createEthereumProvider,
6566
6769
  startEmailAuth,
6567
6770
  useChainSwitch,
6568
6771
  useDecane,
6772
+ useEthereumProvider,
6569
6773
  useSendTransaction,
6570
6774
  useSignMessage,
6571
6775
  useSignTypedData,