التحقق من العنوان

قد يؤدي إرسال الأموال إلى عنوان خاطئ إلى خسارة دائمة. يضمن التحقق من العنوان أن تقتصر عمليات الإرسال، سواء من قِبلك أو من قِبل مستخدميك، على العناوين القادرة على استقبال الأموال والوصول إليها بشكل صحيح. إذا كنت تُشغّل منصة تبادل، يُنصح بإجراء هذه الفحوصات وإلزام المستخدم بالتحقق من العنوان قبل الإرسال.

يعتمد التحقق على ما تُرسله:

  • رموز SPL توفر حماية جزئية ذاتية. يرفض Token Program أي تحويل لا تتطابق حساباته مع الـ mint المتوقع، لذا يفشل التحويل المُوجَّه بشكل خاطئ دون خسارة الأموال. تغطي معظم هذه الصفحة عمليات إرسال رموز SPL.
  • SOL الأصلي لا يمتلك هذه الحماية. ينجح تحويل System Program إلى أي حساب، لذا فإن مستلمًا خاطئًا يُقفل SOL بشكل دائم. انظر إرسال SOL الأصلي.

انظر كيف تعمل المدفوعات على سولانا لمفاهيم الدفع الأساسية.

فهم عناوين سولانا

تمتلك حسابات سولانا نوعين من العناوين: على المنحنى وخارج المنحنى.

العناوين على المنحنى

العناوين القياسية هي المفاتيح العامة المشتقة من keypairs من نوع Ed25519. هذه العناوين:

  • تمتلك مفتاحًا خاصًا مقابلًا يمكنه التوقيع على المعاملات
  • تُستخدم كعناوين محافظ

العناوين خارج المنحنى (PDAs)

Program Derived Addresses مشتقة بشكل حتمي من معرّف البرنامج والبذور. هذه العناوين:

  • لا تمتلك مفتاحًا خاصًا مقابلًا
  • لا يمكن التوقيع عليها إلا من قِبَل البرنامج الذي اشتُق منه العنوان

المحافظ التي تتحكم فيها البرامج، والمعروفة عادةً باسم المحافظ الذكية أو الحسابات الذكية، تمتلك عناوين خارج المنحنى. يستخدم هذا الدليل مصطلح "المحفظة الذكية" طوال محتواه.

أنواع الحسابات في المدفوعات

استخدم العنوان لجلب حساب من الشبكة، وتحقق من مالك البرنامج ونوع الحساب لتحديد كيفية التعامل مع العنوان.

معرفة ما إذا كان العنوان على المنحنى أو خارجه لا تخبرك بنوع الحساب، ولا بالبرنامج الذي يمتلكه، ولا بوجود حساب في ذلك العنوان. يجب عليك جلب الحساب من الشبكة لتحديد هذه التفاصيل.

حسابات System Program (المحافظ)

الحسابات المملوكة لـ System Program هي محافظ قياسية. لإرسال رموز SPL إلى محفظة، تحتاج إلى اشتقاق واستخدام Associated Token Account (ATA) الخاص بها.

بعد اشتقاق عنوان ATA، تحقق مما إذا كان token account موجودًا على السلسلة. إذا لم يكن ATA موجودًا، يمكنك تضمين تعليمة لإنشاء token account الخاص بالمستلم في نفس المعاملة مع التحويل. غير أن هذا يستلزم دفع rent لـ token account الجديد. ونظرًا لأن المستلم يمتلك ATA، فإن SOL المدفوع مقابل rent لا يمكن استرداده من قِبَل المُرسِل.

يمكن أن تتواجد حسابات النظام خارج المنحنى. إذا أراد مستخدمك إرسال رموز مميزة إلى عنوان خارج المنحنى، يُنصح بمطالبته بالتأكيد على أنه يقصد إرسال الأموال إلى محفظة ذكية.

بدون ضمانات كافية، يمكن استغلال دعم إنشاء ATA. إذ يمكن لمستخدم خبيث أن يطلب تحويلًا، وتُنشأ ATA الخاصة به على نفقتك، ثم يغلق ATA لاستعادة rent SOL، ويكرر العملية.

Token Accounts

Token accounts مملوكة لـ Token Program أو Token-2022 Program وتحتفظ بأرصدة الرموز. إذا كان العنوان الذي تستلمه مملوكًا لبرنامج رموز، فيجب عليك التحقق من أن الحساب هو token account (وليس mint account) وأنه يطابق mint account الرمز المتوقع قبل الإرسال.

