Summary
- This lesson delves into some functionality of the RPC calls that we used in the deserializing account data lesson
- To save on computing time, you can fetch a large number of accounts without their data by filtering them to return just an array of public keys
- Once you have a filtered list of public keys, you can order them and fetch the account data they belong to
Lesson
You may have noticed in the last lesson that while we could fetch and display a list of account data, we didn’t have any granular control over how many accounts to fetch or their order. In this lesson, we’ll learn about some configuration options for thegetProgramAccounts function that will enable things like
paging, ordering accounts, and filtering.
Use dataSlice to only fetch the data you need
Imagine the Movie Review app we worked on in past lessons having four million
movie reviews and the average review is 500 bytes. That would make the total
download for all review accounts over 2GB. Not something you want to have your
frontend download every time the page refreshes.
Fortunately, the getProgramAccounts function that you use to get all of the
accounts takes a configuration object as an argument. One of the configuration
options is dataSlice which lets you provide two things:
offset- the offset from the beginning of the data buffer to start slicinglength- the number of bytes to return, starting from the provided offset
dataSlice in the configuration object, the function will
only return the subset of the data buffer that you specified.
Paging Accounts
One area where this becomes helpful is with paging. If you want to have a list that displays all accounts but there are so many accounts that you don’t want to pull all the data at once, you can fetch all of the accounts but not fetch their data by using adataSlice of { offset: 0, length: 0 }. You can then map the
result to a list of account keys whose data you can fetch only when needed.
getMultipleAccountsInfo method:
Ordering Accounts
ThedataSlice option is also helpful when you need to order a list of accounts
while paging. You still don’t want to fetch all the data at once, but you do
need all of the keys and a way to order them upfront. In this case, you need to
understand the layout of the account data and configure the data slice to only
be the data you need to use for ordering.
For example, you might have an account that stores contact information like so:
initializedas a booleanphoneNumberas an unsigned, 64-bit integerfirstNameas a stringsecondNameas a string
initialized, takes the first byte, then phoneNumber takes another 8,
so the firstName field starts at offset 1 + 8 = 9. However, dynamic data
fields in borsh use the first 4 bytes to record the length of the data, so we
can skip an additional 4 bytes, making the offset 13.
You then need to determine the length to make the data slice. Since the length
is variable, we can’t know for sure before fetching the data. But you can choose
a length that is large enough to cover most cases and short enough to not be too
much of a burden to fetch. 15 bytes is plenty for most first names but would
result in a small enough download even with a million users.
Once you’ve fetched accounts with the given data slice, you can use the sort
method to sort the array before mapping it to an array of public keys.
Use filters to only retrieve specific accounts
Limiting the data received per account is great, but what if you only want to
return accounts that match a specific criteria rather than all of them? That’s
where the filters configuration option comes in. This option is an array that
can have objects matching the following:
memcmp- compares a provided series of bytes with program account data at a particular offset. Fields:offset- the number to offset into program account data before comparing databytes- a base-58 encoded string representing the data to match; limited to less than 129 bytes
dataSize- compares the program account data length with the provided data size
memcmp
filter:
- We’re setting the offset to 13 because we determined previously that the
offset for
firstNamein the data layout is 9 and we want to additionally skip the first 4 bytes indicating the length of the string. - We’re using a third-party library
bs58`` to perform base-58 encoding on the search term. You can install it usingnpm install bs58`.
Lab
Remember that Movie Review app we worked on in the last two lessons? We’re going to spice it up a little by paging the review list, ordering the reviews so they aren’t so random, and adding some basic search functionality. No worries if you’re just jumping into this lesson without having looked at the previous ones - as long as you have the prerequisite knowledge, you should be able to follow the lab without having worked in this specific project yet.
1. Download the starter code
If you didn’t complete the lab from the last lesson or just want to make sure that you didn’t miss anything, you can download the starter code. The project is a fairly simple Next.js application. It includes theWalletContextProvider we created in the Wallets lesson, a Card component for
displaying a movie review, a MovieList component that displays reviews in a
list, a Form component for submitting a new review, and a Movie.ts file that
contains a class definition for a Movie object.
2. Add paging to the reviews
First things first, let’s create a space to encapsulate the code for fetching account data. Create a new fileMovieCoordinator.ts and declare a
MovieCoordinator class. Then let’s move the MOVIE_REVIEW_PROGRAM_ID constant
from MovieList into this new file since we’ll be moving all references to it
MovieCoordinator to create a paging implementation. A quick
note before we dive in: this will be as simple a paging implementation as
possible so that we can focus on the complex part of interacting with Orbition Native Chain
accounts. You can, and should, do better for a production application.
With that out of the way, let’s create a static property accounts of type
web3.PublicKey[], a static function
prefetchAccounts(connection: web3.Connection), and a static function
fetchPage(connection: web3.Connection, page: number, perPage: number): Promise<Movie[]>.
You’ll also need to import @orbition-network/web3.js and Movie.
prefetchAccounts to do this and set the retrieved public keys to
the static accounts property.
fetchPage method. First, if the accounts haven’t been
prefetched yet, we’ll need to do that. Then, we can get the account public keys
that correspond to the requested page and call
connection.getMultipleAccountsInfo. Finally, we deserialize the account data
and return the corresponding Movie objects.
MovieList to use these methods. In
MovieList.tsx, add const [page, setPage] = useState(1) near the existing
useState calls. Then, update useEffect to call MovieCoordinator.fetchPage
instead of fetching the accounts inline.
3. Order reviews alphabetically by title
If you look at the reviews, you might notice they aren’t in any specific order. We can fix this by adding back just enough data into our data slice to help us do some sorting. The various properties in the movie review data buffer are laid out as followsinitialized- unsigned 8-bit integer; 1 byterating- unsigned 8-bit integer; 1 bytetitle- string; unknown number of bytesdescription- string; unknown number of bytes
title
is 2. The length, however, is indeterminate, so we can just provide what seems
to be a reasonable length. I’ll stick with 18 as that will cover the length of
most titles without fetching too much data every time.
Once we’ve modified the data slice in getProgramAccounts, we then need to
actually sort the returned array. To do this, we need to compare the part of the
data buffer that actually corresponds to title. The first 4 bytes of a dynamic
field in Borsh are used to store the length of the field in bytes. So in any
given buffer data that is sliced the way we discussed above, the string
portion is data.slice(4, 4 + data[0]).
Now that we’ve thought through this, let’s modify the implementation of
prefetchAccounts in MovieCoordinator:
4. Add search
The last thing we’ll do to improve this app is to add some basic search capability. Let’s add asearch parameter to prefetchAccounts and reconfigure
the body of the function to use it.
We can use the filters property of the config parameter of
getProgramAccounts to filter accounts by specific data. The offset to the
title fields is 2, but the first 4 bytes are the length of the title so the
actual offset to the string itself is 6. Remember that the bytes need to be base
58 encoded, so let’s install and import bs58.
search parameter to fetchPage and update its call to
prefetchAccounts to pass it along. We’ll also need to add a reload boolean
parameter to fetchPage so that we can force a refresh of the account
prefetching every time the search value changes.
MovieList to call this properly.
First, add const [search, setSearch] = useState('') near the other useState
calls. Then update the call to MovieCoordinator.fetchPage in the useEffect
to pass the search parameter and to reload when search !== ''.
search:
Challenge
Now it’s your turn to try and do this on your own. Using the Student Intros app from last lesson, add paging, ordering alphabetically by name, and searching by name.
- You can build this from scratch or you can download the starter code
- Add paging to the project by prefetching accounts without data, then only fetching the account data for each account when it’s needed.
- Order the accounts displayed in the app alphabetically by name.
- Add the ability to search through introductions by a student’s name.

