Mesaj İ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. İşlem, ücret veya ağ çağrısı gerekmez. Uygulamalar imzalı mesajları kullanıcıların oturum açması, bir kullanıcının şartları kabul ettiğinin kaydedilmesi, bir cüzdanın mevcut bir hesaba bağlanması veya sipariş vermek gibi zincir dışında gerçekleşen bir işlemin yetkilendirilmesi 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çimle sarmalar. Bu biçim hiçbir zaman 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ı önek olmadan ve imzalayanlar belirtilmeden olduğu gibi imzalar. Çoğu yazılım cüzdanı bunları destekler, ancak baytlar içinde bunun bir işlem değil mesaj olduğunu belirten 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 türlü 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ım cüzdanlarındaCihaza 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 hiçbir şey yapmayan 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 sorgulama 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 belirten bir ifadeyi değil, işlem baytlarını onaylar.
  • Davranışsal risk. Kullanıcılardan yalnızca oturum açmak için bir işlem imzalamalarını istemek, onları zararlı bir işlemi imzalamaya kandırmayı kolaylaştırır.

Zincir dışı 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 neyi imzalar?

Zincir dışı mesaj, sabit bir imzalama alanıyla başlar: \xffsolana offchain. Geçerli hiçbir işlem bu baytlarla başlamaz, dolayısıyla imzalanmış bir mesaj hiçbir zaman işlem olarak kullanılamaz. Biçimin 1. sürümü ardından gerekli imzalayanları listeler ve mesajı uzunluk sınırı olmadan 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 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

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

  1. Hesap, 1. mesaj sürümüyle 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 açık bir isteğe bağlı seçenek sunun; örneğin "Donanım cüzdanı kullanıyorum" anahtarı. 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, React kılavuzundaki useConnectedWallet tarafından döndürülen account gibi bir UiWalletAccount alır:

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 olan signedOffchainMessage ile signature değerini döndürür. İkisini de Base64 ile kodlayın ve cüzdan adresiyle birlikte sunucunuza gönderin.

Meydan okuma metnini küçük ekranda rahat okunacak şekilde yazın. Uygulamanızın ve alan adınızın adını belirtin, imzalamanın ne yaptığını söyleyin ve sunucunuz tarafından oluşturulan 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ğrulayın

Doğrulama yerel bir imza kontrolüdür ve RPC bağlantısı gerektirmez. Cüzdanın imzaladığı baytların kodunu çözün, verdiğiniz meydan okumayla eşleştiğini teyit edin, 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 guvenmek yerine sunucunuzda nonce'a göre bulun. 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; bu nedenle 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'a göre bulun ve doğrulama başarılı olur olmaz 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.

Sign In With Solana

Sign In With Solana (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 mesaja dönüştürür, 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 tek gerekli imzalayanı imzalayan hesap olan 1. sürüm bir zincir dışı mesaja sarar. Donanım cüzdanları bunu görüntüleyebilir ve imzalayabilir; çı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 yüzden önce özellik sürümünü kontrol edin, 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ğrulanır doğrulanmaz nonce'u tüketin.

Wallet Adapter olmadan, @solana/wallet-standard-util içindeki 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 İmzayı sunucunuzda doğrulayın bölümünde olduğu gibi, diğer zincir dışı mesajlar 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 imzalayabildiğiniz mesaj sürümlerini supportedMessageVersions içinde listeleyin. Bugün bu değer [1] olmalıdır.
  • Sürüm 1 preamble'ını, sıralayıp tekrarlara karşı kontrol ettiğiniz requiredSigners değerinden oluşturun. Hesap gerekli imzalayanlardan biri değilse isteği reddedin.
  • İmzalamadan önce mesaj metnini, donanım cihazları dahil, kullanıcıya eksiksiz olarak gösterin.
  • İmzaladığınız tam baytları, Ed25519 signature ile birlikte signedOffchainMessage olarak döndürün.
  • Zincir dışı Sign In With Solana desteği sunmak için solana:signIn adına version: "1.1.0" bildirin. Girdi useOffchainMessage içerdiğinde, 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ürün. 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.