يتحقق Token Programs تلقائيًا من أن كلا token accounts في التحويل يحتفظان برموز من نفس الـ mint. وإذا فشل التحقق، يُرفض التحويل ولا تُفقد أي أموال.

حسابات Mint

Mint accounts تتتبع إمداد الرمز وبيانات تعريف رمز معين. كما أن Mint accounts مملوكة لـ Token Programs ولكنها ليست مستلِمين صالحين لتحويلات الرموز. تؤدي محاولة إرسال الرموز إلى عنوان mint إلى فشل المعاملة، دون فقدان أي أموال.

حسابات أخرى

الحسابات التي تمتلكها برامج أخرى صالحة، غير أنها تخضع لقواعد الصلاحية التي يحددها البرنامج المالك. يُنصح بمطالبة المستخدم بالتأكيد على أنه يقصد إرسال الأموال إلى محفظة ذكية.

إرسال SOL الأصلي

يحدد التصنيف أعلاه الوجهات المسموح بها لرموز SPL. أما SOL الأصلي فأكثر صرامةً: المستلم الآمن الوحيد هو محفظة System Program (أو عنوان غير ممول على المنحنى يتحول إلى ذلك).

تضيف عملية نقل System Program لـ lamports إلى أي حساب، بما في ذلك عمليات السك و token accounts والبرامج وعناوين PDA. ولا يمكن نقل lamports خارجاً إلا من خلال البرنامج المالك للحساب، لذا فإن إرسال SOL إلى مستلم غير صحيح قد يؤدي إلى فقدان الأموال بشكل دائم.

على خلاف عملية نقل رموز SPL، فإن المعاملة لا تفشل عندما يكون المستلم عنواناً غير متوقع.

عند إرسال SOL الأصلي، لا يُقبل سوى نتيجة IS_WALLET. أما IS_TOKEN_ACCOUNT فلا يُقبل: إذ يحتفظ token account برموز SPL، وأي SOL يُرسل إليه يخرج عن سيطرة المرسل.

هذه طريقة شائعة لفقدان SOL: يقوم المستخدم بلصق عنوان سك الرمز (أو عنوان برنامج) في خانة سحب SOL. تنجح عملية النقل ولا يمكن استرداد SOL. احرص دائماً على تصنيف المستلم قبل التوقيع على أي عملية نقل SOL.

تدفق التحقق

يوضح المخطط التالي شجرة قرار مرجعية للتحقق من صحة العنوان:

Address Verification Flow

جلب الحساب

استخدم العنوان لجلب تفاصيل الحساب من الشبكة.

الحساب غير موجود

إذا لم يكن ثمة حساب على هذا العنوان، تحقق مما إذا كان العنوان على المنحنى أم خارجه:

  • خارج المنحنى (PDA): اشترط التحقق الصريح من المستلم قبل إنشاء ATA أو إرسال SPL tokens. في غياب حساب قائم، لا يمكنك تحديد البرنامج الذي اشتق هذا PDA من العنوان وحده، أو ما إذا كان برنامج المستلم قادرًا على الوصول إلى ATA الخاص به.

  • على المنحنى: هذا عنوان محفظة صالح (مفتاح عام) لم يُموَّل بعد. اشتق ATA، وتحقق من وجوده، وأرسل الرموز المميزة إليه. يجب عليك اتخاذ قرار بشأن السياسة المتعلقة بتمويل إنشاء ATA إذا لم يكن موجودًا.

الحساب موجود

إذا كان الحساب موجودًا، تحقق من البرنامج المالك له:

  • System Program: هذه محفظة قياسية. اشتق ATA، وتحقق من وجوده، وأرسل الرموز المميزة إليه. يجب عليك اتخاذ قرار بشأن السياسة المتعلقة بتمويل إنشاء ATA إذا لم يكن موجودًا.

  • Token Program / Token-2022: تحقق من أن الحساب هو token account (وليس mint account) وأنه يحمل الرمز المميز (mint) الذي تنوي إرساله. إذا كان صالحًا، أرسل الرموز المميزة مباشرةً إلى هذا العنوان. إذا كان mint account أو token account لـ mint مختلف، ارفض العنوان.

  • برنامج آخر: البرامج كمحافظ multisig تُعدّ مالكين مقبولين لـ token accounts. اطلب من المستخدم تأكيد نيته في الإرسال إلى محفظة ذكية.

    بعض المحافظ الذكية الشائعة:

