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 và người dùng của bạn chỉ gửi đến các địa chỉ có thể nhận và truy cập tiền đúng cách. Nếu bạn vận hành một sàn giao dịch, bạn nên thực hiện các kiểm tra này và yêu cầu người dùng xác minh địa chỉ trước khi gửi.
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ừ đó
Ví được kiểm soát bởi chương trình, thường được gọi là smart wallet hoặc smart account, có địa chỉ off-curve. Hướng dẫn này sử dụng thuật ngữ "smart wallet" xuyên suố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.
Tài khoản hệ thống có thể tồn tại off-curve. Nếu người dùng của bạn muốn gửi token đến một địa chỉ off-curve, bạn nên nhắc người dùng xác nhận rằng họ có ý định gửi tiền đến một smart wallet.
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 là hợp lệ nhưng được điều chỉnh bởi các quy tắc quyền hạn do chương trình sở hữu tương ứng đặt ra. Bạn nên nhắc người dùng xác nhận rằng họ có ý định gửi tiền đến một smart wallet.
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ỉ:
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ỉ đó là on-curve hay off-curve:
-
Off-curve (PDA): Yêu cầu xác minh người nhận rõ ràng trước khi tạo một ATA hoặc gửi SPL token. Nếu không có tài khoản hiện tại, bạn không thể xác định chỉ từ địa chỉ xem chương trình nào đã tạo ra PDA này hay liệu chương trình của người nhận có thể truy cập ATA của họ hay không.
-
On-curve: Đây là địa chỉ ví hợp lệ (khóa công khai) chưa được nạp tiền. Hãy tạo 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 nếu nó chưa tồn tại hay không.
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 tạo 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 nếu nó chưa tồn tại hay không.
-
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à rằng nó nắm giữ token (mint) bạn có ý đị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 cho một mint khác, hãy từ chối địa chỉ đó.
-
Chương trình khác: Các chương trình như ví multisig là chủ sở hữu hợp lệ của token account. Yêu cầu người dùng xác nhận rằng họ có ý định gửi đến một smart wallet.
Một số smart wallet phổ biến:
| Wallet | Địa chỉ chương trình |
|---|---|
| Squads | SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf |
| Swig | swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB |
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 account và chuyể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ĩa | Hành động |
|---|---|---|
IS_WALLET | Địa chỉ ví hợp lệ | Dẫn xuất và gửi đến associated token account |
IS_TOKEN_ACCOUNT | Token account hợp lệ | Gửi token trực tiếp đến địa chỉ này |
USER_VERIFICATION | Địa chỉ yêu cầu xác nhận từ người nhận | Yêu cầu xem xét rõ ràng trước khi gửi SPL token hoặc tài trợ cho một ATA |
REJECT | Địa chỉ không hợp lệ | Không gửi |
Sau đó, nó ánh xạ mỗi kết quả đến khả năng chấp nhận theo từng tài sản với canReceiveNativeSol
(chỉ dành cho ví) và canReceiveSplToken (ví hoặc token account). Kết quả
USER_VERIFICATION không được chấp nhận tự động; ứng dụng của bạn
nên yêu cầu người dùng xác nhận ý định của họ trước khi gửi SPL token. Một token
account trả về IS_TOKEN_ACCOUNT, vì vậy nó có thể nhận SPL token nhưng không
nhận SOL gốc — sự phân biệt giúp ngăn SOL bị khóa.
/*** 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 democonsole.log("\nAccount:", account);// Account doesn't exist onchainif (!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 walletreturn { type: "IS_WALLET" };}// Account exists, check program ownerconst 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 accountif (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// =============================================================================
Is this page helpful?