Gửi token đến sai địa chỉ có thể dẫn đến mất vĩnh viễn số tiền. Xác minh địa chỉ đảm bảo bạn chỉ gửi token đến các địa chỉ có thể nhận và truy cập chúng một cách đúng đắn.
Xem Cách thức hoạt động của thanh toán trên Solana để hiểu các khái niệm thanh toán cốt lõi.
Hiểu về địa chỉ Solana
Các tài khoản Solana có hai loại địa chỉ: on-curve và off-curve.
Địa chỉ on-curve
Địa chỉ tiêu chuẩn là các khóa công khai từ cặp khóa Ed25519. Các địa chỉ này:
- Có khóa riêng tương ứng có thể ký giao dịch
- Được sử dụng làm địa chỉ ví
Địa chỉ off-curve (PDA)
Program Derived Addresses được tạo ra một cách xác định từ program ID và các seed. Các địa chỉ này:
- Không có khóa riêng tương ứng
- Chỉ có thể được ký bởi chương trình mà địa chỉ được tạo ra từ đó
Các loại tài khoản trong thanh toán
Sử dụng địa chỉ để lấy 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ỉ.
Việc biết một địa chỉ là on-curve hay off-curve 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ó, hoặc liệu có tồn tại tài khoản tại địa chỉ đó hay không. Bạn phải lấy 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 token SPL đến một ví, bạn tạo ra và sử dụng Associated Token Account (ATA) của nó.
Sau khi tạo ra đị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 không tồn tại, bạn có thể bao gồm một chỉ thị để tạo token account của người nhận trong cùng giao dịch với việc chuyển tiền. Tuy nhiên, điều này yêu cầu trả rent cho token account mới. Vì người nhận sở hữu ATA, số SOL đã trả cho rent không thể được người gửi thu hồi.
Nếu không 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 khoản, tạo ATA của họ bằng chi phí của bạn, đóng ATA để lấy lại SOL từ rent, và 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 token program, bạn nên xác minh tài khoản đó là token account (không phải mint account) và khớp với mint account token mong đợi trước khi gửi.
Token Programs tự động xác thực rằng cả hai token accounts trong một giao dịch chuyển khoả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ó tiền nào bị mất.
Mint accounts
Mint accounts theo dõi nguồn cung token và metadata 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 việc chuyển token. Việc cố gắng gửi token đến địa chỉ mint sẽ dẫn đến giao dịch thất bại, nhưng không có 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 program khác yêu cầu quyết định 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 những tài khoản khác nên bị từ chối.
Quy trình xác minh
Sơ đồ sau đây cho thấy cây quyết định tham khảo để 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 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:
-
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 có, bạn không thể xác định từ địa chỉ đơn thuần chương trình nào đã tạo ra PDA này hoặc liệu địa chỉ có phải là 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.
-
Trên đường cong: Đây là địa chỉ ví hợp lệ (public key) chưa được nạp tiền. 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ó nên 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 tài khoản tồn tại, kiểm tra chương trình nào sở hữu nó:
-
System Program: Đây là ví tiêu chuẩn. 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ó nên 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 tài khoản là 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ệ, gửi token trực tiếp đến địa chỉ này. Nếu đó là mint account hoặc token account cho một mint khác, từ chối địa chỉ.
-
Chương trình khác: Điều này yêu cầu quyết định chính sách. Một số chương trình như ví đa chữ ký có thể là chủ sở hữu chấp nhận được của token account. Nếu chính sách của bạn cho phép, hãy tạo ATA và gửi. Nếu không, 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 cho mục đích minh họa.
Demo không hiển thị cách tạo 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ệ | Tạo 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 |
REJECT | Địa chỉ không hợp lệ | Không gửi |
/*** 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 democonsole.log("\nAccount:", account);// Account doesn't exist on-chainif (!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 walletreturn { type: "IS_WALLET" };}// Account exists, check program ownerconst owner = account.programAddress;// System Program = walletif (owner === SYSTEM_PROGRAM) {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"};}// Unknown program ownerreturn { type: "REJECT", reason: "Unknown program owner" };}// =============================================================================// Examples// =============================================================================
Is this page helpful?