عرض توضيحي

يوضح المثال التالي منطق التحقق من صحة العنوان فقط. هذا كود مرجعي لأغراض توضيحية.

لا يوضح العرض التوضيحي كيفية اشتقاق ATA أو بناء معاملة لإرسال الرموز. راجع وثائق token account وtoken transfer للاطلاع على أمثلة الكود.

يستخدم العرض التوضيحي أدناه ثلاثة نتائج محتملة:

النتيجةالمعنىالإجراء
IS_WALLETعنوان محفظة صالحاشتق وأرسل إلى associated token account
IS_TOKEN_ACCOUNTtoken account صالحأرسل الرموز مباشرةً إلى هذا العنوان
USER_VERIFICATIONيستوجب العنوان تأكيدًا من المستلماشترط المراجعة الصريحة قبل إرسال SPL tokens أو تمويل ATA
REJECTعنوان غير صالحلا ترسل

ثم يُعيّن كل نتيجة إلى قابلية القبول لكل أصل على حدة عبر canReceiveNativeSol (للمحافظ فقط) وcanReceiveSplToken (للمحافظ أو token accounts). نتيجة USER_VERIFICATION لا تُقبل تلقائيًا؛ يجب أن يطلب تطبيقك من المستخدم تأكيد نيته قبل إرسال SPL tokens. يُعيد token account النتيجة IS_TOKEN_ACCOUNT، وبالتالي يمكنه استقبال SPL tokens لكن لا يمكنه استقبال SOL الأصلي — وهذا التمييز هو ما يمنع قفل SOL.

Demo
/**
* Validates an input address and classifies it as a wallet, token account,
* address requiring recipient verification, or invalid.
*
* @param inputAddress - The address to validate
* @param rpc - Optional RPC client (defaults to mainnet)
* @returns Classification result:
* - IS_WALLET: Valid wallet address
* - IS_TOKEN_ACCOUNT: Valid token account
* - USER_VERIFICATION: Require explicit review before sending SPL tokens
* - REJECT: Invalid address for transfers
*/
export async function validateAddress(
inputAddress: Address,
rpc: Rpc<GetAccountInfoApi> = defaultRpc
): Promise<ValidationResult> {
const account = await fetchJsonParsedAccount(rpc, inputAddress);
// Log the account data for demo
console.log("\nAccount:", account);
// Account doesn't exist onchain
if (!account.exists) {
// An unfunded PDA may belong to a smart wallet, but the address
// alone cannot prove that its program can access a newly-created ATA.
if (isOffCurveAddress(inputAddress)) {
return {
type: "USER_VERIFICATION",
reason:
"Unfunded PDA: confirm the recipient is using a smart wallet and controls this address"
};
}
// On-curve = valid keypair address, treat as unfunded wallet
return { type: "IS_WALLET" };
}
// Account exists, check program owner
const owner = account.programAddress;
// A System Program account can be a standard wallet or an off-curve smart
// wallet. Require verification before automatically sending to the
// latter.
if (owner === SYSTEM_PROGRAM) {
if (isOffCurveAddress(inputAddress)) {
return {
type: "USER_VERIFICATION",
reason:
"Off-curve System Program account: confirm recipient is using a smart wallet"
};
}
return { type: "IS_WALLET" };
}
// Token Program or Token-2022, check if token account
if (owner === TOKEN_PROGRAM || owner === TOKEN_2022_PROGRAM) {
const accountType = (
account.data as { parsedAccountMeta?: { type?: string } }
).parsedAccountMeta?.type;
if (accountType === "account") {
return { type: "IS_TOKEN_ACCOUNT" };
}
// Reject if not a token account (mint account)
return {
type: "REJECT",
reason: "Not a token account"
};
}
// An account owned by another program can be a smart wallet.
return {
type: "USER_VERIFICATION",
reason:
"Program-owned account: confirm the recipient is using a smart wallet"
};
}
/**
* Native SOL is only safe to send to a wallet. Any other account locks it.
*/
function canReceiveNativeSol(result: ValidationResult): boolean {
return result.type === "IS_WALLET";
}
/**
* SPL tokens can go to a wallet (via its ATA) or directly to a token account.
*/
function canReceiveSplToken(result: ValidationResult): boolean {
return result.type === "IS_WALLET" || result.type === "IS_TOKEN_ACCOUNT";
}
// =============================================================================
// Examples
// =============================================================================
Console
Click to execute the code.

Is this page helpful?