Xác minh địa chỉ

Gửi tiền đến địa chỉ sai có thể dẫn đến mất vĩnh viễn. Xác minh địa chỉ đảm bảo bạn chỉ gửi đến các địa chỉ có thể nhận và truy cập đúng cách các khoản tiền đó.

Việc xác thực phụ thuộc vào những gì bạn gửi:

  • Token SPL có khả năng tự bảo vệ một phần. Token Program từ chối giao dịch chuyển tiền khi các tài khoản không khớp với mint mong đợi, vì vậy một giao dịch token bị chuyển nhầm sẽ thất bại mà không mất tiền. Hầu hết trang này đề cập đến việc gửi token SPL.
  • SOL gốc không có biện pháp bảo vệ như vậy. Giao dịch chuyển tiền qua System Program thành công vào bất kỳ tài khoản nào, vì vậy một người nhận không đúng sẽ khóa vĩnh viễn SOL đó. Xem Gửi SOL gốc.

Xem Cách Thanh Toán Hoạt Động trên Solana để hiểu các khái niệm thanh toán cơ bản.

Hiểu về Địa Chỉ Solana

Tài khoản Solana có hai loại địa chỉ: trên đường cong và ngoài đường cong.

Địa Chỉ Trên Đường Cong

Địa chỉ tiêu chuẩn là các khóa công khai từ các keypair Ed25519. Các địa chỉ này:

  • Có khóa riêng tư tương ứng có thể ký giao dịch
  • Được sử dụng làm địa chỉ ví

Địa Chỉ Ngoài Đường Cong (PDA)

Program Derived Address được suy ra một cách xác định từ ID chương trình và các seed. Các địa chỉ này:

  • Không có khóa riêng tư tương ứng
  • Chỉ có thể được ký bởi chương trình mà địa chỉ đó được suy ra từ đó

Các Loại Tài Khoản trong Thanh Toán

Sử dụng địa chỉ để truy xuất một tài khoản từ mạng, kiểm tra chủ sở hữu chương trình và loại tài khoản để xác định cách xử lý địa chỉ đó.

Biết một địa chỉ nằm trên đường cong hay ngoài đường cong không cho bạn biết loại tài khoản đó là gì, chương trình nào sở hữu nó, hay liệu tài khoản có tồn tại tại địa chỉ đó hay không. Bạn phải truy xuất tài khoản từ mạng để xác định các chi tiết này.

Tài Khoản System Program (Ví)

Các tài khoản thuộc sở hữu của System Program là các ví tiêu chuẩn. Để gửi SPL token đến một ví, bạn cần tạo và sử dụng Associated Token Account (ATA) của ví đó.

Sau khi tạo địa chỉ ATA, hãy kiểm tra xem token account đó có tồn tại trên chuỗi hay không. Nếu ATA chưa tồn tại, bạn có thể đưa thêm một lệnh để tạo token account cho người nhận trong cùng một giao dịch với lệnh chuyển. Tuy nhiên, điều này yêu cầu phải trả rent cho token account mới. Vì người nhận sở hữu ATA, số SOL đã trả cho rent sẽ không thể được người gửi thu hồi lại.

Nếu không có các biện pháp bảo vệ, việc trợ cấp tạo ATA có thể bị lợi dụng. Một người dùng độc hại có thể yêu cầu chuyển tiền, để ATA của họ được tạo với chi phí của bạn, đóng ATA để lấy lại rent SOL, rồi lặp lại.

Token Accounts

Token accounts thuộc sở hữu của Token Program hoặc Token-2022 Program và lưu trữ số dư token. Nếu địa chỉ bạn nhận được thuộc sở hữu của một token program, bạn nên xác minh rằng tài khoản đó là một token account (không phải mint account) và khớp với mint account token dự kiến trước khi gửi.

Các Token Programs tự động xác thực rằng cả hai token account trong một lần chuyển đều chứa token của cùng một mint. Nếu xác thực thất bại, giao dịch sẽ bị từ chối và không có khoản tiền nào bị mất.

Mint Accounts

Mint accounts theo dõi nguồn cung token và siêu dữ liệu của một token cụ thể. Mint accounts cũng thuộc sở hữu của Token Programs nhưng không phải là người nhận hợp lệ cho các giao dịch chuyển token. Việc cố gắng gửi token đến một địa chỉ mint sẽ dẫn đến giao dịch thất bại, nhưng không có khoản tiền nào bị mất.

Các Tài Khoản Khác

Các tài khoản thuộc sở hữu của các chương trình khác đòi hỏi một quyết định về chính sách. Một số tài khoản (ví dụ: ví multisig) có thể là chủ sở hữu token account hợp lệ, trong khi một số khác nên bị từ chối.

Gửi SOL Gốc

