Mesajları İmzalayın ve Kullanıcıların Kimliğini Doğrulayın

Bir mesajı imzalamak, kullanıcının bir cüzdanı kontrol ettiğini kanıtlar; işlem, ücret ya da ağ çağrısı gerekmez. Uygulamalar imzalı mesajları kullanıcıların oturum açması, bir kullanıcının şartları kabul ettiğini kaydetmesi, bir cüzdanı mevcut bir hesaba bağlaması veya sipariş verme gibi zincir dışı gerçekleşen bir işlemi yetkilendirmesi için kullanır.

Solana cüzdanları mesajları iki şekilde imzalar:

  • Zincir dışı mesajlar (solana:signOffchainMessage), metninizi zincir dışı mesaj imzalama (OCMS) spesifikasyonunda tanımlanan standart bir biçimde sarmalar. Bu biçim asla bir işlemle karıştırılamaz, mesajı imzalaması gereken hesapları belirtir ve donanım cüzdanlarının imzaladığı biçimdir.
  • Ham mesajlar (solana:signMessage), uygulamanızın ilettiği baytları tam olarak imzalar; ön ek ya da bildirilmiş imzalayıcı içermez. Çoğu yazılım cüzdanı bunları destekler, ancak baytlarda bunun bir işlem değil mesaj olduğunu gösteren hiçbir şey yoktur.

Cüzdan destekliyorsa zincir dışı mesajları kullanın, desteklemiyorsa ham mesajlara geri dönün. Bu kılavuzun geri kalanında örnek olarak oturum açma kullanılır, ancak aynı imzalama ve doğrulama adımları her mesaj için geçerlidir.

Bir mesaj biçimi seçin

Zincir dışı mesajHam mesajİşlem
Cüzdan özelliğisignOffchainMessagesignMessagesignTransaction
Donanım cüzdanı desteğiCihaza bağlıHayırEvet
Kullanıcı tarafından okunabilirEvetYalnızca yazılımCihaza bağlı
Gerekli imzalayanları bildirirEvetHayırEvet
RPC çağrıları ve ücretlerYokYokBlockhash, gönderme, ücret, onay

Neden işlem değil?

Donanım cüzdanlarının eskiden okunabilir bir mesajı imzalama yolu yoktu. Birçok uygulama bu kullanıcılardan bunun yerine işlevsiz (no-op) bir işlem imzalamalarını istiyordu. Bu işe yarar, ancak gecikme, maliyet ve risk ekler:

  • Ağ ücretleri. Bir işlem, hiçbir şey yapmasa bile ücret öder.
  • Birden fazla RPC çağrısı. Uygulamanız bir blockhash alır, simülasyon yapar, gönderir ve ardından onay için yoklama yapar.
  • Ek gecikme. Bu çağrıların her biri, kullanıcı oturum açmadan önce bir gidiş-dönüş demektir.
  • Okunabilir hiçbir şey yok. Kullanıcı, neyi kabul ettiğini açıklayan bir ifadeyi değil, işlem baytlarını onaylar.
  • Davranışsal risk. Kullanıcılardan yalnızca oturum açmak için işlem imzalamalarını istemek, onları zararlı bir işlemi imzalamaya kandırmayı kolaylaştırır.

Zincir dışı bir mesaj bunların hepsini atlar. Tek gidiş-dönüş, kendi sunucunuzadır.

A sign-in transaction fetches a blockhash, signs, simulates, sends, and polls for confirmation before the server can verify it. An off-chain message goes from a server challenge to a wallet signature to a server-side check, with no RPC calls and no fee.

Cüzdanın neyi imzaladığı

Zincir dışı bir mesaj, sabit bir imzalama alanıyla başlar: \xffsolana offchain. Geçerli hiçbir işlem bu baytlarla başlamaz, bu nedenle imzalanmış bir mesaj asla işlem olarak kullanılamaz. Biçimin 1. sürümü ardından gerekli imzalayanları listeler ve mesajı uzunluk sınırı olmayan UTF-8 metni olarak taşır.

