4

I've seen some similar threads regarding this problem but none have provided a stable solution. I get the following error when attempting to fetch and deserialize account data:

Deserialization error: RangeError: Trying to access beyond buffer length
    checkOffset index.js:1127
    readUIntLE index.js:1134
    decode Layout.js:570
    decode Layout.js:1234
    decode index.js:64
    decode index.js:64
    decode Layout.js:1234
    deserialize Context.js:50
    fundraisers ExploreSection.js:15
    ExploreSection ExploreSection.js:14
    promise callback*ExploreSection/< ExploreSection.js:13
    React 12
    workLoop scheduler.development.js:266
    flushWork scheduler.development.js:239
    performWorkUntilDeadline scheduler.development.js:533
    EventHandlerNonNull* scheduler.development.js:571
    <anonymous> scheduler.development.js:633

I've tried a few different methods of fetching accounts. I've had the best results with the following class:

export class Fundraiser {
 
         static borshSchema = borsh.struct([
                     borsh.publicKey('beneficiary'),
                     borsh.str('name'),
                     borsh.str('description'),
                     borsh.str('imageLink'),
                     borsh.str('contactLink'),
                     borsh.str('websiteLink'),
                     borsh.u8('raised'),
                 ])
 
         static deserialize(buffer) {
                 if (!buffer) {
                     return null
                 }
                 try {
                     const { beneficiary, name, description, imageLink, contactLink, websiteLink, raised } = this.borshSchema.decode(buffer)
                     return new Fundraiser(beneficiary, name, description, imageLink, contactLink, websiteLink, raised)
                 } catch(error) {
                     console.log('Deserialization error:', error)
                     return null
                 }
          } 
     }

and then attempt to index with the following

export const ExploreSection = () => {

    const connection = new web3.Connection(web3.clusterApiUrl('devnet'))
    const { PROGRAM_ID, getAllFundraisers } = useProvider();
    const [fundraisers, setFundraisers] = useState([]);

    useEffect(() => {
        connection.getProgramAccounts(new web3.PublicKey(PROGRAM_ID)).then(async (accounts) => {
                const fundraisers = accounts.map(({ account }) => {
                    console.log(Fundraiser.deserialize(account.data));
                    return Fundraiser.deserialize(account.data);
                })
            setFundraisers(fundraisers);
        })
    }, [])

    return (
        <div className="mt-4">
            <div className="grid sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4">
            {
                fundraisers.map((fundraiser, i) =>
                    <FundraiserCard 
                    key={i} 
                    fundraiser={fundraiser}
                    />
            )}
            </div>
        </div>
    );
};

I assume one of my fields yields a value that's too long and causes borsh to panic. I haven't gotten much clarity on what causes this from reading other questions/resources so I'm wondering if anyone has ideas as to how the problem can be fixed.

EDIT: After console.logging I've noticed that while it's throwing the same error, it's fetching the correct number of deployed accounts and displaying the schema correctly. The issue seems to lie in the span value staying at "-1" for my string values, as seen below:

console output

2 Answers 2

2

Since your struct has variable-length fields (strings), using the struct type will be very difficult, since it can't calculate the size of each field statically.

There should be an easier way, however. Take a look at the example deserializer in the borsh-js repo: https://github.com/near/borsh-js#examples

Rather than declaring it as a struct, you can just create a mapping with your string types. Here's the example:

const value = new Test({ x: 255, y: 20, z: '123', q: [1, 2, 3] });
const schema = new Map([[Test, { kind: 'struct', fields: [['x', 'u8'], ['y', 'u64'], ['z', 'string'], ['q', [3]]] }]]);
const buffer = borsh.serialize(schema, value);
const newValue = borsh.deserialize(schema, Test, buffer);
0

In addition to Jon's answer, you should generally avoid attempting to write your own Borsh deserializer if you're using Anchor. Using the built-in Anchor utils for fetching and deserializing is easier since it accounts for things like the Account Discriminator (which was the issue here)

Another option is to generate an SDK with something like Solita, which will generate instruction builders and account deserialization utils.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.