> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Đăng ký và cập nhật tài khoản

> Tìm hiểu cách đăng ký nhận thông tin cập nhật tài khoản và theo dõi hiệu quả các thay đổi trạng thái on-chain bằng Laserstream.

Khi xây dựng các ứng dụng cần phản hồi trước những thay đổi on-chain, việc liên tục thăm dò các điểm cuối RPC để tìm cập nhật tài khoản vừa kém hiệu quả vừa chậm. Tính năng đăng ký tài khoản giải quyết vấn đề này bằng cách gửi trực tiếp các cập nhật theo thời gian thực về thay đổi trạng thái tài khoản đến ứng dụng của bạn.

Hướng dẫn này trình bày mọi thông tin bạn cần biết về tính năng đăng ký tài khoản: đó là gì, hoạt động như thế nào và cách tối ưu hóa cho trường hợp sử dụng cụ thể của bạn.

***

## Bối cảnh mô hình tài khoản

<Info>
  Bỏ qua phần này nếu bạn đã quen thuộc với tài khoản Solana và cấu trúc của chúng.
</Info>

Solana sử dụng mô hình dựa trên tài khoản, trong đó mọi dữ liệu đều nằm trong một tài khoản — một vùng chứa lưu cả dữ liệu và siêu dữ liệu. Mỗi tài khoản có:

* **Dữ liệu**: Các byte thực tế lưu trữ trạng thái chương trình, số dư token hoặc thông tin khác
* **Chủ sở hữu**: Chương trình kiểm soát tài khoản này và có thể sửa đổi dữ liệu của tài khoản
* **Lamport**: Số dư SOL của tài khoản để được miễn tiền thuê
* **Có thể thực thi**: Cho biết tài khoản này có chứa mã chương trình hay không

Các chương trình không lưu trạng thái — chúng không lưu dữ liệu bên trong. Thay vào đó, chúng tạo và quản lý các tài khoản riêng để lưu trạng thái. Khi tương tác với một chương trình, bạn truyền vào các tài khoản mà chương trình cần đọc hoặc ghi.

Thiết kế này giúp tính năng đăng ký tài khoản trở nên mạnh mẽ: bạn có thể theo dõi thay đổi đối với các tài khoản cụ thể, tất cả tài khoản thuộc sở hữu của một chương trình hoặc các tài khoản khớp với những tiêu chí nhất định.

***

## Đăng ký tài khoản cơ bản

Hãy bắt đầu với một ví dụ đơn giản đăng ký nhận các thay đổi trong tài khoản token. Tập lệnh này sẽ thông báo cho bạn mỗi khi số dư token thay đổi:

```ts [expandable] theme={"system"}
import { subscribe, CommitmentLevel, SubscribeUpdate, LaserstreamConfig } from 'helius-laserstream';
import bs58 from 'bs58';

// Utility function to recursively convert Buffer objects to base58 strings
function convertBuffersToBase58(obj: any): any {
  if (obj === null || obj === undefined) {
    return obj;
  }
  
  if (Buffer.isBuffer(obj)) {
    return bs58.encode(obj);
  }
  
  if (Array.isArray(obj)) {
    return obj.map(convertBuffersToBase58);
  }
  
  if (typeof obj === 'object') {
    const result: any = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        result[key] = convertBuffersToBase58(obj[key]);
      }
    }
    return result;
  }
  
  return obj;
}

async function main() {
  console.log('🏦 Basic Account Subscription Example');

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the closest region
  };

  const request = {
    accounts: {
      "token-accounts": {
        account: [], // Specific account pubkeys (empty = all)
        owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"], // Token program
        filters: [
          {
            // Only token accounts (165 bytes)
            datasize: 165
          }
        ]
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    transactions: {}, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: []
  };

  const stream = await subscribe(
    config,
    request,
    async (update: SubscribeUpdate) => {
      const readableUpdate = convertBuffersToBase58(update);
      console.log('🏦 Account Update:', JSON.stringify(readableUpdate, null, 2));
    },
    async (err) => console.error('❌ Stream error:', err)
  );

  console.log(`✅ Account subscription started (id: ${stream.id})`);

  process.on('SIGINT', () => {
    console.log('\n🛑 Cancelling stream...');
    stream.cancel();
    process.exit(0);
  });
}

main().catch(console.error);
```

