@tuwaio/orbit-solana 0.0.4 → 0.1.1

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
@@ -2,57 +2,167 @@
2
2
 
3
3
  [![NPM Version](https://img.shields.io/npm/v/@tuwaio/orbit-solana.svg)](https://www.npmjs.com/package/@tuwaio/orbit-solana)
4
4
  [![License](https://img.shields.io/npm/l/@tuwaio/orbit-solana.svg)](./LICENSE)
5
- [![Build Status](https://img.shields.io/github/actions/workflow/status/TuwaIO/satellite-connect/release.yml?branch=main)](https://github.com/TuwaIO/satellite-connect/actions)
5
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/TuwaIO/orbit/release.yml?branch=main)](https://github.com/TuwaIO/orbit/actions)
6
6
 
7
- Solana blockchain implementation for the TUWA ecosystem, providing comprehensive utilities and helpers for interacting with Solana networks.
7
+ Solana-specific adapter implementation and utilities for the **Orbit Utils** ecosystem by **TUWA**. Provides helpers for interacting with Solana networks (mainnet, devnet, testnet) using **gill** and **Wallet Standard**.
8
8
 
9
9
  ---
10
10
 
11
11
  ## 🏛️ What is `@tuwaio/orbit-solana`?
12
12
 
13
- `@tuwaio/orbit-solana` is the Solana-focused extension of the TUWA ecosystem, built with TypeScript and designed for modern Web3 development. It provides specialized tools for interacting with Solana blockchain, including mainnet, devnet, and testnet networks.
13
+ `@tuwaio/orbit-solana` is the Solana-focused adapter within the **Orbit Utils** ecosystem, extending `@tuwaio/orbit-core` with functionalities specific to the Solana blockchain. It simplifies interactions with Solana wallets and RPC endpoints for UI development.
14
+
15
+ Built with **TypeScript**, this package utilizes **`gill`** (an improvement layer over `@solana/kit`) for RPC interactions and leverages the **Wallet Standard** (`@wallet-standard/app`, `@wallet-standard/ui-registry`) for wallet discovery and management. It provides essential tools for building user interfaces that connect to Solana.
14
16
 
15
17
  ---
16
18
 
17
19
  ## ✨ Key Features
18
20
 
19
- - **Modern Solana Support:** Built on latest gill (solana kit improver)
20
- - **Type-Safe Development:** Full TypeScript 5.9 support
21
- - **Tree-Shaking Optimized:** Efficient bundle size through careful exports
21
+ - **RPC Client Management:** Efficiently creates and caches Solana RPC clients (`SolanaClient` and lower-level `Rpc`) using `gill` (`createSolanaClientWithCache`, `createSolanaRPC`). Supports default and custom RPC URLs.
22
+ - **Wallet Standard Integration:** Discovers available Solana wallets compatible with the Wallet Standard (`getAvailableWallets`). Retrieves the currently connected wallet based on stored address (`getConnectedSolanaWallet`).
23
+ - **Account Info Resolution:** Fetches user-set account labels (names) and icons (avatars) directly from the connected wallet's accounts (`getSolanaAddressName`, `getSolanaAddressAvatar`), including caching.
24
+ - **Cluster & RPC URL Helpers:** Utilities to parse cluster names (e.g., 'mainnet', 'devnet') from chain IDs and retrieve corresponding RPC URLs (`getCluster`, `getRpcUrlForCluster`).
25
+ - **Explorer Link Generation:** Creates links to Solana explorers (like Solscan) for transactions, addresses, etc., correctly handling cluster parameters (`getSolanaExplorerLink`).
26
+ - **Type-Safe Development:** Fully typed using TypeScript 5.9+.
27
+ - **Optimized Bundling:** Built with `tsup` for efficient CommonJS and ESM outputs with tree-shaking.
22
28
 
23
29
  ---
24
30
 
25
31
  ## 💾 Installation
26
32
 
27
33
  ### Requirements
28
- - Node.js 20+
29
- - TypeScript 5.9+
30
- - gill: ^0.11
34
+
35
+ - Node.js 20+
36
+ - TypeScript 5.9+
37
+ - `@tuwaio/orbit-core` (as a foundational peer dependency)
31
38
 
32
39
  ```bash
33
40
  # Using pnpm (recommended)
34
- pnpm add @tuwaio/orbit-solana gill
41
+ pnpm add @tuwaio/orbit-solana @tuwaio/orbit-core gill @wallet-standard/app @wallet-standard/ui-core @wallet-standard/ui-registry
35
42
 
36
43
  # Using npm
37
- npm install @tuwaio/orbit-solana gill
44
+ npm install @tuwaio/orbit-solana @tuwaio/orbit-core gill @wallet-standard/app @wallet-standard/ui-core @wallet-standard/ui-registry
38
45
 
39
46
  # Using yarn
40
- yarn add @tuwaio/orbit-solana gill
47
+ yarn add @tuwaio/orbit-solana @tuwaio/orbit-core gill @wallet-standard/app @wallet-standard/ui-core @wallet-standard/ui-registry
48
+ ````
49
+
50
+ *Note: `@tuwaio/orbit-core`, `gill`, `@wallet-standard/app`, `@wallet-standard/ui-core`, and `@wallet-standard/ui-registry` are **peer dependencies** and must be installed alongside `@tuwaio/orbit-solana`*.
51
+
52
+ -----
53
+
54
+ ## 🚀 Quick Start
55
+
56
+ ### Get Available Solana Wallets
57
+
58
+ Discover wallets installed by the user that support the Wallet Standard for Solana.
59
+
60
+ ```typescript
61
+ import { getAvailableWallets } from '@tuwaio/orbit-solana';
62
+
63
+ const wallets = getAvailableWallets();
64
+ console.log('Available Solana Wallets:', wallets.map(w => w.name));
65
+ // Example Output: ['Phantom', 'Backpack', ...]
66
+ ```
67
+
68
+ ### Create a Cached RPC Client
69
+
70
+ Get a `gill` SolanaClient instance for interacting with the mainnet. Caching ensures you reuse the same client instance.
71
+
72
+ ```typescript
73
+ import { createSolanaClientWithCache } from '@tuwaio/orbit-solana';
74
+
75
+ // Get client for mainnet using default RPC URL
76
+ const mainnetClient = createSolanaClientWithCache({ rpcUrlOrMoniker: 'mainnet' });
77
+ console.log('Mainnet Client:', mainnetClient);
78
+
79
+ // Get client using a custom RPC URL
80
+ const customClient = createSolanaClientWithCache({ rpcUrlOrMoniker: 'https://my-custom-rpc.com' });
81
+ console.log('Custom Client:', customClient);
82
+
83
+ // Get client for devnet, potentially using custom URLs if provided
84
+ const devnetClient = createSolanaClientWithCache({
85
+ rpcUrlOrMoniker: 'devnet',
86
+ rpcUrls: { devnet: 'https://api.devnet.solana.com' } // Optional: Provide specific URLs
87
+ });
88
+ console.log('Devnet Client:', devnetClient);
89
+
90
+ // You can now use the client, e.g., mainnetClient.rpc.getBalance(...)
41
91
  ```
42
- ---
92
+
93
+ ### Get Account Name/Label from Connected Wallet
94
+
95
+ Assuming a wallet is connected and its address is stored (e.g., using `lastConnectedWalletHelpers` from `orbit-core`), get the user-defined label for that address.
96
+
97
+ ```typescript
98
+ import { getSolanaAddressName } from '@tuwaio/orbit-solana';
99
+ import { lastConnectedWalletHelpers } from '@tuwaio/orbit-core'; // Needed to know which address is connected
100
+
101
+ async function displayAccountName() {
102
+ const connectedWalletInfo = lastConnectedWalletHelpers.getLastConnectedWallet();
103
+ if (connectedWalletInfo?.address && connectedWalletInfo.walletType.startsWith('solana:')) {
104
+ try {
105
+ const name = await getSolanaAddressName(connectedWalletInfo.address);
106
+ console.log(`Label for address ${connectedWalletInfo.address}: ${name}`);
107
+ // If no label is set in the wallet, 'name' will be the address itself.
108
+ } catch (error) {
109
+ console.error("Could not get account name. Is a Solana wallet connected and registered?", error);
110
+ // This relies on getConnectedSolanaWallet finding the wallet via Wallet Standard registry
111
+ }
112
+ } else {
113
+ console.log("No Solana wallet seems to be connected.");
114
+ }
115
+ }
116
+
117
+ // Make sure Wallet Standard wallets are registered before calling this
118
+ // (This usually happens automatically when wallet extensions load)
119
+ setTimeout(displayAccountName, 1000); // Give wallets time to register
120
+ ```
121
+
122
+ ### Generate Explorer Link
123
+
124
+ Create a URL for a transaction on Solscan for the devnet cluster.
125
+
126
+ ```typescript
127
+ import { getSolanaExplorerLink } from '@tuwaio/orbit-solana';
128
+
129
+ const txHash = '2y...'; // Example transaction hash
130
+ const devnetExplorerUrl = getSolanaExplorerLink(`/tx/${txHash}`, 'devnet');
131
+ console.log(devnetExplorerUrl);
132
+ // Output: [https://solscan.io/tx/2y...?cluster=devnet](https://solscan.io/tx/2y...?cluster=devnet) (or similar, base URL from gill)
133
+ ```
134
+
135
+ -----
43
136
 
44
137
  ## 🔧 Architecture
45
138
 
46
- The package is structured around these core components:
139
+ `@tuwaio/orbit-solana` serves as the **adapter implementation** for `OrbitAdapter.SOLANA`, integrating Solana-specific functionalities into the Orbit Utils framework.
140
+
141
+ ### Core Modules & Exports (`index.ts`)
142
+
143
+ - **Types (`types.ts`)**: Defines Solana-specific types like `SolanaRPCUrls`.
144
+ - **Cluster Helpers (`clusterHelpers.ts`)**: Functions `getCluster` and `getRpcUrlForCluster` for managing Solana network identifiers and RPC endpoints.
145
+ - **Client Creation (`createSolanaClientWithCache.ts`, `createSolanaRPC.ts`)**: Provides cached instances of `gill`'s `SolanaClient` and `Rpc`. Includes default RPC URLs.
146
+ - **Wallet Interaction (`getAvailableSolanaWallets.ts`, `getConnectedSolanaWallet.ts`)**: Leverages `@wallet-standard` to find and identify Solana wallets.
147
+ - **Account Info (`getSolanaAddressAvatar.ts`, `getSolanaAddressName.ts`)**: Retrieves metadata (label, icon) associated with accounts within the connected wallet.
148
+ - **Explorer Links (`getSolanaExplorerLink.ts`)**: Utility for constructing explorer URLs.
47
149
 
48
150
  ### Build System
49
- - Built with `tsup` for optimal bundling
50
- - Outputs both CommonJS and ESM formats
51
- - Generates TypeScript declarations
52
151
 
53
- ### Core Modules
54
- - **RPC Configuration:** Solana network connection utilities
55
- ---
152
+ - Built using `tsup`.
153
+ - Outputs CommonJS (`cjs`) and ECMAScript Module (`esm`) formats.
154
+ - Generates TypeScript declaration files (`.d.ts`).
155
+ - Specifies external dependencies (`@tuwaio/orbit-core`, `gill`, `@wallet-standard/*`) to avoid bundling them.
156
+
157
+ -----
158
+
159
+ ## ✨ How It Connects to the Ecosystem
160
+
161
+ - **Depends on `@tuwaio/orbit-core`:** Relies on core types (`OrbitAdapter`, `BaseAdapter`) and utilities (`lastConnectedWalletHelpers`, `filterUniqueByKey`).
162
+ - **Provides Solana Functionality:** Implements the specific logic for Solana interactions needed by applications using Orbit Utils.
163
+ - **Leverages Gill & Wallet Standard:** Uses `gill` for simplified RPC communication and the Wallet Standard packages for wallet detection and interaction.
164
+
165
+ -----
56
166
 
57
167
  ## 🤝 Contributing & Support
58
168
 
@@ -64,8 +174,4 @@ If you find this library useful, please consider supporting its development. Eve
64
174
 
65
175
  ## 📄 License
66
176
 
67
- This project is licensed under the **Apache-2.0 License** - see the [LICENSE](./LICENSE) file for details.
68
-
69
- ## 👥 Contributors
70
-
71
- - **Oleksandr Tkach** - [GitHub](https://github.com/Argeare5)
177
+ This project is licensed under the **Apache-2.0 License** - see the [LICENSE](./LICENSE) file for details.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { SolanaClusterMoniker, Rpc, SolanaRpcApi } from 'gill';
1
+ import { SolanaClusterMoniker, SolanaClient, Rpc, SolanaRpcApi } from 'gill';
2
+ import * as _wallet_standard_ui_core from '@wallet-standard/ui-core';
2
3
 
3
4
  type SolanaRPCUrls = {
4
5
  rpcUrls: Partial<Record<SolanaClusterMoniker, string>>;
@@ -25,6 +26,41 @@ declare const getRpcUrlForCluster: ({ cluster, walletCluster, rpcUrls, }: {
25
26
  walletCluster?: SolanaClusterMoniker;
26
27
  } & SolanaRPCUrls) => string;
27
28
 
29
+ /**
30
+ * RPC Client Caching Module
31
+ *
32
+ * This module provides a caching mechanism for Solana RPC clients to optimize
33
+ * performance and resource usage by reusing existing client instances.
34
+ *
35
+ * @module RpcClientCache
36
+ */
37
+
38
+ /**
39
+ * Creates or retrieves a cached Solana RPC client instance
40
+ *
41
+ * This function implements a caching mechanism for Solana RPC clients to:
42
+ * - Avoid redundant client instance creation
43
+ * - Optimize memory usage
44
+ * - Maintain consistent client instances throughout the application
45
+ *
46
+ * @param rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')
47
+ * @returns Cached or newly created Solana RPC client instance
48
+ * @throws Error if unable to resolve a valid RPC URL
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * // Using cluster moniker
53
+ * const mainnetClient = createSolanaClientWithCache('mainnet');
54
+ *
55
+ * // Using custom RPC URL
56
+ * const customClient = createSolanaClientWithCache('https://my-rpc.example.com');
57
+ * ```
58
+ */
59
+ declare const createSolanaClientWithCache: ({ rpcUrlOrMoniker, rpcUrls, }: {
60
+ rpcUrlOrMoniker: string;
61
+ rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
62
+ }) => SolanaClient;
63
+
28
64
  /**
29
65
  * Retrieves a cached RPC client for a given URL or cluster moniker.
30
66
  * If no cached client exists, it creates a new instance.
@@ -33,19 +69,50 @@ declare const getRpcUrlForCluster: ({ cluster, walletCluster, rpcUrls, }: {
33
69
  * @returns The RPC client instance.
34
70
  * @internal
35
71
  */
36
- declare const createSolanaRPC: (rpcUrlOrMoniker: string) => Rpc<SolanaRpcApi>;
72
+ declare const createSolanaRPC: ({ rpcUrlOrMoniker, rpcUrls, }: {
73
+ rpcUrlOrMoniker: string;
74
+ rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
75
+ }) => Rpc<SolanaRpcApi>;
37
76
 
38
77
  /**
39
- * @file This file contains a utility function for generating Solana transaction explorer links.
78
+ * The default RPC URLs for each Solana cluster.
79
+ * Not all clusters need to be defined; undefined ones will fall back to other logic.
80
+ * @internal
40
81
  */
82
+ declare const defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>>;
41
83
 
84
+ declare function getAvailableWallets(): _wallet_standard_ui_core.UiWallet[];
85
+
86
+ declare function getConnectedSolanaWallet(): _wallet_standard_ui_core.UiWallet;
87
+
88
+ /**
89
+ * Searches and returns the avatar URL (icon) for a given Solana account name (label)
90
+ * among connected wallets. Includes caching for performance on repeated requests.
91
+ *
92
+ * @param name The account name (label) to look up.
93
+ * @returns A promise that resolves to the account's icon URL, or the original name string if the icon is not found.
94
+ */
95
+ declare const getSolanaAddressAvatar: (name: string) => Promise<string>;
96
+
97
+ /**
98
+ * Searches and returns the account name (label) for a given Solana address
99
+ * among connected wallets. Includes caching for performance on repeated requests.
100
+ *
101
+ * @param address The Solana account address to look up.
102
+ * @returns A promise that resolves to the account's name/label, or the original address string if the name is not found.
103
+ */
104
+ declare const getSolanaAddressName: (address: string) => Promise<string>;
105
+
106
+ /**
107
+ * @file This file contains a utility function for generating Solana transaction explorer links.
108
+ */
42
109
  /**
43
110
  * Generates a full URL to a transaction on a Solana explorer like Solscan.
44
111
  *
45
112
  * @param {string} url - The url after baseUrl.
46
- * @param {SolanaCluster} [cluster] - The optional cluster name ('devnet', 'testnet') to add as a query parameter.
113
+ * @param chainId
47
114
  * @returns {string} The full URL to the transaction on the explorer.
48
115
  */
49
- declare const getSolanaExplorerLink: (url?: string, cluster?: SolanaClusterMoniker) => string;
116
+ declare const getSolanaExplorerLink: (url?: string, chainId?: string | number | undefined) => string;
50
117
 
51
- export { type SolanaRPCUrls, createSolanaRPC, getCluster, getRpcUrlForCluster, getSolanaExplorerLink };
118
+ export { type SolanaRPCUrls, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableWallets, getCluster, getConnectedSolanaWallet, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaExplorerLink };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { SolanaClusterMoniker, Rpc, SolanaRpcApi } from 'gill';
1
+ import { SolanaClusterMoniker, SolanaClient, Rpc, SolanaRpcApi } from 'gill';
2
+ import * as _wallet_standard_ui_core from '@wallet-standard/ui-core';
2
3
 
3
4
  type SolanaRPCUrls = {
4
5
  rpcUrls: Partial<Record<SolanaClusterMoniker, string>>;
@@ -25,6 +26,41 @@ declare const getRpcUrlForCluster: ({ cluster, walletCluster, rpcUrls, }: {
25
26
  walletCluster?: SolanaClusterMoniker;
26
27
  } & SolanaRPCUrls) => string;
27
28
 
29
+ /**
30
+ * RPC Client Caching Module
31
+ *
32
+ * This module provides a caching mechanism for Solana RPC clients to optimize
33
+ * performance and resource usage by reusing existing client instances.
34
+ *
35
+ * @module RpcClientCache
36
+ */
37
+
38
+ /**
39
+ * Creates or retrieves a cached Solana RPC client instance
40
+ *
41
+ * This function implements a caching mechanism for Solana RPC clients to:
42
+ * - Avoid redundant client instance creation
43
+ * - Optimize memory usage
44
+ * - Maintain consistent client instances throughout the application
45
+ *
46
+ * @param rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')
47
+ * @returns Cached or newly created Solana RPC client instance
48
+ * @throws Error if unable to resolve a valid RPC URL
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * // Using cluster moniker
53
+ * const mainnetClient = createSolanaClientWithCache('mainnet');
54
+ *
55
+ * // Using custom RPC URL
56
+ * const customClient = createSolanaClientWithCache('https://my-rpc.example.com');
57
+ * ```
58
+ */
59
+ declare const createSolanaClientWithCache: ({ rpcUrlOrMoniker, rpcUrls, }: {
60
+ rpcUrlOrMoniker: string;
61
+ rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
62
+ }) => SolanaClient;
63
+
28
64
  /**
29
65
  * Retrieves a cached RPC client for a given URL or cluster moniker.
30
66
  * If no cached client exists, it creates a new instance.
@@ -33,19 +69,50 @@ declare const getRpcUrlForCluster: ({ cluster, walletCluster, rpcUrls, }: {
33
69
  * @returns The RPC client instance.
34
70
  * @internal
35
71
  */
36
- declare const createSolanaRPC: (rpcUrlOrMoniker: string) => Rpc<SolanaRpcApi>;
72
+ declare const createSolanaRPC: ({ rpcUrlOrMoniker, rpcUrls, }: {
73
+ rpcUrlOrMoniker: string;
74
+ rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
75
+ }) => Rpc<SolanaRpcApi>;
37
76
 
38
77
  /**
39
- * @file This file contains a utility function for generating Solana transaction explorer links.
78
+ * The default RPC URLs for each Solana cluster.
79
+ * Not all clusters need to be defined; undefined ones will fall back to other logic.
80
+ * @internal
40
81
  */
82
+ declare const defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>>;
41
83
 
84
+ declare function getAvailableWallets(): _wallet_standard_ui_core.UiWallet[];
85
+
86
+ declare function getConnectedSolanaWallet(): _wallet_standard_ui_core.UiWallet;
87
+
88
+ /**
89
+ * Searches and returns the avatar URL (icon) for a given Solana account name (label)
90
+ * among connected wallets. Includes caching for performance on repeated requests.
91
+ *
92
+ * @param name The account name (label) to look up.
93
+ * @returns A promise that resolves to the account's icon URL, or the original name string if the icon is not found.
94
+ */
95
+ declare const getSolanaAddressAvatar: (name: string) => Promise<string>;
96
+
97
+ /**
98
+ * Searches and returns the account name (label) for a given Solana address
99
+ * among connected wallets. Includes caching for performance on repeated requests.
100
+ *
101
+ * @param address The Solana account address to look up.
102
+ * @returns A promise that resolves to the account's name/label, or the original address string if the name is not found.
103
+ */
104
+ declare const getSolanaAddressName: (address: string) => Promise<string>;
105
+
106
+ /**
107
+ * @file This file contains a utility function for generating Solana transaction explorer links.
108
+ */
42
109
  /**
43
110
  * Generates a full URL to a transaction on a Solana explorer like Solscan.
44
111
  *
45
112
  * @param {string} url - The url after baseUrl.
46
- * @param {SolanaCluster} [cluster] - The optional cluster name ('devnet', 'testnet') to add as a query parameter.
113
+ * @param chainId
47
114
  * @returns {string} The full URL to the transaction on the explorer.
48
115
  */
49
- declare const getSolanaExplorerLink: (url?: string, cluster?: SolanaClusterMoniker) => string;
116
+ declare const getSolanaExplorerLink: (url?: string, chainId?: string | number | undefined) => string;
50
117
 
51
- export { type SolanaRPCUrls, createSolanaRPC, getCluster, getRpcUrlForCluster, getSolanaExplorerLink };
118
+ export { type SolanaRPCUrls, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableWallets, getCluster, getConnectedSolanaWallet, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaExplorerLink };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var gill=require('gill');var c=({cluster:t,walletCluster:n})=>{let e="mainnet";return t?t.includes(":")?t.split(":")[1]:t:n??e},u=({cluster:t,walletCluster:n,rpcUrls:e})=>e[n??t]??"https://api.mainnet-beta.solana.com/";function l(t){try{return new URL(t),!0}catch{return false}}var i={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"},r=new Map,g=t=>{if(r.has(t))return r.get(t);let n=l(t)?t:i[t];if(!n)throw new Error(`Unable to resolve RPC URL for input: "${t}". Ensure it's a valid URL or known moniker.`);let e=gill.createSolanaRpc(n);return r.set(t,e),e};var h=(t,n)=>{let e=gill.getExplorerLink(),a=e.endsWith("/")?e.slice(0,-1):e,o=n?`?cluster=${n}`:"";return `${a}${t||"/"}${o}`};
2
- exports.createSolanaRPC=g;exports.getCluster=c;exports.getRpcUrlForCluster=u;exports.getSolanaExplorerLink=h;//# sourceMappingURL=index.js.map
1
+ 'use strict';var gill=require('gill'),orbitCore=require('@tuwaio/orbit-core'),app=require('@wallet-standard/app'),uiRegistry=require('@wallet-standard/ui-registry');var u=({cluster:e,walletCluster:t})=>{let n="mainnet";return e?e.includes(":")?e.split(":")[1]:e:t??n},A=({cluster:e,walletCluster:t,rpcUrls:n})=>n[t??e]??"https://api.mainnet-beta.solana.com/";var o={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function g(e){try{return new URL(e),!0}catch{return false}}var i=new Map,b=({rpcUrlOrMoniker:e,rpcUrls:t})=>{if(i.has(e))return i.get(e);let n=g(e)?e:t?t[e]??o[e]:o[e];if(!n)throw new Error(`Unable to resolve RPC URL for input: "${e}". Ensure it's a valid URL or known moniker.`);let r=gill.createSolanaClient({urlOrMoniker:n});return i.set(e,r),r};function h(e){try{return new URL(e),!0}catch{return false}}var c=new Map,q=({rpcUrlOrMoniker:e,rpcUrls:t})=>{if(c.has(e))return c.get(e);let n=h(e)?e:t?t[e]??o[e]:o[e];if(!n)throw new Error(`Unable to resolve RPC URL for input: "${e}". Ensure it's a valid URL or known moniker.`);let r=gill.createSolanaRpc(n);return c.set(e,r),r};function d(){return orbitCore.filterUniqueByKey(app.getWallets().get().map(uiRegistry.getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED),"name").filter(e=>{try{return !e||!Array.isArray(e.chains)||!e.features||!Array.isArray(e.features)||!["standard:connect","standard:disconnect","standard:events","solana:signAndSendTransaction","solana:signTransaction","solana:signMessage"].every(r=>e.features.includes(r))?!1:e.chains.every(r=>{try{if(typeof r!="string")return !1;let a=r.split(":");return a.length>=1&&a[0]==="solana"}catch(a){return console.warn("Error parsing chain:",r,a),!1}})}catch(t){return console.warn("Error filtering wallet:",t),false}})}function l(){let e=orbitCore.lastConnectedWalletHelpers.getLastConnectedWallet(),n=d().find(r=>r.accounts.find(a=>a.address.toLowerCase()===e?.address?.toLowerCase()));if(!n)throw new Error("Wallet not provided. Cannot perform chain check.");return n}var m=new Map,J=async e=>{let t=e.toLowerCase(),n=m.get(t);if(n!==void 0)return n;let a=l()?.accounts.find(s=>s.address.toLowerCase()===orbitCore.lastConnectedWalletHelpers.getLastConnectedWallet()?.address)?.icon??e??orbitCore.lastConnectedWalletHelpers.getLastConnectedWallet()?.address;return m.set(t,a),a};var p=new Map,Z=async e=>{let t=e.toLowerCase(),n=p.get(t);if(n!==void 0)return n;let a=l().accounts.find(s=>s.address.toLowerCase()===t)?.label??e;return p.set(t,a),a};var ne=(e,t)=>{let n=u({cluster:String(t)})??"mainnet",r=gill.getExplorerLink(),a=r.endsWith("/")?r.slice(0,-1):r,s=n?`?cluster=${n}`:"";return `${a}${e||"/"}${s}`};
2
+ exports.createSolanaClientWithCache=b;exports.createSolanaRPC=q;exports.defaultRpcUrlsByMoniker=o;exports.getAvailableWallets=d;exports.getCluster=u;exports.getConnectedSolanaWallet=l;exports.getRpcUrlForCluster=A;exports.getSolanaAddressAvatar=J;exports.getSolanaAddressName=Z;exports.getSolanaExplorerLink=ne;//# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/clusterHelpers.ts","../src/utils/createSolanaRPC.ts","../src/utils/getSolanaExplorerLink.ts"],"names":["getCluster","cluster","walletCluster","defaultCluster","getRpcUrlForCluster","rpcUrls","isValidUrl","str","defaultRpcUrlsByMoniker","rpcCache","createSolanaRPC","rpcUrlOrMoniker","rpcUrl","newRpc","createSolanaRpc","getSolanaExplorerLink","url","baseUrl","getExplorerLink","sanitizedBaseUrl","clusterParam"],"mappings":"sCAWO,IAAMA,EAAa,CAAC,CAAE,OAAA,CAAAC,CAAAA,CAAS,cAAAC,CAAc,CAAA,GAAoD,CACtG,IAAMC,EAAuC,SAAA,CAC7C,OAAKF,CAAAA,CAGGA,CAAAA,CAAQ,SAAS,GAAG,CAAA,CAAIA,CAAAA,CAAQ,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,CAAIA,CAAAA,CAF/CC,GAAiBC,CAG5B,CAAA,CAOaC,CAAAA,CAAsB,CAAC,CAClC,OAAA,CAAAH,CAAAA,CACA,cAAAC,CAAAA,CACA,OAAA,CAAAG,CACF,CAAA,GAESA,CAAAA,CADeH,CAAAA,EAAiBD,CACX,GAAK,uCCrBnC,SAASK,CAAAA,CAAWC,CAAAA,CAAsB,CACxC,GAAI,CACF,OAAA,IAAI,GAAA,CAAIA,CAAG,EACJ,CAAA,CACT,CAAA,KAAQ,CACN,OAAO,MACT,CACF,CAOA,IAAMC,CAAAA,CAAyE,CAC7E,OAAA,CAAS,qCAAA,CACT,MAAA,CAAQ,+BAAA,CACR,QAAS,gCACX,CAAA,CAMMC,EAAW,IAAI,GAAA,CAURC,EAAmBC,CAAAA,EAA+C,CAE7E,GAAIF,CAAAA,CAAS,IAAIE,CAAe,CAAA,CAC9B,OAAOF,CAAAA,CAAS,IAAIE,CAAe,CAAA,CAGrC,IAAMC,CAAAA,CAASN,EAAWK,CAAe,CAAA,CACrCA,CAAAA,CACAH,CAAAA,CAAwBG,CAAuC,CAAA,CAGnE,GAAI,CAACC,CAAAA,CACH,MAAM,IAAI,KAAA,CACR,CAAA,sCAAA,EAAyCD,CAAe,8CAC1D,CAAA,CAIF,IAAME,CAAAA,CAASC,oBAAAA,CAAgBF,CAAM,CAAA,CAGrC,OAAAH,EAAS,GAAA,CAAIE,CAAAA,CAAiBE,CAAM,CAAA,CAC7BA,CACT,ECpDO,IAAME,CAAAA,CAAwB,CAACC,CAAAA,CAAcf,CAAAA,GAA2C,CAE7F,IAAMgB,EAAUC,oBAAAA,EAAgB,CAC1BC,EAAmBF,CAAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,CAAIA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,EAAE,CAAA,CAAIA,CAAAA,CAGlEG,CAAAA,CAAenB,CAAAA,CAAU,YAAYA,CAAO,CAAA,CAAA,CAAK,EAAA,CAEvD,OAAO,GAAGkB,CAAgB,CAAA,EAAGH,GAAY,GAAG,CAAA,EAAGI,CAAY,CAAA,CAC7D","file":"index.js","sourcesContent":["import type { SolanaClusterMoniker } from 'gill';\n\nimport { SolanaRPCUrls } from '../types';\n\n/**\n * Safely extracts the cluster moniker from a chain identifier.\n * Handles both full chain IDs ('solana:mainnet-beta') and simple monikers ('mainnet-beta').\n * @returns The extracted cluster moniker.\n * @param walletCluster\n * @param cluster\n */\nexport const getCluster = ({ cluster, walletCluster }: { cluster?: string; walletCluster?: string }) => {\n const defaultCluster: SolanaClusterMoniker = 'mainnet';\n if (!cluster) {\n return walletCluster ?? defaultCluster;\n }\n return (cluster.includes(':') ? cluster.split(':')[1] : cluster) as SolanaClusterMoniker;\n};\n\n/**\n * Retrieves the configured RPC URL for a given cluster moniker.\n * @param cluster The target cluster. Defaults to the wallet's active chain.\n * @returns The RPC URL or undefined if not found.\n */\nexport const getRpcUrlForCluster = ({\n cluster,\n walletCluster,\n rpcUrls,\n}: { cluster: SolanaClusterMoniker; walletCluster?: SolanaClusterMoniker } & SolanaRPCUrls) => {\n const targetCluster = walletCluster ?? cluster;\n return rpcUrls[targetCluster] ?? 'https://api.mainnet-beta.solana.com/';\n};\n","// --- RPC Client Caching ---\n\nimport { createSolanaRpc, Rpc, SolanaClusterMoniker, SolanaRpcApi } from 'gill';\n\n/**\n * Validates whether a string is a properly formatted URL.\n * @param str - The string to validate.\n * @returns True if the string is a valid URL, otherwise false.\n */\nfunction isValidUrl(str: string): boolean {\n try {\n new URL(str);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * The default RPC URLs for each Solana cluster.\n * Not all clusters need to be defined; undefined ones will fall back to other logic.\n * @internal\n */\nconst defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>> = {\n mainnet: 'https://api.mainnet-beta.solana.com',\n devnet: 'https://api.devnet.solana.com',\n testnet: 'https://api.testnet.solana.com',\n};\n\n/**\n * An in-memory cache for RPC clients to avoid redundant instance creation.\n * @internal\n */\nconst rpcCache = new Map<string, Rpc<SolanaRpcApi>>();\n\n/**\n * Retrieves a cached RPC client for a given URL or cluster moniker.\n * If no cached client exists, it creates a new instance.\n *\n * @param rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.\n * @returns The RPC client instance.\n * @internal\n */\nexport const createSolanaRPC = (rpcUrlOrMoniker: string): Rpc<SolanaRpcApi> => {\n // Check the cache first for an existing RPC instance.\n if (rpcCache.has(rpcUrlOrMoniker)) {\n return rpcCache.get(rpcUrlOrMoniker)!;\n }\n // Determine the RPC URL: validate if it's a full URL or fall back to default list.\n const rpcUrl = isValidUrl(rpcUrlOrMoniker)\n ? rpcUrlOrMoniker\n : defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker];\n\n // If no valid RPC URL could be resolved, default to the mainnet URL.\n if (!rpcUrl) {\n throw new Error(\n `Unable to resolve RPC URL for input: \"${rpcUrlOrMoniker}\". Ensure it's a valid URL or known moniker.`,\n );\n }\n\n // Create a new RPC client instance.\n const newRpc = createSolanaRpc(rpcUrl);\n\n // Cache the new instance and return it.\n rpcCache.set(rpcUrlOrMoniker, newRpc);\n return newRpc;\n};\n","/**\n * @file This file contains a utility function for generating Solana transaction explorer links.\n */\n\nimport type { SolanaClusterMoniker } from 'gill';\nimport { getExplorerLink } from 'gill';\n\n/**\n * Generates a full URL to a transaction on a Solana explorer like Solscan.\n *\n * @param {string} url - The url after baseUrl.\n * @param {SolanaCluster} [cluster] - The optional cluster name ('devnet', 'testnet') to add as a query parameter.\n * @returns {string} The full URL to the transaction on the explorer.\n */\nexport const getSolanaExplorerLink = (url?: string, cluster?: SolanaClusterMoniker): string => {\n // Ensure there are no trailing slashes on the base URL for clean URL construction.\n const baseUrl = getExplorerLink();\n const sanitizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n\n // Build the cluster query parameter if provided.\n const clusterParam = cluster ? `?cluster=${cluster}` : '';\n\n return `${sanitizedBaseUrl}${url ? url : '/'}${clusterParam}`;\n};\n"]}
1
+ {"version":3,"sources":["../src/utils/clusterHelpers.ts","../src/utils/defaultRpcUrlsByMoniker.ts","../src/utils/createSolanaClientWithCache.ts","../src/utils/createSolanaRPC.ts","../src/utils/getAvailableSolanaWallets.ts","../src/utils/getConnectedSolanaWallet.ts","../src/utils/getSolanaAddressAvatar.ts","../src/utils/getSolanaAddressName.ts","../src/utils/getSolanaExplorerLink.ts"],"names":["getCluster","cluster","walletCluster","defaultCluster","getRpcUrlForCluster","rpcUrls","defaultRpcUrlsByMoniker","isValidUrl","str","clientsCache","createSolanaClientWithCache","rpcUrlOrMoniker","rpcUrl","newClient","createSolanaClient","rpcCache","createSolanaRPC","newRpc","createSolanaRpc","getAvailableWallets","filterUniqueByKey","getWallets","getOrCreateUiWalletForStandardWallet","wallet","requiredFeature","chain","chainParts","error","getConnectedSolanaWallet","lastConnectedWallet","lastConnectedWalletHelpers","connectedWallet","w","solanaAvatarCache","getSolanaAddressAvatar","name","normalizedName","cachedAvatar","resultAvatar","account","solanaNameCache","getSolanaAddressName","address","normalizedAddress","cachedName","resultName","a","getSolanaExplorerLink","url","chainId","baseUrl","getExplorerLink","sanitizedBaseUrl","clusterParam"],"mappings":"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","file":"index.js","sourcesContent":["import type { SolanaClusterMoniker } from 'gill';\n\nimport { SolanaRPCUrls } from '../types';\n\n/**\n * Safely extracts the cluster moniker from a chain identifier.\n * Handles both full chain IDs ('solana:mainnet-beta') and simple monikers ('mainnet-beta').\n * @returns The extracted cluster moniker.\n * @param walletCluster\n * @param cluster\n */\nexport const getCluster = ({ cluster, walletCluster }: { cluster?: string; walletCluster?: string }) => {\n const defaultCluster: SolanaClusterMoniker = 'mainnet';\n if (!cluster) {\n return walletCluster ?? defaultCluster;\n }\n return (cluster.includes(':') ? cluster.split(':')[1] : cluster) as SolanaClusterMoniker;\n};\n\n/**\n * Retrieves the configured RPC URL for a given cluster moniker.\n * @param cluster The target cluster. Defaults to the wallet's active chain.\n * @returns The RPC URL or undefined if not found.\n */\nexport const getRpcUrlForCluster = ({\n cluster,\n walletCluster,\n rpcUrls,\n}: { cluster: SolanaClusterMoniker; walletCluster?: SolanaClusterMoniker } & SolanaRPCUrls) => {\n const targetCluster = walletCluster ?? cluster;\n return rpcUrls[targetCluster] ?? 'https://api.mainnet-beta.solana.com/';\n};\n","import { SolanaClusterMoniker } from 'gill';\n\n/**\n * The default RPC URLs for each Solana cluster.\n * Not all clusters need to be defined; undefined ones will fall back to other logic.\n * @internal\n */\nexport const defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>> = {\n mainnet: 'https://api.mainnet-beta.solana.com',\n devnet: 'https://api.devnet.solana.com',\n testnet: 'https://api.testnet.solana.com',\n};\n","/**\n * RPC Client Caching Module\n *\n * This module provides a caching mechanism for Solana RPC clients to optimize\n * performance and resource usage by reusing existing client instances.\n *\n * @module RpcClientCache\n */\n\nimport { createSolanaClient, SolanaClient, SolanaClusterMoniker } from 'gill';\n\nimport { defaultRpcUrlsByMoniker } from './defaultRpcUrlsByMoniker';\n\n/**\n * Validates if a string represents a properly formatted URL\n *\n * @param str - String to validate as URL\n * @returns Boolean indicating if the string is a valid URL\n *\n * @example\n * ```typescript\n * isValidUrl('https://api.mainnet-beta.solana.com') // returns true\n * isValidUrl('not-a-url') // returns false\n * ```\n */\nfunction isValidUrl(str: string): boolean {\n try {\n new URL(str);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * In-memory cache storage for Solana client instances\n * Maps Solana URLs or monikers to their corresponding client instances\n *\n * @internal\n */\nconst clientsCache = new Map<string, SolanaClient>();\n\n/**\n * Creates or retrieves a cached Solana RPC client instance\n *\n * This function implements a caching mechanism for Solana RPC clients to:\n * - Avoid redundant client instance creation\n * - Optimize memory usage\n * - Maintain consistent client instances throughout the application\n *\n * @param rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')\n * @returns Cached or newly created Solana RPC client instance\n * @throws Error if unable to resolve a valid RPC URL\n *\n * @example\n * ```typescript\n * // Using cluster moniker\n * const mainnetClient = createSolanaClientWithCache('mainnet');\n *\n * // Using custom RPC URL\n * const customClient = createSolanaClientWithCache('https://my-rpc.example.com');\n * ```\n */\nexport const createSolanaClientWithCache = ({\n rpcUrlOrMoniker,\n rpcUrls,\n}: {\n rpcUrlOrMoniker: string;\n rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;\n}): SolanaClient => {\n // Return existing client instance if available in cache\n if (clientsCache.has(rpcUrlOrMoniker)) {\n return clientsCache.get(rpcUrlOrMoniker)!;\n }\n\n // Resolve RPC URL from input: direct URL or cluster moniker\n const rpcUrl = isValidUrl(rpcUrlOrMoniker)\n ? rpcUrlOrMoniker\n : rpcUrls\n ? (rpcUrls[rpcUrlOrMoniker as SolanaClusterMoniker] ??\n defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker])\n : defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker];\n\n // Validate resolved RPC URL\n if (!rpcUrl) {\n throw new Error(\n `Unable to resolve RPC URL for input: \"${rpcUrlOrMoniker}\". Ensure it's a valid URL or known moniker.`,\n );\n }\n\n // Create new client instance with resolved URL\n const newClient = createSolanaClient({ urlOrMoniker: rpcUrl });\n\n // Cache and return the new instance\n clientsCache.set(rpcUrlOrMoniker, newClient);\n return newClient;\n};\n","// --- RPC Client Caching ---\n\nimport { createSolanaRpc, Rpc, SolanaClusterMoniker, SolanaRpcApi } from 'gill';\n\nimport { defaultRpcUrlsByMoniker } from './defaultRpcUrlsByMoniker';\n\n/**\n * Validates whether a string is a properly formatted URL.\n * @param str - The string to validate.\n * @returns True if the string is a valid URL, otherwise false.\n */\nfunction isValidUrl(str: string): boolean {\n try {\n new URL(str);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * An in-memory cache for RPC clients to avoid redundant instance creation.\n * @internal\n */\nconst rpcCache = new Map<string, Rpc<SolanaRpcApi>>();\n\n/**\n * Retrieves a cached RPC client for a given URL or cluster moniker.\n * If no cached client exists, it creates a new instance.\n *\n * @param rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.\n * @returns The RPC client instance.\n * @internal\n */\nexport const createSolanaRPC = ({\n rpcUrlOrMoniker,\n rpcUrls,\n}: {\n rpcUrlOrMoniker: string;\n rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;\n}): Rpc<SolanaRpcApi> => {\n // Check the cache first for an existing RPC instance.\n if (rpcCache.has(rpcUrlOrMoniker)) {\n return rpcCache.get(rpcUrlOrMoniker)!;\n }\n // Determine the RPC URL: validate if it's a full URL or fall back to default list.\n const rpcUrl = isValidUrl(rpcUrlOrMoniker)\n ? rpcUrlOrMoniker\n : rpcUrls\n ? (rpcUrls[rpcUrlOrMoniker as SolanaClusterMoniker] ??\n defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker])\n : defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker];\n\n // If no valid RPC URL could be resolved, default to the mainnet URL.\n if (!rpcUrl) {\n throw new Error(\n `Unable to resolve RPC URL for input: \"${rpcUrlOrMoniker}\". Ensure it's a valid URL or known moniker.`,\n );\n }\n\n // Create a new RPC client instance.\n const newRpc = createSolanaRpc(rpcUrl);\n\n // Cache the new instance and return it.\n rpcCache.set(rpcUrlOrMoniker, newRpc);\n return newRpc;\n};\n","import { filterUniqueByKey } from '@tuwaio/orbit-core';\nimport { getWallets } from '@wallet-standard/app';\nimport { getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED as getOrCreateUiWalletForStandardWallet } from '@wallet-standard/ui-registry';\n\nexport function getAvailableWallets() {\n return filterUniqueByKey(getWallets().get().map(getOrCreateUiWalletForStandardWallet), 'name').filter((wallet) => {\n try {\n // Check if wallet has chains property\n if (!wallet || !Array.isArray(wallet.chains)) {\n return false;\n }\n\n // Check if wallet has features property (features should be an array)\n if (!wallet.features || !Array.isArray(wallet.features)) {\n return false;\n }\n\n // Define required features for Solana wallet functionality\n const requiredFeatures = [\n 'standard:connect',\n 'standard:disconnect',\n 'standard:events',\n 'solana:signAndSendTransaction',\n 'solana:signTransaction',\n 'solana:signMessage',\n ] as const;\n\n // Check if wallet supports all required features\n const hasAllFeatures = requiredFeatures.every((requiredFeature) => {\n return wallet.features.includes(requiredFeature);\n });\n\n if (!hasAllFeatures) {\n return false;\n }\n\n // Check if all chains are Solana chains\n return wallet.chains.every((chain) => {\n try {\n if (typeof chain !== 'string') {\n return false;\n }\n const chainParts = chain.split(':');\n return chainParts.length >= 1 && chainParts[0] === 'solana';\n } catch (error) {\n console.warn('Error parsing chain:', chain, error);\n return false;\n }\n });\n } catch (error) {\n console.warn('Error filtering wallet:', error);\n return false;\n }\n });\n}\n","import { lastConnectedWalletHelpers } from '@tuwaio/orbit-core';\n\nimport { getAvailableWallets } from './getAvailableSolanaWallets';\n\nexport function getConnectedSolanaWallet() {\n const lastConnectedWallet = lastConnectedWalletHelpers.getLastConnectedWallet();\n const wallets = getAvailableWallets();\n const connectedWallet = wallets.find((w) =>\n w.accounts.find((a) => a.address.toLowerCase() === lastConnectedWallet?.address?.toLowerCase()),\n );\n if (!connectedWallet) {\n throw new Error('Wallet not provided. Cannot perform chain check.');\n }\n return connectedWallet;\n}\n","import { lastConnectedWalletHelpers } from '@tuwaio/orbit-core';\n\nimport { getConnectedSolanaWallet } from './getConnectedSolanaWallet';\n\n/**\n * Cache for Solana avatar lookup results.\n * Key: normalized name (lowercase string), Value: Avatar URL (string).\n */\nconst solanaAvatarCache = new Map<string, string>();\n\n/**\n * Searches and returns the avatar URL (icon) for a given Solana account name (label)\n * among connected wallets. Includes caching for performance on repeated requests.\n *\n * @param name The account name (label) to look up.\n * @returns A promise that resolves to the account's icon URL, or the original name string if the icon is not found.\n */\nexport const getSolanaAddressAvatar = async (name: string): Promise<string> => {\n // Normalize the name to use as a cache key, ensuring case-insensitivity.\n const normalizedName = name.toLowerCase();\n\n // Check the cache: if the result exists, return it immediately.\n const cachedAvatar = solanaAvatarCache.get(normalizedName);\n if (cachedAvatar !== undefined) {\n return cachedAvatar;\n }\n // Find the first wallet that contains an account with a matching label.\n const connectedWallet = getConnectedSolanaWallet();\n\n // Retrieve the icon URL for the specific matching account.\n // If no matching account is found, fall back to the original name string.\n const resultAvatar =\n connectedWallet?.accounts.find(\n (account) => account.address.toLowerCase() === lastConnectedWalletHelpers.getLastConnectedWallet()?.address,\n )?.icon ??\n name ??\n lastConnectedWalletHelpers.getLastConnectedWallet()?.address;\n\n // Store the result (including the fallback name if icon is null) in the cache.\n solanaAvatarCache.set(normalizedName, resultAvatar);\n\n return resultAvatar;\n};\n","import { getConnectedSolanaWallet } from './getConnectedSolanaWallet';\n\n/**\n * Cache for Solana address lookup results.\n * Key: normalized address (lowercase string), Value: Account name/label (string).\n */\nconst solanaNameCache = new Map<string, string>();\n\n/**\n * Searches and returns the account name (label) for a given Solana address\n * among connected wallets. Includes caching for performance on repeated requests.\n *\n * @param address The Solana account address to look up.\n * @returns A promise that resolves to the account's name/label, or the original address string if the name is not found.\n */\nexport const getSolanaAddressName = async (address: string): Promise<string> => {\n // Normalize the address to use as a cache key, ensuring case-insensitivity.\n const normalizedAddress = address.toLowerCase();\n\n // Check the cache: if the result exists, return it immediately.\n const cachedName = solanaNameCache.get(normalizedAddress);\n if (cachedName !== undefined) {\n return cachedName;\n }\n\n const connectedWallet = getConnectedSolanaWallet();\n // The result is the found label, or the original address if no label was found.\n const resultName =\n connectedWallet.accounts.find((a) => a.address.toLowerCase() === normalizedAddress)?.label ?? address;\n // Store the result (including the fallback address string if label is null) in the cache.\n solanaNameCache.set(normalizedAddress, resultName);\n\n return resultName;\n};\n","/**\n * @file This file contains a utility function for generating Solana transaction explorer links.\n */\n\nimport { getExplorerLink } from 'gill';\n\nimport { getCluster } from './clusterHelpers';\n\n/**\n * Generates a full URL to a transaction on a Solana explorer like Solscan.\n *\n * @param {string} url - The url after baseUrl.\n * @param chainId\n * @returns {string} The full URL to the transaction on the explorer.\n */\nexport const getSolanaExplorerLink = (url?: string, chainId?: string | number | undefined): string => {\n const cluster = getCluster({ cluster: String(chainId) }) ?? 'mainnet';\n // Ensure there are no trailing slashes on the base URL for clean URL construction.\n const baseUrl = getExplorerLink();\n const sanitizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n // Build the cluster query parameter if provided.\n const clusterParam = cluster ? `?cluster=${cluster}` : '';\n\n return `${sanitizedBaseUrl}${url ? url : '/'}${clusterParam}`;\n};\n"]}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import {createSolanaRpc,getExplorerLink}from'gill';var c=({cluster:t,walletCluster:n})=>{let e="mainnet";return t?t.includes(":")?t.split(":")[1]:t:n??e},u=({cluster:t,walletCluster:n,rpcUrls:e})=>e[n??t]??"https://api.mainnet-beta.solana.com/";function l(t){try{return new URL(t),!0}catch{return false}}var i={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"},r=new Map,g=t=>{if(r.has(t))return r.get(t);let n=l(t)?t:i[t];if(!n)throw new Error(`Unable to resolve RPC URL for input: "${t}". Ensure it's a valid URL or known moniker.`);let e=createSolanaRpc(n);return r.set(t,e),e};var h=(t,n)=>{let e=getExplorerLink(),a=e.endsWith("/")?e.slice(0,-1):e,o=n?`?cluster=${n}`:"";return `${a}${t||"/"}${o}`};
2
- export{g as createSolanaRPC,c as getCluster,u as getRpcUrlForCluster,h as getSolanaExplorerLink};//# sourceMappingURL=index.mjs.map
1
+ import {createSolanaClient,createSolanaRpc,getExplorerLink}from'gill';import {filterUniqueByKey,lastConnectedWalletHelpers}from'@tuwaio/orbit-core';import {getWallets}from'@wallet-standard/app';import {getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED}from'@wallet-standard/ui-registry';var u=({cluster:e,walletCluster:t})=>{let n="mainnet";return e?e.includes(":")?e.split(":")[1]:e:t??n},A=({cluster:e,walletCluster:t,rpcUrls:n})=>n[t??e]??"https://api.mainnet-beta.solana.com/";var o={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function g(e){try{return new URL(e),!0}catch{return false}}var i=new Map,b=({rpcUrlOrMoniker:e,rpcUrls:t})=>{if(i.has(e))return i.get(e);let n=g(e)?e:t?t[e]??o[e]:o[e];if(!n)throw new Error(`Unable to resolve RPC URL for input: "${e}". Ensure it's a valid URL or known moniker.`);let r=createSolanaClient({urlOrMoniker:n});return i.set(e,r),r};function h(e){try{return new URL(e),!0}catch{return false}}var c=new Map,q=({rpcUrlOrMoniker:e,rpcUrls:t})=>{if(c.has(e))return c.get(e);let n=h(e)?e:t?t[e]??o[e]:o[e];if(!n)throw new Error(`Unable to resolve RPC URL for input: "${e}". Ensure it's a valid URL or known moniker.`);let r=createSolanaRpc(n);return c.set(e,r),r};function d(){return filterUniqueByKey(getWallets().get().map(getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED),"name").filter(e=>{try{return !e||!Array.isArray(e.chains)||!e.features||!Array.isArray(e.features)||!["standard:connect","standard:disconnect","standard:events","solana:signAndSendTransaction","solana:signTransaction","solana:signMessage"].every(r=>e.features.includes(r))?!1:e.chains.every(r=>{try{if(typeof r!="string")return !1;let a=r.split(":");return a.length>=1&&a[0]==="solana"}catch(a){return console.warn("Error parsing chain:",r,a),!1}})}catch(t){return console.warn("Error filtering wallet:",t),false}})}function l(){let e=lastConnectedWalletHelpers.getLastConnectedWallet(),n=d().find(r=>r.accounts.find(a=>a.address.toLowerCase()===e?.address?.toLowerCase()));if(!n)throw new Error("Wallet not provided. Cannot perform chain check.");return n}var m=new Map,J=async e=>{let t=e.toLowerCase(),n=m.get(t);if(n!==void 0)return n;let a=l()?.accounts.find(s=>s.address.toLowerCase()===lastConnectedWalletHelpers.getLastConnectedWallet()?.address)?.icon??e??lastConnectedWalletHelpers.getLastConnectedWallet()?.address;return m.set(t,a),a};var p=new Map,Z=async e=>{let t=e.toLowerCase(),n=p.get(t);if(n!==void 0)return n;let a=l().accounts.find(s=>s.address.toLowerCase()===t)?.label??e;return p.set(t,a),a};var ne=(e,t)=>{let n=u({cluster:String(t)})??"mainnet",r=getExplorerLink(),a=r.endsWith("/")?r.slice(0,-1):r,s=n?`?cluster=${n}`:"";return `${a}${e||"/"}${s}`};
2
+ export{b as createSolanaClientWithCache,q as createSolanaRPC,o as defaultRpcUrlsByMoniker,d as getAvailableWallets,u as getCluster,l as getConnectedSolanaWallet,A as getRpcUrlForCluster,J as getSolanaAddressAvatar,Z as getSolanaAddressName,ne as getSolanaExplorerLink};//# sourceMappingURL=index.mjs.map
3
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/clusterHelpers.ts","../src/utils/createSolanaRPC.ts","../src/utils/getSolanaExplorerLink.ts"],"names":["getCluster","cluster","walletCluster","defaultCluster","getRpcUrlForCluster","rpcUrls","isValidUrl","str","defaultRpcUrlsByMoniker","rpcCache","createSolanaRPC","rpcUrlOrMoniker","rpcUrl","newRpc","createSolanaRpc","getSolanaExplorerLink","url","baseUrl","getExplorerLink","sanitizedBaseUrl","clusterParam"],"mappings":"mDAWO,IAAMA,EAAa,CAAC,CAAE,OAAA,CAAAC,CAAAA,CAAS,cAAAC,CAAc,CAAA,GAAoD,CACtG,IAAMC,EAAuC,SAAA,CAC7C,OAAKF,CAAAA,CAGGA,CAAAA,CAAQ,SAAS,GAAG,CAAA,CAAIA,CAAAA,CAAQ,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,CAAIA,CAAAA,CAF/CC,GAAiBC,CAG5B,CAAA,CAOaC,CAAAA,CAAsB,CAAC,CAClC,OAAA,CAAAH,CAAAA,CACA,cAAAC,CAAAA,CACA,OAAA,CAAAG,CACF,CAAA,GAESA,CAAAA,CADeH,CAAAA,EAAiBD,CACX,GAAK,uCCrBnC,SAASK,CAAAA,CAAWC,CAAAA,CAAsB,CACxC,GAAI,CACF,OAAA,IAAI,GAAA,CAAIA,CAAG,EACJ,CAAA,CACT,CAAA,KAAQ,CACN,OAAO,MACT,CACF,CAOA,IAAMC,CAAAA,CAAyE,CAC7E,OAAA,CAAS,qCAAA,CACT,MAAA,CAAQ,+BAAA,CACR,QAAS,gCACX,CAAA,CAMMC,EAAW,IAAI,GAAA,CAURC,EAAmBC,CAAAA,EAA+C,CAE7E,GAAIF,CAAAA,CAAS,IAAIE,CAAe,CAAA,CAC9B,OAAOF,CAAAA,CAAS,IAAIE,CAAe,CAAA,CAGrC,IAAMC,CAAAA,CAASN,EAAWK,CAAe,CAAA,CACrCA,CAAAA,CACAH,CAAAA,CAAwBG,CAAuC,CAAA,CAGnE,GAAI,CAACC,CAAAA,CACH,MAAM,IAAI,KAAA,CACR,CAAA,sCAAA,EAAyCD,CAAe,8CAC1D,CAAA,CAIF,IAAME,CAAAA,CAASC,eAAAA,CAAgBF,CAAM,CAAA,CAGrC,OAAAH,EAAS,GAAA,CAAIE,CAAAA,CAAiBE,CAAM,CAAA,CAC7BA,CACT,ECpDO,IAAME,CAAAA,CAAwB,CAACC,CAAAA,CAAcf,CAAAA,GAA2C,CAE7F,IAAMgB,EAAUC,eAAAA,EAAgB,CAC1BC,EAAmBF,CAAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,CAAIA,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,EAAE,CAAA,CAAIA,CAAAA,CAGlEG,CAAAA,CAAenB,CAAAA,CAAU,YAAYA,CAAO,CAAA,CAAA,CAAK,EAAA,CAEvD,OAAO,GAAGkB,CAAgB,CAAA,EAAGH,GAAY,GAAG,CAAA,EAAGI,CAAY,CAAA,CAC7D","file":"index.mjs","sourcesContent":["import type { SolanaClusterMoniker } from 'gill';\n\nimport { SolanaRPCUrls } from '../types';\n\n/**\n * Safely extracts the cluster moniker from a chain identifier.\n * Handles both full chain IDs ('solana:mainnet-beta') and simple monikers ('mainnet-beta').\n * @returns The extracted cluster moniker.\n * @param walletCluster\n * @param cluster\n */\nexport const getCluster = ({ cluster, walletCluster }: { cluster?: string; walletCluster?: string }) => {\n const defaultCluster: SolanaClusterMoniker = 'mainnet';\n if (!cluster) {\n return walletCluster ?? defaultCluster;\n }\n return (cluster.includes(':') ? cluster.split(':')[1] : cluster) as SolanaClusterMoniker;\n};\n\n/**\n * Retrieves the configured RPC URL for a given cluster moniker.\n * @param cluster The target cluster. Defaults to the wallet's active chain.\n * @returns The RPC URL or undefined if not found.\n */\nexport const getRpcUrlForCluster = ({\n cluster,\n walletCluster,\n rpcUrls,\n}: { cluster: SolanaClusterMoniker; walletCluster?: SolanaClusterMoniker } & SolanaRPCUrls) => {\n const targetCluster = walletCluster ?? cluster;\n return rpcUrls[targetCluster] ?? 'https://api.mainnet-beta.solana.com/';\n};\n","// --- RPC Client Caching ---\n\nimport { createSolanaRpc, Rpc, SolanaClusterMoniker, SolanaRpcApi } from 'gill';\n\n/**\n * Validates whether a string is a properly formatted URL.\n * @param str - The string to validate.\n * @returns True if the string is a valid URL, otherwise false.\n */\nfunction isValidUrl(str: string): boolean {\n try {\n new URL(str);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * The default RPC URLs for each Solana cluster.\n * Not all clusters need to be defined; undefined ones will fall back to other logic.\n * @internal\n */\nconst defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>> = {\n mainnet: 'https://api.mainnet-beta.solana.com',\n devnet: 'https://api.devnet.solana.com',\n testnet: 'https://api.testnet.solana.com',\n};\n\n/**\n * An in-memory cache for RPC clients to avoid redundant instance creation.\n * @internal\n */\nconst rpcCache = new Map<string, Rpc<SolanaRpcApi>>();\n\n/**\n * Retrieves a cached RPC client for a given URL or cluster moniker.\n * If no cached client exists, it creates a new instance.\n *\n * @param rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.\n * @returns The RPC client instance.\n * @internal\n */\nexport const createSolanaRPC = (rpcUrlOrMoniker: string): Rpc<SolanaRpcApi> => {\n // Check the cache first for an existing RPC instance.\n if (rpcCache.has(rpcUrlOrMoniker)) {\n return rpcCache.get(rpcUrlOrMoniker)!;\n }\n // Determine the RPC URL: validate if it's a full URL or fall back to default list.\n const rpcUrl = isValidUrl(rpcUrlOrMoniker)\n ? rpcUrlOrMoniker\n : defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker];\n\n // If no valid RPC URL could be resolved, default to the mainnet URL.\n if (!rpcUrl) {\n throw new Error(\n `Unable to resolve RPC URL for input: \"${rpcUrlOrMoniker}\". Ensure it's a valid URL or known moniker.`,\n );\n }\n\n // Create a new RPC client instance.\n const newRpc = createSolanaRpc(rpcUrl);\n\n // Cache the new instance and return it.\n rpcCache.set(rpcUrlOrMoniker, newRpc);\n return newRpc;\n};\n","/**\n * @file This file contains a utility function for generating Solana transaction explorer links.\n */\n\nimport type { SolanaClusterMoniker } from 'gill';\nimport { getExplorerLink } from 'gill';\n\n/**\n * Generates a full URL to a transaction on a Solana explorer like Solscan.\n *\n * @param {string} url - The url after baseUrl.\n * @param {SolanaCluster} [cluster] - The optional cluster name ('devnet', 'testnet') to add as a query parameter.\n * @returns {string} The full URL to the transaction on the explorer.\n */\nexport const getSolanaExplorerLink = (url?: string, cluster?: SolanaClusterMoniker): string => {\n // Ensure there are no trailing slashes on the base URL for clean URL construction.\n const baseUrl = getExplorerLink();\n const sanitizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n\n // Build the cluster query parameter if provided.\n const clusterParam = cluster ? `?cluster=${cluster}` : '';\n\n return `${sanitizedBaseUrl}${url ? url : '/'}${clusterParam}`;\n};\n"]}
1
+ {"version":3,"sources":["../src/utils/clusterHelpers.ts","../src/utils/defaultRpcUrlsByMoniker.ts","../src/utils/createSolanaClientWithCache.ts","../src/utils/createSolanaRPC.ts","../src/utils/getAvailableSolanaWallets.ts","../src/utils/getConnectedSolanaWallet.ts","../src/utils/getSolanaAddressAvatar.ts","../src/utils/getSolanaAddressName.ts","../src/utils/getSolanaExplorerLink.ts"],"names":["getCluster","cluster","walletCluster","defaultCluster","getRpcUrlForCluster","rpcUrls","defaultRpcUrlsByMoniker","isValidUrl","str","clientsCache","createSolanaClientWithCache","rpcUrlOrMoniker","rpcUrl","newClient","createSolanaClient","rpcCache","createSolanaRPC","newRpc","createSolanaRpc","getAvailableWallets","filterUniqueByKey","getWallets","getOrCreateUiWalletForStandardWallet","wallet","requiredFeature","chain","chainParts","error","getConnectedSolanaWallet","lastConnectedWallet","lastConnectedWalletHelpers","connectedWallet","w","solanaAvatarCache","getSolanaAddressAvatar","name","normalizedName","cachedAvatar","resultAvatar","account","solanaNameCache","getSolanaAddressName","address","normalizedAddress","cachedName","resultName","a","getSolanaExplorerLink","url","chainId","baseUrl","getExplorerLink","sanitizedBaseUrl","clusterParam"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { SolanaClusterMoniker } from 'gill';\n\nimport { SolanaRPCUrls } from '../types';\n\n/**\n * Safely extracts the cluster moniker from a chain identifier.\n * Handles both full chain IDs ('solana:mainnet-beta') and simple monikers ('mainnet-beta').\n * @returns The extracted cluster moniker.\n * @param walletCluster\n * @param cluster\n */\nexport const getCluster = ({ cluster, walletCluster }: { cluster?: string; walletCluster?: string }) => {\n const defaultCluster: SolanaClusterMoniker = 'mainnet';\n if (!cluster) {\n return walletCluster ?? defaultCluster;\n }\n return (cluster.includes(':') ? cluster.split(':')[1] : cluster) as SolanaClusterMoniker;\n};\n\n/**\n * Retrieves the configured RPC URL for a given cluster moniker.\n * @param cluster The target cluster. Defaults to the wallet's active chain.\n * @returns The RPC URL or undefined if not found.\n */\nexport const getRpcUrlForCluster = ({\n cluster,\n walletCluster,\n rpcUrls,\n}: { cluster: SolanaClusterMoniker; walletCluster?: SolanaClusterMoniker } & SolanaRPCUrls) => {\n const targetCluster = walletCluster ?? cluster;\n return rpcUrls[targetCluster] ?? 'https://api.mainnet-beta.solana.com/';\n};\n","import { SolanaClusterMoniker } from 'gill';\n\n/**\n * The default RPC URLs for each Solana cluster.\n * Not all clusters need to be defined; undefined ones will fall back to other logic.\n * @internal\n */\nexport const defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>> = {\n mainnet: 'https://api.mainnet-beta.solana.com',\n devnet: 'https://api.devnet.solana.com',\n testnet: 'https://api.testnet.solana.com',\n};\n","/**\n * RPC Client Caching Module\n *\n * This module provides a caching mechanism for Solana RPC clients to optimize\n * performance and resource usage by reusing existing client instances.\n *\n * @module RpcClientCache\n */\n\nimport { createSolanaClient, SolanaClient, SolanaClusterMoniker } from 'gill';\n\nimport { defaultRpcUrlsByMoniker } from './defaultRpcUrlsByMoniker';\n\n/**\n * Validates if a string represents a properly formatted URL\n *\n * @param str - String to validate as URL\n * @returns Boolean indicating if the string is a valid URL\n *\n * @example\n * ```typescript\n * isValidUrl('https://api.mainnet-beta.solana.com') // returns true\n * isValidUrl('not-a-url') // returns false\n * ```\n */\nfunction isValidUrl(str: string): boolean {\n try {\n new URL(str);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * In-memory cache storage for Solana client instances\n * Maps Solana URLs or monikers to their corresponding client instances\n *\n * @internal\n */\nconst clientsCache = new Map<string, SolanaClient>();\n\n/**\n * Creates or retrieves a cached Solana RPC client instance\n *\n * This function implements a caching mechanism for Solana RPC clients to:\n * - Avoid redundant client instance creation\n * - Optimize memory usage\n * - Maintain consistent client instances throughout the application\n *\n * @param rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')\n * @returns Cached or newly created Solana RPC client instance\n * @throws Error if unable to resolve a valid RPC URL\n *\n * @example\n * ```typescript\n * // Using cluster moniker\n * const mainnetClient = createSolanaClientWithCache('mainnet');\n *\n * // Using custom RPC URL\n * const customClient = createSolanaClientWithCache('https://my-rpc.example.com');\n * ```\n */\nexport const createSolanaClientWithCache = ({\n rpcUrlOrMoniker,\n rpcUrls,\n}: {\n rpcUrlOrMoniker: string;\n rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;\n}): SolanaClient => {\n // Return existing client instance if available in cache\n if (clientsCache.has(rpcUrlOrMoniker)) {\n return clientsCache.get(rpcUrlOrMoniker)!;\n }\n\n // Resolve RPC URL from input: direct URL or cluster moniker\n const rpcUrl = isValidUrl(rpcUrlOrMoniker)\n ? rpcUrlOrMoniker\n : rpcUrls\n ? (rpcUrls[rpcUrlOrMoniker as SolanaClusterMoniker] ??\n defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker])\n : defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker];\n\n // Validate resolved RPC URL\n if (!rpcUrl) {\n throw new Error(\n `Unable to resolve RPC URL for input: \"${rpcUrlOrMoniker}\". Ensure it's a valid URL or known moniker.`,\n );\n }\n\n // Create new client instance with resolved URL\n const newClient = createSolanaClient({ urlOrMoniker: rpcUrl });\n\n // Cache and return the new instance\n clientsCache.set(rpcUrlOrMoniker, newClient);\n return newClient;\n};\n","// --- RPC Client Caching ---\n\nimport { createSolanaRpc, Rpc, SolanaClusterMoniker, SolanaRpcApi } from 'gill';\n\nimport { defaultRpcUrlsByMoniker } from './defaultRpcUrlsByMoniker';\n\n/**\n * Validates whether a string is a properly formatted URL.\n * @param str - The string to validate.\n * @returns True if the string is a valid URL, otherwise false.\n */\nfunction isValidUrl(str: string): boolean {\n try {\n new URL(str);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * An in-memory cache for RPC clients to avoid redundant instance creation.\n * @internal\n */\nconst rpcCache = new Map<string, Rpc<SolanaRpcApi>>();\n\n/**\n * Retrieves a cached RPC client for a given URL or cluster moniker.\n * If no cached client exists, it creates a new instance.\n *\n * @param rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.\n * @returns The RPC client instance.\n * @internal\n */\nexport const createSolanaRPC = ({\n rpcUrlOrMoniker,\n rpcUrls,\n}: {\n rpcUrlOrMoniker: string;\n rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;\n}): Rpc<SolanaRpcApi> => {\n // Check the cache first for an existing RPC instance.\n if (rpcCache.has(rpcUrlOrMoniker)) {\n return rpcCache.get(rpcUrlOrMoniker)!;\n }\n // Determine the RPC URL: validate if it's a full URL or fall back to default list.\n const rpcUrl = isValidUrl(rpcUrlOrMoniker)\n ? rpcUrlOrMoniker\n : rpcUrls\n ? (rpcUrls[rpcUrlOrMoniker as SolanaClusterMoniker] ??\n defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker])\n : defaultRpcUrlsByMoniker[rpcUrlOrMoniker as SolanaClusterMoniker];\n\n // If no valid RPC URL could be resolved, default to the mainnet URL.\n if (!rpcUrl) {\n throw new Error(\n `Unable to resolve RPC URL for input: \"${rpcUrlOrMoniker}\". Ensure it's a valid URL or known moniker.`,\n );\n }\n\n // Create a new RPC client instance.\n const newRpc = createSolanaRpc(rpcUrl);\n\n // Cache the new instance and return it.\n rpcCache.set(rpcUrlOrMoniker, newRpc);\n return newRpc;\n};\n","import { filterUniqueByKey } from '@tuwaio/orbit-core';\nimport { getWallets } from '@wallet-standard/app';\nimport { getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED as getOrCreateUiWalletForStandardWallet } from '@wallet-standard/ui-registry';\n\nexport function getAvailableWallets() {\n return filterUniqueByKey(getWallets().get().map(getOrCreateUiWalletForStandardWallet), 'name').filter((wallet) => {\n try {\n // Check if wallet has chains property\n if (!wallet || !Array.isArray(wallet.chains)) {\n return false;\n }\n\n // Check if wallet has features property (features should be an array)\n if (!wallet.features || !Array.isArray(wallet.features)) {\n return false;\n }\n\n // Define required features for Solana wallet functionality\n const requiredFeatures = [\n 'standard:connect',\n 'standard:disconnect',\n 'standard:events',\n 'solana:signAndSendTransaction',\n 'solana:signTransaction',\n 'solana:signMessage',\n ] as const;\n\n // Check if wallet supports all required features\n const hasAllFeatures = requiredFeatures.every((requiredFeature) => {\n return wallet.features.includes(requiredFeature);\n });\n\n if (!hasAllFeatures) {\n return false;\n }\n\n // Check if all chains are Solana chains\n return wallet.chains.every((chain) => {\n try {\n if (typeof chain !== 'string') {\n return false;\n }\n const chainParts = chain.split(':');\n return chainParts.length >= 1 && chainParts[0] === 'solana';\n } catch (error) {\n console.warn('Error parsing chain:', chain, error);\n return false;\n }\n });\n } catch (error) {\n console.warn('Error filtering wallet:', error);\n return false;\n }\n });\n}\n","import { lastConnectedWalletHelpers } from '@tuwaio/orbit-core';\n\nimport { getAvailableWallets } from './getAvailableSolanaWallets';\n\nexport function getConnectedSolanaWallet() {\n const lastConnectedWallet = lastConnectedWalletHelpers.getLastConnectedWallet();\n const wallets = getAvailableWallets();\n const connectedWallet = wallets.find((w) =>\n w.accounts.find((a) => a.address.toLowerCase() === lastConnectedWallet?.address?.toLowerCase()),\n );\n if (!connectedWallet) {\n throw new Error('Wallet not provided. Cannot perform chain check.');\n }\n return connectedWallet;\n}\n","import { lastConnectedWalletHelpers } from '@tuwaio/orbit-core';\n\nimport { getConnectedSolanaWallet } from './getConnectedSolanaWallet';\n\n/**\n * Cache for Solana avatar lookup results.\n * Key: normalized name (lowercase string), Value: Avatar URL (string).\n */\nconst solanaAvatarCache = new Map<string, string>();\n\n/**\n * Searches and returns the avatar URL (icon) for a given Solana account name (label)\n * among connected wallets. Includes caching for performance on repeated requests.\n *\n * @param name The account name (label) to look up.\n * @returns A promise that resolves to the account's icon URL, or the original name string if the icon is not found.\n */\nexport const getSolanaAddressAvatar = async (name: string): Promise<string> => {\n // Normalize the name to use as a cache key, ensuring case-insensitivity.\n const normalizedName = name.toLowerCase();\n\n // Check the cache: if the result exists, return it immediately.\n const cachedAvatar = solanaAvatarCache.get(normalizedName);\n if (cachedAvatar !== undefined) {\n return cachedAvatar;\n }\n // Find the first wallet that contains an account with a matching label.\n const connectedWallet = getConnectedSolanaWallet();\n\n // Retrieve the icon URL for the specific matching account.\n // If no matching account is found, fall back to the original name string.\n const resultAvatar =\n connectedWallet?.accounts.find(\n (account) => account.address.toLowerCase() === lastConnectedWalletHelpers.getLastConnectedWallet()?.address,\n )?.icon ??\n name ??\n lastConnectedWalletHelpers.getLastConnectedWallet()?.address;\n\n // Store the result (including the fallback name if icon is null) in the cache.\n solanaAvatarCache.set(normalizedName, resultAvatar);\n\n return resultAvatar;\n};\n","import { getConnectedSolanaWallet } from './getConnectedSolanaWallet';\n\n/**\n * Cache for Solana address lookup results.\n * Key: normalized address (lowercase string), Value: Account name/label (string).\n */\nconst solanaNameCache = new Map<string, string>();\n\n/**\n * Searches and returns the account name (label) for a given Solana address\n * among connected wallets. Includes caching for performance on repeated requests.\n *\n * @param address The Solana account address to look up.\n * @returns A promise that resolves to the account's name/label, or the original address string if the name is not found.\n */\nexport const getSolanaAddressName = async (address: string): Promise<string> => {\n // Normalize the address to use as a cache key, ensuring case-insensitivity.\n const normalizedAddress = address.toLowerCase();\n\n // Check the cache: if the result exists, return it immediately.\n const cachedName = solanaNameCache.get(normalizedAddress);\n if (cachedName !== undefined) {\n return cachedName;\n }\n\n const connectedWallet = getConnectedSolanaWallet();\n // The result is the found label, or the original address if no label was found.\n const resultName =\n connectedWallet.accounts.find((a) => a.address.toLowerCase() === normalizedAddress)?.label ?? address;\n // Store the result (including the fallback address string if label is null) in the cache.\n solanaNameCache.set(normalizedAddress, resultName);\n\n return resultName;\n};\n","/**\n * @file This file contains a utility function for generating Solana transaction explorer links.\n */\n\nimport { getExplorerLink } from 'gill';\n\nimport { getCluster } from './clusterHelpers';\n\n/**\n * Generates a full URL to a transaction on a Solana explorer like Solscan.\n *\n * @param {string} url - The url after baseUrl.\n * @param chainId\n * @returns {string} The full URL to the transaction on the explorer.\n */\nexport const getSolanaExplorerLink = (url?: string, chainId?: string | number | undefined): string => {\n const cluster = getCluster({ cluster: String(chainId) }) ?? 'mainnet';\n // Ensure there are no trailing slashes on the base URL for clean URL construction.\n const baseUrl = getExplorerLink();\n const sanitizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n // Build the cluster query parameter if provided.\n const clusterParam = cluster ? `?cluster=${cluster}` : '';\n\n return `${sanitizedBaseUrl}${url ? url : '/'}${clusterParam}`;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuwaio/orbit-solana",
3
- "version": "0.0.4",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "author": "Oleksandr Tkach",
6
6
  "license": "Apache-2.0",
@@ -22,12 +22,12 @@
22
22
  ],
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "git+https://github.com/TuwaIO/satellite-connect.git",
25
+ "url": "git+https://github.com/TuwaIO/orbit.git",
26
26
  "directory": "packages/orbit-solana"
27
27
  },
28
- "homepage": "https://github.com/TuwaIO/satellite-connect",
28
+ "homepage": "https://github.com/TuwaIO/orbit",
29
29
  "bugs": {
30
- "url": "https://github.com/TuwaIO/satellite-connect/issues"
30
+ "url": "https://github.com/TuwaIO/orbit/issues"
31
31
  },
32
32
  "contributors": [
33
33
  {
@@ -36,12 +36,20 @@
36
36
  }
37
37
  ],
38
38
  "peerDependencies": {
39
- "gill": ">=0.11"
39
+ "@tuwaio/orbit-core": ">=0",
40
+ "gill": ">=0.12",
41
+ "@wallet-standard/app": "1.x.x",
42
+ "@wallet-standard/ui-core": "1.x.x",
43
+ "@wallet-standard/ui-registry": "1.x.x"
40
44
  },
41
45
  "devDependencies": {
42
46
  "tsup": "^8.5.0",
43
- "typescript": "^5.9.2",
44
- "gill": "^0.11.0"
47
+ "typescript": "^5.9.3",
48
+ "gill": "^0.12.0",
49
+ "@wallet-standard/app": "^1.1.0",
50
+ "@wallet-standard/ui-core": "^1.0.0",
51
+ "@wallet-standard/ui-registry": "^1.0.1",
52
+ "@tuwaio/orbit-core": "^0.1.1"
45
53
  },
46
54
  "scripts": {
47
55
  "start": "tsup src/index.ts --watch",