Перевірка адреси

Надсилання коштів на неправильну адресу може призвести до їх безповоротньої втрати. Перевірка адреси гарантує, що ви та ваші користувачі надсилатимете кошти лише на адреси, які можуть їх належним чином отримати та використати. Якщо ви керуєте біржею, рекомендується виконувати ці перевірки та просити користувача підтвердити адресу перед надсиланням.

Перевірка залежить від того, що ви надсилаєте:

  • SPL-токени частково захищені самостійно. Token Program відхиляє переказ, якщо акаунти не відповідають очікуваному mint, тому неправильно спрямований переказ токенів завершується невдачею без втрати коштів. Більша частина цієї сторінки присвячена відправленню SPL-токенів.
  • Нативний SOL не має такого захисту. Переказ через System Program виконується на будь-який акаунт, тому SOL, надісланий на неправильного отримувача, блокується назавжди. Дивіться Відправлення нативного SOL.

Дивіться Як працюють платежі в Solana, щоб ознайомитися з основними концепціями платежів.

Розуміння адрес Solana

Акаунти Solana мають два типи адрес: on-curve та off-curve.

On-Curve адреси

Стандартні адреси — це публічні ключі з Ed25519 keypairs. Ці адреси:

  • Мають відповідний приватний ключ, який може підписувати транзакції
  • Використовуються як адреси гаманців

Off-Curve адреси (PDA)

Program Derived Addresses детерміновано виводяться з ідентифікатора програми та seeds. Ці адреси:

  • Не мають відповідного приватного ключа
  • Можуть підписуватися лише програмою, з якої було виведено адресу

Гаманці під керуванням програм, які зазвичай називають смарт-гаманцями або смарт-акаунтами, мають адреси поза кривою. У цьому посібнику скрізь використовується термін «смарт-гаманець».

Типи акаунтів у платежах

Використовуйте адресу для отримання акаунту з мережі, перевірте власника програми та тип акаунту, щоб визначити, як обробляти цю адресу.

Знання того, чи є адреса on-curve або off-curve, не вказує на тип акаунту, яка програма ним володіє або чи існує акаунт за цією адресою. Необхідно отримати акаунт з мережі, щоб з'ясувати ці деталі.

Акаунти 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 program, слід перевірити, що акаунт є token account (а не mint account) і відповідає очікуваному token mint account перед надсиланням.

Token Programs автоматично перевіряють, що обидва token accounts у переказі містять токени одного й того самого mint. Якщо перевірка не проходить, транзакція відхиляється і жодні кошти не втрачаються.

Mint Accounts

Mint accounts відстежують постачання токенів та метадані конкретного токена. Mint accounts також належать Token Programs, але не є дійсними отримувачами для переказу токенів. Спроба надіслати токени на adresu mint призводить до невдалої транзакції, але жодні кошти не втрачаються.

Інші акаунти

Акаунти, якими керують інші програми, є дійсними, але підпорядковуються правилам авторизації, встановленим відповідною програмою-власником. Рекомендується запропонувати користувачеві підтвердити, що він справді має намір надіслати кошти на смарт-гаманець.

Надсилання нативного SOL

Наведена вище класифікація визначає, куди можуть надходити SPL-токени. З нативним SOL все суворіше: єдиним безпечним одержувачем є гаманець System Program (або незаповнена адреса на кривій, яка ним стає).

Переказ через System Program додає lamport до будь-якого акаунту, включно з мінтами, token accounts, програмами та PDA. Lamport може виводити лише програма-власник акаунту, тому надсилання SOL на неправильну адресу може призвести до безповоротної втрати коштів.

На відміну від переказу SPL-токенів, транзакція не відхиляється, якщо одержувач є неочікуваною адресою.

При надсиланні нативного SOL прийнятним результатом є лише IS_WALLET. IS_TOKEN_ACCOUNT не є таким: token account зберігає SPL-токени, і SOL, надісланий туди, виходить з-під контролю відправника.

Це поширена причина втрати SOL: користувач вставляє адресу мінту токена (або адресу програми) у поле виведення SOL. Переказ виконується успішно, але SOL не підлягає поверненню. Завжди перевіряйте одержувача перед підписанням SOL-переказу.

Процес перевірки

На наступній діаграмі показано еталонне дерево рішень для перевірки адреси:

Address Verification Flow

Отримання акаунту

Використайте адресу для отримання деталей акаунту з мережі.

Акаунт не існує

Якщо за цією адресою немає акаунту, перевірте, чи знаходиться адреса на кривій чи поза нею:

  • Поза кривою (PDA): Вимагайте явного підтвердження від одержувача перед створенням ATA або надсиланням SPL-токенів. За відсутності існуючого акаунту неможливо визначити лише за адресою, яка програма породила цей PDA або чи може програма одержувача отримати доступ до його ATA.

  • На кривій: Це дійсна адреса гаманця (публічний ключ), яку ще не профінансовано. Виведіть ATA, перевірте, чи він існує, і надішліть на нього токени. Ви маєте самостійно прийняти рішення щодо фінансування створення ATA, якщо його не існує.

Акаунт існує

Якщо акаунт існує, перевірте, яка програма є його власником:

  • System Program: Це стандартний гаманець. Виведіть ATA, перевірте, чи він існує, і надішліть на нього токени. Ви маєте самостійно прийняти рішення щодо фінансування створення ATA, якщо його не існує.

  • Token Program / Token-2022: Переконайтеся, що акаунт є token account (а не mint account), і що він містить токен (mint), який ви збираєтеся надіслати. Якщо все правильно, надсилайте токени безпосередньо на цю адресу. Якщо це mint account або token account для іншого mint, відхиліть адресу.

  • Інша програма: Такі програми, як мультипідписні гаманці, є прийнятними власниками token account. Вимагайте від користувача підтвердження того, що він має намір надіслати кошти на смарт-гаманець.

    Деякі поширені смарт-гаманці:

Демо

Наступний приклад демонструє лише логіку перевірки адреси. Це довідковий код для ілюстративних цілей.

Демо не показує, як отримати ATA або побудувати транзакцію для надсилання токенів. Зверніться до документації token account та передачі токенів для прикладів коду.

Демо нижче використовує три можливих результати:

РезультатЗначенняДія
IS_WALLETДійсна адреса гаманцяОтримати та надіслати на associated token account
IS_TOKEN_ACCOUNTДійсний token accountНадіслати токени безпосередньо на цю адресу
USER_VERIFICATIONАдреса потребує підтвердження від одержувачаВимагайте явної перевірки перед надсиланням SPL-токенів або фінансуванням ATA
REJECTНедійсна адресаНе надсилати

Потім кожен результат зіставляється з придатністю для прийому конкретного активу за допомогою canReceiveNativeSol (лише для гаманців) та canReceiveSplToken (для гаманців або token account). Результат USER_VERIFICATION не приймається автоматично; ваш застосунок повинен вимагати від користувача підтвердження наміру перед надсиланням SPL-токенів. Token account повертає IS_TOKEN_ACCOUNT, тому він може отримувати SPL-токени, але не нативний 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?