diff --git a/.react-router/types/+register.ts b/.react-router/types/+register.ts index 6d806fb..68d1aa6 100644 --- a/.react-router/types/+register.ts +++ b/.react-router/types/+register.ts @@ -44,6 +44,7 @@ type Params = { "/onboarding/done": {}; "/dashboard": {}; "/profile": {}; + "/solana": {}; "/login": {}; "/logout": {}; "/about": {}; diff --git a/.react-router/types/app/features/solana/+types/user-solana-layout.ts b/.react-router/types/app/features/solana/+types/user-solana-layout.ts new file mode 100644 index 0000000..cfb97a2 --- /dev/null +++ b/.react-router/types/app/features/solana/+types/user-solana-layout.ts @@ -0,0 +1,43 @@ +// React Router generated types for route: +// ./features/solana/user-solana-layout.tsx + +import type * as T from "react-router/route-module" + +import type { Info as Parent0 } from "../../../+types/root.js" +import type { Info as Parent1 } from "../../app/+types/layout-app.js" + +type Module = typeof import("../user-solana-layout.js") + +export type Info = { + parents: [Parent0, Parent1], + id: "features/solana/user-solana-layout" + file: "./features/solana/user-solana-layout.tsx" + path: "undefined" + params: {} & { [key: string]: string | undefined } + module: Module + loaderData: T.CreateLoaderData + actionData: T.CreateActionData +} + +export namespace Route { + export type LinkDescriptors = T.LinkDescriptors + export type LinksFunction = () => LinkDescriptors + + export type MetaArgs = T.CreateMetaArgs + export type MetaDescriptors = T.MetaDescriptors + export type MetaFunction = (args: MetaArgs) => MetaDescriptors + + export type HeadersArgs = T.HeadersArgs + export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit + + export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction + export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction + export type LoaderArgs = T.CreateServerLoaderArgs + export type ClientLoaderArgs = T.CreateClientLoaderArgs + export type ActionArgs = T.CreateServerActionArgs + export type ClientActionArgs = T.CreateClientActionArgs + + export type HydrateFallbackProps = T.CreateHydrateFallbackProps + export type ComponentProps = T.CreateComponentProps + export type ErrorBoundaryProps = T.CreateErrorBoundaryProps +} \ No newline at end of file diff --git a/.react-router/types/app/features/solana/+types/user-solana-wallet.ts b/.react-router/types/app/features/solana/+types/user-solana-wallet.ts new file mode 100644 index 0000000..ecec4a7 --- /dev/null +++ b/.react-router/types/app/features/solana/+types/user-solana-wallet.ts @@ -0,0 +1,44 @@ +// React Router generated types for route: +// ./features/solana/user-solana-wallet.tsx + +import type * as T from "react-router/route-module" + +import type { Info as Parent0 } from "../../../+types/root.js" +import type { Info as Parent1 } from "../../app/+types/layout-app.js" +import type { Info as Parent2 } from "./user-solana-layout.js" + +type Module = typeof import("../user-solana-wallet.js") + +export type Info = { + parents: [Parent0, Parent1, Parent2], + id: "features/solana/user-solana-wallet" + file: "./features/solana/user-solana-wallet.tsx" + path: "solana" + params: {} & { [key: string]: string | undefined } + module: Module + loaderData: T.CreateLoaderData + actionData: T.CreateActionData +} + +export namespace Route { + export type LinkDescriptors = T.LinkDescriptors + export type LinksFunction = () => LinkDescriptors + + export type MetaArgs = T.CreateMetaArgs + export type MetaDescriptors = T.MetaDescriptors + export type MetaFunction = (args: MetaArgs) => MetaDescriptors + + export type HeadersArgs = T.HeadersArgs + export type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit + + export type unstable_MiddlewareFunction = T.CreateServerMiddlewareFunction + export type unstable_ClientMiddlewareFunction = T.CreateClientMiddlewareFunction + export type LoaderArgs = T.CreateServerLoaderArgs + export type ClientLoaderArgs = T.CreateClientLoaderArgs + export type ActionArgs = T.CreateServerActionArgs + export type ClientActionArgs = T.CreateClientActionArgs + + export type HydrateFallbackProps = T.CreateHydrateFallbackProps + export type ComponentProps = T.CreateComponentProps + export type ErrorBoundaryProps = T.CreateErrorBoundaryProps +} \ No newline at end of file diff --git a/app/features/app/layout-app.tsx b/app/features/app/layout-app.tsx index 55f44a9..9ebf6f1 100644 --- a/app/features/app/layout-app.tsx +++ b/app/features/app/layout-app.tsx @@ -22,6 +22,7 @@ export default function LayoutApp({ loaderData: { user } }: Route.ComponentProps const links: UiHeaderLink[] = [ { label: 'Dashboard', to: '/dashboard' }, { label: 'Profile', to: '/profile' }, + { label: 'Solana', to: '/solana' }, ] if (user.admin) { links.push({ label: 'Admin', to: '/admin' }) diff --git a/app/features/auth/data-access/get-user-by-identity.ts b/app/features/auth/data-access/get-user-by-identity.ts new file mode 100644 index 0000000..3a198af --- /dev/null +++ b/app/features/auth/data-access/get-user-by-identity.ts @@ -0,0 +1,9 @@ +import { prisma } from '~/lib/db.server' +import { IdentityProvider } from '@prisma/client' + +export async function getUserByIdentity({ provider, providerId }: { provider: IdentityProvider; providerId: string }) { + return await prisma.user.findFirst({ + include: { identities: true }, + where: { identities: { some: { provider, providerId } } }, + }) +} diff --git a/app/features/auth/data-access/get-user-by-solana-identity.ts b/app/features/auth/data-access/get-user-by-solana-identity.ts new file mode 100644 index 0000000..722d807 --- /dev/null +++ b/app/features/auth/data-access/get-user-by-solana-identity.ts @@ -0,0 +1,10 @@ +import { isValidSolanaPubKey } from '~/lib/solana/is-valid-solana-pubkey' +import { IdentityProvider } from '@prisma/client' +import { getUserByIdentity } from '~/features/auth/data-access/get-user-by-identity' + +export async function getUserBySolanaIdentity({ providerId }: { providerId: string }) { + if (!isValidSolanaPubKey(providerId)) { + throw new Error('Invalid Solana providerId') + } + return getUserByIdentity({ provider: IdentityProvider.Solana, providerId }) +} diff --git a/app/features/pubkey/pubkey-feature-profile-create.tsx b/app/features/pubkey/pubkey-feature-profile-create.tsx index d0cfa81..8c16369 100644 --- a/app/features/pubkey/pubkey-feature-profile-create.tsx +++ b/app/features/pubkey/pubkey-feature-profile-create.tsx @@ -12,8 +12,9 @@ import { UiCard } from '~/ui/ui-card' import { PubkeyUiProfileCreateForm } from './ui/pubkey-ui-profile-create-form' import { getPubkeySdkCommunity } from '~/lib/pubkey/get-pubkey-sdk-community' import { useConnection, useWallet } from '@solana/wallet-adapter-react' -import { PublicKey, VersionedTransaction } from '@solana/web3.js' +import { VersionedTransaction } from '@solana/web3.js' import { base58 } from '@metaplex-foundation/umi' +import { isValidSolanaPubKey } from '~/lib/solana/is-valid-solana-pubkey' export function meta() { return appMeta('Profiles') @@ -114,14 +115,6 @@ export default function PubkeyFeatureCommunityCreate({ loaderData: { config } }: ) } -function isValidSolanaPubKey(address: string) { - try { - return !!new PublicKey(address) - } catch { - return false - } -} - function txToBase58(tx: VersionedTransaction): string { const [res] = base58.deserialize(tx.serialize()) return res diff --git a/app/features/solana/get-solana-verification-type.ts b/app/features/solana/get-solana-verification-type.ts new file mode 100644 index 0000000..fafe519 --- /dev/null +++ b/app/features/solana/get-solana-verification-type.ts @@ -0,0 +1,54 @@ +export enum SolanaVerificationType { + Error = 'Error', + Login = 'Login', + Register = 'Register', + Link = 'Link', + Verify = 'Verify', +} + +export function getSolanaVerificationType({ + actorId, + ownerId, + enabledTypes = [ + SolanaVerificationType.Login, + SolanaVerificationType.Link, + SolanaVerificationType.Register, + SolanaVerificationType.Verify, + ], +}: { + actorId?: string + ownerId?: string + enabledTypes?: SolanaVerificationType[] +}): { + message?: string + type: SolanaVerificationType +} { + function ensureEnabledType(type: SolanaVerificationType) { + if (!enabledTypes.includes(type)) { + throw new Error(`Solana verification type ${type} is not enabled`) + } + return type + } + + // If the wallet isn't owned by any user + if (!ownerId) { + return actorId + ? // If actor is set, we want to link the wallet + { type: ensureEnabledType(SolanaVerificationType.Link) } + : // Otherwise, we want to register a new user + { type: ensureEnabledType(SolanaVerificationType.Register) } + } + + // We are not logged in, so this is a login + if (!actorId) { + return { type: ensureEnabledType(SolanaVerificationType.Login) } + } + + // We are logged in, make sure that the user owns the wallet + if (ownerId !== actorId) { + return { type: SolanaVerificationType.Error, message: 'User does not own public key' } + } + + // Actor owns the wallet, we can now verify it + return { type: ensureEnabledType(SolanaVerificationType.Verify) } +} diff --git a/app/features/solana/index.ts b/app/features/solana/index.ts new file mode 100644 index 0000000..3223025 --- /dev/null +++ b/app/features/solana/index.ts @@ -0,0 +1,8 @@ +import { index, layout, prefix } from '@react-router/dev/routes' + +export const userSolanaRoutes = layout('./features/solana/user-solana-layout.tsx', + prefix('solana', [ + index('./features/solana/user-solana-wallet.tsx'), + ]) +) + diff --git a/app/features/solana/user-solana-layout.tsx b/app/features/solana/user-solana-layout.tsx new file mode 100644 index 0000000..f187526 --- /dev/null +++ b/app/features/solana/user-solana-layout.tsx @@ -0,0 +1,11 @@ +import React, { Suspense } from 'react' +import { Loader } from '@mantine/core' +import { Outlet } from 'react-router' + +export default function LayoutApp() { + return ( + }> + + + ) +} diff --git a/app/features/solana/user-solana-wallet.tsx b/app/features/solana/user-solana-wallet.tsx new file mode 100644 index 0000000..6142702 --- /dev/null +++ b/app/features/solana/user-solana-wallet.tsx @@ -0,0 +1,220 @@ +import { Button, Flex, Group, Stack, Text } from '@mantine/core' +import { useFetcher } from 'react-router' +import { getBase58Decoder, getBase58Encoder, type ReadonlyUint8Array } from 'gill' +import type { Route } from './+types/user-solana-wallet' +import { useWallet } from '@solana/wallet-adapter-react' +import { solanaAuth } from '~/lib/solana-auth/solana-auth' +import type { SolanaAuthMessage, SolanaAuthMessageSigned } from '~/lib/solana-auth/solana-auth-message' +import { getUserBySolanaIdentity } from '~/features/auth/data-access/get-user-by-solana-identity' +import { getUser } from '~/features/auth/data-access/get-user' +import { getSolanaVerificationType, SolanaVerificationType } from './get-solana-verification-type' + +function parsePayload(payload: string = ''): SolanaAuthMessageSigned { + try { + return JSON.parse(payload) + } catch { + throw new Error(`Invalid payload`) + } +} + +export async function action({ request }: Route.LoaderArgs) { + const formData = await request.formData() + const action = formData.get('action')?.toString() + const payload = formData.get('payload')?.toString() + const publicKey = formData.get('publicKey')?.toString() + + if (!publicKey) { + return { success: false, message: `No public key` } + } + const actor = await getUser(request) + const owner = await getUserBySolanaIdentity({ providerId: publicKey }) + + // This determines the type of verification we are performing + const verification = getSolanaVerificationType({ + actorId: actor?.id ?? undefined, + ownerId: owner?.id ?? undefined, + enabledTypes: [ + SolanaVerificationType.Login, + SolanaVerificationType.Link, + SolanaVerificationType.Register, + SolanaVerificationType.Verify, + ], + }) + if (verification.type === SolanaVerificationType.Error) { + return { success: false, message: verification.message } + } + + console.log( + `user-solana-wallet [${action}] -> publicKey: ${publicKey} -> owner: ${owner ? owner.username : 'NONE'} -> type: ${verification.type}`, + ) + + switch (formData.get('action')) { + case 'sign-message-create': + return { + success: true, + message: await solanaAuth.createMessage({ method: 'solana:signMessage', publicKey }), + type: 'solana-auth-message', + } + case 'sign-message-verify': + const parsed = parsePayload(payload) + const result = await solanaAuth.verifyMessage(parsed) + if (!result) { + throw new Error('Invalid signature') + } + console.log(`sign message -> verify`, 'message', parsed, 'signature', parsed.signature, 'result', result) + if (verification.type === SolanaVerificationType.Link) { + // We should link the wallet to the actor. + } + if (verification.type === SolanaVerificationType.Login) { + // We should set the cookie. + } + if (verification.type === SolanaVerificationType.Verify) { + // We don't need to do anything? 🤷‍♂️ + } + if (verification.type === SolanaVerificationType.Register) { + // We should register a new user. + } + return { + success: true, + message: result, + type: 'solana-auth-result', + } + case 'sign-transaction-create': + return { + success: true, + message: await solanaAuth.createMessage({ method: 'solana:signTransaction', publicKey }), + type: 'solana-auth-message', + } + default: + return { + success: false, + message: `Unknown action ${action}`, + } + } +} + +export default function UserSolanaWallet() { + const wallet = useWallet() + const publicKey = wallet.publicKey?.toString() ?? '' + const fetcher = useFetcher() + + async function handleSubmit(data: Record) { + return await fetcher.submit(data, { method: 'post' }) + } + + async function handleCreate(action: 'sign-message-create' | 'sign-transaction-create') { + if (!publicKey.length) { + console.warn(`No public key, please connect your wallet`) + return + } + await handleSubmit({ action, publicKey }) + } + + async function handleVerify( + action: 'sign-message-verify' | 'sign-transaction-verify', + payload: SolanaAuthMessageSigned, + ) { + if (!publicKey.length) { + console.warn(`No public key, please connect your wallet`) + return + } + await handleSubmit({ action, publicKey, payload: JSON.stringify(payload) }) + } + + return ( + + + {publicKey.length ? ( + + Connected to + + {publicKey} + + + ) : null} + + + + + {fetcher.data?.type === 'solana-auth-message' ? ( +
+ handleVerify('sign-message-verify', payload)} + /> +
{JSON.stringify(fetcher.data.message, null, 2)}
+
+ ) : null} +
+
+ ) +} + +function SignComponent({ + message, + sign, +}: { + message: SolanaAuthMessage + sign: (payload: SolanaAuthMessageSigned) => Promise +}) { + const { signMessage } = useWallet() + + return ( +
+ {signMessage ? ( + + ) : ( +
No wallet
+ )} +
+ ) +} + +export interface CreateSignatureWallet { + message: SolanaAuthMessage + signMessage: (message: Uint8Array) => Promise +} + +export function bs58Encode(data: Uint8Array) { + return getBase58Decoder().decode(data) +} + +export function bs58Decode(data: string): ReadonlyUint8Array { + return getBase58Encoder().encode(data) +} + +export async function createSignatureWallet({ + message, + signMessage, +}: CreateSignatureWallet): Promise { + const encoded = encodeMessage(message.message.text) + const signature = await signMessage(encoded) + + return { ...message, signature: bs58Encode(signature) } +} + +export function encodeMessage(message: string): Uint8Array { + return new TextEncoder().encode(message) +} diff --git a/app/lib/solana-auth/create-solana-auth.ts b/app/lib/solana-auth/create-solana-auth.ts new file mode 100644 index 0000000..310ff2f --- /dev/null +++ b/app/lib/solana-auth/create-solana-auth.ts @@ -0,0 +1,16 @@ +import type { SolanaAuthConfig } from './solana-auth-config' +import type { SolanaAuthInstance } from './solana-auth-instance' +import type { SolanaAuthMessageCreateOptions, SolanaAuthMessageSigned } from './solana-auth-message' +import { solanaAuthMethodCreate } from './solana-auth-method-create' +import { solanaAuthMethodVerify } from './solana-auth-method-verify' + +export function createSolanaAuth(config: SolanaAuthConfig): SolanaAuthInstance { + return { + createMessage: async (options: SolanaAuthMessageCreateOptions) => { + return await solanaAuthMethodCreate(config.client, options) + }, + verifyMessage: async (options: SolanaAuthMessageSigned) => { + return await solanaAuthMethodVerify(config.client, options) + }, + } +} diff --git a/app/lib/solana-auth/memo/index.ts b/app/lib/solana-auth/memo/index.ts new file mode 100644 index 0000000..4e3b03f --- /dev/null +++ b/app/lib/solana-auth/memo/index.ts @@ -0,0 +1,109 @@ +/** + * This code was AUTOGENERATED using the codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +/** + * This code was AUTOGENERATED using the codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun codama to update it. + * + * @see https://github.com/codama-idl/codama + */ +import { + AccountRole, + type Address, + type Codec, + combineCodec, + type Decoder, + type Encoder, + getStructDecoder, + getStructEncoder, + getUtf8Decoder, + getUtf8Encoder, + type IAccountMeta, + type IInstruction, + type IInstructionWithAccounts, + type IInstructionWithData, + type TransactionSigner, +} from 'gill' + +export type AddMemoInstruction< + TProgram extends string = typeof MEMO_PROGRAM_ADDRESS, + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & IInstructionWithData & IInstructionWithAccounts + +export type AddMemoInstructionData = { memo: string } + +export type AddMemoInstructionDataArgs = AddMemoInstructionData + +export function getAddMemoInstructionDataEncoder(): Encoder { + return getStructEncoder([['memo', getUtf8Encoder()]]) +} + +export function getAddMemoInstructionDataDecoder(): Decoder { + return getStructDecoder([['memo', getUtf8Decoder()]]) +} + +export function getAddMemoInstructionDataCodec(): Codec { + return combineCodec(getAddMemoInstructionDataEncoder(), getAddMemoInstructionDataDecoder()) +} + +export type AddMemoInput = { + memo: AddMemoInstructionDataArgs['memo'] + signers?: Array +} + +export function getAddMemoInstruction( + input: AddMemoInput, + config?: { programAddress?: TProgramAddress }, +): AddMemoInstruction { + // Program address. + const programAddress = config?.programAddress ?? MEMO_PROGRAM_ADDRESS + + // Original args. + const args = { ...input } + + // Remaining accounts. + const remainingAccounts: IAccountMeta[] = (args.signers ?? []).map((signer) => ({ + address: signer.address, + role: AccountRole.READONLY_SIGNER, + signer, + })) + + const instruction = { + accounts: remainingAccounts, + programAddress, + data: getAddMemoInstructionDataEncoder().encode(args as AddMemoInstructionDataArgs), + } as AddMemoInstruction + + return instruction +} + +export type ParsedAddMemoInstruction = { + programAddress: Address + data: AddMemoInstructionData +} + +export function parseAddMemoInstruction( + instruction: IInstruction & IInstructionWithData, +): ParsedAddMemoInstruction { + return { + programAddress: instruction.programAddress, + data: getAddMemoInstructionDataDecoder().decode(instruction.data), + } +} + +export const MEMO_PROGRAM_ADDRESS = + 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' as Address<'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'> + +export enum MemoInstruction { + AddMemo, +} + +export type ParsedMemoInstruction = { + instructionType: MemoInstruction.AddMemo +} & ParsedAddMemoInstruction diff --git a/app/lib/solana-auth/solana-auth-config.ts b/app/lib/solana-auth/solana-auth-config.ts new file mode 100644 index 0000000..24e8532 --- /dev/null +++ b/app/lib/solana-auth/solana-auth-config.ts @@ -0,0 +1,10 @@ +import type { SolanaClient } from 'gill' +import type { SolanaAuthMethod } from './solana-auth-methods' + +export interface SolanaAuthConfig { + // The client to use for the instance. + // TODO: This should be provided by the plugin. For now, we hardcode it to the new @solana/web3.js v2. + client: SolanaClient + // The methods that the instance will support. + methods: SolanaAuthMethod[] +} diff --git a/app/lib/solana-auth/solana-auth-instance.ts b/app/lib/solana-auth/solana-auth-instance.ts new file mode 100644 index 0000000..22730ab --- /dev/null +++ b/app/lib/solana-auth/solana-auth-instance.ts @@ -0,0 +1,10 @@ +import type { SolanaAuthMessage, SolanaAuthMessageCreateOptions, SolanaAuthMessageSigned } from './solana-auth-message' + +// Public methods that are exposed by the SolanaAuth instance. +export interface SolanaAuthInstance { + // Create a verification message for a user to sign. + createMessage(options: SolanaAuthMessageCreateOptions): Promise + + // Verify the message signed by a user. + verifyMessage(options: SolanaAuthMessageSigned): Promise +} diff --git a/app/lib/solana-auth/solana-auth-message.ts b/app/lib/solana-auth/solana-auth-message.ts new file mode 100644 index 0000000..fb40325 --- /dev/null +++ b/app/lib/solana-auth/solana-auth-message.ts @@ -0,0 +1,25 @@ +import type { SolanaAuthMethod } from './solana-auth-methods' + +export interface SolanaAuthMessage { + method: SolanaAuthMethod + publicKey: string + blockhash: string + nonce: string + message: SolanaAuthMessagePayload +} + +export interface SolanaAuthMessagePayload { + chain: string + text: string + // TODO: Define the properties. +} + +export interface SolanaAuthMessageSigned extends SolanaAuthMessage { + signature: string +} + +export interface SolanaAuthMessageCreateOptions { + method: SolanaAuthMethod + publicKey: string + // TODO: Define the properties. +} diff --git a/app/lib/solana-auth/solana-auth-method-create.ts b/app/lib/solana-auth/solana-auth-method-create.ts new file mode 100644 index 0000000..dff4805 --- /dev/null +++ b/app/lib/solana-auth/solana-auth-method-create.ts @@ -0,0 +1,15 @@ +import type { SolanaAuthMessageCreateOptions } from './solana-auth-message' +import { solanaSignMessage } from './solana-sign-message' +import { solanaSignTransaction } from './solana-sign-transaction' +import type { SolanaClient } from 'gill' + +export function solanaAuthMethodCreate(client: SolanaClient, options: SolanaAuthMessageCreateOptions) { + switch (options.method) { + case 'solana:signMessage': + return solanaSignMessage.create(client, options) + case 'solana:signTransaction': + return solanaSignTransaction.create(client, options) + default: + throw `Method not supported: ${options.method}` + } +} diff --git a/app/lib/solana-auth/solana-auth-method-impl.ts b/app/lib/solana-auth/solana-auth-method-impl.ts new file mode 100644 index 0000000..762d3ad --- /dev/null +++ b/app/lib/solana-auth/solana-auth-method-impl.ts @@ -0,0 +1,9 @@ +import type { SolanaClient } from 'gill' +import type { SolanaAuthMessage, SolanaAuthMessageCreateOptions, SolanaAuthMessageSigned } from './solana-auth-message' +import type { SolanaAuthMethod } from './solana-auth-methods' + +export interface SolanaAuthMethodImpl { + method: SolanaAuthMethod + create: (client: SolanaClient, options: SolanaAuthMessageCreateOptions) => Promise + verify: (client: SolanaClient, options: SolanaAuthMessageSigned) => Promise +} diff --git a/app/lib/solana-auth/solana-auth-method-verify.ts b/app/lib/solana-auth/solana-auth-method-verify.ts new file mode 100644 index 0000000..e7ac7e9 --- /dev/null +++ b/app/lib/solana-auth/solana-auth-method-verify.ts @@ -0,0 +1,15 @@ +import type { SolanaAuthMessageSigned } from './solana-auth-message' +import { solanaSignMessage } from './solana-sign-message' +import { solanaSignTransaction } from './solana-sign-transaction' +import type { SolanaClient } from 'gill' + +export async function solanaAuthMethodVerify(client: SolanaClient, options: SolanaAuthMessageSigned) { + switch (options.method) { + case 'solana:signMessage': + return solanaSignMessage.verify(client, options) + case 'solana:signTransaction': + return solanaSignTransaction.verify(client, options) + default: + throw `Method not supported: ${options.method}` + } +} diff --git a/app/lib/solana-auth/solana-auth-methods.ts b/app/lib/solana-auth/solana-auth-methods.ts new file mode 100644 index 0000000..8dad84e --- /dev/null +++ b/app/lib/solana-auth/solana-auth-methods.ts @@ -0,0 +1,6 @@ +export type SolanaAuthMethodSolanaSignMessage = 'solana:signMessage' +export type SolanaAuthMethodSolanaSignTransaction = 'solana:signTransaction' + +export type SolanaAuthMethod = + | SolanaAuthMethodSolanaSignMessage + | SolanaAuthMethodSolanaSignTransaction diff --git a/app/lib/solana-auth/solana-auth.ts b/app/lib/solana-auth/solana-auth.ts new file mode 100644 index 0000000..2275644 --- /dev/null +++ b/app/lib/solana-auth/solana-auth.ts @@ -0,0 +1,15 @@ +import { createSolanaClient } from 'gill' +import { createSolanaAuth } from './create-solana-auth' + +const client = createSolanaClient({ + urlOrMoniker: 'https://api.devnet.solana.com', +}) + +export const solanaAuth = createSolanaAuth({ + client, + methods: ['solana:signMessage', 'solana:signTransaction'], +}) + +if (!solanaAuth) { + throw new Error('Solana Auth not initialized') +} diff --git a/app/lib/solana-auth/solana-client-helpers.ts b/app/lib/solana-auth/solana-client-helpers.ts new file mode 100644 index 0000000..bcbdeb6 --- /dev/null +++ b/app/lib/solana-auth/solana-client-helpers.ts @@ -0,0 +1,13 @@ +import type { SolanaClient } from 'gill' + +// TODO: This should be internal to the rpc plugin +export async function getLatestBlockhash(client: SolanaClient) { + return client.rpc + .getLatestBlockhash() + .send() + .then((response) => response.value.blockhash.toString()) + .catch((error) => { + console.error(`Error running getLatestBlockhash:`, error) + throw `Error getting latest blockhash: ${error}` + }) +} diff --git a/app/lib/solana-auth/solana-sign-message/index.ts b/app/lib/solana-auth/solana-sign-message/index.ts new file mode 100644 index 0000000..74141e5 --- /dev/null +++ b/app/lib/solana-auth/solana-sign-message/index.ts @@ -0,0 +1,9 @@ +import { solanaSignMessageCreate } from './solana-sign-message-create' +import { solanaSignMessageVerify } from './solana-sign-message-verify' +import type { SolanaAuthMethodImpl } from '../solana-auth-method-impl' + +export const solanaSignMessage: SolanaAuthMethodImpl = { + method: 'solana:signMessage', + create: solanaSignMessageCreate, + verify: solanaSignMessageVerify, +} diff --git a/app/lib/solana-auth/solana-sign-message/solana-sign-message-create.ts b/app/lib/solana-auth/solana-sign-message/solana-sign-message-create.ts new file mode 100644 index 0000000..71aafd9 --- /dev/null +++ b/app/lib/solana-auth/solana-sign-message/solana-sign-message-create.ts @@ -0,0 +1,22 @@ +import type { SolanaClient } from 'gill' +import type { SolanaAuthMessage, SolanaAuthMessageCreateOptions } from '../solana-auth-message' +import { getLatestBlockhash } from '../solana-client-helpers' + +export async function solanaSignMessageCreate( + client: SolanaClient, + options: SolanaAuthMessageCreateOptions, +): Promise { + const blockhash = await getLatestBlockhash(client) + const text = `Sign this transaction to prove you own ${options.publicKey}` + + return { + publicKey: options.publicKey, + method: options.method, + blockhash, + nonce: `Nonce ${Date.now()}`, + message: { + chain: 'solana:devnet', + text, + }, + } +} diff --git a/app/lib/solana-auth/solana-sign-message/solana-sign-message-verify.ts b/app/lib/solana-auth/solana-sign-message/solana-sign-message-verify.ts new file mode 100644 index 0000000..d482fb5 --- /dev/null +++ b/app/lib/solana-auth/solana-sign-message/solana-sign-message-verify.ts @@ -0,0 +1,30 @@ +import { + assertIsAddress, + assertIsSignature, + getBase58Encoder, + getPublicKeyFromAddress, + type SignatureBytes, + type SolanaClient, + verifySignature, +} from 'gill' +import type { SolanaAuthMessageSigned } from '../solana-auth-message' +import { stringToReadonlyUint8Array } from '../string-to-uint8-array' + +export async function solanaSignMessageVerify(client: SolanaClient, options: SolanaAuthMessageSigned) { + assertIsSignature(options.signature) + assertIsAddress(options.publicKey) + + const signature = options.signature + const publicKey = options.publicKey + + const signatureBytes = getBase58Encoder().encode(signature) + const cryptoKey = await getPublicKeyFromAddress(publicKey) + + const message = stringToReadonlyUint8Array(options.message.text) + const verified = await verifySignature(cryptoKey, signatureBytes as SignatureBytes, message) + + if (!verified) { + throw new Error('solanaSignMessageVerify: Invalid signature') + } + return publicKey +} diff --git a/app/lib/solana-auth/solana-sign-transaction/index.ts b/app/lib/solana-auth/solana-sign-transaction/index.ts new file mode 100644 index 0000000..35ba388 --- /dev/null +++ b/app/lib/solana-auth/solana-sign-transaction/index.ts @@ -0,0 +1,9 @@ +import { solanaSignTransactionCreate } from './solana-sign-transaction-create' +import { solanaSignTransactionVerify } from './solana-sign-transaction-verify' +import type { SolanaAuthMethodImpl } from '../solana-auth-method-impl' + +export const solanaSignTransaction: SolanaAuthMethodImpl = { + method: 'solana:signTransaction', + create: solanaSignTransactionCreate, + verify: solanaSignTransactionVerify, +} diff --git a/app/lib/solana-auth/solana-sign-transaction/solana-sign-transaction-create.ts b/app/lib/solana-auth/solana-sign-transaction/solana-sign-transaction-create.ts new file mode 100644 index 0000000..ba12efd --- /dev/null +++ b/app/lib/solana-auth/solana-sign-transaction/solana-sign-transaction-create.ts @@ -0,0 +1,84 @@ +import { + type Address, + address, + appendTransactionMessageInstruction, + assertIsTransactionMessageWithSingleSendingSigner, + compileTransaction, + createTransactionMessage, + getBase58Decoder, + getTransactionEncoder, + pipe, + setTransactionMessageFeePayerSigner, + setTransactionMessageLifetimeUsingBlockhash, + type SignatureBytes, + type SignatureDictionary, + type SolanaClient, + type Transaction, + type TransactionModifyingSignerConfig, + type TransactionPartialSignerConfig, + type TransactionSendingSignerConfig, + type TransactionSigner, +} from 'gill' +import type { SolanaAuthMessage, SolanaAuthMessageCreateOptions } from '../solana-auth-message' +import { getAddMemoInstruction } from '../memo' + +export async function solanaSignTransactionCreate( + client: SolanaClient, + options: SolanaAuthMessageCreateOptions, +): Promise { + // TODO: Implement. + const text = `Sign this transaction to prove you own ${options.publicKey}` + const latestBlockhash = await client.rpc + .getLatestBlockhash() + .send() + .then((response) => response.value) + + const messageSigner = createPartialSigner(address(options.publicKey)) + const message = pipe( + createTransactionMessage({ version: 0 }), + (m) => setTransactionMessageFeePayerSigner(messageSigner, m), + (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), + (m) => appendTransactionMessageInstruction(getAddMemoInstruction({ memo: text }), m), + ) + + const transaction = compileTransaction(message) + const wireTransactionBytes = getTransactionEncoder().encode(transaction) + const base58Transaction = getBase58Decoder().decode(wireTransactionBytes) + + assertIsTransactionMessageWithSingleSendingSigner(message) + + return { + publicKey: options.publicKey, + method: options.method, + blockhash: latestBlockhash.blockhash, + nonce: `Nonce ${Date.now()}`, + message: { + chain: 'solana:devnet', + text: base58Transaction, + }, + } +} + +export function createPartialSigner(address: Address): TransactionSigner { + return { + modifyAndSignTransactions( + transactions: readonly T[], + config: TransactionModifyingSignerConfig | undefined, + ): Promise { + return Promise.resolve([]) + }, + signAndSendTransactions( + transactions: readonly Transaction[], + config: TransactionSendingSignerConfig | undefined, + ): Promise { + return Promise.resolve([]) + }, + signTransactions( + transactions: readonly Transaction[], + config: TransactionPartialSignerConfig | undefined, + ): Promise { + return Promise.resolve([]) + }, + address, + } +} diff --git a/app/lib/solana-auth/solana-sign-transaction/solana-sign-transaction-verify.ts b/app/lib/solana-auth/solana-sign-transaction/solana-sign-transaction-verify.ts new file mode 100644 index 0000000..8dcf641 --- /dev/null +++ b/app/lib/solana-auth/solana-sign-transaction/solana-sign-transaction-verify.ts @@ -0,0 +1,27 @@ +import type { SolanaAuthMessageSigned } from '../solana-auth-message' +import { + address, + getBase58Encoder, + getPublicKeyFromAddress, + getTransactionDecoder, + type SolanaClient, + verifySignature, +} from 'gill' + +export async function solanaSignTransactionVerify(client: SolanaClient, options: SolanaAuthMessageSigned) { + const publicKey = address(options.publicKey) + const base58Transaction = options.signature + const transactionBytes = getBase58Encoder().encode(base58Transaction) + const transaction = getTransactionDecoder().decode(transactionBytes) + const signatureBytes = transaction.signatures[publicKey] + if (!signatureBytes) { + throw new Error(`Signature not found for ${publicKey}`) + } + + const cryptoKey = await getPublicKeyFromAddress(publicKey) + + if (await verifySignature(cryptoKey, signatureBytes, transaction.messageBytes)) { + return publicKey + } + throw new Error(`Invalid signature for ${publicKey}`) +} diff --git a/app/lib/solana-auth/string-to-uint8-array.ts b/app/lib/solana-auth/string-to-uint8-array.ts new file mode 100644 index 0000000..e6df684 --- /dev/null +++ b/app/lib/solana-auth/string-to-uint8-array.ts @@ -0,0 +1,5 @@ +import { getUtf8Encoder, type ReadonlyUint8Array } from 'gill' + +export function stringToReadonlyUint8Array(str: string): ReadonlyUint8Array { + return getUtf8Encoder().encode(str) +} diff --git a/app/lib/solana-auth/uint8-array-to-base58.ts b/app/lib/solana-auth/uint8-array-to-base58.ts new file mode 100644 index 0000000..d90f8c0 --- /dev/null +++ b/app/lib/solana-auth/uint8-array-to-base58.ts @@ -0,0 +1,5 @@ +import { getBase58Decoder } from 'gill' + +export function uint8ArrayToBase58(bytes: Uint8Array): string { + return getBase58Decoder().decode(bytes) +} diff --git a/app/lib/solana/is-valid-solana-pubkey.ts b/app/lib/solana/is-valid-solana-pubkey.ts new file mode 100644 index 0000000..0025021 --- /dev/null +++ b/app/lib/solana/is-valid-solana-pubkey.ts @@ -0,0 +1,9 @@ +import { PublicKey } from '@solana/web3.js' + +export function isValidSolanaPubKey(address: string) { + try { + return !!new PublicKey(address) + } catch { + return false + } +} diff --git a/app/routes.ts b/app/routes.ts index 89f567a..10f248e 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -3,6 +3,7 @@ import { adminUserRoutes } from './features/user' import { onboardingRoutes } from './features/onboarding' import { devRoutes } from './features/dev' import { pubkeyRoutes } from './features/pubkey' +import { userSolanaRoutes } from './features/solana' export default [ ...prefix('api', [ @@ -29,6 +30,7 @@ export default [ layout('features/app/layout-app.tsx', [ route('dashboard', 'features/app/route-dashboard.tsx'), route('profile', 'features/profile/profile-feature.tsx'), + userSolanaRoutes, ]), // Auth routes go here layout('features/auth/layout-auth.tsx', [ diff --git a/package.json b/package.json index 8d2f2b4..eb4164e 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "arctic": "^3.6.0", "bcryptjs": "^3.0.2", "dayjs": "^1.11.13", + "gill": "^0.9.0", "isbot": "^5.1.17", "lucide-react": "^0.487.0", "prisma": "^6.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eeb3a82..b13044d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,6 +89,9 @@ importers: dayjs: specifier: ^1.11.13 version: 1.11.13 + gill: + specifier: ^0.9.0 + version: 0.9.0(@solana/sysvars@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) isbot: specifier: ^5.1.17 version: 5.1.26 @@ -1762,6 +1765,45 @@ packages: peerDependencies: '@solana/web3.js': ^1.58.0 + '@solana-program/address-lookup-table@0.7.0': + resolution: {integrity: sha512-dzCeIO5LtiK3bIg0AwO+TPeGURjSG2BKt0c4FRx7105AgLy7uzTktpUzUj6NXAK9SzbirI8HyvHUvw1uvL8O9A==} + peerDependencies: + '@solana/kit': ^2.1.0 + + '@solana-program/compute-budget@0.7.0': + resolution: {integrity: sha512-/JJSE1fKO5zx7Z55Z2tLGWBDDi7tUE+xMlK8qqkHlY51KpqksMsIBzQMkG9Dqhoe2Cnn5/t3QK1nJKqW6eHzpg==} + peerDependencies: + '@solana/kit': ^2.1.0 + + '@solana-program/system@0.7.0': + resolution: {integrity: sha512-FKTBsKHpvHHNc1ATRm7SlC5nF/VdJtOSjldhcyfMN9R7xo712Mo2jHIzvBgn8zQO5Kg0DcWuKB7268Kv1ocicw==} + peerDependencies: + '@solana/kit': ^2.1.0 + + '@solana-program/token-2022@0.4.1': + resolution: {integrity: sha512-zIjdwUwirmvvF1nb95I/88+76d9HPCmAIcjjM4NpznDFjZpPItpfKIbTyp/uxeVOzi7d5LmvuvXRr373gpdGCg==} + peerDependencies: + '@solana/kit': ^2.1.0 + '@solana/sysvars': ^2.1.0 + + '@solana/accounts@2.1.1': + resolution: {integrity: sha512-Q9mG0o/6oyiUSw1CXCkG50TWlYiODJr3ZilEDLIyXpYJzOstRZM4XOzbRACveX4PXFoufPzpR1sSVK6qfcUUCw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/addresses@2.1.1': + resolution: {integrity: sha512-yX6+brBXFmirxXDJCBDNKDYbGZHMZHaZS4NJWZs31DTe5To3Ky3Y9g3wFEGAX242kfNyJcgg5OeoBuZ7vdFycQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/assertions@2.1.1': + resolution: {integrity: sha512-ln6dXkliyb9ybqLGFf5Gn+LJaPZGmer9KloIFfHiiSfYFdoAqOu6+pVY+323SKWXHG+hHl9VkwuZYpSp02OroA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/buffer-layout-utils@0.2.0': resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} engines: {node: '>= 10'} @@ -1775,38 +1817,209 @@ packages: peerDependencies: typescript: '>=5' + '@solana/codecs-core@2.1.1': + resolution: {integrity: sha512-iPQW3UZ2Vi7QFBo2r9tw0NubtH8EdrhhmZulx6lC8V5a+qjaxovtM/q/UW2BTNpqqHLfO0tIcLyBLrNH4HTWPg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/codecs-data-structures@2.0.0-rc.1': resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} peerDependencies: typescript: '>=5' + '@solana/codecs-data-structures@2.1.1': + resolution: {integrity: sha512-OcR7FIhWDFqg6gEslbs2GVKeDstGcSDpkZo9SeV4bm2RLd1EZfxGhWW+yHZfHqOZiIkw9w+aY45bFgKrsLQmFw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/codecs-numbers@2.0.0-rc.1': resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} peerDependencies: typescript: '>=5' + '@solana/codecs-numbers@2.1.1': + resolution: {integrity: sha512-m20IUPJhPUmPkHSlZ2iMAjJ7PaYUvlMtFhCQYzm9BEBSI6OCvXTG3GAPpAnSGRBfg5y+QNqqmKn4QHU3B6zzCQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/codecs-strings@2.0.0-rc.1': resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 typescript: '>=5' + '@solana/codecs-strings@2.1.1': + resolution: {integrity: sha512-uhj+A7eT6IJn4nuoX8jDdvZa7pjyZyN+k64EZ8+aUtJGt5Ft4NjRM8Jl5LljwYBWKQCgouVuigZHtTO2yAWExA==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5.3.3' + '@solana/codecs@2.0.0-rc.1': resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} peerDependencies: typescript: '>=5' + '@solana/codecs@2.1.1': + resolution: {integrity: sha512-89Fv22fZ5dNiXjOKh6I3U1D/lVO/dF/cPHexdiqjS5k5R5uKeK3506rhcnc4ciawQAoOkDwHzW+HitUumF2PJg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/errors@2.0.0-rc.1': resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} hasBin: true peerDependencies: typescript: '>=5' + '@solana/errors@2.1.1': + resolution: {integrity: sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.3.3' + + '@solana/fast-stable-stringify@2.1.1': + resolution: {integrity: sha512-+gyW8plyMOURMuO9iL6eQBb5wCRwMGLO5T6jBIDGws8KR4tOtIBlQnQnzk81nNepE6lbf8tLCxS8KdYgT/P+wQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/functional@2.1.1': + resolution: {integrity: sha512-HePJ49Cyz4Mb26zm5holPikm8bzsBH5zLR41+gIw9jJBmIteILNnk2OO1dVkb6aJnP42mdhWSXCo3VVEGT6aEw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/instructions@2.1.1': + resolution: {integrity: sha512-Zx48hav9Lu+JuC+U0QJ8B7g7bXQZElXCjvxosIibU2C7ygDuq0ffOly0/irWJv2xmHYm6z8Hm1ILbZ5w0GhDQQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/keys@2.1.1': + resolution: {integrity: sha512-SXuhUz1c2mVnPnB+9Z9Yw6HPluIZbMlSByr+vPFLgaPYM356bRcNnu1pa28tONiQzRBFvl9qL08SL0OaYsmqPg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/kit@2.1.1': + resolution: {integrity: sha512-vV0otDSO9HFWIkAv7lxfeR7W6ruS/kqFYzTeRI+EuaZCgKdueavZnx9ydbpMCXis3BZ4Ao+k/ebzVWXMVvz+Lw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/nominal-types@2.1.1': + resolution: {integrity: sha512-EpdDhuoATsm9bmuduv6yoNm1EKCz2tlq13nAazaVyQvkMBHhVelyT/zq0ruUplQZbl7qyYr5hG7p1SfGgQbgSQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/options@2.0.0-rc.1': resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} peerDependencies: typescript: '>=5' + '@solana/options@2.1.1': + resolution: {integrity: sha512-rnEExUGVOAV79kiFUEl/51gmSbBYxlcuw2VPnbAV/q53mIHoTgCwDD576N9A8wFftxaJHQFBdNuKiRrnU/fFHA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/programs@2.1.1': + resolution: {integrity: sha512-fVOA4SEijrIrpG7GoBWhid43w3pT7RTfmMYciVKMb17s2GcnLLcTDOahPf0mlIctLtbF8PgImtzUkXQyuFGr8Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/promises@2.1.1': + resolution: {integrity: sha512-8M+QBgJAQD0nhHzaezwwHH4WWfJEBPiiPAjMNBbbbTHA8+oYFIGgY1HwDUePK8nrT1Q1dX3gC+epBCqBi/nnGg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-api@2.1.1': + resolution: {integrity: sha512-MTBuoRA9HtxW+CRpj1Ls5XVhDe00g8mW2Ib4/0k6ThFS0+cmjf+O78d8hgjQMqTtuzzSLZ4355+C7XEAuzSQ4g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-parsed-types@2.1.1': + resolution: {integrity: sha512-+n1IWYYglevvNE1neMiLOH6W67EzmWj8GaRlwGxcyu6MwSc/8x1bd2hnEkgK6md+ObPOxoOBdxQXIY/xnZgLcw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-spec-types@2.1.1': + resolution: {integrity: sha512-3/G/MTi/c70TVZcB0DJjh5AGV7xqOYrjrpnIg+rLZuH65qHMimWiTHj0k8lxTzRMrN06Ed0+Q7SCw9hO/grTHA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-spec@2.1.1': + resolution: {integrity: sha512-3Hd21XpaKtW3tG0oXAUlc1k0hX7/eqHpf8Gg744sr9G3ib5gT7EopcZRsH5LdESgS0nbv/c75TznCXjaUyRi+g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-subscriptions-api@2.1.1': + resolution: {integrity: sha512-b4JuVScYGaEgO3jszYf7LqXdJK4GoUIevXcyQWq4Zk+R7P41VxGQWa2kzdPX9LIi+UGBmCThdRBfgOYyyHRKDg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-subscriptions-channel-websocket@2.1.1': + resolution: {integrity: sha512-xEDnMXnwMtKDEpzmIXTkxxvLqGsxqlKILmyfGsQOMJ9RHYkHmz/8MarHcjnYhyZ5lrs2irN/wExUNlSZTegSEw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + ws: ^8.18.0 + + '@solana/rpc-subscriptions-spec@2.1.1': + resolution: {integrity: sha512-ANT5Tub/ZqqewRtklz02km8iCUe0qwBGi3wsKTgiX7kRx3izHn6IHl90w1Y19wPd692mfZW8+Pk5PUrMSXhR3g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-subscriptions@2.1.1': + resolution: {integrity: sha512-xGLIuJHxg0oCNiS40NW/5BPxHM5RurLcEmBAN1VmVtINWTm8wSbEo85a5q7cbMlPP4Vu/28lD7IITjS5qb84UQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-transformers@2.1.1': + resolution: {integrity: sha512-rBOCDQjOI1eQICkqYFV43SsiPdLcahgnrGuDNorS3uOe70pQRPs1PTuuEHqLBwuu9GRw89ifRy49aBNUNmX8uQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-transport-http@2.1.1': + resolution: {integrity: sha512-Wp7018VaPqhodQjQTDlCM7vTYlm3AdmRyvPZiwv5uzFgnC8B0xhEZW+ZSt1zkSXS6WrKqtufobuBFGtfG6v5KQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc-types@2.1.1': + resolution: {integrity: sha512-IaQKiWyTVvDoD0/3IlUxRY3OADj3cEjfLFCp1JvEdl0ANGReHp4jtqUqrYEeAdN/tGmGoiHt3n4x61wR0zFoJA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/rpc@2.1.1': + resolution: {integrity: sha512-X15xAx8U0ATznkoNGPUkGIuxTIOmdew1pjQRHAtPSKQTiPbAnO1sowpt4UT7V7bB6zKPu+xKvhFizUuon0PZxg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/signers@2.1.1': + resolution: {integrity: sha512-OfYEUgrJSrBDTC43kSQCz9A12A9+6xt2azmG8pP78yXN/bDzDmYF2i4nSzg/JzjjA5hBBYtDJ+15qpS/4cSgug==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/spl-token-group@0.0.7': resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} engines: {node: '>=16'} @@ -1825,6 +2038,36 @@ packages: peerDependencies: '@solana/web3.js': ^1.95.5 + '@solana/subscribable@2.1.1': + resolution: {integrity: sha512-k6qe/Iu94nVtapap9Ei+3mm14gx1H+7YgB6n2bj9qJCdVN6z6ZN9nPtDY2ViIH4qAnxyh7pJKF7iCwNC/iALcw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/sysvars@2.1.1': + resolution: {integrity: sha512-bG7hNFpFqZm6qk763z5/P9g9Nxc0WXe+aYl6CQSptaPsmqUz1GhlBjAov9ePVFb29MmyMZ5bA+kmCTytiHK1fQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/transaction-confirmation@2.1.1': + resolution: {integrity: sha512-hXv0D80u1jNEq2/k1o9IBXXq7+JYg8x4tm0kVWjzvdJjYow8EkQay5quq5o0ciFfWqlOyFwYRC7AGrKc3imE7A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/transaction-messages@2.1.1': + resolution: {integrity: sha512-sDf3OWV5X1C8huqsap+DyHIBAUenNJd3h7j/WI9MeIJZdGEtqxssGa2ixhecsMaevtUBKKJM9RqAvfTdRTAnLw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/transactions@2.1.1': + resolution: {integrity: sha512-LX/7XfcHH9o0Kpv+tpnCl56IaatD/0sMWw9NnaeZ2f7pJyav9Jmeu5LJXvdHJw2jh277UEqc9bHwKruoMrtOTw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/wallet-adapter-base-ui@0.1.4': resolution: {integrity: sha512-lI+lfwPypqxanZJBvSd4ryPng0UfP9fxbD4Yj0KKLyUu0Pfl2HkDg+eMWC32Tz5qKTaQjExD3kuXiopRzF8ZvQ==} engines: {node: '22'} @@ -2817,6 +3060,12 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + gill@0.9.0: + resolution: {integrity: sha512-PQzwZI83mUoUJs9hoUNjzyQZb8+NyeFHwuF3SnaZNshi8YzRrgqgHSwmjOkIkqD/6ukyRMivq7XW704tPVvVAQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5' + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -4092,6 +4341,9 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@7.9.0: + resolution: {integrity: sha512-TotbsoWjj1gHRlCBRKtFCKTg3HeHE87dLe3DkuWzQKlyQ0LQXP4pLTb3fBUhdcODdOoT8BkB4zdOShW57NH1rw==} + undici@6.21.2: resolution: {integrity: sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==} engines: {node: '>=18.17'} @@ -5970,6 +6222,51 @@ snapshots: - react - react-native + '@solana-program/address-lookup-table@0.7.0(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + dependencies: + '@solana/kit': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + + '@solana-program/compute-budget@0.7.0(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + dependencies: + '@solana/kit': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + + '@solana-program/system@0.7.0(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + dependencies: + '@solana/kit': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + + '@solana-program/token-2022@0.4.1(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3))': + dependencies: + '@solana/kit': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/sysvars': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + + '@solana/accounts@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec': 2.1.1(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/addresses@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/assertions': 2.1.1(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/nominal-types': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/assertions@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -5990,6 +6287,11 @@ snapshots: '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) typescript: 5.8.3 + '@solana/codecs-core@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) @@ -5997,12 +6299,25 @@ snapshots: '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) typescript: 5.8.3 + '@solana/codecs-data-structures@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) typescript: 5.8.3 + '@solana/codecs-numbers@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) @@ -6011,6 +6326,14 @@ snapshots: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.8.3 + '@solana/codecs-strings@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.8.3 + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) @@ -6022,12 +6345,83 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/codecs@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.1.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/options': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/errors@2.0.0-rc.1(typescript@5.8.3)': dependencies: chalk: 5.4.1 commander: 12.1.0 typescript: 5.8.3 + '@solana/errors@2.1.1(typescript@5.8.3)': + dependencies: + chalk: 5.4.1 + commander: 13.1.0 + typescript: 5.8.3 + + '@solana/fast-stable-stringify@2.1.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@solana/functional@2.1.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@solana/instructions@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/keys@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/assertions': 2.1.1(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/nominal-types': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/accounts': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/functional': 2.1.1(typescript@5.8.3) + '@solana/instructions': 2.1.1(typescript@5.8.3) + '@solana/keys': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/programs': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/signers': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/sysvars': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-confirmation': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + + '@solana/nominal-types@2.1.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) @@ -6039,6 +6433,168 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/options@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.1.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/programs@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/promises@2.1.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@solana/rpc-api@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/keys': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec': 2.1.1(typescript@5.8.3) + '@solana/rpc-transformers': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-parsed-types@2.1.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@solana/rpc-spec-types@2.1.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@solana/rpc-spec@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec-types': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/rpc-subscriptions-api@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/keys': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 2.1.1(typescript@5.8.3) + '@solana/rpc-transformers': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-subscriptions-channel-websocket@2.1.1(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/functional': 2.1.1(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 2.1.1(typescript@5.8.3) + '@solana/subscribable': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + + '@solana/rpc-subscriptions-spec@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/promises': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec-types': 2.1.1(typescript@5.8.3) + '@solana/subscribable': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/rpc-subscriptions@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/fast-stable-stringify': 2.1.1(typescript@5.8.3) + '@solana/functional': 2.1.1(typescript@5.8.3) + '@solana/promises': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-subscriptions-api': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-channel-websocket': 2.1.1(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 2.1.1(typescript@5.8.3) + '@solana/rpc-transformers': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/subscribable': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + + '@solana/rpc-transformers@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/functional': 2.1.1(typescript@5.8.3) + '@solana/nominal-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-transport-http@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec-types': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + undici-types: 7.9.0 + + '@solana/rpc-types@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/nominal-types': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/fast-stable-stringify': 2.1.1(typescript@5.8.3) + '@solana/functional': 2.1.1(typescript@5.8.3) + '@solana/rpc-api': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-spec': 2.1.1(typescript@5.8.3) + '@solana/rpc-spec-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-transformers': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-transport-http': 2.1.1(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/signers@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/instructions': 2.1.1(typescript@5.8.3) + '@solana/keys': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/nominal-types': 2.1.1(typescript@5.8.3) + '@solana/transaction-messages': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) @@ -6070,6 +6626,71 @@ snapshots: - typescript - utf-8-validate + '@solana/subscribable@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/sysvars@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/accounts': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transaction-confirmation@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/keys': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/promises': 2.1.1(typescript@5.8.3) + '@solana/rpc': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + + '@solana/transaction-messages@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.1.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/functional': 2.1.1(typescript@5.8.3) + '@solana/instructions': 2.1.1(typescript@5.8.3) + '@solana/nominal-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transactions@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.1.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) + '@solana/codecs-strings': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + '@solana/functional': 2.1.1(typescript@5.8.3) + '@solana/instructions': 2.1.1(typescript@5.8.3) + '@solana/keys': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/nominal-types': 2.1.1(typescript@5.8.3) + '@solana/rpc-types': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/wallet-adapter-base-ui@0.1.4(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.2)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': dependencies: '@solana/wallet-adapter-react': 0.15.37(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.79.1(@babel/core@7.26.10)(@types/react@19.1.2)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) @@ -7233,6 +7854,22 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + gill@0.9.0(@solana/sysvars@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + dependencies: + '@solana-program/address-lookup-table': 0.7.0(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/compute-budget': 0.7.0(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/system': 0.7.0(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token-2022': 0.4.1(@solana/kit@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)) + '@solana/assertions': 2.1.1(typescript@5.8.3) + '@solana/codecs': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/kit': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.1.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + typescript: 5.8.3 + transitivePeerDependencies: + - '@solana/sysvars' + - fastestsmallesttextencoderdecoder + - ws + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -8664,6 +9301,8 @@ snapshots: undici-types@6.19.8: {} + undici-types@7.9.0: {} + undici@6.21.2: {} universalify@2.0.1: {}