Khi chạy đăng ký cơ bản này, bạn sẽ thấy các cập nhật tài khoản theo thời gian thực được truyền đến bảng điều khiển:

```
🏦 Basic Account Subscription Example
✅ Account subscription started (id: xyz789)

🏦 Account Update: {
  "filters": ["token-accounts"],
  "account": {
    "account": {
      "pubkey": "BKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWF",
      "lamports": "2039280",
      "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
      "rentEpoch": "18446744073709551615",
      "data": "2NUx6Xw9QkmgJCyYUP3d8TPsjJhUpSM7hcy9Fi1juGc6g9DrpPFyGyvBZzu9qiAjFtyEDbNiLHYFJsq1dD5Wxr4LPcF9Dqs4AJa15L1N92pfinnoKVfCsVCcybhV1iwkCCTMeMyxTRA4tqJm6MrLwgKG3HmmwVdhsEuXjSsGJFXGzgfgPHucVzBEgAqcpH9JPpoaQyis2MFwRJLjenxzkE8xJzWHv1Zk2T",
      "writeVersion": "2697618495",
      "txnSignature": "5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD"
    },
    "slot": "352366983"
  },
  "createdAt": "2025-07-10T11:56:22.027Z"
}
```

**Điều gì vừa xảy ra?** Đăng ký của chúng ta đã hoạt động hoàn hảo! Chúng ta yêu cầu Laserstream thông báo về các thay đổi của tài khoản token và dịch vụ đã gửi một cập nhật về tài khoản `BKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWF`.

Tài khoản này có:

* **2.039.280 lamport** (số dư khoảng 0,002 SOL — đây là số tiền được miễn tiền thuê cho tài khoản token này)
* **Chương trình chủ sở hữu** `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` (đây là chương trình SPL Token)
* **Chữ ký giao dịch** `5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD` cho biết giao dịch cụ thể nào đã khiến tài khoản này thay đổi
* **Slot 352366983** cho biết thời điểm cập nhật này xảy ra trên blockchain
* **Trường dữ liệu** chứa 165 byte dữ liệu tài khoản được mã hóa theo base58

### Tìm hiểu cách lọc tài khoản bằng datasize

Trường dữ liệu rất quan trọng vì chứa cấu trúc thực tế của tài khoản token. Hãy sử dụng hiểu biết này để **lọc tài khoản thông minh**.

#### Tại sao nên lọc bằng datasize?

Để hiểu tại sao cần lọc, trước tiên hãy tìm hiểu tài khoản token thực sự là gì. **Mỗi token mà một ví nắm giữ đều có một tài khoản riêng trên chuỗi.** Nếu ví của bạn nắm giữ 3 token khác nhau (USDC, BONK và SOL), bạn thực sự có 1 tài khoản ví (tài khoản SOL chính) cùng 3 tài khoản token (mỗi loại token có một tài khoản). Mỗi tài khoản token có kích thước chính xác là 165 byte và lưu trữ: token mà tài khoản nắm giữ (địa chỉ mint), chủ sở hữu (địa chỉ ví của bạn) và lượng token trong tài khoản (số lượng).

Token Program sở hữu **hàng triệu tài khoản** trên Solana, nhưng không phải tất cả đều là "tài khoản token" lưu giữ số dư người dùng như chúng ta thường hiểu. Sau đây là những gì xảy ra khi không lọc và khi có lọc:

**Không lọc — Luồng dữ liệu ồ ạt:**