Cüzdan bu baytları oluşturur, imzalar ve imzayla birlikte geri döndürür. Spesifikasyon, eski 0. sürüm de dahil olmak üzere biçimin tamamını kapsar.

A version 1 off-chain message is a 16-byte signing domain, a version byte, a signer count, each signer's 32-byte public key in sorted order, and the UTF-8 content. The envelope prefixes it with a signature count and one 64-byte signature per signer.

Bir oturum açma yöntemi seçin

Her cüzdan henüz zincir dışı mesajları desteklemez. İmza istemeden önce bağlı hesabın özelliklerini kontrol edin ve özellik yoksa yedek yönteme geçin:

  1. Hesap, mesaj sürümü 1 ile solana:signOffchainMessage özelliğini destekliyorsa, zincir dışı bir mesaj imzalayın.
  2. Aksi halde aynı meydan okumayı (challenge) solana:signMessage ile imzalayın. Çoğu yazılım cüzdanı bunu destekler.
  3. Mesaj desteği olmayan donanım cüzdanı kullanıcılarına, bunun yerine bir oturum açma işlemi imzalayan, "Donanım cüzdanı kullanıyorum" anahtarı gibi açık bir katılım seçeneği sunun. Onları varsayılan olarak bu yola yönlendirmeyin.
When a wallet connects, check whether its account supports solana:signOffchainMessage version 1. If it does, sign an off-chain message. If not, sign a raw message with solana:signMessage, and let hardware wallet users opt into signing a sign-in transaction.

Tarayıcıda mesaj imzalama

Wallet Standard paketlerini Kit ile birlikte yükleyin:

Terminal
$
npm install @solana/kit @solana/wallet-standard-features @wallet-standard/ui @wallet-standard/ui-features @wallet-standard/ui-registry

Bu yardımcılar bir UiWalletAccount alır; örneğin React kılavuzundaki useConnectedWallet tarafından döndürülen account:

offchain-message.ts
import {
SolanaSignOffchainMessage,
type SolanaSignOffchainMessageFeature
} from "@solana/wallet-standard-features";
import type { UiWalletAccount } from "@wallet-standard/ui";
import { getWalletAccountFeature } from "@wallet-standard/ui-features";
import { getWalletAccountForUiWalletAccount } from "@wallet-standard/ui-registry";
type OffchainMessageFeature =
SolanaSignOffchainMessageFeature[typeof SolanaSignOffchainMessage];
export function supportsOffchainMessages(account: UiWalletAccount) {
if (!account.features.includes(SolanaSignOffchainMessage)) return false;
const feature = getWalletAccountFeature(
account,
SolanaSignOffchainMessage
) as OffchainMessageFeature;
return feature.supportedMessageVersions.includes(1);
}
export async function signOffchainMessage(
account: UiWalletAccount,
message: string
) {
const feature = getWalletAccountFeature(
account,
SolanaSignOffchainMessage
) as OffchainMessageFeature;
const [output] = await feature.signOffchainMessage({
messageVersion: 1,
account: getWalletAccountForUiWalletAccount(account),
message,
requiredSigners: [account.publicKey]
});
return output;
}

signOffchainMessage, cüzdanın imzaladığı tüm baytları içeren signedOffchainMessage ile signature değerlerini döndürür. İkisini de Base64 ile kodlayın ve cüzdan adresiyle birlikte sunucunuza gönderin.

Meydan okuma metnini küçük ekranlarda iyi okunacak şekilde yazın. Uygulamanızın ve alan adınızın adını belirtin, imzalamanın ne yaptığını söyleyin ve sunucunuzdan alınan tek kullanımlık bir nonce ile bir düzenlenme zamanı ekleyin:

swap.app wants you to sign in with your Solana account.
Signing in costs nothing and does not submit a transaction.
Nonce: 8f3a1c9e
Issued at: 2026-09-29T17:04:00Z

