@tuwaio/orbit-solana 0.2.13 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,15 +3,16 @@
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
5
 
6
- `@tuwaio/orbit-solana` provides concrete implementations of low-level Solana-specific communication primitives for Layer 2 (L2) of the TUWA Orbit stack. Engineered strictly on top of **`gill`** (the modern high-performance alternative to legacy Solana web3.js classes), this package integrates Wallet Standard capabilities for discovery and persistent caching of RPC connections.
6
+ `@tuwaio/orbit-solana` provides concrete implementations of low-level Solana-specific communication primitives for Layer 2 (L2) of the TUWA Orbit stack. Engineered strictly on top of **`@solana/kit`** (the official modern high-performance modular Solana JavaScript SDK), this package integrates Wallet Standard capabilities for discovery and persistent caching of RPC connections. It is fully compatible with instructions generated by **Codama** (`@codama/renderers-js`), completely eradicating legacy dependencies such as `gill` or `@solana/web3.js`.
7
7
 
8
8
  ---
9
9
 
10
10
  ## 🏛️ Core Capabilities
11
11
 
12
- - **Gill RPC Caching:** Creates and caches high-performance Solana RPC connections (`createSolanaClientWithCache`, `createSolanaRPC`) to optimize transaction dispatching and query pipelines.
12
+ - **Modular Solana RPC Caching:** Creates and caches high-performance Solana RPC connections (`createSolanaClientWithCache`, `createSolanaRPC`) powered by `@solana/kit` to optimize transaction dispatching and query pipelines.
13
13
  - **Wallet Standard Integration:** Low-level discovery routines (`getAvailableSolanaConnectors`, `getConnectedSolanaConnector`) that strictly align with the `@wallet-standard/app` specifications.
14
- - **Profile Metadata Engine:** Resolves user-defined account descriptors (names, avatars) directly from Wallet Standard providers.
14
+ - **Codama Program Architecture:** Seamlessly executes instructions rendered by Codama (`kitImportStrategy: 'rootOnly'`) across Solana Devnet, Testnet, and Mainnet.
15
+ - **Profile Metadata Engine:** Resolves user-defined account descriptors (names, avatars, SNS resolution) directly from Solana accounts.
15
16
  - **Cluster Parameter Resolution:** Maps Solana RPC clusters (mainnet, devnet, testnet) to explorer endpoints and RPC configurations.
16
17
 
17
18
  ---
@@ -19,11 +20,11 @@
19
20
  ## 💾 Installation
20
21
 
21
22
  ```bash
22
- pnpm add @tuwaio/orbit-solana @tuwaio/orbit-core gill @wallet-standard/app @wallet-standard/ui-core @wallet-standard/ui-registry
23
+ pnpm add @tuwaio/orbit-solana @tuwaio/orbit-core @solana/kit @wallet-standard/app @wallet-standard/ui-core @wallet-standard/ui-registry
23
24
  ```
24
25
 
25
26
  > [!IMPORTANT]
26
- > `gill` and `@wallet-standard` packages are peer dependencies and must be installed alongside `@tuwaio/orbit-solana`.
27
+ > `@tuwaio/orbit-core` (>=0.2), `@solana/kit` (>=8.2), and `@wallet-standard` packages (v1.x) are peer dependencies and must be installed alongside `@tuwaio/orbit-solana`.
27
28
 
28
29
  ---
29
30
 
@@ -54,6 +55,20 @@ console.log(
54
55
  );