Phân loại ở trên xác định nơi token SPL có thể đến. SOL gốc thì chặt chẽ hơn: người nhận an toàn duy nhất là ví System Program (hoặc một địa chỉ trên đường cong chưa được cấp vốn mà sẽ trở thành một ví như vậy).

Một lệnh chuyển System Program thêm lamport vào bất kỳ tài khoản nào, bao gồm các mint, token accounts, chương trình và PDA. Lamport chỉ có thể được rút ra bởi program account sở hữu tài khoản đó, vì vậy việc gửi SOL đến người nhận không đúng có thể dẫn đến việc tiền bị mất vĩnh viễn.

Không giống như lệnh chuyển token SPL, giao dịch không thất bại khi người nhận là một địa chỉ không mong đợi.

Khi gửi SOL gốc, chỉ kết quả IS_WALLET mới được chấp nhận. IS_TOKEN_ACCOUNT thì không: một token account chứa token SPL, và SOL được gửi đến đó nằm ngoài tầm kiểm soát của người gửi.

Đây là cách phổ biến khiến SOL bị mất: người dùng dán địa chỉ mint của token (hoặc địa chỉ chương trình) vào lệnh rút SOL. Lệnh chuyển thành công và SOL không thể thu hồi được. Luôn phân loại người nhận trước khi ký một lệnh chuyển SOL.

Luồng Xác Minh

Sơ đồ sau đây hiển thị một cây quyết định tham chiếu để xác thực một địa chỉ:

Address Verification Flow

Lấy Thông Tin Tài Khoản

Sử dụng địa chỉ để lấy thông tin chi tiết của tài khoản từ mạng.

Tài Khoản Không Tồn Tại

Nếu không có tài khoản nào tồn tại tại địa chỉ này, hãy kiểm tra xem địa chỉ đó nằm trên đường cong hay ngoài đường cong:

  • Ngoài đường cong (PDA): Từ chối địa chỉ một cách thận trọng để tránh gửi đến một ATA có thể không truy cập được. Nếu không có tài khoản hiện hữu, bạn không thể xác định chỉ từ địa chỉ đó chương trình nào đã tạo ra PDA này hay liệu địa chỉ đó có dành cho một ATA hay không. Việc tạo ATA cho địa chỉ này để gửi token có thể dẫn đến việc tiền bị khóa trong một token account không thể truy cập.

  • On-curve: Đây là địa chỉ ví hợp lệ (khóa công khai) chưa được nạp tiền. Hãy dẫn xuất ATA, kiểm tra xem nó có tồn tại không, và gửi token đến đó. Bạn phải đưa ra quyết định chính sách về việc có tài trợ cho việc tạo ATA hay không nếu nó chưa tồn tại.

Tài khoản đã tồn tại

Nếu một tài khoản tồn tại, hãy kiểm tra chương trình nào sở hữu nó:

  • System Program: Đây là ví tiêu chuẩn. Hãy dẫn xuất ATA, kiểm tra xem nó có tồn tại không, và gửi token đến đó. Bạn phải đưa ra quyết định chính sách về việc có tài trợ cho việc tạo ATA hay không nếu nó chưa tồn tại.

  • Token Program / Token-2022: Xác minh rằng tài khoản là một token account (không phải mint account) và nó chứa token (mint) mà bạn định gửi. Nếu hợp lệ, hãy gửi token trực tiếp đến địa chỉ này. Nếu đó là mint account hoặc một token account của một mint khác, hãy từ chối địa chỉ đó.

  • Other Program: Điều này đòi hỏi một quyết định chính sách. Một số chương trình như ví multisig có thể là chủ sở hữu hợp lệ của token account. Nếu chính sách của bạn cho phép, hãy dẫn xuất ATA và gửi. Nếu không, hãy từ chối địa chỉ đó.

Demo

Ví dụ sau đây chỉ hiển thị logic xác thực địa chỉ. Đây là mã tham khảo dùng cho mục đích minh họa.

Demo không hướng dẫn cách dẫn xuất ATA hoặc xây dựng giao dịch để gửi token. Tham khảo tài liệu về token accountchuyển token để xem mã ví dụ.

Demo dưới đây sử dụng ba kết quả có thể xảy ra:

Kết quảÝ nghĩaHành động
IS_WALLETĐịa chỉ ví hợp lệDẫn xuất và gửi đến associated token account
IS_TOKEN_ACCOUNTToken account hợp lệGửi token trực tiếp đến địa chỉ này
REJECTĐịa chỉ không hợp lệKhông gửi

Sau đó, nó ánh xạ từng kết quả sang khả năng chấp nhận theo từng tài sản với canReceiveNativeSol (chỉ ví) và canReceiveSplToken (ví hoặc associated token account). Một token account trả về IS_TOKEN_ACCOUNT, do đó nó có thể nhận SPL token nhưng không thể nhận SOL gốc — sự phân biệt giúp ngăn SOL bị khóa.

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?