@tuwaio/siwx-solana
@tuwaio/siwx-solana is the Solana Layer 2 (L2) package of SIWX (Sign-In With X), the authentication project of TUWA Stage 1 (“Core Auth & Primitives”, next to Orbit Utils). Built on @tuwaio/siwx-core and @solana/kit, it signs CAIP-122 messages with Solana wallets and verifies solana ed25519 signatures with the native Web Crypto API. It does not use the legacy @solana/web3.js or gill.
🏛️ Core Capabilities
- Signing:
createSolanaSiwxSignerturns a Wallet Standard wallet and account (typed with@wallet-standard/base), an@solana/kitmessage signer, a legacy adapter oruseWallet()of@solana/wallet-adapter(v1 or v3) into the(message) => signaturefunction thatuseSiwxfrom@tuwaio/siwx-reactexpects. Signatures are returned as base58 strings. It signs with the message signer shared with Satellite Connect,createSolanaMessageSignerof@tuwaio/orbit-solana. - Off-chain messages and hardware wallets: the wallet signs either the UTF-8 bytes of the message (
solana:signMessage) or its version 1 off-chain message (solana:signOffchainMessage), which hardware wallets can show and sign. By default the signer picks the off-chain message when the account supports it but notsolana:signMessage;messageFormatforces one or the other. - Verification:
verifyEd25519accepts a{ message, signature }payload (base58 strings or bytes) or the output of the Wallet Standardsolana:signInfeature, signed as the message itself or as its version 1 off-chain message. It requires asolanachain and a 64-byte signature, validates the message (format, expiration,notBeforeand anissuedAtin the future) and the address, and checks the signature withcrypto.subtle. It runs locally, without RPC calls, and returns{ success, data, error }instead of throwing. - Runtimes: any runtime whose Web Crypto API supports Ed25519, such as Node.js 20+ and current browsers. No native modules or polyfills.
💾 Installation
pnpm add @tuwaio/siwx-solana @tuwaio/siwx-core @tuwaio/orbit-core @tuwaio/orbit-solana @solana/kit @wallet-standard/base[!IMPORTANT]
@tuwaio/siwx-core,@tuwaio/orbit-core,@tuwaio/orbit-solana(>=0.7.1),@solana/kit(>=6) and@wallet-standard/base(>=1.1.0) are peer dependencies and must be installed alongside@tuwaio/siwx-solana. The signer comes from@tuwaio/orbit-solana/signer, which loads only@solana/kit, so the server verification of@tuwaio/siwx-serverneeds no Wallet Standard runtime.
🚀 Usage
Signing with a Wallet Standard wallet
import { createSolanaSiwxSigner } from '@tuwaio/siwx-solana';
import type { Wallet, WalletAccount } from '@wallet-standard/base';
declare const wallet: Wallet; // a wallet with the `solana:signMessage` feature
declare const account: WalletAccount; // the connected account, one of `wallet.accounts`
declare const message: string;
const signer = createSolanaSiwxSigner({ wallet, account });
// Opens the wallet prompt. Rejects with "[SIWX-SOLANA] Signing failed: …" (original error in `cause`).
const signature = await signer(message); // base58Pass the Wallet Standard Wallet and WalletAccount objects. The UI handles of @wallet-standard/ui (UiWallet, UiWalletAccount) only list feature names and cannot sign.
Signing with other signers
import type { MessageModifyingSigner } from '@solana/kit';
import { createSolanaSiwxSigner } from '@tuwaio/siwx-solana';
declare const kitSigner: MessageModifyingSigner; // an @solana/kit message signer
declare const walletAdapter: { signMessage(message: Uint8Array): Promise<Uint8Array> }; // legacy wallet adapter
const signWithKit = createSolanaSiwxSigner(kitSigner);
const signWithAdapter = createSolanaSiwxSigner(walletAdapter);The signer uses the first capability it finds: modifyAndSignMessages (an @solana/kit MessageModifyingSigner), the solana:signMessage feature of wallet.features, a signMessages method, or a legacy signMessage method (on the wallet, its adapter, the account or the target itself, so useWallet() of @solana/wallet-adapter v1 and v3 can be passed as is).
Off-chain messages (hardware wallets)
import { createSolanaSiwxSigner } from '@tuwaio/siwx-solana';
import type { Wallet, WalletAccount } from '@wallet-standard/base';
declare const wallet: Wallet; // a wallet with the `solana:signOffchainMessage` feature
declare const account: WalletAccount;
declare const message: string;
// 'auto' (default): the off-chain message when the account has `solana:signOffchainMessage` but not
// `solana:signMessage`. 'offchainMessage' always signs it, 'message' never does.
const signer = createSolanaSiwxSigner({ wallet, account }, { messageFormat: 'offchainMessage' });
const signature = await signer(message); // base58; verifyEd25519 and @tuwaio/siwx-server accept it as isThe wallet signs the version 1 off-chain message of the text with the account as its only signer. When the wallet returns the bytes it signed, the signer checks that they are that envelope and rejects otherwise. The signature is sent to the server like any other: the envelope is fully determined by the text and the address, so no server option is needed. A signOffchainMessage(message) method, as on useWallet() of @solana/wallet-adapter v3, works too.
Verifying a signature
import { verifyEd25519 } from '@tuwaio/siwx-solana';
declare const message: string;
declare const signature: string; // base58, as returned by createSolanaSiwxSigner
const result = await verifyEd25519({ message, signature });
if (result.success) {
console.log(result.data?.address); // "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:…"
} else {
console.error(result.error);
}The output of the Wallet Standard solana:signIn feature can be passed as-is (or as { output }); the signed message must be a CAIP-122 message. The signature may cover the message itself or its version 1 off-chain message; signed message bytes that are such an envelope are decoded, and its only signer must be the address of the message.
verifyEd25519 does not check the domain, URI, nonce or other policy rules. On a server, use @tuwaio/siwx-server, which adds the policy and single-use nonces, or run validatePolicy from @tuwaio/siwx-core yourself.
📚 API Reference
Every export, with signatures and types generated from the source, is documented at siwx.docs.tuwa.io/packages/siwx-solana .
📄 License
Licensed under the Apache-2.0 License. See the LICENSE file for details.
Interfaces
- SolanaLegacyMessageSigner
- SolanaSignInAccount
- SolanaSignInOutput
- SolanaSiwxSignerOptions
- SolanaWalletStandardSignerTarget