```ts theme={"system"}
accounts: {
  "all-token-program-accounts": {
    owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"] // ❌ Overwhelming!
  }
}
```

Thao tác này đăng ký TẤT CẢ tài khoản thuộc sở hữu của Token Program, bao gồm:

* **Tài khoản token** (165 byte) — Số dư người dùng: hàng triệu tài khoản
* **Tài khoản mint** (82 byte) — Định nghĩa token: hàng trăm nghìn tài khoản
* **Tài khoản multisig** (355 byte) — Cơ chế kiểm soát ví dùng chung: hàng chục nghìn tài khoản
* **Tài khoản Associated Token Program** (nhiều kích thước khác nhau) — hàng triệu tài khoản

<Warning>
  **Kết quả:** Ứng dụng của bạn liên tục nhận hàng triệu cập nhật tài khoản, trong đó phần lớn không liên quan đến nhu cầu của bạn.
</Warning>

**Lọc thông minh — Chính xác như phẫu thuật:**

```ts theme={"system"}
accounts: {
  "token-accounts-only": {
    owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
    filters: [{ datasize: 165 }] // ✅ Only standard token accounts
  }
}
```

Thao tác này chỉ giữ lại các tài khoản 165 byte, cụ thể là các tài khoản số dư token của người dùng — chính xác là những gì bạn cần để theo dõi giao dịch chuyển token, thay đổi số dư và cập nhật danh mục đầu tư.

**Sự khác biệt:**

* **Không lọc:** Hàng triệu cập nhật tài khoản (tạo mint, thay đổi multisig, v.v.)
* **Lọc bằng datasize:** Chỉ các thay đổi số dư token

Điều này giúp giảm đáng kể dữ liệu nhiễu và chỉ tập trung vào các tài khoản thực sự đại diện cho lượng token mà người dùng nắm giữ.

#### Con số 165 byte đến từ đâu?