İmzayı sunucunuzda doğrulama

Doğrulama yerel bir imza kontrolüdür ve RPC bağlantısı gerektirmez. Cüzdanın imzaladığı baytları çözün, bunların verdiğiniz meydan okumayla eşleştiğini doğrulayın, ardından imzayı kontrol edin:

verify-sign-in.ts
import {
address,
assertOffchainMessageV1Equal,
getBase64Encoder,
getOffchainMessageDecoder,
verifyOffchainMessageEnvelope,
type OffchainMessageBytes,
type SignatureBytes
} from "@solana/kit";
export async function verifySignIn(input: {
walletAddress: string;
expectedMessage: string;
signedOffchainMessage: string;
signature: string;
}) {
const signer = address(input.walletAddress);
const base64 = getBase64Encoder();
const content = base64.encode(
input.signedOffchainMessage
) as OffchainMessageBytes;
const signature = base64.encode(input.signature) as SignatureBytes;
const received = getOffchainMessageDecoder().decode(content);
if (received.version !== 1) {
throw new Error(`Expected a v1 message, got v${received.version}`);
}
assertOffchainMessageV1Equal(received, {
version: 1,
requiredSignatories: [{ address: signer }],
content: input.expectedMessage
});
await verifyOffchainMessageEnvelope({
content,
signatures: { [signer]: signature }
});
}

assertOffchainMessageV1Equal, içerik veya imzalayanlar beklediğinizden farklıysa hata fırlatır. verifyOffchainMessageEnvelope, imza eksik veya geçersizse hata fırlatır. expectedMessage değerini istemcinin geri gönderdiği kopyaya güvenmek yerine sunucunuzda nonce ile arayın. Doğrulama başarılı olur olmaz nonce'u tüketin; böylece aynı imza iki kez oturum açamaz.

Ham mesajı doğrulama

Bir signMessage imzası doğrudan meydan okuma baytlarını kapsar, dolayısıyla istemcinin yalnızca imzayı ve cüzdan adresini göndermesi yeterlidir. İmzayı verdiğiniz meydan okumaya karşı kontrol edin:

verify-raw-sign-in.ts
import {
address,
getBase64Encoder,
getPublicKeyFromAddress,
getUtf8Encoder,
signatureBytes,
verifySignature
} from "@solana/kit";
export async function verifyRawSignIn(input: {
walletAddress: string;
expectedMessage: string;
signature: string;
}) {
const publicKey = await getPublicKeyFromAddress(address(input.walletAddress));
const signature = signatureBytes(getBase64Encoder().encode(input.signature));
const message = getUtf8Encoder().encode(input.expectedMessage);
if (!(await verifySignature(publicKey, signature, message))) {
throw new Error("Invalid signature");
}
}

Aynı nonce kuralları geçerlidir: expectedMessage değerini nonce ile arayın ve doğrulama başarılı olunca nonce'u tüketin.

@solana/kit, bu yardımcıları @solana/offchain-messages paketinden yeniden dışa aktarır. Sunucunuz Kit'e bağımlı değilse bağımsız paketi kullanın.

Solana ile Oturum Açma

Solana ile Oturum Açma (SIWS), meydan okuma metnini standartlaştırır. Uygulamanız domain, statement, nonce ve issuedAt gibi yapılandırılmış alanları cüzdanın solana:signIn özelliğine iletir. Cüzdan bunları standart bir mesaj olarak biçimlendirir, gerekirse bağlanır ve tek adımda imzalar.

solana:signIn özelliğinin 1.1.0 sürümü bir useOffchainMessage seçeneği ekler. useOffchainMessage: { messageVersion: 1 } ilettiğinizde cüzdan, SIWS metnini yalnızca imzalayan hesabı gerekli imzalayan olarak içeren 1. sürüm bir zincir dışı mesajda sarmalar. Donanım cüzdanları bunu görüntüleyip imzalayabilir ve çıktı signedMessageFormat: { kind: "offchainMessage", messageVersion: 1 } değerini bildirir. 1.0.0 sürümünü bildiren cüzdanlar bu seçeneği tanımaz; bu nedenle önce özellik sürümünü kontrol edin ve aksi halde seçeneği atlayın.

