@sodax/wallet-sdk-react 1.3.1-beta → 1.4.1-beta

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sodax/wallet-sdk-react",
3
3
  "license": "MIT",
4
- "version": "1.3.1-beta",
4
+ "version": "1.4.1-beta",
5
5
  "description": "Wallet SDK of Sodax",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",
@@ -23,6 +23,9 @@
23
23
  "url": "https://github.com/icon-project/sodax-frontend"
24
24
  },
25
25
  "dependencies": {
26
+ "@stacks/connect": "8.2.6",
27
+ "@stacks/network": "7.3.1",
28
+ "@stacks/transactions": "7.3.1",
26
29
  "@creit.tech/stellar-wallets-kit": "^1.7.5",
27
30
  "@hot-labs/near-connect": "0.10.0",
28
31
  "@injectivelabs/networks": "1.16.10",
@@ -48,8 +51,8 @@
48
51
  "wagmi": "2.16.9",
49
52
  "zustand": "4.5.2",
50
53
  "bs58": "6.0.0",
51
- "@sodax/types": "1.3.1-beta",
52
- "@sodax/wallet-sdk-core": "1.3.1-beta"
54
+ "@sodax/wallet-sdk-core": "1.4.1-beta",
55
+ "@sodax/types": "1.4.1-beta"
53
56
  },