Đây không phải là điều kỳ diệu — con số này xuất phát từ [cấu trúc tài khoản của chương trình SPL Token](https://github.com/solana-program/token/blob/d05d10807fe8cf157f6e1f024c708274c30c953a/program/src/state.rs#L87). Khi xem mã nguồn, chúng ta có thể thấy struct `Account` xác định chính xác 165 byte:

```rust theme={"system"}
pub struct Account {
    pub mint: Pubkey,                    // 32 bytes
    pub owner: Pubkey,                   // 32 bytes  
    pub amount: u64,                     // 8 bytes
    pub delegate: COption<Pubkey>,       // 4 + 32 bytes
    pub state: AccountState,             // 1 byte
    pub is_native: COption<u64>,         // 4 + 8 bytes
    pub delegated_amount: u64,           // 8 bytes
    pub close_authority: COption<Pubkey> // 4 + 32 bytes
}
// Total: 32+32+8+36+1+12+8+36 = 165 bytes
```

Kích thước cố định này cho phép chúng ta lọc chính xác các tài khoản token tiêu chuẩn và loại trừ:

* Tài khoản mint (82 byte)
* Tài khoản multisig (355 byte)
* Tài khoản chương trình associated token account
* Các tài khoản khác liên quan đến token có kích thước khác

Để tính kích thước tài khoản trong các chương trình khác, hãy xem [Tài liệu tham khảo về không gian Anchor](https://www.anchor-lang.com/docs/references/space). Tài liệu này cho biết mỗi kiểu dữ liệu chiếm bao nhiêu không gian (Pubkey = 32 byte, u64 = 8 byte, v.v.).

#### Giải mã cấu trúc tài khoản

Giờ đây, khi đã hiểu tại sao chúng ta lọc theo 165 byte, hãy giải mã nội dung bên trong tài khoản ví dụ:

```
Base58 data: 2NUx6Xw9QkmgJCyYUP3d8TPsjJhUpSM7hcy9Fi1juGc6g9...
```

165 byte được phân chia như sau:

* **Byte 0-31:** Địa chỉ mint (tài khoản này nắm giữ token nào)
* **Byte 32-63:** Địa chỉ chủ sở hữu (ai sở hữu tài khoản token này)
* **Byte 64-71:** Số lượng token (có bao nhiêu token trong tài khoản)
* **Byte 72-164:** Siêu dữ liệu bổ sung (người được ủy quyền, trạng thái, quyền đóng, v.v.)

Phương pháp có cấu trúc này mang lại độ chính xác như phẫu thuật: chúng ta chỉ nhận cập nhật cho các tài khoản token tiêu chuẩn, không nhận dữ liệu nhiễu từ các loại tài khoản khác.

### Kết hợp bộ lọc: datasize + memcmp để đạt độ chính xác tuyệt đối

Giờ đây, khi biết địa chỉ mint nằm tại các byte 0-31, chúng ta có thể lọc cụ thể hơn nữa. Giả sử chúng ta chỉ muốn giám sát các tài khoản token USDC. Có thể kết hợp bộ lọc `datasize` với bộ lọc `memcmp` để nhắm chính xác đến địa chỉ mint:

```ts theme={"system"}
const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

const request = {
  accounts: {
    "usdc-only": {
      owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
      filters: [
        { datasize: 165 },                    // Standard token accounts only
        { 
          memcmp: {
            offset: 0,                         // Mint address starts at byte 0
            base58: USDC_MINT                  // Match this specific mint
          }
        }
      ]
    }
  },
  // ... other config
};
```

**Chiến lược lọc tăng dần:**

1. **Bộ lọc chủ sở hữu:** "Cung cấp cho tôi các tài khoản thuộc sở hữu của Token Program" (hàng triệu tài khoản)
2. **Bộ lọc datasize:** "Nhưng chỉ các tài khoản token tiêu chuẩn 165 byte" (hàng trăm nghìn)
3. **Bộ lọc memcmp:** "Và chỉ những tài khoản nắm giữ USDC" (hàng nghìn)

Quy trình đi từ phạm vi rộng đến cụ thể này là chìa khóa để giám sát tài khoản hiệu quả. Mỗi bộ lọc thu hẹp tập kết quả để bạn chỉ nhận đúng những cập nhật mình quan tâm.

**Quan trọng:** Tất cả bộ lọc đều sử dụng logic AND — mọi điều kiện phải được đáp ứng thì cập nhật tài khoản mới được kích hoạt.

### Đọc cập nhật tài khoản USDC: Ai, bao nhiêu, ở đâu?

Bây giờ, hãy xem các cập nhật đã lọc này thực sự chứa những gì. Hãy tạo một trình giám sát dành riêng cho USDC để trả lời các câu hỏi chính khi tài khoản token thay đổi:

* **Ai** sở hữu tài khoản token này?
* Tài khoản hiện chứa **bao nhiêu** USDC?
* **Ở đâu** (tài khoản cụ thể nào) đã thay đổi?
* Thay đổi này xảy ra **khi nào**?
* **Giao dịch nào** đã gây ra thay đổi?

Các cập nhật tài khoản thô chứa dữ liệu nhị phân mà chúng ta cần giải mã. Vì Solana sử dụng mã hóa base58 cho địa chỉ và chữ ký, chúng ta dùng hàm `bs58.encode()` để chuyển đổi các đối tượng Buffer nhị phân thành chuỗi có thể đọc được.

```ts [expandable] theme={"system"}
import { subscribe, CommitmentLevel, SubscribeUpdate, LaserstreamConfig } from 'helius-laserstream';
import bs58 from 'bs58';

async function main() {
  console.log('USDC Account Monitor');

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the closest region
  };

  const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

  const request = {
    accounts: {
      "usdc-accounts": {
        account: [],
        owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
        filters: [
          { datasize: 165 },                           // Standard token accounts
          { memcmp: { offset: 0, base58: USDC_MINT } } // Only USDC
        ]
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    transactions: {}, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: []
  };

  const stream = await subscribe(
    config,
    request,
    async (update: SubscribeUpdate) => {
      explainAccountUpdate(update);
    },
    async (err) => console.error('Stream error:', err)
  );

  console.log(`Account monitor started (id: ${stream.id})`);

  process.on('SIGINT', () => {
    console.log('\nCancelling stream...');
    stream.cancel();
    process.exit(0);
  });
}

function explainAccountUpdate(update: SubscribeUpdate) {
  if (!update.account) return;
  
  const account = update.account.account;
  
  // Decode the key addresses
  const tokenAccountAddress = bs58.encode(account.pubkey);
  const transactionSignature = account.txnSignature ? bs58.encode(account.txnSignature) : 'Unknown';
  
  // Extract and decode the token account data (165 bytes)
  const walletOwner = bs58.encode(account.data.slice(32, 64));       // Bytes 32-63: Owner
  const tokenAmount = account.data.readBigUInt64LE(64);              // Bytes 64-71: Amount
  const usdcAmount = Number(tokenAmount) / 1_000_000;                // Convert to USDC (6 decimals)
  
  console.log(`Account: ${tokenAccountAddress}`);
  console.log(`Owner: ${walletOwner}`);
  console.log(`Balance: ${usdcAmount.toLocaleString()} USDC`);
  console.log(`Slot: ${update.account.slot}`);
  console.log(`Transaction: ${transactionSignature.slice(0, 8)}...`);
  console.log('---');
}

main().catch(console.error);
```

Khi chạy trình giám sát USDC này, bạn sẽ thấy đầu ra rõ ràng, có cấu trúc như sau:

```
USDC Account Monitor
Account monitor started (id: abc123)

Account: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
Owner: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM
Balance: 1,500 USDC
Slot: 352154103
Transaction: 5v8fy0eJ...
---
Account: BQy5rNRxLfcaK6554PMzsg4VJsFXzwGnAnayb8TZKgZX
Owner: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
Balance: 0 USDC
Slot: 352154103
Transaction: 5v8fy0eJ...
---
```

Mỗi khối đại diện cho một tài khoản USDC đã thay đổi trạng thái. Tài khoản đầu tiên hiện giữ 1.500 USDC, còn tài khoản thứ hai đã được rút hết về 0 USDC. Bạn nhận được số dư hiện tại ngay sau mỗi giao dịch, cùng với thông tin tài khoản cụ thể nào đã thay đổi và thời điểm thay đổi.

Tính năng đăng ký tài khoản cho bạn biết kết quả cuối cùng của những gì đã xảy ra với từng tài khoản, chứ không cung cấp chi tiết giao dịch. Nếu cần hiểu toàn bộ ngữ cảnh giao dịch (ai gửi cho ai, phí, v.v.), bạn cần truy xuất toàn bộ giao dịch bằng chữ ký được hiển thị.

## Tài liệu tham khảo đầy đủ về bộ lọc

Ngoài các bộ lọc cơ bản `owner`, `datasize` và `memcmp` mà chúng ta đã sử dụng, tính năng đăng ký tài khoản còn hỗ trợ các tùy chọn lọc bổ sung để thu hẹp kết quả hơn nữa:

### Lọc tài khoản cụ thể

Giám sát chính xác các tài khoản theo khóa công khai của chúng:

```ts theme={"system"}
accounts: {
  "specific-accounts": {
    account: [
      "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "BQy5rNRxLfcaK6554PMzsg4VJsFXzwGnAnayb8TZKgZX"
    ]
  }
}
```

Phương pháp này phù hợp khi bạn biết chính xác tài khoản nào quan trọng với ứng dụng, chẳng hạn như giám sát tài khoản ngân quỹ của ứng dụng hoặc tài khoản người dùng cụ thể.

Đối với các tập hợp tài khoản rất lớn, danh sách pubkey tường minh sẽ trở nên tốn kém — mỗi tài khoản chiếm 32 byte trong yêu cầu đăng ký. Khi vượt quá khoảng 10.000 tài khoản, hãy dùng [bộ lọc cuckoo](/docs/vi/laserstream/cuckoo-filters) đã nén (khoảng 3–4 byte cho mỗi tài khoản) để theo dõi hàng trăm nghìn tài khoản trong một luồng duy nhất. Tính năng này có trong các SDK Rust và JavaScript.

### Các chiến lược lọc kết hợp

Sức mạnh đến từ việc kết hợp nhiều loại bộ lọc. Sau đây là mô hình tư duy:

1. **Mở rộng phạm vi** bằng `owner` — "Cung cấp cho tôi tất cả tài khoản do chương trình này quản lý"
2. **Lọc theo cấu trúc** bằng `datasize` — "Nhưng chỉ các tài khoản thuộc loại cụ thể này"
3. **Nhắm đến dữ liệu cụ thể** bằng `memcmp` — "Và chỉ những tài khoản chứa thông tin cụ thể này"
4. **Giám sát các tài khoản đã biết** bằng `account` — "Hoặc chỉ theo dõi chính xác những tài khoản mà tôi quan tâm"

Ví dụ: giám sát các tài khoản USDC có giá trị cao:

```ts theme={"system"}
accounts: {
  "high-value-usdc": {
    owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
    filters: [
      { datasize: 165 },                           // Token accounts only
      { memcmp: { offset: 0, base58: USDC_MINT } } // USDC only
      // Note: You'd implement balance filtering in your callback logic
    ]
  }
}
```

Điểm mấu chốt là mỗi bộ lọc đều làm giảm khối lượng cập nhật bạn nhận được. Nếu không lọc, bạn có thể nhận một lượng cập nhật tài khoản khổng lồ. Với cách lọc thông minh, bạn chỉ nhận những cập nhật quan trọng đối với trường hợp sử dụng cụ thể của mình.

### Hiểu bức tranh tổng thể

Hãy hình dung tính năng đăng ký tài khoản giống như việc theo dõi nguồn cấp dữ liệu trực tiếp về các thay đổi trong cơ sở dữ liệu. Trạng thái của Solana về cơ bản là một kho khóa-giá trị khổng lồ, trong đó mỗi tài khoản là một mục nhập. Khi các chương trình thực thi, chúng sửa đổi những tài khoản này. Đăng ký cho phép bạn theo dõi các mục nhập cụ thể thay đổi theo thời gian thực.

Hệ thống lọc hoạt động giống như chỉ mục cơ sở dữ liệu — bạn không chỉ theo dõi "tất cả thay đổi" mà theo dõi "các thay đổi đối với tài khoản khớp với những tiêu chí này". Nhờ đó, bạn có thể xây dựng các ứng dụng có khả năng phản hồi, lập tức phản ứng với các sự kiện on-chain liên quan mà không khiến hệ thống quá tải vì dữ liệu không liên quan.

## Áp dụng mẫu này cho các chương trình khác

Phương pháp chúng ta đã tìm hiểu có thể áp dụng cho mọi chương trình Solana. Sau đây là quy trình chung:

1. **Nghiên cứu cấu trúc tài khoản** — Kiểm tra mã nguồn hoặc tài liệu của chương trình
2. **Bắt đầu bằng bộ lọc chủ sở hữu** — Nhắm đến chương trình quản lý các tài khoản
3. **Áp dụng bộ lọc cấu trúc** — Dùng kích thước tài khoản, mẫu dữ liệu hoặc các đặc điểm khác để thu hẹp xuống những loại tài khoản cụ thể
4. **Thêm bộ lọc có mục tiêu** — Tập trung vào các tài khoản, trạng thái hoặc giá trị dữ liệu cụ thể quan trọng đối với ứng dụng của bạn
