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

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

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

  • 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-адрес приведёт к неудачной транзакции, однако средства не будут потеряны.

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

Аккаунты, принадлежащие другим программам, требуют принятия политического решения. Некоторые аккаунты (например, мультисиг-кошельки) могут являться допустимыми владельцами token account, тогда как другие должны быть отклонены.

Отправка нативного 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, который может оказаться недоступным. При отсутствии существующего аккаунта невозможно определить только по адресу, какая программа вывела этот PDA и предназначен ли адрес для ATA. Деривация ATA для этого адреса с целью отправки токенов может привести к блокировке средств в недоступном token account.

  • На кривой: Это действительный адрес кошелька (публичный ключ), который ещё не пополнен. Получите ATA, проверьте его существование и отправьте на него токены. Вы должны принять политическое решение о том, финансировать ли создание ATA, если он не существует.

Аккаунт существует

Если аккаунт существует, проверьте, какая программа им владеет:

  • System Program: Это стандартный кошелёк. Получите ATA, проверьте его существование и отправьте на него токены. Вы должны принять политическое решение о том, финансировать ли создание ATA, если он не существует.

  • Token Program / Token-2022: Убедитесь, что аккаунт является token account (а не mint account), и что он содержит токен (mint), который вы намерены отправить. Если всё верно, отправьте токены непосредственно на этот адрес. Если это mint account или token account для другого mint, отклоните адрес.

  • Другая программа: Это требует принятия политического решения. Некоторые программы, например мультисиг-кошельки, могут быть допустимыми владельцами token accounts. Если ваша политика это допускает, получите ATA и выполните отправку. В противном случае отклоните адрес.

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

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

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

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

РезультатЗначениеДействие
IS_WALLETДействительный адрес кошелькаПолучить ATA и отправить на него токены
IS_TOKEN_ACCOUNTДействительный token accountОтправить токены непосредственно на этот адрес
REJECTНедействительный адресНе отправлять

Затем каждый результат сопоставляется с допустимостью для конкретного актива с помощью canReceiveNativeSol (только кошельки) и canReceiveSplToken (кошельки или token accounts). associated token account возвращает IS_TOKEN_ACCOUNT, поэтому он может принимать SPL-токены, но не нативный SOL — именно это различие предотвращает блокировку SOL.

Demo
/**
* Validates an input address and classifies it as a wallet, token account, 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
* - 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) {
// Off-curve = PDA that doesn't exist as an account
// Reject conservatively to avoid sending to an address that may be inaccessible.
if (isOffCurveAddress(inputAddress)) {
return { type: "REJECT", reason: "PDA doesn't exist as an account" };
}
// On-curve = valid keypair address, treat as unfunded wallet
return { type: "IS_WALLET" };
}
// Account exists, check program owner
const owner = account.programAddress;
// System Program = wallet
if (owner === SYSTEM_PROGRAM) {
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"
};
}
// Unknown program owner
return { type: "REJECT", reason: "Unknown program owner" };
}
/**
* 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?