Проверка адреса

Отправка средств на неверный адрес может привести к их безвозвратной потере. Проверка адреса гарантирует, что вы и ваши пользователи отправляете средства только на адреса, которые могут их корректно принять и использовать. Если вы управляете биржей, рекомендуется выполнять эти проверки и просить пользователя подтвердить адрес перед отправкой.

Валидация зависит от того, что именно вы отправляете:

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

См. Как работают платежи в Solana для ознакомления с основными концепциями платежей.

Понимание адресов Solana

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

On-Curve адреса

Стандартные адреса — это публичные ключи из Ed25519 keypair. Такие адреса:

  • Имеют соответствующий приватный ключ, которым можно подписывать транзакции
  • Используются в качестве адресов кошельков

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

Program Derived Addresses детерминированно выводятся из идентификатора программы и сидов. Такие адреса:

  • Не имеют соответствующего приватного ключа
  • Могут быть подписаны только той программой, из которой был выведен адрес

Кошельки, управляемые программами, которые часто называют смарт-кошельками или смарт-аккаунтами, имеют адреса вне кривой. В данном руководстве повсеместно используется термин «смарт-кошелёк».

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

Используйте адрес для получения аккаунта из сети, проверьте владельца программы и тип аккаунта, чтобы определить порядок работы с данным адресом.

Знание того, является ли адрес 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) и соответствует ожидаемому mint account токена перед отправкой.

Token Programs автоматически проверяют, что оба token account в транзакции перевода содержат токены одного и того же mint. Если проверка не пройдена, транзакция отклоняется и средства не теряются.

Mint Accounts

Mint accounts отслеживают объём предложения токенов и метаданные конкретного токена. Mint accounts также принадлежат Token Programs, но не являются допустимыми получателями при переводе токенов. Попытка отправить токены на mint-адрес приведёт к неудачной транзакции, однако средства не будут потеряны.

Другие аккаунты

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

Отправка нативного SOL

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

Перевод через System Program добавляет lamport на любой аккаунт, включая минты, token account, программы и 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 accounts. Попросите пользователя подтвердить, что он намерен отправить средства на смарт-кошелёк.

    Некоторые распространённые смарт-кошельки:

Демонстрация

Следующий пример демонстрирует только логику проверки адреса. Это справочный код в ознакомительных целях.

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

Демонстрация ниже использует три возможных результата:

РезультатЗначениеДействие
IS_WALLETДействительный адрес кошелькаПолучить ATA и отправить на него токены
IS_TOKEN_ACCOUNTДействительный token accountОтправить токены непосредственно на этот адрес
USER_VERIFICATIONАдрес требует подтверждения от получателяТребуйте явной проверки перед отправкой SPL-токенов или финансированием ATA
REJECTНедействительный адресНе отправлять

Затем каждый результат сопоставляется с допустимостью для конкретного актива с помощью canReceiveNativeSol (только для кошельков) и canReceiveSplToken (для кошельков или token accounts). Результат 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?