@vue-solana/vue 0.1.2 → 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 CHANGED
@@ -9,8 +9,8 @@ New to Solana? Start with the official docs and the project concepts guide:
9
9
  - [Solana Documentation](https://solana.com/docs)
10
10
  - [Solana RPC Methods](https://solana.com/docs/rpc)
11
11
  - [Solana Clusters](https://solana.com/docs/references/clusters)
12
- - [Vue Solana Concepts Guide](https://github.com/vue-solana/vue-solana/tree/main/apps/docs/content/concepts/solana-for-vue-developers.md)
13
- - [`@vue-solana/vue` docs](https://github.com/vue-solana/vue-solana/tree/main/apps/docs/content/packages/vue.md)
12
+ - [Vue Solana Concepts Guide](https://vue-solana-docs.vercel.app/concepts/solana-for-vue-developers)
13
+ - [`@vue-solana/vue` docs](https://vue-solana-docs.vercel.app/packages/vue)
14
14
 
15
15
  ## Install
16
16
 
@@ -25,25 +25,29 @@ npm install @vue-solana/vue @vue-solana/core @solana/web3-compat
25
25
  ## Plugin Setup
26
26
 
27
27
  ```ts
28
- import { createApp } from 'vue'
29
- import { createSolanaPlugin } from '@vue-solana/vue'
30
- import App from './App.vue'
28
+ import { createApp } from "vue";
29
+ import { createSolanaPlugin } from "@vue-solana/vue";
30
+ import App from "./App.vue";
31
31
 
32
32
  createApp(App)
33
- .use(createSolanaPlugin({
34
- cluster: 'devnet'
35
- }))
36
- .mount('#app')
33
+ .use(
34
+ createSolanaPlugin({
35
+ cluster: "devnet",
36
+ }),
37
+ )
38
+ .mount("#app");
37
39
  ```
38
40
 
39
41
  You can also pass a custom RPC endpoint:
40
42
 
41
43
  ```ts
42
- createApp(App).use(createSolanaPlugin({
43
- cluster: 'mainnet-beta',
44
- endpoint: 'https://your-rpc.example.com',
45
- commitment: 'confirmed'
46
- }))
44
+ createApp(App).use(
45
+ createSolanaPlugin({
46
+ cluster: "mainnet-beta",
47
+ endpoint: "https://your-rpc.example.com",
48
+ commitment: "confirmed",
49
+ }),
50
+ );
47
51
  ```
48
52
 
49
53
  Supported clusters are `mainnet-beta`, `devnet`, `testnet`, and `localnet`. Use `mainnet-beta` for Solana mainnet; this is Solana's official cluster name.
@@ -58,16 +62,9 @@ https://faucet.solana.com
58
62
 
59
63
  ```vue
60
64
  <script setup lang="ts">
61
- import { useRpc } from '@vue-solana/vue'
62
-
63
- const {
64
- cluster,
65
- endpoint,
66
- status,
67
- error,
68
- latestBlockhash,
69
- checkConnection
70
- } = useRpc()
65
+ import { useRpc } from "@vue-solana/vue";
66
+
67
+ const { cluster, endpoint, status, error, latestBlockhash, checkConnection } = useRpc();
71
68
  </script>
72
69
 
73
70
  <template>
@@ -86,11 +83,11 @@ const {
86
83
 
87
84
  ```vue
88
85
  <script setup lang="ts">
89
- import { ref } from 'vue'
90
- import { useBalance } from '@vue-solana/vue'
86
+ import { ref } from "vue";
87
+ import { useBalance } from "@vue-solana/vue";
91
88
 
92
- const address = ref('PASTE_A_SOLANA_ADDRESS')
93
- const { balance, loading, error, refresh } = useBalance(address)
89
+ const address = ref("PASTE_A_SOLANA_ADDRESS");
90
+ const { balance, loading, error, refresh } = useBalance(address);
94
91
  </script>
95
92
 
96
93
  <template>
@@ -107,34 +104,47 @@ const { balance, loading, error, refresh } = useBalance(address)
107
104
 
108
105
  ```vue
109
106
  <script setup lang="ts">
110
- import { useWallet } from '@vue-solana/vue'
107
+ import { useWallet, useWallets } from "@vue-solana/vue";
111
108
 
112
- const { publicKey, connected, connecting, connect, disconnect } = useWallet()
109
+ const { wallets, selectedWallet, selectWallet, refreshWallets } = useWallets();
110
+ const { publicKey, connected, connecting, connect, disconnect } = useWallet();
113
111
  </script>
114
112
 
115
113
  <template>
116
114
  <section>
115
+ <button type="button" @click="refreshWallets">Refresh Wallets</button>
116
+ <button
117
+ v-for="wallet in wallets"
118
+ :key="wallet.name"
119
+ type="button"
120
+ @click="selectWallet(wallet)"
121
+ >
122
+ {{ wallet.name }}
123
+ </button>
124
+ <p>Selected: {{ selectedWallet?.name ?? "None" }}</p>
117
125
  <p>Connected: {{ connected }}</p>
118
126
  <p>Public key: {{ publicKey?.toBase58() }}</p>
119
127
  <p v-if="connecting">Connecting...</p>
120
- <button type="button" @click="connect">Connect</button>
121
- <button type="button" @click="disconnect">Disconnect</button>
128
+ <button type="button" :disabled="!selectedWallet || connected || connecting" @click="connect">
129
+ Connect
130
+ </button>
131
+ <button type="button" :disabled="!connected" @click="disconnect">Disconnect</button>
122
132
  </section>
123
133
  </template>
124
134
  ```
125
135
 
126
- Browser wallet discovery is not included yet. `connect()` works only after you configure a wallet object that implements `SolanaWallet`.
136
+ Browser wallets are discovered through the Solana Wallet Standard. `connect()` works after selecting a discovered wallet or configuring a custom `SolanaWallet`.
127
137
 
128
138
  ## Transaction State
129
139
 
130
140
  ```ts
131
- import { useSignAndSendTransaction } from '@vue-solana/vue'
141
+ import { useSignAndSendTransaction } from "@vue-solana/vue";
132
142
 
133
- const { signature, loading, error, execute } = useSignAndSendTransaction()
143
+ const { signature, loading, error, execute } = useSignAndSendTransaction();
134
144
 
135
145
  await execute(transaction, {
136
- skipPreflight: false
137
- })
146
+ skipPreflight: false,
147
+ });
138
148
  ```
139
149
 
140
150
  The current wallet must be connected and support either `signAndSendTransaction` or `signTransaction`.
@@ -147,7 +157,7 @@ This README includes small snippets for quick reference. For a complete runnable
147
157
  pnpm dev:vue
148
158
  ```
149
159
 
150
- Source: [`examples/vue-vite`](https://github.com/vue-solana/vue-solana/tree/main/examples/vue-vite)
160
+ Docs: [`examples/vue-vite`](https://vue-solana-docs.vercel.app/examples/vue-vite)
151
161
 
152
162
  ## API
153
163
 
@@ -168,12 +178,8 @@ Source: [`examples/vue-vite`](https://github.com/vue-solana/vue-solana/tree/main
168
178
  If TypeScript cannot resolve `@solana/web3-compat`, add `types/web3-compat.d.ts` to your app:
169
179
 
170
180
  ```ts
171
- declare module '@solana/web3-compat' {
172
- export type {
173
- Commitment,
174
- SendOptions,
175
- TransactionSignature
176
- } from '@solana/web3.js'
181
+ declare module "@solana/web3-compat" {
182
+ export type { Commitment, SendOptions, TransactionSignature } from "@solana/web3.js";
177
183
  export {
178
184
  Connection,
179
185
  Keypair,
@@ -181,8 +187,8 @@ declare module '@solana/web3-compat' {
181
187
  SystemProgram,
182
188
  Transaction,
183
189
  TransactionInstruction,
184
- VersionedTransaction
185
- } from '@solana/web3.js'
190
+ VersionedTransaction,
191
+ } from "@solana/web3.js";
186
192
  }
187
193
  ```
188
194
 
@@ -190,4 +196,4 @@ Make sure your `tsconfig.json` includes `types/**/*.d.ts` or another pattern tha
190
196
 
191
197
  ## Status
192
198
 
193
- This package is early-stage. RPC and balance reads are usable; first-class browser wallet adapter support is planned.
199
+ This package is early-stage. RPC, balance, wallet, and transaction composables are usable.
package/dist/index.cjs CHANGED
@@ -56,9 +56,13 @@ function useBalance(address, commitment) {
56
56
  loading.value = false;
57
57
  }
58
58
  }
59
- vue.watch(() => vue.toValue(address), () => {
60
- void refresh();
61
- }, { immediate: true });
59
+ vue.watch(
60
+ () => vue.toValue(address),
61
+ () => {
62
+ void refresh().catch(() => void 0);
63
+ },
64
+ { immediate: true }
65
+ );
62
66
  return {
63
67
  balance,
64
68
  loading,
@@ -70,14 +74,63 @@ function useBalance(address, commitment) {
70
74
  function useWallet() {
71
75
  const solana = useSolana();
72
76
  const wallet = solana.wallet;
77
+ const connecting = vue.ref(false);
78
+ const disconnecting = vue.ref(false);
79
+ async function connect() {
80
+ const activeWallet = wallet.value;
81
+ if (!activeWallet) {
82
+ throw new Error("No Solana wallet is configured");
83
+ }
84
+ connecting.value = true;
85
+ try {
86
+ const connection = activeWallet.connect();
87
+ vue.triggerRef(wallet);
88
+ await connection;
89
+ vue.triggerRef(wallet);
90
+ console.info("[Vue Solana] Wallet connected", {
91
+ publicKey: activeWallet.publicKey?.toBase58() ?? null
92
+ });
93
+ } catch (error) {
94
+ vue.triggerRef(wallet);
95
+ console.error("[Vue Solana] Wallet connection failed", error);
96
+ throw error;
97
+ } finally {
98
+ connecting.value = false;
99
+ }
100
+ }
101
+ async function disconnect() {
102
+ const activeWallet = wallet.value;
103
+ if (!activeWallet) {
104
+ return;
105
+ }
106
+ const publicKey = activeWallet.publicKey?.toBase58() ?? null;
107
+ disconnecting.value = true;
108
+ try {
109
+ await activeWallet.disconnect();
110
+ vue.triggerRef(wallet);
111
+ console.info("[Vue Solana] Wallet disconnected", { publicKey });
112
+ } catch (error) {
113
+ vue.triggerRef(wallet);
114
+ console.error("[Vue Solana] Wallet disconnection failed", error);
115
+ throw error;
116
+ } finally {
117
+ disconnecting.value = false;
118
+ }
119
+ }
120
+ const isConnecting = vue.computed(() => Boolean(connecting.value || wallet.value?.connecting));
121
+ const isDisconnecting = vue.computed(
122
+ () => Boolean(disconnecting.value || wallet.value?.disconnecting)
123
+ );
73
124
  return {
74
125
  wallet,
75
126
  publicKey: vue.computed(() => wallet.value?.publicKey ?? null),
76
127
  connected: vue.computed(() => Boolean(wallet.value?.connected && wallet.value.publicKey)),
77
- connecting: vue.computed(() => Boolean(wallet.value?.connecting)),
128
+ connecting: isConnecting,
129
+ disconnecting: isDisconnecting,
130
+ loading: vue.computed(() => isConnecting.value || isDisconnecting.value),
78
131
  setWallet: solana.setWallet,
79
- connect: () => wallet.value?.connect() ?? Promise.reject(new Error("No Solana wallet is configured")),
80
- disconnect: () => wallet.value?.disconnect() ?? Promise.resolve()
132
+ connect,
133
+ disconnect
81
134
  };
82
135
  }
83
136
 
@@ -117,14 +170,27 @@ function useSignAndSendTransaction() {
117
170
  });
118
171
  }
119
172
 
173
+ function useWallets() {
174
+ const solana = useSolana();
175
+ return {
176
+ wallets: solana.wallets,
177
+ selectedWallet: solana.selectedWallet,
178
+ refreshWallets: solana.refreshWallets,
179
+ selectWallet: solana.selectWallet
180
+ };
181
+ }
182
+
120
183
  function createSolanaPlugin(options = {}) {
121
184
  return {
122
185
  install(app) {
123
186
  const context = core.createSolanaContext(options);
124
187
  const wallet = vue.shallowRef(options.wallet ?? null);
188
+ const wallets = vue.shallowRef([]);
189
+ const selectedWallet = vue.shallowRef(null);
125
190
  const status = vue.ref("idle");
126
191
  const error = vue.ref(null);
127
192
  const latestBlockhash = vue.ref(null);
193
+ let unsubscribeWallets = null;
128
194
  async function checkConnection() {
129
195
  status.value = "checking";
130
196
  error.value = null;
@@ -149,14 +215,36 @@ function createSolanaPlugin(options = {}) {
149
215
  console.error("[Vue Solana] Connection failed", cause);
150
216
  }
151
217
  }
218
+ function refreshWallets() {
219
+ unsubscribeWallets ??= core.subscribeSolanaWallets(refreshWallets);
220
+ wallets.value = core.getRegisteredSolanaWallets();
221
+ if (selectedWallet.value) {
222
+ selectedWallet.value = wallets.value.find((nextWallet) => nextWallet.name === selectedWallet.value?.name) ?? null;
223
+ if (!selectedWallet.value) {
224
+ wallet.value = options.wallet ?? null;
225
+ }
226
+ }
227
+ }
228
+ function selectWallet(nextWallet) {
229
+ selectedWallet.value = nextWallet;
230
+ wallet.value = nextWallet ? core.adaptSolanaStandardWallet(nextWallet, {
231
+ chain: core.getSolanaChain(context.cluster),
232
+ onChange: () => vue.triggerRef(wallet)
233
+ }) : options.wallet ?? null;
234
+ }
152
235
  const vueContext = {
153
236
  ...context,
154
237
  wallet,
155
238
  status,
156
239
  error,
157
240
  latestBlockhash,
241
+ wallets,
242
+ selectedWallet,
158
243
  checkConnection,
244
+ refreshWallets,
245
+ selectWallet,
159
246
  setWallet(nextWallet) {
247
+ selectedWallet.value = null;
160
248
  wallet.value = nextWallet;
161
249
  }
162
250
  };
@@ -177,3 +265,4 @@ exports.useSignAndSendTransaction = useSignAndSendTransaction;
177
265
  exports.useSolana = useSolana;
178
266
  exports.useTransaction = useTransaction;
179
267
  exports.useWallet = useWallet;
268
+ exports.useWallets = useWallets;
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import * as vue from 'vue';
2
2
  import { MaybeRefOrGetter, Ref, InjectionKey, App } from 'vue';
3
3
  import { PublicKey, Commitment, TransactionSignature } from '@solana/web3-compat';
4
4
  import * as _vue_solana_core from '@vue-solana/core';
5
- import { SendTransactionOptions, SolanaContext, SolanaWallet, SolanaConfig } from '@vue-solana/core';
5
+ import { SendTransactionOptions, SolanaContext, SolanaWallet, SolanaWalletInfo, SolanaConfig } from '@vue-solana/core';
6
6
 
7
7
  declare function useBalance(address: MaybeRefOrGetter<PublicKey | string | null | undefined>, commitment?: Commitment): {
8
8
  balance: vue.Ref<number | null, number | null>;
@@ -45,19 +45,32 @@ declare function useWallet(): {
45
45
  publicKey: vue.ComputedRef<any>;
46
46
  connected: vue.ComputedRef<boolean>;
47
47
  connecting: vue.ComputedRef<boolean>;
48
+ disconnecting: vue.ComputedRef<boolean>;
49
+ loading: vue.ComputedRef<boolean>;
48
50
  setWallet: (wallet: _vue_solana_core.SolanaWallet | null) => void;
49
51
  connect: () => Promise<void>;
50
52
  disconnect: () => Promise<void>;
51
53
  };
52
54
 
53
- type SolanaConnectionStatus = 'idle' | 'checking' | 'connected' | 'error';
55
+ declare function useWallets(): {
56
+ wallets: vue.Ref<_vue_solana_core.SolanaWalletInfo[], _vue_solana_core.SolanaWalletInfo[]>;
57
+ selectedWallet: vue.Ref<_vue_solana_core.SolanaWalletInfo | null, _vue_solana_core.SolanaWalletInfo | null>;
58
+ refreshWallets: () => void;
59
+ selectWallet: (wallet: _vue_solana_core.SolanaWalletInfo | null) => void;
60
+ };
61
+
62
+ type SolanaConnectionStatus = "idle" | "checking" | "connected" | "error";
54
63
  interface VueSolanaContext extends SolanaContext {
55
64
  wallet: Ref<SolanaWallet | null>;
56
65
  status: Ref<SolanaConnectionStatus>;
57
66
  error: Ref<string | null>;
58
67
  latestBlockhash: Ref<string | null>;
68
+ wallets: Ref<SolanaWalletInfo[]>;
69
+ selectedWallet: Ref<SolanaWalletInfo | null>;
59
70
  checkConnection: () => Promise<void>;
60
71
  setWallet: (wallet: SolanaWallet | null) => void;
72
+ refreshWallets: () => void;
73
+ selectWallet: (wallet: SolanaWalletInfo | null) => void;
61
74
  }
62
75
  declare const solanaInjectionKey: InjectionKey<VueSolanaContext>;
63
76
 
@@ -69,5 +82,5 @@ declare function createSolanaPlugin(options?: VueSolanaPluginOptions): {
69
82
  };
70
83
  declare const VueSolana: typeof createSolanaPlugin;
71
84
 
72
- export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet };
85
+ export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet, useWallets };
73
86
  export type { SolanaConnectionStatus, VueSolanaContext, VueSolanaPluginOptions };
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import * as vue from 'vue';
2
2
  import { MaybeRefOrGetter, Ref, InjectionKey, App } from 'vue';
3
3
  import { PublicKey, Commitment, TransactionSignature } from '@solana/web3-compat';
4
4
  import * as _vue_solana_core from '@vue-solana/core';
5
- import { SendTransactionOptions, SolanaContext, SolanaWallet, SolanaConfig } from '@vue-solana/core';
5
+ import { SendTransactionOptions, SolanaContext, SolanaWallet, SolanaWalletInfo, SolanaConfig } from '@vue-solana/core';
6
6
 
7
7
  declare function useBalance(address: MaybeRefOrGetter<PublicKey | string | null | undefined>, commitment?: Commitment): {
8
8
  balance: vue.Ref<number | null, number | null>;
@@ -45,19 +45,32 @@ declare function useWallet(): {
45
45
  publicKey: vue.ComputedRef<any>;
46
46
  connected: vue.ComputedRef<boolean>;
47
47
  connecting: vue.ComputedRef<boolean>;
48
+ disconnecting: vue.ComputedRef<boolean>;
49
+ loading: vue.ComputedRef<boolean>;
48
50
  setWallet: (wallet: _vue_solana_core.SolanaWallet | null) => void;
49
51
  connect: () => Promise<void>;
50
52
  disconnect: () => Promise<void>;
51
53
  };
52
54
 
53
- type SolanaConnectionStatus = 'idle' | 'checking' | 'connected' | 'error';
55
+ declare function useWallets(): {
56
+ wallets: vue.Ref<_vue_solana_core.SolanaWalletInfo[], _vue_solana_core.SolanaWalletInfo[]>;
57
+ selectedWallet: vue.Ref<_vue_solana_core.SolanaWalletInfo | null, _vue_solana_core.SolanaWalletInfo | null>;
58
+ refreshWallets: () => void;
59
+ selectWallet: (wallet: _vue_solana_core.SolanaWalletInfo | null) => void;
60
+ };
61
+
62
+ type SolanaConnectionStatus = "idle" | "checking" | "connected" | "error";
54
63
  interface VueSolanaContext extends SolanaContext {
55
64
  wallet: Ref<SolanaWallet | null>;
56
65
  status: Ref<SolanaConnectionStatus>;
57
66
  error: Ref<string | null>;
58
67
  latestBlockhash: Ref<string | null>;
68
+ wallets: Ref<SolanaWalletInfo[]>;
69
+ selectedWallet: Ref<SolanaWalletInfo | null>;
59
70
  checkConnection: () => Promise<void>;
60
71
  setWallet: (wallet: SolanaWallet | null) => void;
72
+ refreshWallets: () => void;
73
+ selectWallet: (wallet: SolanaWalletInfo | null) => void;
61
74
  }
62
75
  declare const solanaInjectionKey: InjectionKey<VueSolanaContext>;
63
76
 
@@ -69,5 +82,5 @@ declare function createSolanaPlugin(options?: VueSolanaPluginOptions): {
69
82
  };
70
83
  declare const VueSolana: typeof createSolanaPlugin;
71
84
 
72
- export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet };
85
+ export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet, useWallets };
73
86
  export type { SolanaConnectionStatus, VueSolanaContext, VueSolanaPluginOptions };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as vue from 'vue';
2
2
  import { MaybeRefOrGetter, Ref, InjectionKey, App } from 'vue';
3
3
  import { PublicKey, Commitment, TransactionSignature } from '@solana/web3-compat';
4
4
  import * as _vue_solana_core from '@vue-solana/core';
5
- import { SendTransactionOptions, SolanaContext, SolanaWallet, SolanaConfig } from '@vue-solana/core';
5
+ import { SendTransactionOptions, SolanaContext, SolanaWallet, SolanaWalletInfo, SolanaConfig } from '@vue-solana/core';
6
6
 
7
7
  declare function useBalance(address: MaybeRefOrGetter<PublicKey | string | null | undefined>, commitment?: Commitment): {
8
8
  balance: vue.Ref<number | null, number | null>;
@@ -45,19 +45,32 @@ declare function useWallet(): {
45
45
  publicKey: vue.ComputedRef<any>;
46
46
  connected: vue.ComputedRef<boolean>;
47
47
  connecting: vue.ComputedRef<boolean>;
48
+ disconnecting: vue.ComputedRef<boolean>;
49
+ loading: vue.ComputedRef<boolean>;
48
50
  setWallet: (wallet: _vue_solana_core.SolanaWallet | null) => void;
49
51
  connect: () => Promise<void>;
50
52
  disconnect: () => Promise<void>;
51
53
  };
52
54
 
53
- type SolanaConnectionStatus = 'idle' | 'checking' | 'connected' | 'error';
55
+ declare function useWallets(): {
56
+ wallets: vue.Ref<_vue_solana_core.SolanaWalletInfo[], _vue_solana_core.SolanaWalletInfo[]>;
57
+ selectedWallet: vue.Ref<_vue_solana_core.SolanaWalletInfo | null, _vue_solana_core.SolanaWalletInfo | null>;
58
+ refreshWallets: () => void;
59
+ selectWallet: (wallet: _vue_solana_core.SolanaWalletInfo | null) => void;
60
+ };
61
+
62
+ type SolanaConnectionStatus = "idle" | "checking" | "connected" | "error";
54
63
  interface VueSolanaContext extends SolanaContext {
55
64
  wallet: Ref<SolanaWallet | null>;
56
65
  status: Ref<SolanaConnectionStatus>;
57
66
  error: Ref<string | null>;
58
67
  latestBlockhash: Ref<string | null>;
68
+ wallets: Ref<SolanaWalletInfo[]>;
69
+ selectedWallet: Ref<SolanaWalletInfo | null>;
59
70
  checkConnection: () => Promise<void>;
60
71
  setWallet: (wallet: SolanaWallet | null) => void;
72
+ refreshWallets: () => void;
73
+ selectWallet: (wallet: SolanaWalletInfo | null) => void;
61
74
  }
62
75
  declare const solanaInjectionKey: InjectionKey<VueSolanaContext>;
63
76
 
@@ -69,5 +82,5 @@ declare function createSolanaPlugin(options?: VueSolanaPluginOptions): {
69
82
  };
70
83
  declare const VueSolana: typeof createSolanaPlugin;
71
84
 
72
- export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet };
85
+ export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet, useWallets };
73
86
  export type { SolanaConnectionStatus, VueSolanaContext, VueSolanaPluginOptions };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PublicKey } from '@solana/web3-compat';
2
- import { inject, computed, ref, watch, toValue, shallowRef } from 'vue';
3
- import { signAndSendTransaction, createSolanaContext } from '@vue-solana/core';
2
+ import { inject, computed, ref, watch, toValue, triggerRef, shallowRef } from 'vue';
3
+ import { signAndSendTransaction, createSolanaContext, adaptSolanaStandardWallet, getSolanaChain, subscribeSolanaWallets, getRegisteredSolanaWallets } from '@vue-solana/core';
4
4
 
5
5
  const solanaInjectionKey = Symbol("VueSolana");
6
6
 
@@ -54,9 +54,13 @@ function useBalance(address, commitment) {
54
54
  loading.value = false;
55
55
  }
56
56
  }
57
- watch(() => toValue(address), () => {
58
- void refresh();
59
- }, { immediate: true });
57
+ watch(
58
+ () => toValue(address),
59
+ () => {
60
+ void refresh().catch(() => void 0);
61
+ },
62
+ { immediate: true }
63
+ );
60
64
  return {
61
65
  balance,
62
66
  loading,
@@ -68,14 +72,63 @@ function useBalance(address, commitment) {
68
72
  function useWallet() {
69
73
  const solana = useSolana();
70
74
  const wallet = solana.wallet;
75
+ const connecting = ref(false);
76
+ const disconnecting = ref(false);
77
+ async function connect() {
78
+ const activeWallet = wallet.value;
79
+ if (!activeWallet) {
80
+ throw new Error("No Solana wallet is configured");
81
+ }
82
+ connecting.value = true;
83
+ try {
84
+ const connection = activeWallet.connect();
85
+ triggerRef(wallet);
86
+ await connection;
87
+ triggerRef(wallet);
88
+ console.info("[Vue Solana] Wallet connected", {
89
+ publicKey: activeWallet.publicKey?.toBase58() ?? null
90
+ });
91
+ } catch (error) {
92
+ triggerRef(wallet);
93
+ console.error("[Vue Solana] Wallet connection failed", error);
94
+ throw error;
95
+ } finally {
96
+ connecting.value = false;
97
+ }
98
+ }
99
+ async function disconnect() {
100
+ const activeWallet = wallet.value;
101
+ if (!activeWallet) {
102
+ return;
103
+ }
104
+ const publicKey = activeWallet.publicKey?.toBase58() ?? null;
105
+ disconnecting.value = true;
106
+ try {
107
+ await activeWallet.disconnect();
108
+ triggerRef(wallet);
109
+ console.info("[Vue Solana] Wallet disconnected", { publicKey });
110
+ } catch (error) {
111
+ triggerRef(wallet);
112
+ console.error("[Vue Solana] Wallet disconnection failed", error);
113
+ throw error;
114
+ } finally {
115
+ disconnecting.value = false;
116
+ }
117
+ }
118
+ const isConnecting = computed(() => Boolean(connecting.value || wallet.value?.connecting));
119
+ const isDisconnecting = computed(
120
+ () => Boolean(disconnecting.value || wallet.value?.disconnecting)
121
+ );
71
122
  return {
72
123
  wallet,
73
124
  publicKey: computed(() => wallet.value?.publicKey ?? null),
74
125
  connected: computed(() => Boolean(wallet.value?.connected && wallet.value.publicKey)),
75
- connecting: computed(() => Boolean(wallet.value?.connecting)),
126
+ connecting: isConnecting,
127
+ disconnecting: isDisconnecting,
128
+ loading: computed(() => isConnecting.value || isDisconnecting.value),
76
129
  setWallet: solana.setWallet,
77
- connect: () => wallet.value?.connect() ?? Promise.reject(new Error("No Solana wallet is configured")),
78
- disconnect: () => wallet.value?.disconnect() ?? Promise.resolve()
130
+ connect,
131
+ disconnect
79
132
  };
80
133
  }
81
134
 
@@ -115,14 +168,27 @@ function useSignAndSendTransaction() {
115
168
  });
116
169
  }
117
170
 
171
+ function useWallets() {
172
+ const solana = useSolana();
173
+ return {
174
+ wallets: solana.wallets,
175
+ selectedWallet: solana.selectedWallet,
176
+ refreshWallets: solana.refreshWallets,
177
+ selectWallet: solana.selectWallet
178
+ };
179
+ }
180
+
118
181
  function createSolanaPlugin(options = {}) {
119
182
  return {
120
183
  install(app) {
121
184
  const context = createSolanaContext(options);
122
185
  const wallet = shallowRef(options.wallet ?? null);
186
+ const wallets = shallowRef([]);
187
+ const selectedWallet = shallowRef(null);
123
188
  const status = ref("idle");
124
189
  const error = ref(null);
125
190
  const latestBlockhash = ref(null);
191
+ let unsubscribeWallets = null;
126
192
  async function checkConnection() {
127
193
  status.value = "checking";
128
194
  error.value = null;
@@ -147,14 +213,36 @@ function createSolanaPlugin(options = {}) {
147
213
  console.error("[Vue Solana] Connection failed", cause);
148
214
  }
149
215
  }
216
+ function refreshWallets() {
217
+ unsubscribeWallets ??= subscribeSolanaWallets(refreshWallets);
218
+ wallets.value = getRegisteredSolanaWallets();
219
+ if (selectedWallet.value) {
220
+ selectedWallet.value = wallets.value.find((nextWallet) => nextWallet.name === selectedWallet.value?.name) ?? null;
221
+ if (!selectedWallet.value) {
222
+ wallet.value = options.wallet ?? null;
223
+ }
224
+ }
225
+ }
226
+ function selectWallet(nextWallet) {
227
+ selectedWallet.value = nextWallet;
228
+ wallet.value = nextWallet ? adaptSolanaStandardWallet(nextWallet, {
229
+ chain: getSolanaChain(context.cluster),
230
+ onChange: () => triggerRef(wallet)
231
+ }) : options.wallet ?? null;
232
+ }
150
233
  const vueContext = {
151
234
  ...context,
152
235
  wallet,
153
236
  status,
154
237
  error,
155
238
  latestBlockhash,
239
+ wallets,
240
+ selectedWallet,
156
241
  checkConnection,
242
+ refreshWallets,
243
+ selectWallet,
157
244
  setWallet(nextWallet) {
245
+ selectedWallet.value = null;
158
246
  wallet.value = nextWallet;
159
247
  }
160
248
  };
@@ -165,4 +253,4 @@ function createSolanaPlugin(options = {}) {
165
253
  }
166
254
  const VueSolana = createSolanaPlugin;
167
255
 
168
- export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet };
256
+ export { VueSolana, createSolanaPlugin, solanaInjectionKey, useBalance, useConnection, useRpc, useSignAndSendTransaction, useSolana, useTransaction, useWallet, useWallets };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue-solana/vue",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Vue plugin and composables for Solana applications.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@vue-solana/core": "0.1.2"
41
+ "@vue-solana/core": "0.2.0"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@solana/web3-compat": "^0.0.21",