54
57
  "devDependencies": {
55
58
  "@types/react": "^19.0.8",
package/src/Hydrate.ts CHANGED
@@ -7,8 +7,11 @@ import { SolanaXService } from './xchains/solana/SolanaXService';
7
7
  import { SuiXService } from './xchains/sui';
8
8
  import { useConnection, useWallet } from '@solana/wallet-adapter-react';
9
9
  import { useConfig } from 'wagmi';
10
+ import { StacksXService } from './xchains/stacks/StacksXService';
11
+ import { createNetwork } from '@stacks/network';
12
+ import type { RpcConfig } from '@sodax/types';
10
13
 
11
- export const Hydrate = () => {
14
+ export const Hydrate = ({ rpcConfig }: { rpcConfig: RpcConfig }) => {
12
15
  // sui
13
16
  const suiClient = useSuiClient();
14
17
  useEffect(() => {
@@ -51,5 +54,12 @@ export const Hydrate = () => {
51
54
  }
52
55
  }, [wagmiConfig]);
53
56
 
57
+ // stacks
58
+ useEffect(() => {
59
+ StacksXService.getInstance().network = createNetwork({
60
+ network: 'mainnet',
61
+ client: { baseUrl: rpcConfig.stacks ?? 'https://api.mainnet.hiro.so' },
62
+ });
63
+ }, [rpcConfig.stacks]);
54
64
  return null;
55
65
  };
@@ -41,7 +41,7 @@ export const SodaxWalletProvider = ({ children, rpcConfig }: { children: React.R
41
41
  <SuiWalletProvider autoConnect={true}>
42
42
  <SolanaConnectionProvider endpoint={rpcConfig['solana'] ?? 'https://api.mainnet-beta.solana.com'}>
43
43
  <SolanaWalletProvider wallets={wallets} autoConnect>
44
- <Hydrate />
44
+ <Hydrate rpcConfig={rpcConfig} />
45
45
  {children}
46
46
  </SolanaWalletProvider>
47
47
  </SolanaConnectionProvider>
@@ -5,6 +5,7 @@ import { SuiXService } from '..';
5
5
  import { EvmXService } from '..';
6
6
  import type { XService } from '../core';
7
7
  import { NearXService } from '../xchains/near/NearXService';
8
+ import { StacksXService } from '../xchains/stacks/StacksXService';
8
9
 
9
10
  export function getXService(xChainType: ChainType): XService {
10
11
  switch (xChainType) {
@@ -24,6 +25,8 @@ export function getXService(xChainType: ChainType): XService {
24
25
  return StellarXService.getInstance();
25
26
  case 'NEAR':
26
27
  return NearXService.getInstance();
28
+ case 'STACKS':
29
+ return StacksXService.getInstance();
27
30
  default:
28
31
  throw new Error(`Unsupported chain type: ${xChainType}`);
29
32
  }
@@ -5,6 +5,7 @@ import type {
5
5
  IInjectiveWalletProvider,
6
6
  INearWalletProvider,
7
7
  ISolanaWalletProvider,
8
+ IStacksWalletProvider,
8
9
  IStellarWalletProvider,
9
10
  ISuiWalletProvider,
10
11
  IBitcoinWalletProvider,
@@ -20,14 +21,18 @@ import {
20
21
  StellarWalletProvider,
21
22
  SolanaWalletProvider,
22
23
  NearWalletProvider,
24
+ StacksWalletProvider,
23
25
  } from '@sodax/wallet-sdk-core';
24
26
  import { getXChainType } from '../actions';
25
27
  import { usePublicClient, useWalletClient } from 'wagmi';
26
- import { type SolanaXService, type StellarXService, useXAccount, useXService, useXConnection } from '..';
28
+ import { type SolanaXService, type StellarXService, useXAccount, useXService } from '..';
27
29
  import type { SuiXService } from '../xchains/sui/SuiXService';
28
30
  import { CHAIN_INFO, SupportedChainId } from '../xchains/icon/IconXService';
29
31
  import type { InjectiveXService } from '../xchains/injective/InjectiveXService';
30
32
  import type { NearXService } from '../xchains/near/NearXService';
33
+ import { useXConnection } from './useXConnection';
34
+ import { useXConnectors } from './useXConnectors';
35
+ import type { StacksXConnector } from '../xchains/stacks';
31
36
 
32
37
  /**
33
38
  * Hook to get the appropriate wallet provider based on the chain type.
@@ -57,6 +62,7 @@ export function useWalletProvider(
57
62
  | ISolanaWalletProvider
58
63
  | IBitcoinWalletProvider
59
64
  | INearWalletProvider
65
+ | IStacksWalletProvider
60
66
  | undefined {
61
67
  const xChainType = getXChainType(spokeChainId);
62
68
  // EVM-specific hooks
@@ -67,6 +73,8 @@ export function useWalletProvider(
67
73
  // Cross-chain hooks
68
74
  const xService = useXService(getXChainType(spokeChainId));
69
75
  const xAccount = useXAccount(spokeChainId);
76
+ const stacksConnection = useXConnection('STACKS');
77
+ const stacksConnectors = useXConnectors('STACKS');
70
78
  const xConnection = useXConnection(xChainType);
71
79
 
72
80
  return useMemo(() => {
@@ -152,11 +160,14 @@ export function useWalletProvider(
152
160
 
153
161
  case 'BITCOIN': {
154
162
  if (!xConnection?.xConnectorId) return undefined;
155
- const connector = BitcoinXService.getInstance().getXConnectorById(xConnection.xConnectorId) as BitcoinXConnector | undefined;
163
+ const connector = BitcoinXService.getInstance().getXConnectorById(xConnection.xConnectorId) as
164
+ | BitcoinXConnector
165
+ | undefined;
156
166
  if (!connector) return undefined;
157
167
  // Recreate from window extension object — works after page reload without reconnect
158
168
  return connector.recreateWalletProvider(xConnection.xAccount);
159
169
  }
170
+
160
171
  case 'NEAR': {
161
172
  const nearXService = xService as NearXService;
162
173
  if (!nearXService.walletSelector) {
@@ -166,8 +177,30 @@ export function useWalletProvider(
166
177
  return new NearWalletProvider({ wallet: nearXService.walletSelector });
167
178
  }
168
179
 
180
+ case 'STACKS': {
181
+ const address = xAccount.address;
182
+ if (!address) {
183
+ return undefined;
184
+ }
185
+
186
+ const activeStacksConnector = stacksConnectors.find(c => c.id === stacksConnection?.xConnectorId) as
187
+ | StacksXConnector
188
+ | undefined;
189
+
190
+ return new StacksWalletProvider({ address, provider: activeStacksConnector?.getProvider() });
191
+ }
192
+
169
193
  default:
170
194
  return undefined;
171
195
  }
172
- }, [xChainType, evmPublicClient, evmWalletClient, xService, xAccount, xConnection]);
196
+ }, [
197
+ xChainType,
198
+ evmPublicClient,
199
+ evmWalletClient,
200
+ xService,
201
+ xAccount,
202
+ stacksConnection,
203
+ stacksConnectors,
204
+ xConnection,
205
+ ]);
173
206
  }
@@ -10,6 +10,7 @@ import { useStellarXConnectors } from '../xchains/stellar/useStellarXConnectors'
10
10
  import { SuiXConnector } from '../xchains/sui';
11
11
  import { useXService } from './useXService';
12
12
  import { useNearXConnectors } from '../xchains/near/useNearXConnectors';
13
+ import { useStacksXConnectors } from '../xchains/stacks/useStacksXConnectors';
13
14
 
14
15
  /**
15
16
  * Hook to retrieve available wallet connectors for a specific blockchain type.
@@ -20,7 +21,7 @@ import { useNearXConnectors } from '../xchains/near/useNearXConnectors';
20
21
  * - Stellar: Uses custom Stellar connectors
21
22
  * - Solana: Uses Solana wallet adapters (filtered to installed wallets only)
22
23
  *
23
- * @param xChainType - The blockchain type to get connectors for ('EVM' | 'SUI' | 'STELLAR' | 'SOLANA' | 'NEAR')
24
+ * @param xChainType - The blockchain type to get connectors for ('EVM' | 'SUI' | 'STELLAR' | 'SOLANA' | 'NEAR' | 'STACKS')
24
25
  * @returns An array of XConnector instances compatible with the specified chain type
25
26
  */
26
27
 
@@ -30,6 +31,7 @@ export function useXConnectors(xChainType: ChainType | undefined): XConnector[]
30
31
  const suiWallets = useWallets();
31
32
  const { data: stellarXConnectors } = useStellarXConnectors();
32
33
  const { data: nearXConnectors } = useNearXConnectors();
34
+ const stacksXConnectors = useStacksXConnectors();
33
35
  const { wallets: solanaWallets } = useWallet();
34
36
 
35
37
  const xConnectors = useMemo((): XConnector[] => {
@@ -50,10 +52,21 @@ export function useXConnectors(xChainType: ChainType | undefined): XConnector[]
50
52
  .map(wallet => new SolanaXConnector(wallet));
51
53
  case 'NEAR':
52
54
  return nearXConnectors || [];
55
+ case 'STACKS':
56
+ return stacksXConnectors;
53
57
  default:
54
58
  return xService.getXConnectors();
55
59
  }
56
- }, [xService, xChainType, evmConnectors, suiWallets, stellarXConnectors, solanaWallets, nearXConnectors]);
60
+ }, [
61
+ xService,
62
+ xChainType,
63
+ evmConnectors,
64
+ suiWallets,
65
+ stellarXConnectors,
66
+ solanaWallets,
67
+ nearXConnectors,
68
+ stacksXConnectors,
69
+ ]);
57
70
 
58
71
  return xConnectors;
59
72
  }
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export * from './xchains/injective';
10
10
  export * from './xchains/solana';
11
11
  export * from './xchains/stellar';
12
12
  export * from './xchains/sui';
13
+ export * from './xchains/stacks';
13
14
  export * from './hooks';
14
15
  export * from './useXWagmiStore';
15
16
  export * from './SodaxWalletProvider';
@@ -18,6 +18,7 @@ import { UnisatXConnector } from './xchains/bitcoin/UnisatXConnector';
18
18
  import { XverseXConnector } from './xchains/bitcoin/XverseXConnector';
19
19
  import { OKXXConnector } from './xchains/bitcoin/OKXXConnector';
20
20
  import { NearXService } from './xchains/near/NearXService';
21
+ import { StacksXService, StacksXConnector, STACKS_PROVIDERS } from './xchains/stacks';
21
22
 
22
23
  type XWagmiStore = {
23
24
  xServices: Partial<Record<ChainType, XService>>;
@@ -29,7 +30,7 @@ type XWagmiStore = {
29
30
 
30
31
  const initXServices = () => {
31
32
  const xServices = {};
32
- ['EVM', 'BITCOIN', 'INJECTIVE', 'STELLAR', 'SUI', 'SOLANA', 'ICON', 'NEAR'].forEach(key => {
33
+ ['EVM', 'BITCOIN', 'INJECTIVE', 'STELLAR', 'SUI', 'SOLANA', 'ICON', 'NEAR', 'STACKS'].forEach(key => {
33
34
  const xChainType = key as ChainType;
34
35
 
35
36
  switch (xChainType) {
@@ -72,6 +73,10 @@ const initXServices = () => {
72
73
  xServices[xChainType] = NearXService.getInstance();
73
74
  xServices[xChainType].setXConnectors([]);
74
75
  break;
76
+ case 'STACKS':
77
+ xServices[xChainType] = StacksXService.getInstance();
78
+ xServices[xChainType].setXConnectors(STACKS_PROVIDERS.map(config => new StacksXConnector(config)));
79
+ break;
75
80
  default:
76
81
  break;
77
82
  }
@@ -9,6 +9,7 @@ export const isNativeToken = (xToken: XToken) => {
9
9
  'hx0000000000000000000000000000000000000000',
10
10
  '11111111111111111111111111111111', // solana
11
11
  'CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA', // stellar
12
+ 'ST000000000000000000002AMW42H.nativetoken', // stacks
12
13
  '0:0', // bitcoin
13
14
  ];
14
15
 
@@ -0,0 +1,63 @@
1
+ import type { XAccount } from '@/types';
2
+ import { XConnector } from '@/core';
3
+ import type { StacksProvider } from '@stacks/connect';
4
+ import { request, disconnect } from '@stacks/connect';
5
+
6
+ export interface StacksProviderConfig {
7
+ /** The provider ID matching the window path, e.g. 'LeatherProvider' or 'XverseProviders.BitcoinProvider' */
8
+ id: string;
9
+ name: string;
10
+ icon: string;
11
+ installUrl?: string;
12
+ }
13
+
14
+ /** Resolves a provider from `window` by dot-separated ID, matching @stacks/connect-ui's getProviderFromId */
15
+ function getProviderFromId(id: string): StacksProvider | undefined {
16
+ // biome-ignore lint/suspicious/noExplicitAny: window property traversal requires any
17
+ return id.split('.').reduce<any>((acc, part) => acc?.[part], window) as StacksProvider | undefined;
18
+ }
19
+
20
+ export class StacksXConnector extends XConnector {
21
+ private readonly config: StacksProviderConfig;
22
+
23
+ constructor(config: StacksProviderConfig) {
24
+ super('STACKS', config.name, config.id);
25
+ this.config = config;
26
+ }
27
+
28
+ async connect(): Promise<XAccount | undefined> {
29
+ const provider = this.getProvider();
30
+
31
+ if (!provider) {
32
+ if (this.config.installUrl) {
33
+ window.open(this.config.installUrl, '_blank');
34
+ }
35
+ return undefined;
36
+ }
37
+
38
+ const response = await request({ provider }, 'stx_getAddresses');
39
+ // @ts-ignore
40
+ const stxAddress = response.addresses.find(a => a.purpose === 'stacks');
41
+
42
+ if (!stxAddress) {
43
+ return undefined;
44
+ }
45
+
46
+ return {
47
+ address: stxAddress.address,
48
+ xChainType: this.xChainType,
49
+ };
50
+ }
51
+
52
+ async disconnect(): Promise<void> {
53
+ disconnect();
54
+ }
55
+
56
+ public get icon(): string {
57
+ return this.config.icon;
58
+ }
59
+
60
+ public getProvider(): StacksProvider | undefined {
61
+ return getProviderFromId(this.config.id);
62
+ }
63
+ }
@@ -0,0 +1,59 @@
1
+ import { XService } from '@/core/XService';
2
+ import type { XToken } from '@sodax/types';
3
+ import { fetchCallReadOnlyFunction, Cl, type UIntCV, type ResponseOkCV } from '@stacks/transactions';
4
+ import { networkFrom, type StacksNetwork } from '@stacks/network';
5
+
6
+ export class StacksXService extends XService {
7
+ private static instance: StacksXService;
8
+
9
+ public network: StacksNetwork | undefined;
10
+
11
+ private constructor() {
12
+ super('STACKS');
13
+ this.network = networkFrom('mainnet');
14
+ }
15
+
16
+ public static getInstance(): StacksXService {
17
+ if (!StacksXService.instance) {
18
+ StacksXService.instance = new StacksXService();
19
+ }
20
+ return StacksXService.instance;
21
+ }
22
+
23
+ async getBalance(address: string | undefined, xToken: XToken): Promise<bigint> {
24
+ if (!address) return 0n;
25
+
26
+ // native STX balance
27
+ if (xToken.symbol === 'STX') {
28
+ const url = `${this.network?.client.baseUrl}/extended/v1/address/${address}/balances`;
29
+ try {
30
+ const response = await fetch(url);
31
+ if (!response.ok) {
32
+ throw new Error(`Error fetching data: ${response.statusText}`);
33
+ }
34
+ const data = await response.json();
35
+ return BigInt(data.stx.balance);
36
+ } catch (error) {
37
+ console.error('Error fetching STX balance:', error);
38
+ return 0n;
39
+ }
40
+ }
41
+
42
+ // SIP-010 fungible token balance via read-only contract call
43
+ const [contractAddress, contractName] = xToken.address.split('.');
44
+ try {
45
+ const result = (await fetchCallReadOnlyFunction({
46
+ contractAddress,
47
+ contractName,
48
+ functionName: 'get-balance',
49
+ functionArgs: [Cl.principal(address)],
50
+ network: this.network,
51
+ senderAddress: address,
52
+ })) as ResponseOkCV<UIntCV>;
53
+ return result.value.value as bigint;
54
+ } catch (error) {
55
+ console.error('Error fetching token balance:', error);
56
+ return 0n;
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,42 @@
1
+ import type { StacksProviderConfig } from './StacksXConnector';
2
+
3
+ // Icons sourced from @stacks/connect DEFAULT_PROVIDERS
4
+ // https://github.com/stx-labs/connect/blob/main/packages/connect/src/providers.ts
5
+ const LEATHER_ICON =
6
+ 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYuODM4NyIgZmlsbD0iIzEyMTAwRiIvPgo8cGF0aCBkPSJNNzQuOTE3MSA1Mi43MTE0QzgyLjQ3NjYgNTEuNTQwOCA5My40MDg3IDQzLjU4MDQgOTMuNDA4NyAzNy4zNzYxQzkzLjQwODcgMzUuNTAzMSA5MS44OTY4IDM0LjIxNTQgODkuNjg3MSAzNC4yMTU0Qzg1LjUwMDQgMzQuMjE1NCA3OC40MDYxIDQwLjUzNjggNzQuOTE3MSA1Mi43MTE0Wk0zOS45MTEgODMuNDk5MUMzMC4wMjU2IDgzLjQ5OTEgMjkuMjExNSA5My4zMzI0IDM5LjA5NjkgOTMuMzMyNEM0My41MTYzIDkzLjMzMjQgNDguODY2MSA5MS41NzY0IDUxLjY1NzMgODguNDE1N0M0Ny41ODY4IDg0LjkwMzggNDQuMjE0MSA4My40OTkxIDM5LjkxMSA4My40OTkxWk0xMDIuODI5IDc5LjI4NDhDMTAzLjQxIDk1Ljc5MDcgOTUuMDM2OSAxMDUuMDM5IDgwLjg0ODQgMTA1LjAzOUM3Mi40NzQ4IDEwNS4wMzkgNjguMjg4MSAxMDEuODc4IDU5LjMzMyA5Ni4wMjQ5QzU0LjY4MSAxMDEuMTc2IDQ1Ljg0MjMgMTA1LjAzOSAzOC41MTU0IDEwNS4wMzlDMTMuMjc4NSAxMDUuMDM5IDE0LjMyNTIgNzIuODQ2MyA0MC4wMjczIDcyLjg0NjNDNDUuMzc3MSA3Mi44NDYzIDQ5LjkxMjggNzQuMjUxMSA1NS43Mjc3IDc3Ljg4TDU5LjU2NTYgNjQuNDE3N0M0My43NDg5IDYwLjA4NjQgMzUuODQwNSA0Ny45MTE4IDQzLjYzMjYgMzAuNDY5M0g1Ni4xOTI5QzQ5LjIxNSA0Mi4wNTg2IDUzLjk4MzIgNTEuNjU3OCA2Mi44MjIgNTIuNzExNEM2Ny41OTAzIDM1LjczNzIgNzcuODI0NiAyMi41MDkgOTEuNDMxNiAyMi41MDlDOTkuMTA3NCAyMi41MDkgMTA1LjE1NSAyNy41NDI4IDEwNS4xNTUgMzYuNjczN0MxMDUuMTU1IDUxLjMwNjYgODYuMDgxOSA2My4yNDcxIDcxLjY2MDcgNjQuNDE3N0w2NS43Mjk1IDg1LjM3MjFDNzIuNDc0OCA5My4yMTUzIDkxLjE5OSAxMDAuODI0IDkxLjE5OSA3OS4yODQ4SDEwMi44MjlaIiBmaWxsPSIjRjVGMUVEIi8+Cjwvc3ZnPgo=';
7
+
8
+ const XVERSE_ICON =
9
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9IiMxNzE3MTciIGQ9Ik0wIDBoNjAwdjYwMEgweiIvPjxwYXRoIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyIgZD0iTTQ0MCA0MzUuNHYtNTFjMC0yLS44LTMuOS0yLjItNS4zTDIyMCAxNjIuMmE3LjYgNy42IDAgMCAwLTUuNC0yLjJoLTUxLjFjLTIuNSAwLTQuNiAyLTQuNiA0LjZ2NDcuM2MwIDIgLjggNCAyLjIgNS40bDc4LjIgNzcuOGE0LjYgNC42IDAgMCAxIDAgNi41bC03OSA3OC43Yy0xIC45LTEuNCAyLTEuNCAzLjJ2NTJjMCAyLjQgMiA0LjUgNC42IDQuNUgyNDljMi42IDAgNC42LTIgNC42LTQuNlY0MDVjMC0xLjIuNS0yLjQgMS40LTMuM2w0Mi40LTQyLjJhNC42IDQuNiAwIDAgMSA2LjQgMGw3OC43IDc4LjRhNy42IDcuNiAwIDAgMCA1LjQgMi4yaDQ3LjVjMi41IDAgNC42LTIgNC42LTQuNloiLz48cGF0aCBmaWxsPSIjRUU3QTMwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0zMjUuNiAyMjcuMmg0Mi44YzIuNiAwIDQuNiAyLjEgNC42IDQuNnY0Mi42YzAgNCA1IDYuMSA4IDMuMmw1OC43LTU4LjVjLjgtLjggMS4zLTIgMS4zLTMuMnYtNTEuMmMwLTIuNi0yLTQuNi00LjYtNC42TDM4NCAxNjBjLTEuMiAwLTIuNC41LTMuMyAxLjNsLTU4LjQgNTguMWE0LjYgNC42IDAgMCAwIDMuMiA3LjhaIi8+PC9nPjwvc3ZnPg==';
10
+
11
+ const ASIGNA_ICON =
12
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzAwMDEwMCIgZD0iTTAgMGgzMnYzMkgweiIvPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik0xNS4xMSA1LjU1YTMgMyAwIDAgMC0xLjgyIDEuM2wtLjA1LjA4LS40My43Mi0uMDcuMTEtLjUuODUtLjA1LjA5LTEuMjkgMi4xOC0uMDQuMDctLjQ3LjgtLjA2LjEtLjQ2Ljc4LS4wNy4xMS0xLjYzIDIuNzYtLjA3LjExLS4zOC42Ni0uMDUuMDgtLjczIDEuMjQtLjM1LjYtLjQuNjctLjA1LjA5TDUuMSAyMC43bC0uMTEuMTgtLjE0LjIzLS4wNy4xMy0uMzMuNTUtLjA0LjA3di4wMWExLjI2IDEuMjYgMCAwIDAtLjE0LjQ3IDEuMzEgMS4zMSAwIDAgMCAxLjI0IDEuNGgxLjVsLjA1LS4wNi4wNC0uMDYuODctMS4yMS4wNS0uMDguNzctMS4wNy4wNS0uMDcuNC0uNTcuMDUtLjA2LjI0LS4zNGExLjUyIDEuNTIgMCAwIDEgMS4zOS0uNjIgMS41IDEuNSAwIDAgMSAuNjQuMiAxLjQ3IDEuNDcgMCAwIDEgLjczIDEuMjcgMS40NCAxLjQ0IDAgMCAxLS4yNy44NGwtLjYzLjg4LS4wNS4wNy0uMzIuNDUtLjA2LjA4LS4wOC4xMi0uMTIuMTYtLjA1LjA4aDIuMTNhMi4zMiAyLjMyIDAgMCAwIDEuNzctLjk2bDEuMTgtMS42My43Ny0xLjA4IDEuMy0xLjhhMS4yNCAxLjI0IDAgMCAxIC41NS0uNDNsLjA4LS4wM2ExLjMgMS4zIDAgMCAxIC4zLS4wNiAxLjI4IDEuMjggMCAwIDEgMS4xNS41NGwuMTEuMmExLjEzIDEuMTMgMCAwIDEgLjEuNDEgMS4xOSAxLjE5IDAgMCAxLS4yMy43N2wtLjAzLjA1LS41Ny44LS43Ljk4LS4yNy4zN2ExLjIyIDEuMjIgMCAwIDAtLjIuNSAxLjA1IDEuMDUgMCAwIDAtLjAyLjIzdi4wNmExLjE3IDEuMTcgMCAwIDAgLjE0LjQzbC4wMi4wNS4wNy4xYTEuNDQgMS40NCAwIDAgMCAuMS4xMWwuMDUuMDYuMDEuMDFhMS44IDEuOCAwIDAgMCAuMTQuMWMwIC4wMi4wMi4wMy4wNC4wM2ExIDEgMCAwIDAgLjA4LjA1bC4wNy4wNGExLjI1IDEuMjUgMCAwIDAgLjUuMWg2LjljLjEgMCAuMi0uMDEuMjktLjAzbC4wNi0uMDJhMS4yNyAxLjI3IDAgMCAwIC4yNy0uMS41Ny41NyAwIDAgMCAuMDctLjAzIDEuMjEgMS4yMSAwIDAgMCAuMjYtLjE5bC4wOC0uMDdhLjkyLjkyIDAgMCAwIC4xNS0uMTkgMS41NSAxLjU1IDAgMCAwIC4wOS0uMTdsLjAyLS4wNWExLjIyIDEuMjIgMCAwIDAgLjA4LS4yNnYtLjA0bC4wMi0uMDh2LS4wOGExLjMyIDEuMzIgMCAwIDAtLjItLjc0bC0xLjYtMi42NC0uMDYtLjEtLjItLjMyLS4zMy0uNTR2LS4wMWwtLjA1LS4wOC0xLjMtMi4xNS0uMDctLjEtLjA0LS4wNi0uOC0xLjMyLS4wNC0uMDctLjItLjM0LS4xLS4xNC0uMS0uMTYtLjUzLS45LS4xMy0uMi0uMDktLjE0LTIuMTctMy41Ny0uMDQtLjA3LS43Mi0xLjE5LS4wNS0uMDctLjQtLjY1YTIuNjUgMi42NSAwIDAgMC0uMy0uNCAyLjk2IDIuOTYgMCAwIDAtLjk3LS43NCAzLjA0IDMuMDQgMCAwIDAtMS4zLS4zYy0uMjUgMC0uNS4wNC0uNzQuMVoiLz48cGF0aCBmaWxsPSJ1cmwoI2IpIiBkPSJNMTkgMTYuM2E1LjQ1IDUuNDUgMCAwIDAtLjgzIDEuNTZsLS4wNC4xNWExLjM2IDEuMzYgMCAwIDEgLjI4LS4xNiAxLjI0IDEuMjQgMCAwIDEgLjM4LS4wOGguMWExLjI4IDEuMjggMCAwIDEgMS4wNS41NGMuMDQuMDYuMDguMTMuMS4yYTEuMjQgMS4yNCAwIDAgMSAuMDkuMjcgMS4xOSAxLjE5IDAgMCAxLS4yLjkxbC0uMDQuMDUtLjU3Ljc5LS43Ljk5LS4yNy4zN2ExLjIzIDEuMjMgMCAwIDAtLjIuNDIgMS4wNiAxLjA2IDAgMCAwLS4wMi4zMXYuMDZhMS4xNyAxLjE3IDAgMCAwIC4xNi40Ny45My45MyAwIDAgMCAuMDcuMSAxLjUgMS41IDAgMCAwIC4xLjEybC4wNS4wNmguMDFhMS45NCAxLjk0IDAgMCAwIC4wOS4wOCAxIDEgMCAwIDAgLjE3LjFsLjA3LjA0YTEuMjUgMS4yNSAwIDAgMCAuNS4xaDYuOWMuMSAwIC4yIDAgLjI4LS4wMmwuMDctLjAyYTEuMzIgMS4zMiAwIDAgMCAuMzQtLjEzbC4xNi0uMS4wMy0uMDNhMS4yOSAxLjI5IDAgMCAwIC4yLS4yIDIuNDMgMi40MyAwIDAgMCAuMTItLjE3Yy4wMy0uMDMuMDUtLjA4LjA3LS4xMmwuMDItLjA1YTEuMjEgMS4yMSAwIDAgMCAuMDktLjN2LS4wOGwuMDEtLjA5YTEuMzIgMS4zMiAwIDAgMC0uMi0uNzNsLTEuNi0yLjY0LS4wNi0uMS0uMi0uMzItLjMzLS41NHYtLjAybC0uMDUtLjA3LTEuMy0yLjE1LS4xMi0uMDctLjA3LS4wNGE0Ljk0IDQuOTQgMCAwIDAtMi40Ni0uNjdjLTEuMDMgMC0xLjc2LjU3LTIuMjYgMS4yWiIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMi4yOSAyMS4wOGMwIC4yOS0uMDkuNTgtLjI3Ljg0bC0xLjMxIDEuODRIN2wyLjUyLTMuNTNhMS41NCAxLjU0IDAgMCAxIDIuMS0uMzZjLjQzLjI4LjY2Ljc0LjY2IDEuMloiLz48cGF0aCBmaWxsPSIjMDAwIiBkPSJNMTEuMTYgMjEuMjVhLjU2LjU2IDAgMCAxLS41Ny41NS41Ni41NiAwIDAgMS0uNTctLjU2LjU2LjU2IDAgMCAxIC41Ny0uNTUuNTYuNTYgMCAwIDEgLjU3LjU2WiIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjE1LjIzIiB4Mj0iMTkuMyIgeTE9IjI1Ljc4IiB5Mj0iNi4xMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiM2NTIyRjQiLz48c3RvcCBvZmZzZXQ9Ii41NSIgc3RvcC1jb2xvcj0iIzlCNkJGRiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0E1ODVGRiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMjIuNTkiIHgyPSIyNC44IiB5MT0iMjQuNzEiIHkyPSIxNS41MyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiM0MjFGOEIiLz48c3RvcCBvZmZzZXQ9Ii41NSIgc3RvcC1jb2xvcj0iIzcyMzBGRiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzk3NzNGRiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjwvc3ZnPg==';
13
+
14
+ const FORDEFI_ICON =
15
+ 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZmlsbD0iIzEwMTExNCIgZD0iTTAgMGg0MnY0MkgweiIvPgogIDxwYXRoIGQ9Ik0xOS40NyAyNi44OUg1djMuNTdhNC41NyA0LjU3IDAgMCAwIDQuNTggNC41N2g1LjgzbDQuMDYtOC4xNFoiIGZpbGw9IiM3OTk0RkYiLz4KICA8cGF0aCBkPSJNNSAxNy40aDI3LjU4bC0zLjIgNi43OEg1VjE3LjRaIiBmaWxsPSIjNDg2REZGIi8+CiAgPHBhdGggZD0iTTE0LjY3IDdINXY3LjY4aDMzVjdoLTkuNjd2NS43NGgtMlY3aC05LjY3djUuNzRoLTEuOTlWN1oiIGZpbGw9IiM1Q0QxRkEiLz4KPC9zdmc+Cg==';
16
+
17
+ export const STACKS_PROVIDERS: StacksProviderConfig[] = [
18
+ {
19
+ id: 'LeatherProvider',
20
+ name: 'Leather',
21
+ icon: LEATHER_ICON,
22
+ installUrl: 'https://chrome.google.com/webstore/detail/hiro-wallet/ldinpeekobnhjjdofggfgjlcehhmanlj',
23
+ },
24
+ {
25
+ id: 'XverseProviders.BitcoinProvider',
26
+ name: 'Xverse Wallet',
27
+ icon: XVERSE_ICON,
28
+ installUrl: 'https://chrome.google.com/webstore/detail/xverse-wallet/idnnbdplmphpflfnlkomgpfbpcgelopg',
29
+ },
30
+ {
31
+ id: 'AsignaProvider',
32
+ name: 'Asigna Multisig',
33
+ icon: ASIGNA_ICON,
34
+ installUrl: 'https://stx.asigna.io/',
35
+ },
36
+ {
37
+ id: 'FordefiProviders.UtxoProvider',
38
+ name: 'Fordefi',
39
+ icon: FORDEFI_ICON,
40
+ installUrl: 'https://chromewebstore.google.com/detail/fordefi/hcmehenccjdmfbojapcbcofkgdpbnlle',
41
+ },
42
+ ];
@@ -0,0 +1,4 @@
1
+ export { StacksXService } from './StacksXService';
2
+ export { StacksXConnector, type StacksProviderConfig } from './StacksXConnector';
3
+ export { STACKS_PROVIDERS } from './constants';
4
+ export { useStacksXConnectors } from './useStacksXConnectors';
@@ -0,0 +1,7 @@
1
+ import { useMemo } from 'react';
2
+ import { StacksXConnector } from './StacksXConnector';
3
+ import { STACKS_PROVIDERS } from './constants';
4
+
5
+ export function useStacksXConnectors(): StacksXConnector[] {
6
+ return useMemo(() => STACKS_PROVIDERS.map(config => new StacksXConnector(config)), []);
7
+ }