55
56
  ```
56
57
 
58
+ ### Working with Codama Generated Instructions
59
+
60
+ Use Codama (`@codama/renderers-js`) to generate type-safe instruction builders, then dispatch them using `@solana/kit` and `@tuwaio/orbit-solana`:
61
+
62
+ ```typescript
63
+ import { createSolanaClientWithCache } from '@tuwaio/orbit-solana';
64
+ import { pipe } from '@solana/kit';
65
+ // Instruction generated via Codama
66
+ // import { getIncrementInstruction } from './generated';
67
+
68
+ const client = createSolanaClientWithCache({ rpcUrlOrMoniker: 'devnet' });
69
+ // Send and confirm using client.rpc and standard signers
70
+ ```
71
+
57
72
  ---
58
73
 
59
74
  ## 🔧 API & Module Architecture
package/dist/index.d.mts CHANGED
@@ -1,10 +1,28 @@
1
- import { SolanaClusterMoniker, SolanaClient, Rpc, SolanaRpcApi } from 'gill';
1
+ import { Rpc, SolanaRpcApi, RpcSubscriptions, SolanaRpcSubscriptionsApi, MainnetUrl, DevnetUrl, TestnetUrl } from '@solana/kit';
2
2
  import { ChainIdentifierArray } from '@tuwaio/orbit-core';
3
3
  import * as _wallet_standard_ui_core from '@wallet-standard/ui-core';
4
4
 
5
+ /**
6
+ * Solana cluster moniker type inferred directly from @solana/kit branded cluster URLs plus localnet.
7
+ */
8
+ type SolanaClusterMoniker = (MainnetUrl | DevnetUrl | TestnetUrl)['~cluster'] | 'localnet';
9
+ /**
10
+ * Configuration mapping Solana clusters to their RPC URLs.
11
+ */
5
12
  type SolanaRPCUrls = {
6
13
  rpcUrls: Partial<Record<SolanaClusterMoniker, string>>;
7
14
  };
15
+ /**
16
+ * Alias for Solana RPC methods interface.
17
+ */
18
+ type SolanaRpcMethods = SolanaRpcApi;
19
+ /**
20
+ * Lightweight Solana client wrapper holding RPC and optional subscriptions clients.
21
+ */
22
+ interface SolanaClient {
23
+ rpc: Rpc<SolanaRpcApi>;
24
+ rpcSubscriptions?: RpcSubscriptions<SolanaRpcSubscriptionsApi>;
25
+ }
8
26
 
9
27
  /**
10
28
  * Get Solana clusters from configuration
@@ -61,36 +79,40 @@ declare const getRpcUrlForCluster: ({ cluster, walletCluster, rpcUrls, }: {
61
79
  * - Optimize memory usage
62
80
  * - Maintain consistent client instances throughout the application
63
81
  *
64
- * @param rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')
82
+ * @param params - Object containing rpcUrlOrMoniker and optional rpcUrls
83
+ * @param params.rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')
84
+ * @param params.rpcUrls - Optional custom mapping of cluster monikers to RPC endpoints
65
85
  * @returns Cached or newly created Solana RPC client instance
66
86
  * @throws Error if unable to resolve a valid RPC URL
67
- *
68
- * @example
69
- * ```typescript
70
- * // Using cluster moniker
71
- * const mainnetClient = createSolanaClientWithCache('mainnet');
72
- *
73
- * // Using custom RPC URL
74
- * const customClient = createSolanaClientWithCache('https://my-rpc.example.com');
75
- * ```
76
87
  */
77
88
  declare const createSolanaClientWithCache: ({ rpcUrlOrMoniker, rpcUrls, }: {
78
89
  rpcUrlOrMoniker: string;
79
90
  rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
80
91
  }) => SolanaClient;
92
+ /**
93
+ * Clears the Solana client cache.
94
+ * @internal
95
+ */
96
+ declare const clearSolanaClientCache: () => void;
81
97
 
82
98
  /**
83
99
  * Retrieves a cached RPC client for a given URL or cluster moniker.
84
100
  * If no cached client exists, it creates a new instance.
85
101
  *
86
- * @param rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.
102
+ * @param params - Object containing rpcUrlOrMoniker and optional rpcUrls map.
103
+ * @param params.rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.
104
+ * @param params.rpcUrls - Optional custom mapping of cluster monikers to RPC endpoints.
87
105
  * @returns The RPC client instance.
88
- * @internal
89
106
  */
90
107
  declare const createSolanaRPC: ({ rpcUrlOrMoniker, rpcUrls, }: {
91
108
  rpcUrlOrMoniker: string;
92
109
  rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
93
110
  }) => Rpc<SolanaRpcApi>;
111
+ /**
112
+ * Clears the Solana RPC client cache.
113
+ * @internal
114
+ */
115
+ declare const clearSolanaRpcCache: () => void;
94
116
 
95
117
  /**
96
118
  * The default RPC URLs for each Solana cluster.
@@ -153,12 +175,12 @@ declare const getSolanaNameCacheSize: () => number;
153
175
  * @file This file contains a utility function for generating Solana transaction explorer links.
154
176
  */
155
177
  /**
156
- * Generates a full URL to a transaction on a Solana explorer like Solscan.
178
+ * Generates a full URL to an account, transaction, or block on the Solana explorer.
157
179
  *
158
- * @param {string} url - The url after baseUrl.
159
- * @param chainId
160
- * @returns {string} The full URL to the transaction on the explorer.
180
+ * @param url - The path after baseUrl (e.g. '/tx/...' or '/address/...').
181
+ * @param chainId - Chain ID or cluster name.
182
+ * @returns The full URL on the Solana explorer.
161
183
  */
162
184
  declare const getSolanaExplorerLink: (url?: string, chainId?: string | number | undefined) => string;
163
185
 
164
- export { type SolanaRPCUrls, clearSolanaAvatarCache, clearSolanaNameCache, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableSolanaClusters, getAvailableSolanaConnectors, getCluster, getConnectedSolanaConnector, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaAvatarCacheSize, getSolanaClusters, getSolanaExplorerLink, getSolanaNameCacheSize, isSolanaChainList, isValidSolanaCluster, preloadSolanaAvatar };
186
+ export { type SolanaClient, type SolanaClusterMoniker, type SolanaRPCUrls, type SolanaRpcMethods, clearSolanaAvatarCache, clearSolanaClientCache, clearSolanaNameCache, clearSolanaRpcCache, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableSolanaClusters, getAvailableSolanaConnectors, getCluster, getConnectedSolanaConnector, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaAvatarCacheSize, getSolanaClusters, getSolanaExplorerLink, getSolanaNameCacheSize, isSolanaChainList, isValidSolanaCluster, preloadSolanaAvatar };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,28 @@
1
- import { SolanaClusterMoniker, SolanaClient, Rpc, SolanaRpcApi } from 'gill';
1
+ import { Rpc, SolanaRpcApi, RpcSubscriptions, SolanaRpcSubscriptionsApi, MainnetUrl, DevnetUrl, TestnetUrl } from '@solana/kit';
2
2
  import { ChainIdentifierArray } from '@tuwaio/orbit-core';
3
3
  import * as _wallet_standard_ui_core from '@wallet-standard/ui-core';
4
4
 
5
+ /**
6
+ * Solana cluster moniker type inferred directly from @solana/kit branded cluster URLs plus localnet.
7
+ */
8
+ type SolanaClusterMoniker = (MainnetUrl | DevnetUrl | TestnetUrl)['~cluster'] | 'localnet';
9
+ /**
10
+ * Configuration mapping Solana clusters to their RPC URLs.
11
+ */
5
12
  type SolanaRPCUrls = {
6
13
  rpcUrls: Partial<Record<SolanaClusterMoniker, string>>;
7
14
  };
15
+ /**
16
+ * Alias for Solana RPC methods interface.
17
+ */
18
+ type SolanaRpcMethods = SolanaRpcApi;
19
+ /**
20
+ * Lightweight Solana client wrapper holding RPC and optional subscriptions clients.
21
+ */
22
+ interface SolanaClient {
23
+ rpc: Rpc<SolanaRpcApi>;
24
+ rpcSubscriptions?: RpcSubscriptions<SolanaRpcSubscriptionsApi>;
25
+ }
8
26
 
9
27
  /**
10
28
  * Get Solana clusters from configuration
@@ -61,36 +79,40 @@ declare const getRpcUrlForCluster: ({ cluster, walletCluster, rpcUrls, }: {
61
79
  * - Optimize memory usage
62
80
  * - Maintain consistent client instances throughout the application
63
81
  *
64
- * @param rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')
82
+ * @param params - Object containing rpcUrlOrMoniker and optional rpcUrls
83
+ * @param params.rpcUrlOrMoniker - RPC endpoint URL or cluster moniker (e.g., 'mainnet', 'devnet')
84
+ * @param params.rpcUrls - Optional custom mapping of cluster monikers to RPC endpoints
65
85
  * @returns Cached or newly created Solana RPC client instance
66
86
  * @throws Error if unable to resolve a valid RPC URL
67
- *
68
- * @example
69
- * ```typescript
70
- * // Using cluster moniker
71
- * const mainnetClient = createSolanaClientWithCache('mainnet');
72
- *
73
- * // Using custom RPC URL
74
- * const customClient = createSolanaClientWithCache('https://my-rpc.example.com');
75
- * ```
76
87
  */
77
88
  declare const createSolanaClientWithCache: ({ rpcUrlOrMoniker, rpcUrls, }: {
78
89
  rpcUrlOrMoniker: string;
79
90
  rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
80
91
  }) => SolanaClient;
92
+ /**
93
+ * Clears the Solana client cache.
94
+ * @internal
95
+ */
96
+ declare const clearSolanaClientCache: () => void;
81
97
 
82
98
  /**
83
99
  * Retrieves a cached RPC client for a given URL or cluster moniker.
84
100
  * If no cached client exists, it creates a new instance.
85
101
  *
86
- * @param rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.
102
+ * @param params - Object containing rpcUrlOrMoniker and optional rpcUrls map.
103
+ * @param params.rpcUrlOrMoniker - Either a full RPC URL or a cluster moniker like 'mainnet'.
104
+ * @param params.rpcUrls - Optional custom mapping of cluster monikers to RPC endpoints.
87
105
  * @returns The RPC client instance.
88
- * @internal
89
106
  */
90
107
  declare const createSolanaRPC: ({ rpcUrlOrMoniker, rpcUrls, }: {
91
108
  rpcUrlOrMoniker: string;
92
109
  rpcUrls?: Partial<Record<SolanaClusterMoniker, string>>;
93
110
  }) => Rpc<SolanaRpcApi>;
111
+ /**
112
+ * Clears the Solana RPC client cache.
113
+ * @internal
114
+ */
115
+ declare const clearSolanaRpcCache: () => void;
94
116
 
95
117
  /**
96
118
  * The default RPC URLs for each Solana cluster.
@@ -153,12 +175,12 @@ declare const getSolanaNameCacheSize: () => number;
153
175
  * @file This file contains a utility function for generating Solana transaction explorer links.
154
176
  */
155
177
  /**
156
- * Generates a full URL to a transaction on a Solana explorer like Solscan.
178
+ * Generates a full URL to an account, transaction, or block on the Solana explorer.
157
179
  *
158
- * @param {string} url - The url after baseUrl.
159
- * @param chainId
160
- * @returns {string} The full URL to the transaction on the explorer.
180
+ * @param url - The path after baseUrl (e.g. '/tx/...' or '/address/...').
181
+ * @param chainId - Chain ID or cluster name.
182
+ * @returns The full URL on the Solana explorer.
161
183
  */
162
184
  declare const getSolanaExplorerLink: (url?: string, chainId?: string | number | undefined) => string;
163
185
 
164
- export { type SolanaRPCUrls, clearSolanaAvatarCache, clearSolanaNameCache, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableSolanaClusters, getAvailableSolanaConnectors, getCluster, getConnectedSolanaConnector, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaAvatarCacheSize, getSolanaClusters, getSolanaExplorerLink, getSolanaNameCacheSize, isSolanaChainList, isValidSolanaCluster, preloadSolanaAvatar };
186
+ export { type SolanaClient, type SolanaClusterMoniker, type SolanaRPCUrls, type SolanaRpcMethods, clearSolanaAvatarCache, clearSolanaClientCache, clearSolanaNameCache, clearSolanaRpcCache, createSolanaClientWithCache, createSolanaRPC, defaultRpcUrlsByMoniker, getAvailableSolanaClusters, getAvailableSolanaConnectors, getCluster, getConnectedSolanaConnector, getRpcUrlForCluster, getSolanaAddressAvatar, getSolanaAddressName, getSolanaAvatarCacheSize, getSolanaClusters, getSolanaExplorerLink, getSolanaNameCacheSize, isSolanaChainList, isValidSolanaCluster, preloadSolanaAvatar };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var gill=require('gill'),orbitCore=require('@tuwaio/orbit-core'),app=require('@wallet-standard/app'),uiRegistry=require('@wallet-standard/ui-registry');var o={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function d(t){let e=t.split(":");if(e.length<2)return null;let r=e[1];r==="mainnet-beta"&&(r="mainnet");let n=r;return n in o?n:null}function S(t,e){let r={},n=Object.keys(e||o);for(let a of t){if(typeof a!="string")continue;let s=d(a);if(!s||!n.includes(s))continue;let f=e?.[s]??o[s];f&&(r[s]=f);}return r}function F(t,e){if(e&&e.length>0){let r=S(e,t);return Object.keys(r)}return Object.keys(t||o)}function j(t){return t.length>0&&t.every(e=>typeof e=="string")}function B(){return Object.keys(o)}function T(t){return t in o}var m=({cluster:t,walletCluster:e})=>{let r="mainnet";return t?t.includes(":")?t.split(":")[1]:t:e??r},_=({cluster:t,walletCluster:e,rpcUrls:r})=>r[e??t]??"https://api.mainnet-beta.solana.com/";function h(t){try{return new URL(t),!0}catch{return false}}var u=new Map,Z=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(u.has(t))return u.get(t);let r=h(t)?t:e?e[t]??o[t]:o[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 A(t){try{return new URL(t),!0}catch{return false}}var p=new Map,tt=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(p.has(t))return p.get(t);let r=A(t)?t:e?e[t]??o[t]:o[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 p.set(t,n),n};function g(){return orbitCore.filterUniqueByKey(app.getWallets().get().map(uiRegistry.getOrCreateUiWalletForStandardWallet),"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 lt(){let t=orbitCore.lastConnectedConnectorHelpers.getLastConnectedConnector(),r=g().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,b="https://image-api.bonfida.com",k=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),P=t=>/^[a-z0-9-]+\.sol$/.test(t.toLowerCase()),U=async t=>{try{let e=await fetch(`${b}/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)}`,N=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(P(e)){let s=await U(e);s?a=s:a=i(e);}else k(e),a=i(e);}catch(s){console.warn("Error resolving avatar:",s),a=i(e);}return l.set(r,a),a},ut=()=>{l.clear();},pt=()=>l.size,ft=async t=>{try{await N(t);}catch(e){console.warn("Failed to preload avatar:",e);}};var c=new Map,M="https://sns-api.bonfida.com",L=async t=>{try{let e=await fetch(`${M}/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}},$=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),gt=async t=>{if(!t)return t||"";if(!$(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 L(t);a&&(n=a);}catch(a){console.warn("Error resolving address name:",a);}return c.set(e,n),n},dt=()=>{c.clear();},St=()=>c.size;var At=(t,e)=>{let r=m({cluster:String(e)})??"mainnet",n=gill.getExplorerLink(),a=n.endsWith("/")?n.slice(0,-1):n,s=r?`?cluster=${r}`:"";return `${a}${t||"/"}${s}`};
2
- exports.clearSolanaAvatarCache=ut;exports.clearSolanaNameCache=dt;exports.createSolanaClientWithCache=Z;exports.createSolanaRPC=tt;exports.defaultRpcUrlsByMoniker=o;exports.getAvailableSolanaClusters=B;exports.getAvailableSolanaConnectors=g;exports.getCluster=m;exports.getConnectedSolanaConnector=lt;exports.getRpcUrlForCluster=_;exports.getSolanaAddressAvatar=N;exports.getSolanaAddressName=gt;exports.getSolanaAvatarCacheSize=pt;exports.getSolanaClusters=F;exports.getSolanaExplorerLink=At;exports.getSolanaNameCacheSize=St;exports.isSolanaChainList=j;exports.isValidSolanaCluster=T;exports.preloadSolanaAvatar=ft;
1
+ 'use strict';var kit=require('@solana/kit'),orbitCore=require('@tuwaio/orbit-core'),app=require('@wallet-standard/app'),uiRegistry=require('@wallet-standard/ui-registry');var s={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function d(t){let e=t.split(":");if(e.length<2)return null;let r=e[1];r==="mainnet-beta"&&(r="mainnet");let a=r;return a in s?a:null}function S(t,e){let r={},a=Object.keys(e||s);for(let n of t){if(typeof n!="string")continue;let o=d(n);if(!o||!a.includes(o))continue;let f=e?.[o]??s[o];f&&(r[o]=f);}return r}function j(t,e){if(e&&e.length>0){let r=S(e,t);return Object.keys(r)}return Object.keys(t||s)}function z(t){return t.length>0&&t.every(e=>typeof e=="string")}function T(){return Object.keys(s)}function W(t){return t in s}var m=({cluster:t,walletCluster:e})=>{let r="mainnet";return t?t.includes(":")?t.split(":")[1]:t:e??r},q=({cluster:t,walletCluster:e,rpcUrls:r})=>r[e??t]??"https://api.mainnet-beta.solana.com/";function h(t){try{return new URL(t),!0}catch{return false}}function R(t){return t.replace(/^http/,"ws")}var l=new Map,X=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(l.has(t))return l.get(t);let r=h(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 a=kit.createSolanaRpc(r),n;try{n=kit.createSolanaRpcSubscriptions(R(r));}catch{}let o={rpc:a,...n?{rpcSubscriptions:n}:{}};return l.set(t,o),o},G=()=>{l.clear();};function v(t){try{return new URL(t),!0}catch{return false}}var c=new Map,Z=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(c.has(t))return c.get(t);let r=v(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 a=kit.createSolanaRpc(r);return c.set(t,a),a},O=()=>{c.clear();};function g(){return orbitCore.filterUniqueByKey(app.getWallets().get().map(uiRegistry.getOrCreateUiWalletForStandardWallet),"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(a=>t.features.includes(a))?!1:t.chains.every(a=>{try{if(typeof a!="string")return !1;let n=a.split(":");return n.length>=1&&n[0]==="solana"}catch(n){return console.warn("Error parsing chain:",a,n),!1}})}catch(e){return console.warn("Error filtering wallet:",e),false}})}function it(){let t=orbitCore.lastConnectedConnectorHelpers.getLastConnectedConnector(),r=g().find(a=>a.accounts.find(n=>n.address.toLowerCase()===t?.address?.toLowerCase()));if(!r)throw new Error("Connector not provided. Cannot perform chain check.");return r}var u=new Map,P="https://image-api.bonfida.com",N=t=>/^[a-z0-9-]+\.sol$/.test(t.toLowerCase()),L=async t=>{try{let e=await fetch(`${P}/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(n=>n.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)}`,M=async t=>{if(!t)return i("default");let e=t.trim();if(!e)return i("default");let r=e.toLowerCase(),a=u.get(r);if(a!==void 0)return a;let n;try{if(N(e)){let o=await L(e);o?n=o:n=i(e);}else kit.isAddress(e),n=i(e);}catch(o){console.warn("Error resolving avatar:",o),n=i(e);}return u.set(r,n),n},ut=()=>{u.clear();},pt=()=>u.size,ft=async t=>{try{await M(t);}catch(e){console.warn("Failed to preload avatar:",e);}};var p=new Map,$="https://sns-api.bonfida.com",I=async t=>{try{let e=await fetch(`${$}/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}},dt=async t=>{if(!t)return t||"";let e=t.trim();if(!kit.isAddress(e))return t;let r=e.toLowerCase(),a=p.get(r);if(a!==void 0)return a;let n=t;try{let o=await I(e);o&&(n=o);}catch(o){console.warn("Error resolving address name:",o);}return p.set(r,n),n},St=()=>{p.clear();},Ct=()=>p.size;var B="https://explorer.solana.com",Rt=(t,e)=>{let r=m({cluster:String(e)})??"mainnet",a=B,n=r&&r!=="mainnet"?`?cluster=${r}`:"",o=t?t.startsWith("/")?t:`/${t}`:"/";return `${a}${o}${n}`};
2
+ exports.clearSolanaAvatarCache=ut;exports.clearSolanaClientCache=G;exports.clearSolanaNameCache=St;exports.clearSolanaRpcCache=O;exports.createSolanaClientWithCache=X;exports.createSolanaRPC=Z;exports.defaultRpcUrlsByMoniker=s;exports.getAvailableSolanaClusters=T;exports.getAvailableSolanaConnectors=g;exports.getCluster=m;exports.getConnectedSolanaConnector=it;exports.getRpcUrlForCluster=q;exports.getSolanaAddressAvatar=M;exports.getSolanaAddressName=dt;exports.getSolanaAvatarCacheSize=pt;exports.getSolanaClusters=j;exports.getSolanaExplorerLink=Rt;exports.getSolanaNameCacheSize=Ct;exports.isSolanaChainList=z;exports.isValidSolanaCluster=W;exports.preloadSolanaAvatar=ft;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import {createSolanaClient,createSolanaRpc,getExplorerLink}from'gill';import {filterUniqueByKey,lastConnectedConnectorHelpers}from'@tuwaio/orbit-core';import {getWallets}from'@wallet-standard/app';import {getOrCreateUiWalletForStandardWallet}from'@wallet-standard/ui-registry';var o={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function d(t){let e=t.split(":");if(e.length<2)return null;let r=e[1];r==="mainnet-beta"&&(r="mainnet");let n=r;return n in o?n:null}function S(t,e){let r={},n=Object.keys(e||o);for(let a of t){if(typeof a!="string")continue;let s=d(a);if(!s||!n.includes(s))continue;let f=e?.[s]??o[s];f&&(r[s]=f);}return r}function F(t,e){if(e&&e.length>0){let r=S(e,t);return Object.keys(r)}return Object.keys(t||o)}function j(t){return t.length>0&&t.every(e=>typeof e=="string")}function B(){return Object.keys(o)}function T(t){return t in o}var m=({cluster:t,walletCluster:e})=>{let r="mainnet";return t?t.includes(":")?t.split(":")[1]:t:e??r},_=({cluster:t,walletCluster:e,rpcUrls:r})=>r[e??t]??"https://api.mainnet-beta.solana.com/";function h(t){try{return new URL(t),!0}catch{return false}}var u=new Map,Z=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(u.has(t))return u.get(t);let r=h(t)?t:e?e[t]??o[t]:o[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 A(t){try{return new URL(t),!0}catch{return false}}var p=new Map,tt=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(p.has(t))return p.get(t);let r=A(t)?t:e?e[t]??o[t]:o[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 p.set(t,n),n};function g(){return filterUniqueByKey(getWallets().get().map(getOrCreateUiWalletForStandardWallet),"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 lt(){let t=lastConnectedConnectorHelpers.getLastConnectedConnector(),r=g().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,b="https://image-api.bonfida.com",k=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),P=t=>/^[a-z0-9-]+\.sol$/.test(t.toLowerCase()),U=async t=>{try{let e=await fetch(`${b}/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)}`,N=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(P(e)){let s=await U(e);s?a=s:a=i(e);}else k(e),a=i(e);}catch(s){console.warn("Error resolving avatar:",s),a=i(e);}return l.set(r,a),a},ut=()=>{l.clear();},pt=()=>l.size,ft=async t=>{try{await N(t);}catch(e){console.warn("Failed to preload avatar:",e);}};var c=new Map,M="https://sns-api.bonfida.com",L=async t=>{try{let e=await fetch(`${M}/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}},$=t=>/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(t),gt=async t=>{if(!t)return t||"";if(!$(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 L(t);a&&(n=a);}catch(a){console.warn("Error resolving address name:",a);}return c.set(e,n),n},dt=()=>{c.clear();},St=()=>c.size;var At=(t,e)=>{let r=m({cluster:String(e)})??"mainnet",n=getExplorerLink(),a=n.endsWith("/")?n.slice(0,-1):n,s=r?`?cluster=${r}`:"";return `${a}${t||"/"}${s}`};
2
- export{ut as clearSolanaAvatarCache,dt as clearSolanaNameCache,Z as createSolanaClientWithCache,tt as createSolanaRPC,o as defaultRpcUrlsByMoniker,B as getAvailableSolanaClusters,g as getAvailableSolanaConnectors,m as getCluster,lt as getConnectedSolanaConnector,_ as getRpcUrlForCluster,N as getSolanaAddressAvatar,gt as getSolanaAddressName,pt as getSolanaAvatarCacheSize,F as getSolanaClusters,At as getSolanaExplorerLink,St as getSolanaNameCacheSize,j as isSolanaChainList,T as isValidSolanaCluster,ft as preloadSolanaAvatar};
1
+ import {createSolanaRpc,createSolanaRpcSubscriptions,isAddress}from'@solana/kit';import {filterUniqueByKey,lastConnectedConnectorHelpers}from'@tuwaio/orbit-core';import {getWallets}from'@wallet-standard/app';import {getOrCreateUiWalletForStandardWallet}from'@wallet-standard/ui-registry';var s={mainnet:"https://api.mainnet-beta.solana.com",devnet:"https://api.devnet.solana.com",testnet:"https://api.testnet.solana.com"};function d(t){let e=t.split(":");if(e.length<2)return null;let r=e[1];r==="mainnet-beta"&&(r="mainnet");let a=r;return a in s?a:null}function S(t,e){let r={},a=Object.keys(e||s);for(let n of t){if(typeof n!="string")continue;let o=d(n);if(!o||!a.includes(o))continue;let f=e?.[o]??s[o];f&&(r[o]=f);}return r}function j(t,e){if(e&&e.length>0){let r=S(e,t);return Object.keys(r)}return Object.keys(t||s)}function z(t){return t.length>0&&t.every(e=>typeof e=="string")}function T(){return Object.keys(s)}function W(t){return t in s}var m=({cluster:t,walletCluster:e})=>{let r="mainnet";return t?t.includes(":")?t.split(":")[1]:t:e??r},q=({cluster:t,walletCluster:e,rpcUrls:r})=>r[e??t]??"https://api.mainnet-beta.solana.com/";function h(t){try{return new URL(t),!0}catch{return false}}function R(t){return t.replace(/^http/,"ws")}var l=new Map,X=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(l.has(t))return l.get(t);let r=h(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 a=createSolanaRpc(r),n;try{n=createSolanaRpcSubscriptions(R(r));}catch{}let o={rpc:a,...n?{rpcSubscriptions:n}:{}};return l.set(t,o),o},G=()=>{l.clear();};function v(t){try{return new URL(t),!0}catch{return false}}var c=new Map,Z=({rpcUrlOrMoniker:t,rpcUrls:e})=>{if(c.has(t))return c.get(t);let r=v(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 a=createSolanaRpc(r);return c.set(t,a),a},O=()=>{c.clear();};function g(){return filterUniqueByKey(getWallets().get().map(getOrCreateUiWalletForStandardWallet),"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(a=>t.features.includes(a))?!1:t.chains.every(a=>{try{if(typeof a!="string")return !1;let n=a.split(":");return n.length>=1&&n[0]==="solana"}catch(n){return console.warn("Error parsing chain:",a,n),!1}})}catch(e){return console.warn("Error filtering wallet:",e),false}})}function it(){let t=lastConnectedConnectorHelpers.getLastConnectedConnector(),r=g().find(a=>a.accounts.find(n=>n.address.toLowerCase()===t?.address?.toLowerCase()));if(!r)throw new Error("Connector not provided. Cannot perform chain check.");return r}var u=new Map,P="https://image-api.bonfida.com",N=t=>/^[a-z0-9-]+\.sol$/.test(t.toLowerCase()),L=async t=>{try{let e=await fetch(`${P}/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(n=>n.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)}`,M=async t=>{if(!t)return i("default");let e=t.trim();if(!e)return i("default");let r=e.toLowerCase(),a=u.get(r);if(a!==void 0)return a;let n;try{if(N(e)){let o=await L(e);o?n=o:n=i(e);}else isAddress(e),n=i(e);}catch(o){console.warn("Error resolving avatar:",o),n=i(e);}return u.set(r,n),n},ut=()=>{u.clear();},pt=()=>u.size,ft=async t=>{try{await M(t);}catch(e){console.warn("Failed to preload avatar:",e);}};var p=new Map,$="https://sns-api.bonfida.com",I=async t=>{try{let e=await fetch(`${$}/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}},dt=async t=>{if(!t)return t||"";let e=t.trim();if(!isAddress(e))return t;let r=e.toLowerCase(),a=p.get(r);if(a!==void 0)return a;let n=t;try{let o=await I(e);o&&(n=o);}catch(o){console.warn("Error resolving address name:",o);}return p.set(r,n),n},St=()=>{p.clear();},Ct=()=>p.size;var B="https://explorer.solana.com",Rt=(t,e)=>{let r=m({cluster:String(e)})??"mainnet",a=B,n=r&&r!=="mainnet"?`?cluster=${r}`:"",o=t?t.startsWith("/")?t:`/${t}`:"/";return `${a}${o}${n}`};
2
+ export{ut as clearSolanaAvatarCache,G as clearSolanaClientCache,St as clearSolanaNameCache,O as clearSolanaRpcCache,X as createSolanaClientWithCache,Z as createSolanaRPC,s as defaultRpcUrlsByMoniker,T as getAvailableSolanaClusters,g as getAvailableSolanaConnectors,m as getCluster,it as getConnectedSolanaConnector,q as getRpcUrlForCluster,M as getSolanaAddressAvatar,dt as getSolanaAddressName,pt as getSolanaAvatarCacheSize,j as getSolanaClusters,Rt as getSolanaExplorerLink,Ct as getSolanaNameCacheSize,z as isSolanaChainList,W as isValidSolanaCluster,ft as preloadSolanaAvatar};
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@tuwaio/orbit-solana",
3
- "version": "0.2.13",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "author": "Oleksandr Tkach",
6
6
  "license": "Apache-2.0",
7
- "description": "Layer 2 (L2) of the TUWA Ecosystem. Low-level Solana-specific communication primitives and RPC caching utilities powered strictly by gill.",
7
+ "description": "Layer 2 (L2) of the TUWA Ecosystem. Low-level Solana-specific communication primitives and RPC caching utilities powered strictly by @solana/kit.",
8
8
  "main": "./dist/index.js",
9
9
  "module": "./dist/index.mjs",
10
10
  "types": "./dist/index.d.ts",
@@ -38,22 +38,23 @@
38
38
  ],
39
39
  "peerDependencies": {
40
40
  "@tuwaio/orbit-core": ">=0.2",
41
- "gill": ">=0.14",
41
+ "@solana/kit": ">=8.2",
42
42
  "@wallet-standard/app": "1.x.x",
43
43
  "@wallet-standard/ui-core": "1.x.x",
44
44
  "@wallet-standard/ui-registry": "1.x.x"
45
45
  },
46
46
  "devDependencies": {
47
+ "@tuwaio/orbit-core": "^0.3.0",
47
48
  "tsup": "^8.5.1",
48
49
  "typescript": "^6.0.3",
49
- "gill": "^0.14.0",
50
+ "@solana/kit": "^8.2.0",
50
51
  "@wallet-standard/app": "^1.1.1",
51
52
  "@wallet-standard/ui-core": "^1.0.1",
52
- "@wallet-standard/ui-registry": "^1.1.1",
53
- "@tuwaio/orbit-core": "^0.2.15"
53
+ "@wallet-standard/ui-registry": "^1.1.1"
54
54
  },
55
55
  "scripts": {
56
56
  "start": "tsup src/index.ts --watch",
57
- "build": "tsup"
57
+ "build": "tsup",
58
+ "test": "vitest run"
58
59
  }
59
60
  }