solana:signIn bir hesap özelliği değil, cüzdan özelliğidir; bu nedenle bu yardımcılar bir UiWallet alır:

sign-in.ts
import {
SolanaSignIn,
type SolanaSignInFeature,
type SolanaSignInInput
} from "@solana/wallet-standard-features";
import type { UiWallet } from "@wallet-standard/ui";
import { getWalletFeature } from "@wallet-standard/ui-features";
type SignInFeature = SolanaSignInFeature[typeof SolanaSignIn];
export function supportsOffchainSignIn(wallet: UiWallet) {
if (!wallet.features.includes(SolanaSignIn)) return false;
const feature = getWalletFeature(wallet, SolanaSignIn) as SignInFeature;
return feature.version === "1.1.0";
}
export async function signIn(wallet: UiWallet, input: SolanaSignInInput) {
const feature = getWalletFeature(wallet, SolanaSignIn) as SignInFeature;
const [output] = await feature.signIn({
...input,
...(supportsOffchainSignIn(wallet) && {
useOffchainMessage: { messageVersion: 1 }
})
});
return output;
}

Girdiyi sunucunuzda tek kullanımlık bir nonce ve bir issuedAt zamanıyla oluşturun ve nonce'a göre anahtarlanmış bir kopyasını saklayın. Oturum açma doğrulandığında nonce'u tüketin.

Wallet Adapter olmadan, @solana/wallet-standard-util paketindeki verifySignIn yalnızca düz SIWS çıktısını kontrol eder. signedMessageFormat bir zincir dışı mesaj bildirdiğinde, signedMessage içeriği SIWS metni olan bir zincir dışı mesajdır. Beklenen metni, saklanan girdinizden ve imzalayan adresten @solana/wallet-standard-util içindeki createSignInMessageText ile yeniden oluşturun, ardından diğer tüm zincir dışı mesajlar gibi, İmzayı sunucunuzda doğrulama bölümünde olduğu gibi doğrulayın. @solana/offchain-messages README dosyası bu akışı Kit içinde ele alır.

Cüzdanlar için

Cüzdanlar, @solana/wallet-standard-features paketindeki solana:signOffchainMessage Wallet Standard özelliğini uygulayarak destek ekler:

  • version: "1.0.0" bildirin ve imzalayabileceğiniz mesaj sürümlerini supportedMessageVersions içinde listeleyin. Bugün bu değer [1]'dir.
  • requiredSigners içinden sürüm 1 önsözünü (preamble) oluşturun; bu listeyi sıralayın ve yinelenen girdiler olup olmadığını kontrol edin. Hesap gerekli imzalayanlardan biri değilse isteği reddedin.
  • İmzalamadan önce mesaj metnini, donanım cihazlarında da dahil olmak üzere, kullanıcıya eksiksiz olarak gösterin.
  • İmzaladığınız baytların tamamını, Ed25519 signature ile birlikte signedOffchainMessage olarak döndürün.
  • Zincir dışı (off-chain) Sign In With Solana desteği sunmak için solana:signIn için version: "1.1.0" bildirin. Girdi useOffchainMessage içeriyorsa SIWS metnini sürüm 1 zincir dışı mesaj olarak imzalayın, baytlarını signedMessage olarak döndürün ve signedMessageFormat değerini ayarlayın.
  • solana:signMessage desteğini sürdürmeye devam edin. Bu yöntem değişmedi ve uygulamalar onu hâlâ yedek yöntem olarak kullanıyor.

Is this page helpful?

İçindekiler

Sayfayı Düzenle
© 2026 Solana Vakfı. Tüm hakları saklıdır.