@vue-solana/core 4.0.0 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -30
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/shared/core.CQFtdLrA.mjs +334 -0
- package/dist/solana-pay-confirmation.d.ts +23 -0
- package/dist/solana-pay-confirmation.mjs +97 -0
- package/dist/solana-pay-status.d.ts +5 -0
- package/dist/solana-pay-status.mjs +13 -0
- package/dist/solana-pay.d.ts +140 -0
- package/dist/solana-pay.mjs +1 -0
- package/package.json +15 -1
package/README.md
CHANGED
|
@@ -12,7 +12,9 @@ Framework-agnostic Solana primitives for Vue Solana libraries and apps that want
|
|
|
12
12
|
This package is part of the Vue Solana family. Choose based on your app framework:
|
|
13
13
|
|
|
14
14
|
- [`@vue-solana/core`](https://www.npmjs.com/package/@vue-solana/core) — framework-agnostic Solana primitives: Kit RPC client helpers, wallet types, Android MWA registration, iOS browser wallet helpers, message signing, token account reads, and transaction helpers. **Use this directly** when you want shared RPC/wallet/transaction helpers without installing a Vue plugin (non-Vue apps, server/relayer code, or your own framework integration).
|
|
15
|
+
|
|
15
16
|
- [`@vue-solana/vue`](https://www.npmjs.com/package/@vue-solana/vue) — Vue 3 plugin plus composables for RPC, account, balance, wallet, message signing, live data, and transactions. **Use this** in plain Vue 3 apps (Vite, etc.).
|
|
17
|
+
|
|
16
18
|
- [`@vue-solana/nuxt`](https://www.npmjs.com/package/@vue-solana/nuxt) — Nuxt module that installs the Vue plugin and auto-imports the composables. **Use this** in Nuxt 3/4 apps.
|
|
17
19
|
|
|
18
20
|
This package (`@vue-solana/core`) is the foundation the other two build on; most apps should install `@vue-solana/vue` or `@vue-solana/nuxt` rather than this package directly.
|
|
@@ -24,28 +26,45 @@ Use this package directly when you want Kit RPC client helpers, shared wallet ty
|
|
|
24
26
|
Official Solana docs:
|
|
25
27
|
|
|
26
28
|
- [Solana Documentation](https://solana.com/docs)
|
|
29
|
+
|
|
27
30
|
- [Solana RPC Methods](https://solana.com/docs/rpc)
|
|
31
|
+
|
|
28
32
|
- [Solana Clusters](https://solana.com/docs/references/clusters)
|
|
29
33
|
|
|
30
34
|
Full Vue Solana docs:
|
|
31
35
|
|
|
32
36
|
- [`@vue-solana/core`](https://vue-solana-docs.vercel.app/packages/core)
|
|
37
|
+
|
|
33
38
|
- [Live demo](https://vue-solana-docs.vercel.app/demo)
|
|
39
|
+
|
|
34
40
|
- [Getting Started](https://vue-solana-docs.vercel.app/getting-started)
|
|
41
|
+
|
|
35
42
|
- [Agent Skill](https://vue-solana-docs.vercel.app/agent-skill)
|
|
43
|
+
|
|
36
44
|
- [Troubleshooting](https://vue-solana-docs.vercel.app/troubleshooting)
|
|
37
45
|
|
|
38
46
|
## Features
|
|
39
47
|
|
|
40
48
|
- Cluster-aware RPC helpers with HTTP and WebSocket endpoint defaults.
|
|
49
|
+
|
|
41
50
|
- A Kit `client` with lazy `rpc` and `rpcSubscriptions` plus the official transaction planner and RPC plan-sending executor through `createSolanaClient()` / `createSolanaContext()`.
|
|
51
|
+
|
|
42
52
|
- Shared `SolanaConfig`, `SolanaContext`, and `SolanaWallet` types for framework integrations.
|
|
53
|
+
|
|
43
54
|
- Wallet capability assertions for connection, message signing, and transaction signing flows.
|
|
55
|
+
|
|
44
56
|
- Browser Wallet Standard adaptation primitives.
|
|
57
|
+
|
|
45
58
|
- Android Mobile Wallet Adapter registration helpers.
|
|
59
|
+
|
|
46
60
|
- iOS browser wallet link helpers for supported wallets.
|
|
61
|
+
|
|
47
62
|
- Transaction submission and confirmation helpers.
|
|
63
|
+
|
|
48
64
|
- SPL token account reads through Kit RPC `jsonParsed`.
|
|
65
|
+
|
|
66
|
+
- Solana Pay transfer request URL encoding and parsing, QR rendering, and reference-based settlement confirmation, implemented directly on Kit.
|
|
67
|
+
|
|
49
68
|
- A framework-agnostic action state machine (`createSolanaActionStore`) for async Solana work with per-attempt `AbortSignal` handling.
|
|
50
69
|
|
|
51
70
|
## Compatibility
|
|
@@ -153,54 +172,85 @@ https://faucet.solana.com
|
|
|
153
172
|
Direct subpaths:
|
|
154
173
|
|
|
155
174
|
- `@vue-solana/core/action`
|
|
175
|
+
|
|
156
176
|
- `@vue-solana/core/types`
|
|
177
|
+
|
|
157
178
|
- `@vue-solana/core/address`
|
|
179
|
+
|
|
158
180
|
- `@vue-solana/core/buffer-polyfill`
|
|
181
|
+
|
|
159
182
|
- `@vue-solana/core/clusters`
|
|
183
|
+
|
|
160
184
|
- `@vue-solana/core/errors`
|
|
185
|
+
|
|
161
186
|
- `@vue-solana/core/ios-wallet`
|
|
187
|
+
|
|
162
188
|
- `@vue-solana/core/kit`
|
|
189
|
+
|
|
163
190
|
- `@vue-solana/core/mobile-wallet`
|
|
191
|
+
|
|
164
192
|
- `@vue-solana/core/mobile-wallet-support`
|
|
193
|
+
|
|
165
194
|
- `@vue-solana/core/rpc`
|
|
195
|
+
|
|
166
196
|
- `@vue-solana/core/token-accounts`
|
|
197
|
+
|
|
198
|
+
- `@vue-solana/core/solana-pay`
|
|
199
|
+
|
|
200
|
+
- `@vue-solana/core/solana-pay-confirmation`
|
|
201
|
+
|
|
202
|
+
- `@vue-solana/core/solana-pay-status`
|
|
203
|
+
|
|
167
204
|
- `@vue-solana/core/timeout`
|
|
205
|
+
|
|
168
206
|
- `@vue-solana/core/transaction`
|
|
207
|
+
|
|
169
208
|
- `@vue-solana/core/wallet`
|
|
209
|
+
|
|
170
210
|
- `@vue-solana/core/wallet-standard`
|
|
171
211
|
|
|
172
|
-
| API
|
|
173
|
-
|
|
|
174
|
-
| `DEFAULT_CLUSTER`
|
|
175
|
-
| `parseAddress(value)`
|
|
176
|
-
| `createSolanaClient(config?)`
|
|
177
|
-
| `createSolanaContext(config?)`
|
|
178
|
-
| `getClusterEndpoint(cluster?)`
|
|
179
|
-
| `getClusterWebSocketEndpoint(cluster?)`
|
|
180
|
-
| `getWebSocketEndpoint(endpoint)`
|
|
181
|
-
| `isWalletConnected(wallet)`
|
|
182
|
-
| `assertWalletConnected(wallet)`
|
|
183
|
-
| `assertWalletCanSignMessage(wallet)`
|
|
184
|
-
| `assertWalletCanSign(wallet)`
|
|
185
|
-
| `signAndSendTransaction(client, wallet, transaction, options?)`
|
|
186
|
-
| `confirmTransactionSignature(client, signature, options?)`
|
|
187
|
-
| `getTokenAccountsByOwner(client, owner, options?)`
|
|
188
|
-
| `getTokenAccount(client, address, commitment?)`
|
|
189
|
-
| `getTokenBalance(client, mint, owner, commitment?)`
|
|
190
|
-
| `
|
|
212
|
+
| API | Description |
|
|
213
|
+
| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
214
|
+
| `DEFAULT_CLUSTER` | Default cluster, currently `devnet`. |
|
|
215
|
+
| `parseAddress(value)` | Parses an `Address`, address string, ref-like `{ value }`, getter, `null`, or `undefined` into an `Address \| null`. Invalid strings throw `INVALID_ADDRESS`. |
|
|
216
|
+
| `createSolanaClient(config?)` | Creates a Kit client exposing `rpc`, `rpcSubscriptions`, the official transaction planner, and the official RPC transaction-sending executor. |
|
|
217
|
+
| `createSolanaContext(config?)` | Creates `{ cluster, endpoint, wsEndpoint, client }`. |
|
|
218
|
+
| `getClusterEndpoint(cluster?)` | Returns the HTTP RPC endpoint for a cluster. |
|
|
219
|
+
| `getClusterWebSocketEndpoint(cluster?)` | Returns the WebSocket endpoint for a cluster. |
|
|
220
|
+
| `getWebSocketEndpoint(endpoint)` | Converts `http`/`https` RPC URLs to `ws`/`wss` URLs. |
|
|
221
|
+
| `isWalletConnected(wallet)` | Checks whether a wallet is connected and has an address. |
|
|
222
|
+
| `assertWalletConnected(wallet)` | Throws if the wallet is not connected. |
|
|
223
|
+
| `assertWalletCanSignMessage(wallet)` | Throws if the wallet is disconnected or cannot sign messages. |
|
|
224
|
+
| `assertWalletCanSign(wallet)` | Throws if the wallet cannot sign transactions. |
|
|
225
|
+
| `signAndSendTransaction(client, wallet, transaction, options?)` | Signs and sends raw wire bytes (`SolanaTransaction = Uint8Array`) using a configured wallet. |
|
|
226
|
+
| `confirmTransactionSignature(client, signature, options?)` | Waits for a submitted signature to reach a requested commitment by polling `getSignatureStatuses`. Defaults to `confirmed` and a 60 second timeout. |
|
|
227
|
+
| `getTokenAccountsByOwner(client, owner, options?)` | Returns SPL token accounts for an owner (token and token-2022 programs by default). |
|
|
228
|
+
| `getTokenAccount(client, address, commitment?)` | Reads a single token account, returning `null` when it does not exist. |
|
|
229
|
+
| `getTokenBalance(client, mint, owner, commitment?)` | Reads the token balance for an owner's associated token account as `{ amount, decimals }`. |
|
|
230
|
+
| `encodeURL(fields)` | Builds a `solana:` transfer or transaction request URL from Kit `Address` values; transfer amounts are `bigint` lamports encoded as decimal SOL. |
|
|
231
|
+
| `parseURL(value)` | Parses a transfer request into typed `recipient`, `amount`, `splToken`, `reference`, `label`, `message`, and `memo` fields, or a transaction request `link`. Throws `ParseURLError`. |
|
|
232
|
+
| `createQR(url, size?, background?, color?)` | Renders a Solana Pay URL as an SVG string; `createQRDataURL(url, options)` and `createQRCanvas(canvas, url, options)` cover the other render targets. The `qrcode` encoder loads lazily. |
|
|
233
|
+
| `encodeTransferRequestURL(fields)` / `encodeTransactionRequestURL(fields)` | Builds one request kind explicitly; `encodeURL()` picks the same one from the fields you pass. |
|
|
234
|
+
| `createRecipient(address)` / `createSPLToken(address)` / `isValidSolanaAddress(value)` | Address helpers for request fields and scanned-URL validation. |
|
|
235
|
+
| `createSolanaPayReference()` | Generates a reference `Address` that `getSignaturesForAddress()` can look up later. |
|
|
236
|
+
| `verifySolanaPayPayment(rpc, options)` | Polls the reference and verifies the settled transaction pays `expectedRecipient`, plus `expectedAmount` and `expectedTokenMint` when given. |
|
|
237
|
+
| `getSolanaPayPaymentStatus(rpc, reference)` | Non-verifying read: `pending` before a signature exists, `found` after one does, `error` when the RPC call fails. |
|
|
238
|
+
| `createSolanaActionStore(fn)` | Wraps an async `(signal, ...args) => Promise` function into a `{ getState, subscribe, dispatch, reset, withSignal }` store; each dispatch aborts the prior in-flight call. |
|
|
239
|
+
| `registerSolanaMobileWallet(config?)` | Registers an Android Mobile Wallet Adapter wallet through `@solana-mobile/wallet-standard-mobile`. |
|
|
240
|
+
| `isSolanaMobileWalletSupported()` | Android-support gate used to decide whether to lazy-load the MWA module. |
|
|
241
|
+
| `getSolanaIosWallets(options?)` | Builds iOS browser wallet entries (Phantom, Solflare, Backpack) as universal links. |
|
|
242
|
+
| `handleSolanaIosWalletCallback()` | Completes a pending iOS wallet deep-link callback after redirect. |
|
|
243
|
+
| `adaptSolanaStandardWallet(wallet)` | Adapts a Solana Wallet Standard wallet into `SolanaWallet`. |
|
|
244
|
+
| `disconnectSolanaStandardWallet(walletInfo)` | Disconnects a discovered Wallet Standard wallet through its own `standard:disconnect` feature, without adapting it. Resolves `false` if the wallet has no disconnect feature, and ignores failures after a 2 s timeout. |
|
|
245
|
+
| `getRegisteredSolanaWallets()` | Returns the Wallet Standard wallets currently registered in the app. |
|
|
246
|
+
| `withTimeout(promise, timeoutMs, options?)` | Races a promise against a timeout, with optional abort signal. |
|
|
247
|
+
| `withSolanaTimeout(promise, timeoutMs, options?)` | Solana-specific timeout wrapper with abort and error-code support. |
|
|
248
|
+
| `normalizeSolanaError(error)` | Normalizes an unknown error into a `SolanaError`. |
|
|
249
|
+
| `isSolanaActionAborted(error)` | Detects abort errors from superseded action dispatches. |
|
|
250
|
+
|
|
251
|
+
The Solana Pay request helpers live in `@vue-solana/core/solana-pay`; `createSolanaPayReference()` and `verifySolanaPayPayment()` live in `@vue-solana/core/solana-pay-confirmation`, and `getSolanaPayPaymentStatus()` lives in `@vue-solana/core/solana-pay-status`.
|
|
191
252
|
|
|
192
253
|
The client-sent API is the official Kit `ClientWithTransactionSending` capability installed by `createSolanaClient()`. It does not use a wallet popup; `sent` is reached after the RPC executor completes its send-and-confirm operation at `confirmed` commitment. Use a `payer`, and keep funded keys in a trusted server or relayer context.
|
|
193
|
-
| `registerSolanaMobileWallet(config?)` | Registers an Android Mobile Wallet Adapter wallet through `@solana-mobile/wallet-standard-mobile`. |
|
|
194
|
-
| `isSolanaMobileWalletSupported()` | Android-support gate used to decide whether to lazy-load the MWA module. |
|
|
195
|
-
| `getSolanaIosWallets(options?)` | Builds iOS browser wallet entries (Phantom, Solflare, Backpack) as universal links. |
|
|
196
|
-
| `handleSolanaIosWalletCallback()` | Completes a pending iOS wallet deep-link callback after redirect. |
|
|
197
|
-
| `adaptSolanaStandardWallet(wallet)` | Adapts a Solana Wallet Standard wallet into `SolanaWallet`. |
|
|
198
|
-
| `disconnectSolanaStandardWallet(walletInfo)` | Disconnects a discovered Wallet Standard wallet through its own `standard:disconnect` feature, without adapting it. Resolves `false` if the wallet has no disconnect feature, and ignores failures after a 2 s timeout. |
|
|
199
|
-
| `getRegisteredSolanaWallets()` | Returns the Wallet Standard wallets currently registered in the app. |
|
|
200
|
-
| `withTimeout(promise, timeoutMs, options?)` | Races a promise against a timeout, with optional abort signal. |
|
|
201
|
-
| `withSolanaTimeout(promise, timeoutMs, options?)` | Solana-specific timeout wrapper with abort and error-code support. |
|
|
202
|
-
| `normalizeSolanaError(error)` | Normalizes an unknown error into a `SolanaError`. |
|
|
203
|
-
| `isSolanaActionAborted(error)` | Detects abort errors from superseded action dispatches. |
|
|
204
254
|
|
|
205
255
|
## Wallet Interface
|
|
206
256
|
|
|
@@ -226,9 +276,13 @@ The Wallet Standard adapter verifies the signature a wallet returns from `signMe
|
|
|
226
276
|
Current wallet support:
|
|
227
277
|
|
|
228
278
|
- Browser extension wallets through Wallet Standard packages.
|
|
279
|
+
|
|
229
280
|
- Android native mobile wallets through `@solana-mobile/wallet-standard-mobile` on Android Chrome and Chrome PWAs.
|
|
281
|
+
|
|
230
282
|
- iOS browser wallets for Phantom, Solflare, and Backpack through wallet-specific universal links.
|
|
283
|
+
|
|
231
284
|
- Message signing when the active wallet exposes `signMessage`.
|
|
285
|
+
|
|
232
286
|
- Manual/custom wallet objects that implement `SolanaWallet`.
|
|
233
287
|
|
|
234
288
|
Planned but not supported yet:
|
|
@@ -240,7 +294,9 @@ Planned but not supported yet:
|
|
|
240
294
|
For complete runnable Vue and Nuxt examples that use this package through the framework integrations, see:
|
|
241
295
|
|
|
242
296
|
- [Live demo](https://vue-solana-docs.vercel.app/demo)
|
|
297
|
+
|
|
243
298
|
- <a href="https://vue-solana-docs.vercel.app/examples/vue-vite" target="_blank" rel="noopener noreferrer"><code>examples/vue-vite</code></a>
|
|
299
|
+
|
|
244
300
|
- <a href="https://vue-solana-docs.vercel.app/examples/nuxt" target="_blank" rel="noopener noreferrer"><code>examples/nuxt</code></a>
|
|
245
301
|
|
|
246
302
|
## Bundle size and lean imports
|
|
@@ -250,11 +306,17 @@ The root barrel (`@vue-solana/core`) intentionally re-exports the full `@solana/
|
|
|
250
306
|
Size-sensitive entries can avoid pulling the whole mirror by importing the narrow subpaths instead of the root:
|
|
251
307
|
|
|
252
308
|
- `@vue-solana/core/address`
|
|
309
|
+
|
|
253
310
|
- `@vue-solana/core/errors`
|
|
311
|
+
|
|
254
312
|
- `@vue-solana/core/timeout`
|
|
313
|
+
|
|
255
314
|
- `@vue-solana/core/clusters`
|
|
315
|
+
|
|
256
316
|
- `@vue-solana/core/rpc`
|
|
317
|
+
|
|
257
318
|
- `@vue-solana/core/types`
|
|
319
|
+
|
|
258
320
|
- `@vue-solana/core/kit` (when you explicitly want the Kit surface)
|
|
259
321
|
|
|
260
322
|
Avoid importing `@vue-solana/core/ios-wallet` or `@vue-solana/core/mobile-wallet` from lean code paths: they own `tweetnacl` and the Mobile Wallet Adapter protocol graph respectively. The `@vue-solana/core/mobile-wallet-support` subpath ships only the Android-support gate used to decide whether to lazy-load the MWA module.
|
|
@@ -274,9 +336,13 @@ Docs: [Vue Solana Agent Skill](https://vue-solana-docs.vercel.app/agent-skill)
|
|
|
274
336
|
## Caveats
|
|
275
337
|
|
|
276
338
|
- Public Solana RPC endpoints are useful for development, but production apps should use dedicated RPC infrastructure.
|
|
339
|
+
|
|
277
340
|
- Use `mainnet` for Solana mainnet. This is Solana's official mainnet cluster name; the legacy `mainnet-beta` spelling is still accepted and redirects to the same endpoint.
|
|
341
|
+
|
|
278
342
|
- Transactions are raw wire bytes. Build transaction messages with `@vue-solana/core/kit` (`createTransactionMessage()`, `compileTransaction()`) and serialize them before passing them to wallet flows.
|
|
343
|
+
|
|
279
344
|
- v2.0.0 removed `@solana/web3-compat` and the `web3` subpaths. See the [Kit Migration guide](https://vue-solana-docs.vercel.app/guides/kit-migration) for migrating from v1.
|
|
345
|
+
|
|
280
346
|
- Desktop native app wallets are planned but not implemented yet.
|
|
281
347
|
|
|
282
348
|
## Status
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,10 @@ export { TokenAccountInfo, TokenAccountsByOwnerOptions, TokenBalanceResult, getT
|
|
|
8
8
|
export { confirmTransactionSignature, signAndSendTransaction } from './transaction.js';
|
|
9
9
|
export { WithTimeoutOptions, withSolanaTimeout, withTimeout } from './timeout.js';
|
|
10
10
|
export { SolanaWalletError, SolanaWalletErrorCode, assertWalletCanSign, assertWalletCanSignAndSendTransactions, assertWalletCanSignIn, assertWalletCanSignMessage, assertWalletCanSignTransactions, assertWalletConnected, createNoWalletSelectedError, isWalletConnected } from './wallet.js';
|
|
11
|
+
export { SolanaPayVerificationOptions, SolanaPayVerificationResult, createSolanaPayReference, findTransactionSignaturesByReference, verifySolanaPayPayment } from './solana-pay-confirmation.js';
|
|
12
|
+
export { SolanaPayPaymentStatus, getSolanaPayPaymentStatus } from './solana-pay-status.js';
|
|
11
13
|
export * from '@solana/kit';
|
|
12
14
|
export { AdaptSolanaWalletOptions, AdaptedSolanaWallet, SOLANA_CHAINS, SOLANA_MOBILE_WALLET_ADAPTER_WALLET_NAME, adaptSolanaStandardWallet, disconnectSolanaStandardWallet, getRegisteredSolanaWallets, getSolanaChain, isSolanaStandardWallet, subscribeSolanaWallets } from './wallet-standard.js';
|
|
15
|
+
export { Amount, CreateTransferError, HTTPS_PROTOCOL, Label, Link, MEMO_PROGRAM_ID, Memo, Message, ParseURLError, QROptions, Recipient, Reference, References, SOLANA_PROTOCOL, SOL_DECIMALS, SPLToken, TEN, TOKEN_PROGRAM_ID, TransactionRequestURLFields, TransferRequestURLFields, createQR, createQRCanvas, createQRDataURL, createQROptions, createRecipient, createSPLToken, createStyledQRCode, encodeTransactionRequestURL, encodeTransferRequestURL, encodeURL, isValidSolanaAddress, parseURL } from './solana-pay.js';
|
|
13
16
|
import '@solana/kit-plugin-rpc';
|
|
14
17
|
import '@wallet-standard/base';
|
package/dist/index.mjs
CHANGED
|
@@ -8,5 +8,8 @@ export { getTokenAccount, getTokenAccountsByOwner, getTokenBalance } from './tok
|
|
|
8
8
|
export { confirmTransactionSignature, signAndSendTransaction } from './transaction.mjs';
|
|
9
9
|
export { withSolanaTimeout, withTimeout } from './timeout.mjs';
|
|
10
10
|
export { SolanaWalletError, assertWalletCanSign, assertWalletCanSignAndSendTransactions, assertWalletCanSignIn, assertWalletCanSignMessage, assertWalletCanSignTransactions, assertWalletConnected, createNoWalletSelectedError, isWalletConnected } from './wallet.mjs';
|
|
11
|
+
export { createSolanaPayReference, findTransactionSignaturesByReference, verifySolanaPayPayment } from './solana-pay-confirmation.mjs';
|
|
12
|
+
export { getSolanaPayPaymentStatus } from './solana-pay-status.mjs';
|
|
11
13
|
export * from '@solana/kit';
|
|
14
|
+
export { C as CreateTransferError, H as HTTPS_PROTOCOL, M as MEMO_PROGRAM_ID, P as ParseURLError, S as SOLANA_PROTOCOL, a as SOL_DECIMALS, T as TEN, b as TOKEN_PROGRAM_ID, c as createQR, d as createQRCanvas, e as createQRDataURL, f as createQROptions, g as createRecipient, h as createSPLToken, i as createStyledQRCode, j as encodeTransactionRequestURL, k as encodeTransferRequestURL, l as encodeURL, m as isValidSolanaAddress, p as parseURL } from './shared/core.CQFtdLrA.mjs';
|
|
12
15
|
export { S as SOLANA_CHAINS, a as SOLANA_MOBILE_WALLET_ADAPTER_WALLET_NAME, b as adaptSolanaStandardWallet, d as disconnectSolanaStandardWallet, g as getRegisteredSolanaWallets, c as getSolanaChain, i as isSolanaStandardWallet, s as subscribeSolanaWallets } from './shared/core.A4I__4IR.mjs';
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { address } from '@solana/kit';
|
|
2
|
+
|
|
3
|
+
const SOLANA_PROTOCOL = "solana:";
|
|
4
|
+
const HTTPS_PROTOCOL = "https:";
|
|
5
|
+
const MEMO_PROGRAM_ID = address("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
|
|
6
|
+
const TOKEN_PROGRAM_ID = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
7
|
+
const SOL_DECIMALS = 9;
|
|
8
|
+
const TEN = 10n;
|
|
9
|
+
|
|
10
|
+
class CreateTransferError extends Error {
|
|
11
|
+
name = "CreateTransferError";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function lamportsToDecimal(amount, decimals = SOL_DECIMALS) {
|
|
15
|
+
const neg = amount < 0n;
|
|
16
|
+
const abs = neg ? -amount : amount;
|
|
17
|
+
const base = 10n ** BigInt(decimals);
|
|
18
|
+
const int = abs / base;
|
|
19
|
+
const frac = abs % base;
|
|
20
|
+
if (frac === 0n) return `${neg ? "-" : ""}${int}`;
|
|
21
|
+
const fracStr = frac.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
22
|
+
return `${neg ? "-" : ""}${int}.${fracStr}`;
|
|
23
|
+
}
|
|
24
|
+
function encodeURL(fields) {
|
|
25
|
+
if ("link" in fields) {
|
|
26
|
+
return encodeTransactionRequestURL(fields);
|
|
27
|
+
}
|
|
28
|
+
return encodeTransferRequestURL(fields);
|
|
29
|
+
}
|
|
30
|
+
function encodeTransferRequestURL({
|
|
31
|
+
recipient,
|
|
32
|
+
amount,
|
|
33
|
+
splToken,
|
|
34
|
+
reference,
|
|
35
|
+
label,
|
|
36
|
+
message,
|
|
37
|
+
memo
|
|
38
|
+
}) {
|
|
39
|
+
const url = new URL(SOLANA_PROTOCOL + recipient.toString());
|
|
40
|
+
if (amount !== void 0) {
|
|
41
|
+
url.searchParams.append("amount", lamportsToDecimal(amount, SOL_DECIMALS));
|
|
42
|
+
}
|
|
43
|
+
if (splToken) {
|
|
44
|
+
url.searchParams.append("spl-token", splToken.toString());
|
|
45
|
+
}
|
|
46
|
+
if (reference) {
|
|
47
|
+
const references = Array.isArray(reference) ? reference : [reference];
|
|
48
|
+
for (const ref of references) {
|
|
49
|
+
url.searchParams.append("reference", ref.toString());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (label) {
|
|
53
|
+
url.searchParams.append("label", label);
|
|
54
|
+
}
|
|
55
|
+
if (message) {
|
|
56
|
+
url.searchParams.append("message", message);
|
|
57
|
+
}
|
|
58
|
+
if (memo) {
|
|
59
|
+
url.searchParams.append("memo", memo);
|
|
60
|
+
}
|
|
61
|
+
return url;
|
|
62
|
+
}
|
|
63
|
+
function encodeTransactionRequestURL({
|
|
64
|
+
link,
|
|
65
|
+
label,
|
|
66
|
+
message
|
|
67
|
+
}) {
|
|
68
|
+
const pathname = String(link).replace(/\/\?/, "?").replace(/\/$/, "");
|
|
69
|
+
const url = pathname.startsWith("http") ? new URL(pathname) : new URL(SOLANA_PROTOCOL + pathname);
|
|
70
|
+
if (url.protocol !== SOLANA_PROTOCOL && url.protocol !== HTTPS_PROTOCOL) {
|
|
71
|
+
throw new Error("invalid link");
|
|
72
|
+
}
|
|
73
|
+
if (label) {
|
|
74
|
+
url.searchParams.append("label", label);
|
|
75
|
+
}
|
|
76
|
+
if (message) {
|
|
77
|
+
url.searchParams.append("message", message);
|
|
78
|
+
}
|
|
79
|
+
return url;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class ParseURLError extends Error {
|
|
83
|
+
name = "ParseURLError";
|
|
84
|
+
}
|
|
85
|
+
function toAtomicUnits(amount, decimals) {
|
|
86
|
+
if (!amount || !amount.trim()) {
|
|
87
|
+
throw new ParseURLError("amount invalid");
|
|
88
|
+
}
|
|
89
|
+
const trimmed = amount.trim();
|
|
90
|
+
if (trimmed.startsWith("-")) {
|
|
91
|
+
throw new ParseURLError("amount invalid");
|
|
92
|
+
}
|
|
93
|
+
const [integerPart = "0", fractionalPart = ""] = trimmed.split(".");
|
|
94
|
+
if ((integerPart === "" || integerPart === "0") && fractionalPart === "") {
|
|
95
|
+
throw new ParseURLError("amount invalid");
|
|
96
|
+
}
|
|
97
|
+
if (fractionalPart.length > decimals) {
|
|
98
|
+
throw new ParseURLError("amount decimals invalid");
|
|
99
|
+
}
|
|
100
|
+
const paddedFractional = fractionalPart.padEnd(decimals, "0");
|
|
101
|
+
const integerLamports = BigInt(integerPart || "0") * 10n ** BigInt(decimals);
|
|
102
|
+
const fractionalLamports = BigInt(paddedFractional || "0");
|
|
103
|
+
return integerLamports + fractionalLamports;
|
|
104
|
+
}
|
|
105
|
+
function parseURL(url) {
|
|
106
|
+
if (typeof url === "string") {
|
|
107
|
+
if (url.length > 2048) throw new ParseURLError("length invalid");
|
|
108
|
+
url = new URL(url);
|
|
109
|
+
}
|
|
110
|
+
if (url.protocol !== SOLANA_PROTOCOL) throw new ParseURLError("protocol invalid");
|
|
111
|
+
if (!url.pathname) throw new ParseURLError("pathname missing");
|
|
112
|
+
if (/^https?:\/\//.test(url.pathname)) {
|
|
113
|
+
return parseTransactionRequestURL(url);
|
|
114
|
+
}
|
|
115
|
+
return parseTransferRequestURL(url);
|
|
116
|
+
}
|
|
117
|
+
function parseTransferRequestURL(url) {
|
|
118
|
+
const { pathname, searchParams } = url;
|
|
119
|
+
let recipient;
|
|
120
|
+
try {
|
|
121
|
+
recipient = address(pathname);
|
|
122
|
+
} catch {
|
|
123
|
+
throw new ParseURLError("recipient invalid");
|
|
124
|
+
}
|
|
125
|
+
let amount;
|
|
126
|
+
const amountParam = searchParams.get("amount");
|
|
127
|
+
if (amountParam != null) {
|
|
128
|
+
if (!/^\d+(\.\d+)?$/.test(amountParam)) throw new ParseURLError("amount invalid");
|
|
129
|
+
if (Number.isNaN(Number.parseFloat(amountParam))) throw new ParseURLError("amount invalid");
|
|
130
|
+
amount = toAtomicUnits(amountParam, SOL_DECIMALS);
|
|
131
|
+
}
|
|
132
|
+
let splToken;
|
|
133
|
+
const splTokenParam = searchParams.get("spl-token");
|
|
134
|
+
if (splTokenParam != null) {
|
|
135
|
+
try {
|
|
136
|
+
splToken = address(splTokenParam);
|
|
137
|
+
} catch {
|
|
138
|
+
throw new ParseURLError("token invalid");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const referenceParams = searchParams.getAll("reference");
|
|
142
|
+
let reference;
|
|
143
|
+
if (referenceParams.length) {
|
|
144
|
+
try {
|
|
145
|
+
reference = referenceParams.map((param) => address(param));
|
|
146
|
+
} catch {
|
|
147
|
+
throw new ParseURLError("reference invalid");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const label = searchParams.get("label") || void 0;
|
|
151
|
+
const message = searchParams.get("message") || void 0;
|
|
152
|
+
const memo = searchParams.get("memo") || void 0;
|
|
153
|
+
return {
|
|
154
|
+
recipient,
|
|
155
|
+
amount,
|
|
156
|
+
splToken,
|
|
157
|
+
reference,
|
|
158
|
+
label,
|
|
159
|
+
message,
|
|
160
|
+
memo
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function parseTransactionRequestURL(url) {
|
|
164
|
+
let link;
|
|
165
|
+
try {
|
|
166
|
+
const linkParam = url.pathname.slice(2);
|
|
167
|
+
link = new URL(decodeURIComponent(linkParam) + url.search);
|
|
168
|
+
if (link.protocol !== HTTPS_PROTOCOL) throw new ParseURLError("link invalid");
|
|
169
|
+
} catch {
|
|
170
|
+
throw new ParseURLError("link invalid");
|
|
171
|
+
}
|
|
172
|
+
const label = url.searchParams.get("label") || void 0;
|
|
173
|
+
const message = url.searchParams.get("message") || void 0;
|
|
174
|
+
return {
|
|
175
|
+
link,
|
|
176
|
+
label,
|
|
177
|
+
message
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function loadQRCode() {
|
|
182
|
+
const mod = await import('qrcode');
|
|
183
|
+
return mod.default ?? mod;
|
|
184
|
+
}
|
|
185
|
+
function createQROptions(_url, size = 512, background = "white", color = "black") {
|
|
186
|
+
return {
|
|
187
|
+
width: size,
|
|
188
|
+
margin: 2,
|
|
189
|
+
color: {
|
|
190
|
+
dark: color,
|
|
191
|
+
light: background
|
|
192
|
+
},
|
|
193
|
+
errorCorrectionLevel: "Q",
|
|
194
|
+
dotStyle: "rounded",
|
|
195
|
+
cornerStyle: "extra-rounded"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
async function createStyledQRCode(url, options) {
|
|
199
|
+
const QRCode = await loadQRCode();
|
|
200
|
+
const qrMatrix = QRCode.create(String(url), {
|
|
201
|
+
errorCorrectionLevel: options.errorCorrectionLevel ?? "Q"
|
|
202
|
+
});
|
|
203
|
+
const size = options.width ?? 512;
|
|
204
|
+
const margin = options.margin ?? 2;
|
|
205
|
+
const moduleCount = qrMatrix.modules.size;
|
|
206
|
+
const cellSize = (size - margin * 2) / moduleCount;
|
|
207
|
+
const dotSize = cellSize * 0.8;
|
|
208
|
+
const darkColor = options.color?.dark ?? "black";
|
|
209
|
+
const lightColor = options.color?.light ?? "white";
|
|
210
|
+
let svg = `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">`;
|
|
211
|
+
svg += `<rect width="${size}" height="${size}" fill="${lightColor}"/>`;
|
|
212
|
+
const isFinderPattern = (row, col) => {
|
|
213
|
+
if (row < 7 && col < 7) return true;
|
|
214
|
+
if (row < 7 && col >= moduleCount - 7) return true;
|
|
215
|
+
if (row >= moduleCount - 7 && col < 7) return true;
|
|
216
|
+
return false;
|
|
217
|
+
};
|
|
218
|
+
const drawFinderPattern = (centerX, centerY) => {
|
|
219
|
+
const patternSize = 7 * cellSize;
|
|
220
|
+
const innerSize = 3 * cellSize;
|
|
221
|
+
const middleSize = 5 * cellSize;
|
|
222
|
+
let outerRadius = cellSize;
|
|
223
|
+
let middleRadius = cellSize * 0.5;
|
|
224
|
+
let innerRadius = cellSize * 0.3;
|
|
225
|
+
switch (options.cornerStyle) {
|
|
226
|
+
case "square":
|
|
227
|
+
outerRadius = 0;
|
|
228
|
+
middleRadius = 0;
|
|
229
|
+
innerRadius = 0;
|
|
230
|
+
break;
|
|
231
|
+
case "rounded":
|
|
232
|
+
outerRadius = cellSize * 0.5;
|
|
233
|
+
middleRadius = cellSize * 0.3;
|
|
234
|
+
innerRadius = cellSize * 0.2;
|
|
235
|
+
break;
|
|
236
|
+
case "extra-rounded":
|
|
237
|
+
outerRadius = cellSize;
|
|
238
|
+
middleRadius = cellSize * 0.5;
|
|
239
|
+
innerRadius = cellSize * 0.3;
|
|
240
|
+
break;
|
|
241
|
+
case "full-rounded":
|
|
242
|
+
outerRadius = cellSize * 1.5;
|
|
243
|
+
middleRadius = cellSize * 0.8;
|
|
244
|
+
innerRadius = cellSize * 0.5;
|
|
245
|
+
break;
|
|
246
|
+
case "maximum-rounded":
|
|
247
|
+
outerRadius = cellSize * 2;
|
|
248
|
+
middleRadius = cellSize * 1.2;
|
|
249
|
+
innerRadius = cellSize * 0.8;
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
svg += `<rect x="${centerX}" y="${centerY}" width="${patternSize}" height="${patternSize}" fill="${darkColor}" rx="${outerRadius}" ry="${outerRadius}"/>`;
|
|
253
|
+
svg += `<rect x="${centerX + cellSize}" y="${centerY + cellSize}" width="${middleSize}" height="${middleSize}" fill="${lightColor}" rx="${middleRadius}" ry="${middleRadius}"/>`;
|
|
254
|
+
svg += `<rect x="${centerX + 2 * cellSize}" y="${centerY + 2 * cellSize}" width="${innerSize}" height="${innerSize}" fill="${darkColor}" rx="${innerRadius}" ry="${innerRadius}"/>`;
|
|
255
|
+
};
|
|
256
|
+
drawFinderPattern(margin, margin);
|
|
257
|
+
drawFinderPattern(margin + (moduleCount - 7) * cellSize, margin);
|
|
258
|
+
drawFinderPattern(margin, margin + (moduleCount - 7) * cellSize);
|
|
259
|
+
for (let row = 0; row < moduleCount; row++) {
|
|
260
|
+
for (let col = 0; col < moduleCount; col++) {
|
|
261
|
+
if (!qrMatrix.modules.get(row, col) || isFinderPattern(row, col)) continue;
|
|
262
|
+
const x = margin + col * cellSize + (cellSize - dotSize) / 2;
|
|
263
|
+
const y = margin + row * cellSize + (cellSize - dotSize) / 2;
|
|
264
|
+
if (options.dotStyle === "dots") {
|
|
265
|
+
svg += `<circle cx="${x + dotSize / 2}" cy="${y + dotSize / 2}" r="${dotSize / 2}" fill="${darkColor}"/>`;
|
|
266
|
+
} else if (options.dotStyle === "rounded") {
|
|
267
|
+
const radius = dotSize * 0.3;
|
|
268
|
+
svg += `<rect x="${x}" y="${y}" width="${dotSize}" height="${dotSize}" fill="${darkColor}" rx="${radius}" ry="${radius}"/>`;
|
|
269
|
+
} else {
|
|
270
|
+
svg += `<rect x="${x}" y="${y}" width="${dotSize}" height="${dotSize}" fill="${darkColor}"/>`;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (options.logo) {
|
|
275
|
+
const logoSize = options.logoSize ?? size * 0.2;
|
|
276
|
+
const logoMargin = options.logoMargin ?? 8;
|
|
277
|
+
const logoX = (size - logoSize) / 2;
|
|
278
|
+
const logoY = (size - logoSize) / 2;
|
|
279
|
+
if (options.logoBackgroundColor) {
|
|
280
|
+
const bgSize = logoSize + logoMargin * 2;
|
|
281
|
+
const bgX = (size - bgSize) / 2;
|
|
282
|
+
const bgY = (size - bgSize) / 2;
|
|
283
|
+
svg += `<rect x="${bgX}" y="${bgY}" width="${bgSize}" height="${bgSize}" fill="${options.logoBackgroundColor}" rx="${logoMargin}" ry="${logoMargin}"/>`;
|
|
284
|
+
}
|
|
285
|
+
svg += `<image x="${logoX}" y="${logoY}" width="${logoSize}" height="${logoSize}" href="${options.logo}" preserveAspectRatio="xMidYMid meet"/>`;
|
|
286
|
+
}
|
|
287
|
+
svg += "</svg>";
|
|
288
|
+
return svg;
|
|
289
|
+
}
|
|
290
|
+
async function createQR(url, size = 512, background = "white", color = "black") {
|
|
291
|
+
return createStyledQRCode(url, createQROptions(url, size, background, color));
|
|
292
|
+
}
|
|
293
|
+
async function createQRDataURL(url, options) {
|
|
294
|
+
const svg = await createStyledQRCode(url, options);
|
|
295
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
296
|
+
}
|
|
297
|
+
async function createQRCanvas(canvas, url, options) {
|
|
298
|
+
const QRCode = await loadQRCode();
|
|
299
|
+
await QRCode.toCanvas(canvas, String(url), {
|
|
300
|
+
width: options.width,
|
|
301
|
+
margin: options.margin,
|
|
302
|
+
color: options.color,
|
|
303
|
+
errorCorrectionLevel: options.errorCorrectionLevel
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function createSPLToken(tokenAddress) {
|
|
308
|
+
try {
|
|
309
|
+
return address(tokenAddress);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
throw new Error(`Invalid SPL token address: ${tokenAddress}. ${String(error)}`, {
|
|
312
|
+
cause: error
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function createRecipient(recipientAddress) {
|
|
317
|
+
try {
|
|
318
|
+
return address(recipientAddress);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
throw new Error(`Invalid recipient address: ${recipientAddress}. ${String(error)}`, {
|
|
321
|
+
cause: error
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function isValidSolanaAddress(addr) {
|
|
326
|
+
try {
|
|
327
|
+
address(addr);
|
|
328
|
+
return true;
|
|
329
|
+
} catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export { CreateTransferError as C, HTTPS_PROTOCOL as H, MEMO_PROGRAM_ID as M, ParseURLError as P, SOLANA_PROTOCOL as S, TEN as T, SOL_DECIMALS as a, TOKEN_PROGRAM_ID as b, createQR as c, createQRCanvas as d, createQRDataURL as e, createQROptions as f, createRecipient as g, createSPLToken as h, createStyledQRCode as i, encodeTransactionRequestURL as j, encodeTransferRequestURL as k, encodeURL as l, isValidSolanaAddress as m, parseURL as p };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Address } from '@solana/kit';
|
|
2
|
+
|
|
3
|
+
declare function createSolanaPayReference(): Promise<Address>;
|
|
4
|
+
declare function findTransactionSignaturesByReference(rpc: unknown, reference: string): Promise<unknown>;
|
|
5
|
+
interface SolanaPayVerificationOptions {
|
|
6
|
+
reference: string;
|
|
7
|
+
expectedRecipient: string;
|
|
8
|
+
expectedAmount?: bigint;
|
|
9
|
+
expectedTokenMint?: string;
|
|
10
|
+
commitment?: "processed" | "confirmed" | "finalized";
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
pollIntervalMs?: number;
|
|
13
|
+
}
|
|
14
|
+
interface SolanaPayVerificationResult {
|
|
15
|
+
status: "pending" | "found" | "confirmed" | "finalized" | "expired" | "error";
|
|
16
|
+
signature?: string;
|
|
17
|
+
verified: boolean;
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function verifySolanaPayPayment(rpc: unknown, options: SolanaPayVerificationOptions): Promise<SolanaPayVerificationResult>;
|
|
21
|
+
|
|
22
|
+
export { createSolanaPayReference, findTransactionSignaturesByReference, verifySolanaPayPayment };
|
|
23
|
+
export type { SolanaPayVerificationOptions, SolanaPayVerificationResult };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { generateKeyPairSigner } from '@solana/kit';
|
|
2
|
+
|
|
3
|
+
async function createSolanaPayReference() {
|
|
4
|
+
const signer = await generateKeyPairSigner();
|
|
5
|
+
return signer.address;
|
|
6
|
+
}
|
|
7
|
+
async function findTransactionSignaturesByReference(rpc, reference) {
|
|
8
|
+
const res = await rpc.getSignaturesForAddress(reference).send();
|
|
9
|
+
return res;
|
|
10
|
+
}
|
|
11
|
+
function sleep(ms) {
|
|
12
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
13
|
+
}
|
|
14
|
+
async function verifySolanaPayPayment(rpc, options) {
|
|
15
|
+
const {
|
|
16
|
+
reference,
|
|
17
|
+
expectedRecipient,
|
|
18
|
+
expectedAmount,
|
|
19
|
+
expectedTokenMint,
|
|
20
|
+
commitment = "confirmed",
|
|
21
|
+
timeoutMs = 3e4,
|
|
22
|
+
pollIntervalMs = 1e3
|
|
23
|
+
} = options;
|
|
24
|
+
const deadline = Date.now() + timeoutMs;
|
|
25
|
+
let signature;
|
|
26
|
+
while (Date.now() < deadline) {
|
|
27
|
+
try {
|
|
28
|
+
const sigs = await findTransactionSignaturesByReference(rpc, reference);
|
|
29
|
+
if (Array.isArray(sigs) && sigs.length > 0) {
|
|
30
|
+
signature = sigs[0].signature;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
return { status: "error", verified: false, reason: "rpc_failure" };
|
|
35
|
+
}
|
|
36
|
+
await sleep(pollIntervalMs);
|
|
37
|
+
}
|
|
38
|
+
if (!signature) {
|
|
39
|
+
return { status: "expired", verified: false, reason: "no_payment_found" };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const tx = await rpc.getTransaction(signature, { encoding: "jsonParsed", commitment }).send();
|
|
43
|
+
if (!tx) {
|
|
44
|
+
return { status: "pending", verified: false, signature, reason: "transaction_not_found" };
|
|
45
|
+
}
|
|
46
|
+
if (tx.meta?.err) {
|
|
47
|
+
return { status: "error", verified: false, signature, reason: "transaction_failed" };
|
|
48
|
+
}
|
|
49
|
+
const confStatus = commitment === "finalized" ? "finalized" : "confirmed";
|
|
50
|
+
const instructions = tx.transaction?.message?.instructions ?? [];
|
|
51
|
+
let matched = false;
|
|
52
|
+
if (!expectedTokenMint) {
|
|
53
|
+
for (const ix of instructions) {
|
|
54
|
+
const parsed = ix.parsed;
|
|
55
|
+
if (parsed?.type === "transfer" && parsed.info?.destination === expectedRecipient) {
|
|
56
|
+
if (expectedAmount !== void 0) {
|
|
57
|
+
const lamports = BigInt(
|
|
58
|
+
parsed.info.lamports ?? 0
|
|
59
|
+
);
|
|
60
|
+
if (lamports === expectedAmount) matched = true;
|
|
61
|
+
} else {
|
|
62
|
+
matched = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
for (const ix of instructions) {
|
|
68
|
+
const parsed = ix.parsed;
|
|
69
|
+
if (parsed?.type === "transfer" || parsed?.type === "transferChecked") {
|
|
70
|
+
const info = parsed.info ?? {};
|
|
71
|
+
if (info.destination === expectedRecipient || info.owner === expectedRecipient) {
|
|
72
|
+
if (expectedAmount !== void 0) {
|
|
73
|
+
const amount = BigInt(
|
|
74
|
+
info.amount ?? info.tokenAmount?.amount ?? 0
|
|
75
|
+
);
|
|
76
|
+
if (amount === expectedAmount) matched = true;
|
|
77
|
+
} else {
|
|
78
|
+
matched = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!matched) {
|
|
85
|
+
return { status: "found", verified: false, signature, reason: "wrong_recipient_or_amount" };
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
status: confStatus,
|
|
89
|
+
verified: true,
|
|
90
|
+
signature
|
|
91
|
+
};
|
|
92
|
+
} catch {
|
|
93
|
+
return { status: "error", verified: false, signature, reason: "rpc_failure" };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { createSolanaPayReference, findTransactionSignaturesByReference, verifySolanaPayPayment };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type SolanaPayPaymentStatus = "pending" | "found" | "confirmed" | "finalized" | "expired" | "error";
|
|
2
|
+
declare function getSolanaPayPaymentStatus(rpc: unknown, reference: string): Promise<SolanaPayPaymentStatus>;
|
|
3
|
+
|
|
4
|
+
export { getSolanaPayPaymentStatus };
|
|
5
|
+
export type { SolanaPayPaymentStatus };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { findTransactionSignaturesByReference } from './solana-pay-confirmation.mjs';
|
|
2
|
+
|
|
3
|
+
async function getSolanaPayPaymentStatus(rpc, reference) {
|
|
4
|
+
try {
|
|
5
|
+
const sigs = await findTransactionSignaturesByReference(rpc, reference);
|
|
6
|
+
if (!sigs || Array.isArray(sigs) && sigs.length === 0) return "pending";
|
|
7
|
+
return "found";
|
|
8
|
+
} catch {
|
|
9
|
+
return "error";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { getSolanaPayPaymentStatus };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as _solana_kit from '@solana/kit';
|
|
2
|
+
import { Address } from '@solana/kit';
|
|
3
|
+
|
|
4
|
+
declare const SOLANA_PROTOCOL = "solana:";
|
|
5
|
+
declare const HTTPS_PROTOCOL = "https:";
|
|
6
|
+
declare const MEMO_PROGRAM_ID: _solana_kit.Address<"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr">;
|
|
7
|
+
declare const TOKEN_PROGRAM_ID: _solana_kit.Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
|
|
8
|
+
declare const SOL_DECIMALS = 9;
|
|
9
|
+
declare const TEN = 10n;
|
|
10
|
+
|
|
11
|
+
type Recipient = Address;
|
|
12
|
+
type Amount = bigint;
|
|
13
|
+
type SPLToken = Address;
|
|
14
|
+
type Reference = Address;
|
|
15
|
+
type References = Reference | Reference[];
|
|
16
|
+
type Label = string;
|
|
17
|
+
type Message = string;
|
|
18
|
+
type Memo = string;
|
|
19
|
+
type Link = URL;
|
|
20
|
+
|
|
21
|
+
declare class CreateTransferError extends Error {
|
|
22
|
+
name: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Fields of a Solana Pay transfer request URL.
|
|
27
|
+
*/
|
|
28
|
+
interface TransferRequestURLFields {
|
|
29
|
+
/** `recipient` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#recipient). */
|
|
30
|
+
recipient: Recipient;
|
|
31
|
+
/** `amount` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#amount). */
|
|
32
|
+
amount?: Amount;
|
|
33
|
+
/** `spl-token` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#spl-token). */
|
|
34
|
+
splToken?: SPLToken;
|
|
35
|
+
/** `reference` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#reference). */
|
|
36
|
+
reference?: References;
|
|
37
|
+
/** `label` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#label). */
|
|
38
|
+
label?: Label;
|
|
39
|
+
/** `message` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#message). */
|
|
40
|
+
message?: Message;
|
|
41
|
+
/** `memo` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#memo). */
|
|
42
|
+
memo?: Memo;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Fields of a Solana Pay transaction request URL.
|
|
46
|
+
*/
|
|
47
|
+
interface TransactionRequestURLFields {
|
|
48
|
+
/** `link` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#link). */
|
|
49
|
+
link: Link;
|
|
50
|
+
/** `label` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#label-1). */
|
|
51
|
+
label?: Label;
|
|
52
|
+
/** `message` in the [Solana Pay spec](https://github.com/solana-labs/solana-pay/blob/master/SPEC.md#message-1). */
|
|
53
|
+
message?: Message;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Encode a Solana Pay transfer or transaction request URL.
|
|
57
|
+
*/
|
|
58
|
+
declare function encodeURL(fields: TransferRequestURLFields | TransactionRequestURLFields): URL;
|
|
59
|
+
/**
|
|
60
|
+
* Encode a Solana Pay transfer request URL.
|
|
61
|
+
*/
|
|
62
|
+
declare function encodeTransferRequestURL({ recipient, amount, splToken, reference, label, message, memo, }: TransferRequestURLFields): URL;
|
|
63
|
+
/**
|
|
64
|
+
* Encode a Solana Pay transaction request URL.
|
|
65
|
+
*/
|
|
66
|
+
declare function encodeTransactionRequestURL({ link, label, message, }: TransactionRequestURLFields): URL;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Error thrown when a URL can't be parsed as a Solana Pay URL.
|
|
70
|
+
*/
|
|
71
|
+
declare class ParseURLError extends Error {
|
|
72
|
+
name: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parse a Solana Pay URL.
|
|
76
|
+
*
|
|
77
|
+
* @throws {ParseURLError} when the URL is not a valid Solana Pay URL.
|
|
78
|
+
*/
|
|
79
|
+
declare function parseURL(url: string | URL): TransferRequestURLFields | TransactionRequestURLFields;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* A small QR renderer for Solana Pay URLs.
|
|
83
|
+
*
|
|
84
|
+
* The `qrcode` package is loaded lazily so apps that only encode/parse payment
|
|
85
|
+
* URLs never pay for the QR encoder in their initial bundle.
|
|
86
|
+
*/
|
|
87
|
+
interface QROptions {
|
|
88
|
+
width?: number;
|
|
89
|
+
margin?: number;
|
|
90
|
+
color?: {
|
|
91
|
+
dark?: string;
|
|
92
|
+
light?: string;
|
|
93
|
+
};
|
|
94
|
+
errorCorrectionLevel?: "L" | "M" | "Q" | "H";
|
|
95
|
+
logo?: string;
|
|
96
|
+
logoSize?: number;
|
|
97
|
+
logoBackgroundColor?: string;
|
|
98
|
+
logoMargin?: number;
|
|
99
|
+
dotStyle?: "dots" | "rounded" | "square";
|
|
100
|
+
cornerStyle?: "square" | "rounded" | "extra-rounded" | "full-rounded" | "maximum-rounded";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Build the default styling options for a Solana Pay QR code.
|
|
104
|
+
*/
|
|
105
|
+
declare function createQROptions(_url: string | URL, size?: number, background?: string, color?: string): QROptions;
|
|
106
|
+
/**
|
|
107
|
+
* Render a Solana Pay URL as styled SVG.
|
|
108
|
+
*/
|
|
109
|
+
declare function createStyledQRCode(url: string | URL, options: QROptions): Promise<string>;
|
|
110
|
+
/**
|
|
111
|
+
* Render a Solana Pay URL as an SVG string.
|
|
112
|
+
*/
|
|
113
|
+
declare function createQR(url: string | URL, size?: number, background?: string, color?: string): Promise<string>;
|
|
114
|
+
/**
|
|
115
|
+
* Render a Solana Pay URL as a `data:` URL containing SVG markup.
|
|
116
|
+
*
|
|
117
|
+
* Percent-encoding keeps this free of `Buffer`, so it works in the browser and
|
|
118
|
+
* during SSR without the Buffer polyfill.
|
|
119
|
+
*/
|
|
120
|
+
declare function createQRDataURL(url: string | URL, options: QROptions): Promise<string>;
|
|
121
|
+
/**
|
|
122
|
+
* Render a Solana Pay URL into an existing canvas element.
|
|
123
|
+
*/
|
|
124
|
+
declare function createQRCanvas(canvas: HTMLCanvasElement, url: string | URL, options: QROptions): Promise<void>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Safely convert a string address to an `SPLToken`.
|
|
128
|
+
*/
|
|
129
|
+
declare function createSPLToken(tokenAddress: string): SPLToken;
|
|
130
|
+
/**
|
|
131
|
+
* Safely convert a string address to a `Recipient`.
|
|
132
|
+
*/
|
|
133
|
+
declare function createRecipient(recipientAddress: string): Recipient;
|
|
134
|
+
/**
|
|
135
|
+
* Validate a string as a Solana address.
|
|
136
|
+
*/
|
|
137
|
+
declare function isValidSolanaAddress(addr: string): boolean;
|
|
138
|
+
|
|
139
|
+
export { CreateTransferError, HTTPS_PROTOCOL, MEMO_PROGRAM_ID, ParseURLError, SOLANA_PROTOCOL, SOL_DECIMALS, TEN, TOKEN_PROGRAM_ID, createQR, createQRCanvas, createQRDataURL, createQROptions, createRecipient, createSPLToken, createStyledQRCode, encodeTransactionRequestURL, encodeTransferRequestURL, encodeURL, isValidSolanaAddress, parseURL };
|
|
140
|
+
export type { Amount, Label, Link, Memo, Message, QROptions, Recipient, Reference, References, SPLToken, TransactionRequestURLFields, TransferRequestURLFields };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { C as CreateTransferError, H as HTTPS_PROTOCOL, M as MEMO_PROGRAM_ID, P as ParseURLError, S as SOLANA_PROTOCOL, a as SOL_DECIMALS, T as TEN, b as TOKEN_PROGRAM_ID, c as createQR, d as createQRCanvas, e as createQRDataURL, f as createQROptions, g as createRecipient, h as createSPLToken, i as createStyledQRCode, j as encodeTransactionRequestURL, k as encodeTransferRequestURL, l as encodeURL, m as isValidSolanaAddress, p as parseURL } from './shared/core.CQFtdLrA.mjs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vue-solana/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.1",
|
|
4
4
|
"description": "Framework-agnostic primitives for Vue Solana libraries.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -92,6 +92,18 @@
|
|
|
92
92
|
"./token-accounts": {
|
|
93
93
|
"types": "./dist/token-accounts.d.ts",
|
|
94
94
|
"import": "./dist/token-accounts.mjs"
|
|
95
|
+
},
|
|
96
|
+
"./solana-pay": {
|
|
97
|
+
"types": "./dist/solana-pay.d.ts",
|
|
98
|
+
"import": "./dist/solana-pay.mjs"
|
|
99
|
+
},
|
|
100
|
+
"./solana-pay-confirmation": {
|
|
101
|
+
"types": "./dist/solana-pay-confirmation.d.ts",
|
|
102
|
+
"import": "./dist/solana-pay-confirmation.mjs"
|
|
103
|
+
},
|
|
104
|
+
"./solana-pay-status": {
|
|
105
|
+
"types": "./dist/solana-pay-status.d.ts",
|
|
106
|
+
"import": "./dist/solana-pay-status.mjs"
|
|
95
107
|
}
|
|
96
108
|
},
|
|
97
109
|
"files": [
|
|
@@ -110,9 +122,11 @@
|
|
|
110
122
|
"@wallet-standard/app": "^1.1.1",
|
|
111
123
|
"@wallet-standard/base": "^1.1.1",
|
|
112
124
|
"@wallet-standard/features": "^1.1.1",
|
|
125
|
+
"qrcode": "^1.5.4",
|
|
113
126
|
"tweetnacl": "^1.0.3"
|
|
114
127
|
},
|
|
115
128
|
"devDependencies": {
|
|
129
|
+
"@types/qrcode": "^1.5.5",
|
|
116
130
|
"typescript": "^5.8.3",
|
|
117
131
|
"unbuild": "^3.5.0"
|
|
118
132
|
},
|