I use getProgramAccounts
to get a big number of accounts.
I would like to only get the first 5 accounts with the highest field score
. I know that pagination and ordering is not supported officially, but are there tricks to do it ?
You can deserialize the data of each account and then sort the deserialized accounts using the score field.
const accounts = await provider.connection.getProgramAccounts(program.programId);
deserialized_accounts = []
accounts.forEach((account) => {
// deserialize the data
})
// sort deserialized accounts
deserialized_accounts.sort((a, b) => (a.score > b.score) ? 1 : -1)
There are multiple ways to deserialize an account.
program.accounts.myAccount.fetch(pubkey)
Assuming you have a list of all the accounts that exist, e.g. let keys: PublicKey[] = [....]
, if you are using Anchor, you can fetch multiple accounts with:
program.account.whatever.fetchMultiple(keys)
If you really want ALL of the accounts of that type, use this.program.account.whatever.all()
In either case, now you have an array of the serialized accounts and you can sort easily.
If you REALLY want to fetch only the matching accounts on chain, you can use memcmp, but you will have to find the offset of score
to figure out which bytes to compare, and you'll have to specify some value to use as a cutoff, so "5 highest" isn't really possible:
import { sha256 } from "js-sha256"
import bs58 from "bs58"
async function fetchAccounts(connection: Connection, programId: PublicKey) {
// If using Anchor, filter accounts that match the discriminator (first 8 bytes)
const discriminator = Buffer.from(sha256.digest("account:MyAccount")).subarray(0, 8)
const filter = { memcmp: { offset: 0,
bytes: bs58.encode(discriminator)
}
// Figure out the offset of "score", and the values you want to filter
}
const accounts = await connection.getProgramAccounts(programId, { filters: [filter] })
return accounts
}