@tuwaio/orbit-solana 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/dist/index.d.mts +39 -11
- package/dist/index.d.ts +39 -11
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -18,13 +18,13 @@ Built with **TypeScript**, this package utilizes **`gill`** (an improvement laye
|
|
|
18
18
|
|
|
19
19
|
## ✨ Key Features
|
|
20
20
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
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 (`getAvailableSolanaConnectors`). Retrieves the currently connected wallet based on stored address (`getConnectedSolanaConnector`).
|
|
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.
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
@@ -32,9 +32,9 @@ Built with **TypeScript**, this package utilizes **`gill`** (an improvement laye
|
|
|
32
32
|
|
|
33
33
|
### Requirements
|
|
34
34
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
35
|
+
- Node.js 20+
|
|
36
|
+
- TypeScript 5.9+
|
|
37
|
+
- `@tuwaio/orbit-core` (as a foundational peer dependency)
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
# Using pnpm (recommended)
|
|
@@ -96,10 +96,10 @@ Assuming a wallet is connected and its address is stored (e.g., using `lastConne
|
|
|
96
96
|
|
|
97
97
|
```typescript
|
|
98
98
|
import { getSolanaAddressName } from '@tuwaio/orbit-solana';
|
|
99
|
-
import {
|
|
99
|
+
import { lastConnectedConnectorHelpers } from '@tuwaio/orbit-core'; // Needed to know which address is connected
|
|
100
100
|
|
|
101
101
|
async function displayAccountName() {
|
|
102
|
-
const connectedWalletInfo =
|
|
102
|
+
const connectedWalletInfo = lastConnectedConnectorHelpers.getLastConnectedConnector();
|
|
103
103
|
if (connectedWalletInfo?.address && connectedWalletInfo.walletType.startsWith('solana:')) {
|
|
104
104
|
try {
|
|
105
105
|
const name = await getSolanaAddressName(connectedWalletInfo.address);
|
|
@@ -143,7 +143,7 @@ console.log(devnetExplorerUrl);
|
|
|
143
143
|
- **Types (`types.ts`)**: Defines Solana-specific types like `SolanaRPCUrls`.
|
|
144
144
|
- **Cluster Helpers (`clusterHelpers.ts`)**: Functions `getCluster` and `getRpcUrlForCluster` for managing Solana network identifiers and RPC endpoints.
|
|
145
145
|
- **Client Creation (`createSolanaClientWithCache.ts`, `createSolanaRPC.ts`)**: Provides cached instances of `gill`'s `SolanaClient` and `Rpc`. Includes default RPC URLs.
|
|
146
|
-
- **Wallet Interaction (`
|
|
146
|
+
- **Wallet Interaction (`getAvailableSolanaConnectors.ts`, `getConnectedSolanaConnector.ts`)**: Leverages `@wallet-standard` to find and identify Solana wallets.
|
|
147
147
|
- **Account Info (`getSolanaAddressAvatar.ts`, `getSolanaAddressName.ts`)**: Retrieves metadata (label, icon) associated with accounts within the connected wallet.
|
|
148
148
|
- **Explorer Links (`getSolanaExplorerLink.ts`)**: Utility for constructing explorer URLs.
|
|
149
149
|
|
|
@@ -158,7 +158,7 @@ console.log(devnetExplorerUrl);
|
|
|
158
158
|
|
|
159
159
|
## ✨ How It Connects to the Ecosystem
|
|
160
160
|
|
|
161
|
-
- **Depends on `@tuwaio/orbit-core`:** Relies on core types (`OrbitAdapter`, `BaseAdapter`) and utilities (`
|
|
161
|
+
- **Depends on `@tuwaio/orbit-core`:** Relies on core types (`OrbitAdapter`, `BaseAdapter`) and utilities (`lastConnectedConnectorHelpers`, `filterUniqueByKey`).
|
|
162
162
|
- **Provides Solana Functionality:** Implements the specific logic for Solana interactions needed by applications using Orbit Utils.
|
|
163
163
|
- **Leverages Gill & Wallet Standard:** Uses `gill` for simplified RPC communication and the Wallet Standard packages for wallet detection and interaction.
|
|
164
164
|
|
|
@@ -174,4 +174,4 @@ If you find this library useful, please consider supporting its development. Eve
|
|
|
174
174
|
|
|
175
175
|
## 📄 License
|
|
176
176
|
|
|
177
|
-
This project is licensed under the **Apache-2.0 License** - see the [LICENSE](./LICENSE) file for details.
|
|
177
|
+
This project is licensed under the **Apache-2.0 License** - see the [LICENSE](./LICENSE) file for details.
|
package/dist/index.d.mts
CHANGED
|
@@ -81,27 +81,55 @@ declare const createSolanaRPC: ({ rpcUrlOrMoniker, rpcUrls, }: {
|
|
|
81
81
|
*/
|
|
82
82
|
declare const defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>>;
|
|
83
83
|
|
|
84
|
-
declare function
|
|
84
|
+
declare function getAvailableSolanaConnectors(): _wallet_standard_ui_core.UiWallet[];
|
|
85
85
|
|
|
86
|
-
declare function
|
|
86
|
+
declare function getConnectedSolanaConnector(): _wallet_standard_ui_core.UiWallet;
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
|
-
* Searches and returns the avatar URL
|
|
90
|
-
*
|
|
89
|
+
* Searches and returns the avatar URL for a given Solana address or SNS domain.
|
|
90
|
+
* Priority order:
|
|
91
|
+
* 1. Check cache
|
|
92
|
+
* 2. If it's a valid SNS domain (.sol), try to get SNS avatar
|
|
93
|
+
* 3. Generate default avatar based on identifier
|
|
91
94
|
*
|
|
92
|
-
* @param
|
|
93
|
-
* @returns A promise that resolves to the
|
|
95
|
+
* @param addressOrDomain The Solana address or SNS domain to look up avatar for
|
|
96
|
+
* @returns A promise that resolves to the avatar URL
|
|
94
97
|
*/
|
|
95
|
-
declare const getSolanaAddressAvatar: (
|
|
98
|
+
declare const getSolanaAddressAvatar: (addressOrDomain: string) => Promise<string>;
|
|
99
|
+
/**
|
|
100
|
+
* Clears the Solana avatar cache
|
|
101
|
+
*/
|
|
102
|
+
declare const clearSolanaAvatarCache: () => void;
|
|
103
|
+
/**
|
|
104
|
+
* Gets the current avatar cache size
|
|
105
|
+
*/
|
|
106
|
+
declare const getSolanaAvatarCacheSize: () => number;
|
|
107
|
+
/**
|
|
108
|
+
* Preloads avatar for a given address/domain
|
|
109
|
+
*
|
|
110
|
+
* @param addressOrDomain The address or domain to preload avatar for
|
|
111
|
+
*/
|
|
112
|
+
declare const preloadSolanaAvatar: (addressOrDomain: string) => Promise<void>;
|
|
96
113
|
|
|
97
114
|
/**
|
|
98
|
-
* Searches and returns the account name (label) for a given Solana address
|
|
99
|
-
*
|
|
115
|
+
* Searches and returns the account name (label) for a given Solana address.
|
|
116
|
+
* Priority order:
|
|
117
|
+
* 1. Check cache
|
|
118
|
+
* 2. Try to resolve SNS domain name
|
|
119
|
+
* 3. Fall back to original address
|
|
100
120
|
*
|
|
101
121
|
* @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
|
|
122
|
+
* @returns A promise that resolves to the account's name/label, or the original address string if no name is found.
|
|
103
123
|
*/
|
|
104
124
|
declare const getSolanaAddressName: (address: string) => Promise<string>;
|
|
125
|
+
/**
|
|
126
|
+
* Clears the Solana name cache
|
|
127
|
+
*/
|
|
128
|
+
declare const clearSolanaNameCache: () => void;
|
|
129
|
+
/**
|
|
130
|
+
* Gets the current cache size
|
|
131
|
+
*/
|
|
132
|
+
declare const getSolanaNameCacheSize: () => number;
|
|
105
133
|
|
|
106
134
|
/**
|
|
107
135
|
* @file This file contains a utility function for generating Solana transaction explorer links.
|
|
@@ -115,4 +143,4 @@ declare const getSolanaAddressName: (address: string) => Promise<string>;
|
|
|
115
143
|
*/
|
|
116
144
|
declare const getSolanaExplorerLink: (url?: string, chainId?: string | number | undefined) => string;
|
|
117
145
|
|
|
118
|
-
export { type SolanaRPCUrls, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker,
|
|
146
|
+
export { type SolanaRPCUrls, clearSolanaAvatarCache, clearSolanaNameCache, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableSolanaConnectors, getCluster, getConnectedSolanaConnector, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaAvatarCacheSize, getSolanaExplorerLink, getSolanaNameCacheSize, preloadSolanaAvatar };
|
package/dist/index.d.ts
CHANGED
|
@@ -81,27 +81,55 @@ declare const createSolanaRPC: ({ rpcUrlOrMoniker, rpcUrls, }: {
|
|
|
81
81
|
*/
|
|
82
82
|
declare const defaultRpcUrlsByMoniker: Partial<Record<SolanaClusterMoniker, string>>;
|
|
83
83
|
|
|
84
|
-
declare function
|
|
84
|
+
declare function getAvailableSolanaConnectors(): _wallet_standard_ui_core.UiWallet[];
|
|
85
85
|
|
|
86
|
-
declare function
|
|
86
|
+
declare function getConnectedSolanaConnector(): _wallet_standard_ui_core.UiWallet;
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
|
-
* Searches and returns the avatar URL
|
|
90
|
-
*
|
|
89
|
+
* Searches and returns the avatar URL for a given Solana address or SNS domain.
|
|
90
|
+
* Priority order:
|
|
91
|
+
* 1. Check cache
|
|
92
|
+
* 2. If it's a valid SNS domain (.sol), try to get SNS avatar
|
|
93
|
+
* 3. Generate default avatar based on identifier
|
|
91
94
|
*
|
|
92
|
-
* @param
|
|
93
|
-
* @returns A promise that resolves to the
|
|
95
|
+
* @param addressOrDomain The Solana address or SNS domain to look up avatar for
|
|
96
|
+
* @returns A promise that resolves to the avatar URL
|
|
94
97
|
*/
|
|
95
|
-
declare const getSolanaAddressAvatar: (
|
|
98
|
+
declare const getSolanaAddressAvatar: (addressOrDomain: string) => Promise<string>;
|
|
99
|
+
/**
|
|
100
|
+
* Clears the Solana avatar cache
|
|
101
|
+
*/
|
|
102
|
+
declare const clearSolanaAvatarCache: () => void;
|
|
103
|
+
/**
|
|
104
|
+
* Gets the current avatar cache size
|
|
105
|
+
*/
|
|
106
|
+
declare const getSolanaAvatarCacheSize: () => number;
|
|
107
|
+
/**
|
|
108
|
+
* Preloads avatar for a given address/domain
|
|
109
|
+
*
|
|
110
|
+
* @param addressOrDomain The address or domain to preload avatar for
|
|
111
|
+
*/
|
|
112
|
+
declare const preloadSolanaAvatar: (addressOrDomain: string) => Promise<void>;
|
|
96
113
|
|
|
97
114
|
/**
|
|
98
|
-
* Searches and returns the account name (label) for a given Solana address
|
|
99
|
-
*
|
|
115
|
+
* Searches and returns the account name (label) for a given Solana address.
|
|
116
|
+
* Priority order:
|
|
117
|
+
* 1. Check cache
|
|
118
|
+
* 2. Try to resolve SNS domain name
|
|
119
|
+
* 3. Fall back to original address
|
|
100
120
|
*
|
|
101
121
|
* @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
|
|
122
|
+
* @returns A promise that resolves to the account's name/label, or the original address string if no name is found.
|
|
103
123
|
*/
|
|
104
124
|
declare const getSolanaAddressName: (address: string) => Promise<string>;
|
|
125
|
+
/**
|
|
126
|
+
* Clears the Solana name cache
|
|
127
|
+
*/
|
|
128
|
+
declare const clearSolanaNameCache: () => void;
|
|
129
|
+
/**
|
|
130
|
+
* Gets the current cache size
|
|
131
|
+
*/
|
|
132
|
+
declare const getSolanaNameCacheSize: () => number;
|
|
105
133
|
|
|
106
134
|
/**
|
|
107
135
|
* @file This file contains a utility function for generating Solana transaction explorer links.
|
|
@@ -115,4 +143,4 @@ declare const getSolanaAddressName: (address: string) => Promise<string>;
|
|
|
115
143
|
*/
|
|
116
144
|
declare const getSolanaExplorerLink: (url?: string, chainId?: string | number | undefined) => string;
|
|
117
145
|
|
|
118
|
-
export { type SolanaRPCUrls, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker,
|
|
146
|
+
export { type SolanaRPCUrls, clearSolanaAvatarCache, clearSolanaNameCache, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableSolanaConnectors, getCluster, getConnectedSolanaConnector, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaAvatarCacheSize, getSolanaExplorerLink, getSolanaNameCacheSize, preloadSolanaAvatar };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';var gill=require('gill'),orbitCore=require('@tuwaio/orbit-core'),app=require('@wallet-standard/app'),uiRegistry=require('@wallet-standard/ui-registry');var
|
|
2
|
-
exports.createSolanaClientWithCache=
|
|
1
|
+
'use strict';var gill=require('gill'),orbitCore=require('@tuwaio/orbit-core'),app=require('@wallet-standard/app'),uiRegistry=require('@wallet-standard/ui-registry');var f=({cluster:t,walletCluster:e})=>{let r="mainnet";return t?t.includes(":")?t.split(":")[1]:t:e??r},U=({cluster:t,walletCluster:e,rpcUrls:r})=>r[e??t]??"https://api.mainnet-beta.solana.com/";var s={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function d(t){try{return new URL(t),!0}catch{return false}}var u=new Map,B=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(u.has(t))return u.get(t);let r=d(t)?t:e?e[t]??s[t]:s[t];if(!r)throw new Error(`Unable to resolve RPC URL for input: "${t}". Ensure it's a valid URL or known moniker.`);let n=gill.createSolanaClient({urlOrMoniker:r});return u.set(t,n),n};function C(t){try{return new URL(t),!0}catch{return false}}var m=new Map,T=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(m.has(t))return m.get(t);let r=C(t)?t:e?e[t]??s[t]:s[t];if(!r)throw new Error(`Unable to resolve RPC URL for input: "${t}". Ensure it's a valid URL or known moniker.`);let n=gill.createSolanaRpc(r);return m.set(t,n),n};function p(){return orbitCore.filterUniqueByKey(app.getWallets().get().map(uiRegistry.getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED),"name").filter(t=>{try{return !t||!Array.isArray(t.chains)||!t.features||!Array.isArray(t.features)||!["standard:connect","standard:disconnect","standard:events","solana:signAndSendTransaction","solana:signTransaction","solana:signMessage"].every(n=>t.features.includes(n))?!1:t.chains.every(n=>{try{if(typeof n!="string")return !1;let a=n.split(":");return a.length>=1&&a[0]==="solana"}catch(a){return console.warn("Error parsing chain:",n,a),!1}})}catch(e){return console.warn("Error filtering wallet:",e),false}})}function O(){let t=orbitCore.lastConnectedConnectorHelpers.getLastConnectedConnector(),r=p().find(n=>n.accounts.find(a=>a.address.toLowerCase()===t?.address?.toLowerCase()));if(!r)throw new Error("Connector not provided. Cannot perform chain check.");return r}var l=new Map,w="https://image-api.bonfida.com",y=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),x=t=>/^[a-z0-9-]+\.sol$/.test(t.toLowerCase()),N=async t=>{try{let e=await fetch(`${w}/image?domain=${encodeURIComponent(t)}`);if(!e.ok)return null;let r=await e.json();return r.result&&Array.isArray(r.result)&&r.result.length>0?r.result.find(a=>a.domain===t)?.image??r.result[0]?.image??null:null}catch(e){return console.warn("Failed to fetch SNS avatar:",e),null}},i=t=>`https://api.dicebear.com/7.x/identicon/svg?seed=${encodeURIComponent(t)}`,b=async t=>{if(!t)return i("default");let e=t.trim();if(!e)return i("default");let r=e.toLowerCase(),n=l.get(r);if(n!==void 0)return n;let a;try{if(x(e)){let o=await N(e);o?a=o:a=i(e);}else y(e),a=i(e);}catch(o){console.warn("Error resolving avatar:",o),a=i(e);}return l.set(r,a),a},et=()=>{l.clear();},rt=()=>l.size,nt=async t=>{try{await b(t);}catch(e){console.warn("Failed to preload avatar:",e);}};var c=new Map,P="https://sns-api.bonfida.com",k=async t=>{try{let e=await fetch(`${P}/v2/user/fav-domains/${t}`);if(!e.ok)return null;let r=await e.json();return r.result&&typeof r.result=="object"&&t in r.result?r.result[t]:null}catch(e){return console.warn("Failed to fetch SNS name:",e),null}},L=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),ot=async t=>{if(!t)return t||"";if(!L(t.trim()))return t;let e=t.toLowerCase().trim(),r=c.get(e);if(r!==void 0)return r;let n=t;try{let a=await k(t);a&&(n=a);}catch(a){console.warn("Error resolving address name:",a);}return c.set(e,n),n},st=()=>{c.clear();},it=()=>c.size;var mt=(t,e)=>{let r=f({cluster:String(e)})??"mainnet",n=gill.getExplorerLink(),a=n.endsWith("/")?n.slice(0,-1):n,o=r?`?cluster=${r}`:"";return `${a}${t||"/"}${o}`};
|
|
2
|
+
exports.clearSolanaAvatarCache=et;exports.clearSolanaNameCache=st;exports.createSolanaClientWithCache=B;exports.createSolanaRPC=T;exports.defaultRpcUrlsByMoniker=s;exports.getAvailableSolanaConnectors=p;exports.getCluster=f;exports.getConnectedSolanaConnector=O;exports.getRpcUrlForCluster=U;exports.getSolanaAddressAvatar=b;exports.getSolanaAddressName=ot;exports.getSolanaAvatarCacheSize=rt;exports.getSolanaExplorerLink=mt;exports.getSolanaNameCacheSize=it;exports.preloadSolanaAvatar=nt;//# 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/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"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/clusterHelpers.ts","../src/utils/defaultRpcUrlsByMoniker.ts","../src/utils/createSolanaClientWithCache.ts","../src/utils/createSolanaRPC.ts","../src/utils/getAvailableSolanaConnectors.ts","../src/utils/getConnectedSolanaConnector.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","getAvailableSolanaConnectors","filterUniqueByKey","getWallets","getOrCreateUiWalletForStandardWallet","wallet","requiredFeature","chain","chainParts","error","getConnectedSolanaConnector","lastConnectedConnector","lastConnectedConnectorHelpers","connectedConnector","w","solanaAvatarCache","SNS_API_BASE_URL","isValidSolanaAddress","address","isValidSNSDomain","domain","getSNSAvatar","response","data","item","getDefaultAvatar","identifier","getSolanaAddressAvatar","addressOrDomain","trimmedInput","normalizedInput","cachedAvatar","resultAvatar","snsAvatar","clearSolanaAvatarCache","getSolanaAvatarCacheSize","preloadSolanaAvatar","solanaNameCache","getSNSName","getSolanaAddressName","normalizedAddress","cachedName","resultName","snsName","clearSolanaNameCache","getSolanaNameCacheSize","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 getAvailableSolanaConnectors() {\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 { lastConnectedConnectorHelpers } from '@tuwaio/orbit-core';\n\nimport { getAvailableSolanaConnectors } from './getAvailableSolanaConnectors';\n\nexport function getConnectedSolanaConnector() {\n const lastConnectedConnector = lastConnectedConnectorHelpers.getLastConnectedConnector();\n const connectors = getAvailableSolanaConnectors();\n const connectedConnector = connectors.find((w) =>\n w.accounts.find((a) => a.address.toLowerCase() === lastConnectedConnector?.address?.toLowerCase()),\n );\n if (!connectedConnector) {\n throw new Error('Connector not provided. Cannot perform chain check.');\n }\n return connectedConnector;\n}\n","/**\n * Cache for Solana avatar lookup results.\n * Key: normalized address/domain (lowercase string), Value: Avatar URL (string).\n */\nconst solanaAvatarCache = new Map<string, string>();\n\n/**\n * SNS API endpoint for resolving domain images\n */\nconst SNS_API_BASE_URL = 'https://image-api.bonfida.com';\n\n/**\n * Interface for SNS Image API response\n */\ninterface SNSImageApiResponse {\n result: { domain: string; image: string }[];\n}\n\n/**\n * Validates if the provided string is a valid Solana address\n *\n * @param address The string to validate\n * @returns boolean indicating if the address is valid\n */\nconst isValidSolanaAddress = (address: string): boolean => {\n return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);\n};\n\n/**\n * Validates if the provided string is a valid SNS domain\n *\n * @param domain The string to validate\n * @returns boolean indicating if the domain is valid\n */\nconst isValidSNSDomain = (domain: string): boolean => {\n return /^[a-z0-9-]+\\.sol$/.test(domain.toLowerCase());\n};\n\n/**\n * Fetches the avatar image URL for a given SNS domain using the SNS API\n *\n * @param domain The SNS domain to get avatar for (e.g., \"example.sol\")\n * @returns Promise that resolves to the avatar URL or null if not found\n */\nconst getSNSAvatar = async (domain: string): Promise<string | null> => {\n try {\n const response = await fetch(`${SNS_API_BASE_URL}/image?domain=${encodeURIComponent(domain)}`);\n\n if (!response.ok) {\n return null;\n }\n\n const data: SNSImageApiResponse = await response.json();\n\n if (data.result && Array.isArray(data.result) && data.result.length > 0) {\n const imageData = data.result.find((item) => item.domain === domain);\n return imageData?.image ?? data.result[0]?.image ?? null;\n }\n\n return null;\n } catch (error) {\n console.warn('Failed to fetch SNS avatar:', error);\n return null;\n }\n};\n\n/**\n * Generates a default avatar URL based on address/domain\n * Using DiceBear API for consistent avatars\n *\n * @param identifier The address or domain to generate avatar for\n * @returns Default avatar URL\n */\nconst getDefaultAvatar = (identifier: string): string => {\n const seed = encodeURIComponent(identifier);\n return `https://api.dicebear.com/7.x/identicon/svg?seed=${seed}`;\n};\n\n/**\n * Searches and returns the avatar URL for a given Solana address or SNS domain.\n * Priority order:\n * 1. Check cache\n * 2. If it's a valid SNS domain (.sol), try to get SNS avatar\n * 3. Generate default avatar based on identifier\n *\n * @param addressOrDomain The Solana address or SNS domain to look up avatar for\n * @returns A promise that resolves to the avatar URL\n */\nexport const getSolanaAddressAvatar = async (addressOrDomain: string): Promise<string> => {\n if (!addressOrDomain) {\n return getDefaultAvatar('default');\n }\n\n const trimmedInput = addressOrDomain.trim();\n\n if (!trimmedInput) {\n return getDefaultAvatar('default');\n }\n\n // Normalize the input to use as a cache key\n const normalizedInput = trimmedInput.toLowerCase();\n\n // Check the cache: if the result exists, return it immediately\n const cachedAvatar = solanaAvatarCache.get(normalizedInput);\n if (cachedAvatar !== undefined) {\n return cachedAvatar;\n }\n\n let resultAvatar: string;\n\n try {\n if (isValidSNSDomain(trimmedInput)) {\n const snsAvatar = await getSNSAvatar(trimmedInput);\n if (snsAvatar) {\n resultAvatar = snsAvatar;\n } else {\n resultAvatar = getDefaultAvatar(trimmedInput);\n }\n } else if (isValidSolanaAddress(trimmedInput)) {\n resultAvatar = getDefaultAvatar(trimmedInput);\n } else {\n resultAvatar = getDefaultAvatar(trimmedInput);\n }\n } catch (error) {\n console.warn('Error resolving avatar:', error);\n resultAvatar = getDefaultAvatar(trimmedInput);\n }\n\n // Store the result in the cache\n solanaAvatarCache.set(normalizedInput, resultAvatar);\n\n return resultAvatar;\n};\n\n/**\n * Clears the Solana avatar cache\n */\nexport const clearSolanaAvatarCache = (): void => {\n solanaAvatarCache.clear();\n};\n\n/**\n * Gets the current avatar cache size\n */\nexport const getSolanaAvatarCacheSize = (): number => {\n return solanaAvatarCache.size;\n};\n\n/**\n * Preloads avatar for a given address/domain\n *\n * @param addressOrDomain The address or domain to preload avatar for\n */\nexport const preloadSolanaAvatar = async (addressOrDomain: string): Promise<void> => {\n try {\n await getSolanaAddressAvatar(addressOrDomain);\n } catch (error) {\n console.warn('Failed to preload avatar:', error);\n }\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 * SNS API endpoint for resolving Solana addresses to domain names\n */\nconst SNS_API_BASE_URL = 'https://sns-api.bonfida.com';\n\n/**\n * Interface for SNS API response\n */\ninterface SNSApiResponse {\n result: Record<string, string> | null;\n}\n\n/**\n * Fetches the SNS domain name for a given Solana address using the SNS API\n *\n * @param address The Solana address to resolve\n * @returns Promise that resolves to the SNS domain name or null if not found\n */\nconst getSNSName = async (address: string): Promise<string | null> => {\n try {\n const response = await fetch(`${SNS_API_BASE_URL}/v2/user/fav-domains/${address}`);\n\n if (!response.ok) {\n return null;\n }\n\n const data: SNSApiResponse = await response.json();\n\n if (data.result && typeof data.result === 'object' && address in data.result) {\n return data.result[address];\n }\n\n return null;\n } catch (error) {\n console.warn('Failed to fetch SNS name:', error);\n return null;\n }\n};\n\n/**\n * Validates if the provided string is a valid Solana address\n *\n * @param address The string to validate\n * @returns boolean indicating if the address is valid\n */\nconst isValidSolanaAddress = (address: string): boolean => {\n return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);\n};\n\n/**\n * Searches and returns the account name (label) for a given Solana address.\n * Priority order:\n * 1. Check cache\n * 2. Try to resolve SNS domain name\n * 3. Fall back to original address\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 no name is found.\n */\nexport const getSolanaAddressName = async (address: string): Promise<string> => {\n if (!address) {\n return address || '';\n }\n\n if (!isValidSolanaAddress(address.trim())) {\n return address;\n }\n\n // Normalize the address to use as a cache key, ensuring case-insensitivity.\n const normalizedAddress = address.toLowerCase().trim();\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 let resultName = address; // Default fallback\n\n try {\n const snsName = await getSNSName(address);\n if (snsName) {\n resultName = snsName;\n }\n } catch (error) {\n console.warn('Error resolving address name:', error);\n // resultName остается как оригинальный адрес\n }\n\n // Store the result in the cache\n solanaNameCache.set(normalizedAddress, resultName);\n\n return resultName;\n};\n\n/**\n * Clears the Solana name cache\n */\nexport const clearSolanaNameCache = (): void => {\n solanaNameCache.clear();\n};\n\n/**\n * Gets the current cache size\n */\nexport const getSolanaNameCacheSize = (): number => {\n return solanaNameCache.size;\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 {createSolanaClient,createSolanaRpc,getExplorerLink}from'gill';import {filterUniqueByKey,
|
|
2
|
-
export{
|
|
1
|
+
import {createSolanaClient,createSolanaRpc,getExplorerLink}from'gill';import {filterUniqueByKey,lastConnectedConnectorHelpers}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 f=({cluster:t,walletCluster:e})=>{let r="mainnet";return t?t.includes(":")?t.split(":")[1]:t:e??r},U=({cluster:t,walletCluster:e,rpcUrls:r})=>r[e??t]??"https://api.mainnet-beta.solana.com/";var s={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function d(t){try{return new URL(t),!0}catch{return false}}var u=new Map,B=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(u.has(t))return u.get(t);let r=d(t)?t:e?e[t]??s[t]:s[t];if(!r)throw new Error(`Unable to resolve RPC URL for input: "${t}". Ensure it's a valid URL or known moniker.`);let n=createSolanaClient({urlOrMoniker:r});return u.set(t,n),n};function C(t){try{return new URL(t),!0}catch{return false}}var m=new Map,T=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(m.has(t))return m.get(t);let r=C(t)?t:e?e[t]??s[t]:s[t];if(!r)throw new Error(`Unable to resolve RPC URL for input: "${t}". Ensure it's a valid URL or known moniker.`);let n=createSolanaRpc(r);return m.set(t,n),n};function p(){return filterUniqueByKey(getWallets().get().map(getOrCreateUiWalletForStandardWallet_DO_NOT_USE_OR_YOU_WILL_BE_FIRED),"name").filter(t=>{try{return !t||!Array.isArray(t.chains)||!t.features||!Array.isArray(t.features)||!["standard:connect","standard:disconnect","standard:events","solana:signAndSendTransaction","solana:signTransaction","solana:signMessage"].every(n=>t.features.includes(n))?!1:t.chains.every(n=>{try{if(typeof n!="string")return !1;let a=n.split(":");return a.length>=1&&a[0]==="solana"}catch(a){return console.warn("Error parsing chain:",n,a),!1}})}catch(e){return console.warn("Error filtering wallet:",e),false}})}function O(){let t=lastConnectedConnectorHelpers.getLastConnectedConnector(),r=p().find(n=>n.accounts.find(a=>a.address.toLowerCase()===t?.address?.toLowerCase()));if(!r)throw new Error("Connector not provided. Cannot perform chain check.");return r}var l=new Map,w="https://image-api.bonfida.com",y=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),x=t=>/^[a-z0-9-]+\.sol$/.test(t.toLowerCase()),N=async t=>{try{let e=await fetch(`${w}/image?domain=${encodeURIComponent(t)}`);if(!e.ok)return null;let r=await e.json();return r.result&&Array.isArray(r.result)&&r.result.length>0?r.result.find(a=>a.domain===t)?.image??r.result[0]?.image??null:null}catch(e){return console.warn("Failed to fetch SNS avatar:",e),null}},i=t=>`https://api.dicebear.com/7.x/identicon/svg?seed=${encodeURIComponent(t)}`,b=async t=>{if(!t)return i("default");let e=t.trim();if(!e)return i("default");let r=e.toLowerCase(),n=l.get(r);if(n!==void 0)return n;let a;try{if(x(e)){let o=await N(e);o?a=o:a=i(e);}else y(e),a=i(e);}catch(o){console.warn("Error resolving avatar:",o),a=i(e);}return l.set(r,a),a},et=()=>{l.clear();},rt=()=>l.size,nt=async t=>{try{await b(t);}catch(e){console.warn("Failed to preload avatar:",e);}};var c=new Map,P="https://sns-api.bonfida.com",k=async t=>{try{let e=await fetch(`${P}/v2/user/fav-domains/${t}`);if(!e.ok)return null;let r=await e.json();return r.result&&typeof r.result=="object"&&t in r.result?r.result[t]:null}catch(e){return console.warn("Failed to fetch SNS name:",e),null}},L=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),ot=async t=>{if(!t)return t||"";if(!L(t.trim()))return t;let e=t.toLowerCase().trim(),r=c.get(e);if(r!==void 0)return r;let n=t;try{let a=await k(t);a&&(n=a);}catch(a){console.warn("Error resolving address name:",a);}return c.set(e,n),n},st=()=>{c.clear();},it=()=>c.size;var mt=(t,e)=>{let r=f({cluster:String(e)})??"mainnet",n=getExplorerLink(),a=n.endsWith("/")?n.slice(0,-1):n,o=r?`?cluster=${r}`:"";return `${a}${t||"/"}${o}`};
|
|
2
|
+
export{et as clearSolanaAvatarCache,st as clearSolanaNameCache,B as createSolanaClientWithCache,T as createSolanaRPC,s as defaultRpcUrlsByMoniker,p as getAvailableSolanaConnectors,f as getCluster,O as getConnectedSolanaConnector,U as getRpcUrlForCluster,b as getSolanaAddressAvatar,ot as getSolanaAddressName,rt as getSolanaAvatarCacheSize,mt as getSolanaExplorerLink,it as getSolanaNameCacheSize,nt as preloadSolanaAvatar};//# sourceMappingURL=index.mjs.map
|
|
3
3
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/clusterHelpers.ts","../src/utils/defaultRpcUrlsByMoniker.ts","../src/utils/createSolanaClientWithCache.ts","../src/utils/createSolanaRPC.ts","../src/utils/getAvailableSolanaConnectors.ts","../src/utils/getConnectedSolanaConnector.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","getAvailableSolanaConnectors","filterUniqueByKey","getWallets","getOrCreateUiWalletForStandardWallet","wallet","requiredFeature","chain","chainParts","error","getConnectedSolanaConnector","lastConnectedConnector","lastConnectedConnectorHelpers","connectedConnector","w","solanaAvatarCache","SNS_API_BASE_URL","isValidSolanaAddress","address","isValidSNSDomain","domain","getSNSAvatar","response","data","item","getDefaultAvatar","identifier","getSolanaAddressAvatar","addressOrDomain","trimmedInput","normalizedInput","cachedAvatar","resultAvatar","snsAvatar","clearSolanaAvatarCache","getSolanaAvatarCacheSize","preloadSolanaAvatar","solanaNameCache","getSNSName","getSolanaAddressName","normalizedAddress","cachedName","resultName","snsName","clearSolanaNameCache","getSolanaNameCacheSize","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 getAvailableSolanaConnectors() {\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 { lastConnectedConnectorHelpers } from '@tuwaio/orbit-core';\n\nimport { getAvailableSolanaConnectors } from './getAvailableSolanaConnectors';\n\nexport function getConnectedSolanaConnector() {\n const lastConnectedConnector = lastConnectedConnectorHelpers.getLastConnectedConnector();\n const connectors = getAvailableSolanaConnectors();\n const connectedConnector = connectors.find((w) =>\n w.accounts.find((a) => a.address.toLowerCase() === lastConnectedConnector?.address?.toLowerCase()),\n );\n if (!connectedConnector) {\n throw new Error('Connector not provided. Cannot perform chain check.');\n }\n return connectedConnector;\n}\n","/**\n * Cache for Solana avatar lookup results.\n * Key: normalized address/domain (lowercase string), Value: Avatar URL (string).\n */\nconst solanaAvatarCache = new Map<string, string>();\n\n/**\n * SNS API endpoint for resolving domain images\n */\nconst SNS_API_BASE_URL = 'https://image-api.bonfida.com';\n\n/**\n * Interface for SNS Image API response\n */\ninterface SNSImageApiResponse {\n result: { domain: string; image: string }[];\n}\n\n/**\n * Validates if the provided string is a valid Solana address\n *\n * @param address The string to validate\n * @returns boolean indicating if the address is valid\n */\nconst isValidSolanaAddress = (address: string): boolean => {\n return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);\n};\n\n/**\n * Validates if the provided string is a valid SNS domain\n *\n * @param domain The string to validate\n * @returns boolean indicating if the domain is valid\n */\nconst isValidSNSDomain = (domain: string): boolean => {\n return /^[a-z0-9-]+\\.sol$/.test(domain.toLowerCase());\n};\n\n/**\n * Fetches the avatar image URL for a given SNS domain using the SNS API\n *\n * @param domain The SNS domain to get avatar for (e.g., \"example.sol\")\n * @returns Promise that resolves to the avatar URL or null if not found\n */\nconst getSNSAvatar = async (domain: string): Promise<string | null> => {\n try {\n const response = await fetch(`${SNS_API_BASE_URL}/image?domain=${encodeURIComponent(domain)}`);\n\n if (!response.ok) {\n return null;\n }\n\n const data: SNSImageApiResponse = await response.json();\n\n if (data.result && Array.isArray(data.result) && data.result.length > 0) {\n const imageData = data.result.find((item) => item.domain === domain);\n return imageData?.image ?? data.result[0]?.image ?? null;\n }\n\n return null;\n } catch (error) {\n console.warn('Failed to fetch SNS avatar:', error);\n return null;\n }\n};\n\n/**\n * Generates a default avatar URL based on address/domain\n * Using DiceBear API for consistent avatars\n *\n * @param identifier The address or domain to generate avatar for\n * @returns Default avatar URL\n */\nconst getDefaultAvatar = (identifier: string): string => {\n const seed = encodeURIComponent(identifier);\n return `https://api.dicebear.com/7.x/identicon/svg?seed=${seed}`;\n};\n\n/**\n * Searches and returns the avatar URL for a given Solana address or SNS domain.\n * Priority order:\n * 1. Check cache\n * 2. If it's a valid SNS domain (.sol), try to get SNS avatar\n * 3. Generate default avatar based on identifier\n *\n * @param addressOrDomain The Solana address or SNS domain to look up avatar for\n * @returns A promise that resolves to the avatar URL\n */\nexport const getSolanaAddressAvatar = async (addressOrDomain: string): Promise<string> => {\n if (!addressOrDomain) {\n return getDefaultAvatar('default');\n }\n\n const trimmedInput = addressOrDomain.trim();\n\n if (!trimmedInput) {\n return getDefaultAvatar('default');\n }\n\n // Normalize the input to use as a cache key\n const normalizedInput = trimmedInput.toLowerCase();\n\n // Check the cache: if the result exists, return it immediately\n const cachedAvatar = solanaAvatarCache.get(normalizedInput);\n if (cachedAvatar !== undefined) {\n return cachedAvatar;\n }\n\n let resultAvatar: string;\n\n try {\n if (isValidSNSDomain(trimmedInput)) {\n const snsAvatar = await getSNSAvatar(trimmedInput);\n if (snsAvatar) {\n resultAvatar = snsAvatar;\n } else {\n resultAvatar = getDefaultAvatar(trimmedInput);\n }\n } else if (isValidSolanaAddress(trimmedInput)) {\n resultAvatar = getDefaultAvatar(trimmedInput);\n } else {\n resultAvatar = getDefaultAvatar(trimmedInput);\n }\n } catch (error) {\n console.warn('Error resolving avatar:', error);\n resultAvatar = getDefaultAvatar(trimmedInput);\n }\n\n // Store the result in the cache\n solanaAvatarCache.set(normalizedInput, resultAvatar);\n\n return resultAvatar;\n};\n\n/**\n * Clears the Solana avatar cache\n */\nexport const clearSolanaAvatarCache = (): void => {\n solanaAvatarCache.clear();\n};\n\n/**\n * Gets the current avatar cache size\n */\nexport const getSolanaAvatarCacheSize = (): number => {\n return solanaAvatarCache.size;\n};\n\n/**\n * Preloads avatar for a given address/domain\n *\n * @param addressOrDomain The address or domain to preload avatar for\n */\nexport const preloadSolanaAvatar = async (addressOrDomain: string): Promise<void> => {\n try {\n await getSolanaAddressAvatar(addressOrDomain);\n } catch (error) {\n console.warn('Failed to preload avatar:', error);\n }\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 * SNS API endpoint for resolving Solana addresses to domain names\n */\nconst SNS_API_BASE_URL = 'https://sns-api.bonfida.com';\n\n/**\n * Interface for SNS API response\n */\ninterface SNSApiResponse {\n result: Record<string, string> | null;\n}\n\n/**\n * Fetches the SNS domain name for a given Solana address using the SNS API\n *\n * @param address The Solana address to resolve\n * @returns Promise that resolves to the SNS domain name or null if not found\n */\nconst getSNSName = async (address: string): Promise<string | null> => {\n try {\n const response = await fetch(`${SNS_API_BASE_URL}/v2/user/fav-domains/${address}`);\n\n if (!response.ok) {\n return null;\n }\n\n const data: SNSApiResponse = await response.json();\n\n if (data.result && typeof data.result === 'object' && address in data.result) {\n return data.result[address];\n }\n\n return null;\n } catch (error) {\n console.warn('Failed to fetch SNS name:', error);\n return null;\n }\n};\n\n/**\n * Validates if the provided string is a valid Solana address\n *\n * @param address The string to validate\n * @returns boolean indicating if the address is valid\n */\nconst isValidSolanaAddress = (address: string): boolean => {\n return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);\n};\n\n/**\n * Searches and returns the account name (label) for a given Solana address.\n * Priority order:\n * 1. Check cache\n * 2. Try to resolve SNS domain name\n * 3. Fall back to original address\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 no name is found.\n */\nexport const getSolanaAddressName = async (address: string): Promise<string> => {\n if (!address) {\n return address || '';\n }\n\n if (!isValidSolanaAddress(address.trim())) {\n return address;\n }\n\n // Normalize the address to use as a cache key, ensuring case-insensitivity.\n const normalizedAddress = address.toLowerCase().trim();\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 let resultName = address; // Default fallback\n\n try {\n const snsName = await getSNSName(address);\n if (snsName) {\n resultName = snsName;\n }\n } catch (error) {\n console.warn('Error resolving address name:', error);\n // resultName остается как оригинальный адрес\n }\n\n // Store the result in the cache\n solanaNameCache.set(normalizedAddress, resultName);\n\n return resultName;\n};\n\n/**\n * Clears the Solana name cache\n */\nexport const clearSolanaNameCache = (): void => {\n solanaNameCache.clear();\n};\n\n/**\n * Gets the current cache size\n */\nexport const getSolanaNameCacheSize = (): number => {\n return solanaNameCache.size;\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.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Oleksandr Tkach",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
}
|
|
37
37
|
],
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@tuwaio/orbit-core": ">=0",
|
|
40
|
-
"gill": ">=0.
|
|
39
|
+
"@tuwaio/orbit-core": ">=0.2",
|
|
40
|
+
"gill": ">=0.14",
|
|
41
41
|
"@wallet-standard/app": "1.x.x",
|
|
42
42
|
"@wallet-standard/ui-core": "1.x.x",
|
|
43
43
|
"@wallet-standard/ui-registry": "1.x.x"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"tsup": "^8.5.
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
47
|
"typescript": "^5.9.3",
|
|
48
|
-
"gill": "^0.
|
|
48
|
+
"gill": "^0.14.0",
|
|
49
49
|
"@wallet-standard/app": "^1.1.0",
|
|
50
50
|
"@wallet-standard/ui-core": "^1.0.0",
|
|
51
51
|
"@wallet-standard/ui-registry": "^1.0.1",
|
|
52
|
-
"@tuwaio/orbit-core": "^0.
|
|
52
|
+
"@tuwaio/orbit-core": "^0.2.0"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"start": "tsup src/index.ts --watch",
|