diff --git a/content/courses/native-onchain-development/program-state-management.md b/content/courses/native-onchain-development/program-state-management.md index f7d331381..1f858b9f6 100644 --- a/content/courses/native-onchain-development/program-state-management.md +++ b/content/courses/native-onchain-development/program-state-management.md @@ -542,19 +542,21 @@ You're now ready to build and deploy your program! You can test your program by submitting a transaction with the right instruction data. For that, feel free to use -[this script](https://github.com/Unboxed-Software/solana-movie-client) or -[the frontend](https://github.com/Unboxed-Software/solana-movie-frontend) we +[this script](https://github.com/solana-developers/movie-review-program-client) +or [the frontend](https://github.com/solana-developers/movie-review-frontend) we built in the -[Deserialize Custom Instruction Data lesson](deserialize-custom-data). In both -cases, make sure you copy and paste the program ID for your program into the -appropriate area of the source code to make sure you're testing the right +[Deserialize Custom Instruction Data lesson](/content/courses/native-onchain-development/deserialize-custom-data-frontend.md). +In both cases, set the program ID for your program in the appropriate file +`web/components/ui/review-form.ts` to make sure you're testing the right program. -If you use the frontend, simply replace the `MOVIE_REVIEW_PROGRAM_ID` in both -the `MovieList.tsx` and `Form.tsx` components with the address of the program -you've deployed. Then run the frontend, submit a view, and refresh the browser -to see the review. +- If you're using the script, simply replace the value assigned to + `movieProgramId` in the `index.ts` component with the public key of the + program you've deployed. +- If you use the frontend, simply replace the `MOVIE_REVIEW_PROGRAM_ID` in the + `review-form.tsx` components with the address of the program you’ve deployed. +Then run the frontend, submit a view, and refresh the browser to see the review. If you need more time with this project to feel comfortable with these concepts, have a look at the [solution code](https://beta.solpg.io/66d67f31cffcf4b13384d334) before @@ -578,7 +580,7 @@ taking a name a short message as instruction data, the program should: string in each account You can test your program by building the -[frontend](https://github.com/Unboxed-Software/solana-student-intros-frontend) +[frontend](https://github.com/solana-developers/solana-student-intro-frontend) we created in the [Page, Order, and Filter Program Data lesson](/content/courses/native-onchain-development/paging-ordering-filtering-data-frontend). Remember to replace the program ID in the frontend code with the one you've diff --git a/content/courses/native-onchain-development/serialize-instruction-data-frontend.md b/content/courses/native-onchain-development/serialize-instruction-data-frontend.md index 3154f69b5..798a3ac0d 100644 --- a/content/courses/native-onchain-development/serialize-instruction-data-frontend.md +++ b/content/courses/native-onchain-development/serialize-instruction-data-frontend.md @@ -354,30 +354,69 @@ Now that we have the buffer layout set up, let’s create a method in `Movie` called `serialize()` that will return a `Buffer` with a `Movie` object’s properties encoded into the appropriate layout. +Instead of allocating a fixed buffer size, we'll calculate the size dynamically +using known constants for the space required by each field in the `Movie` +object. Specifically, we'll use `INIT_SPACE` (to account for string length +metadata) and `ANCHOR_DISCRIMINATOR` (to account for the 8-byte discriminator +used by Anchor). + ```typescript -import * as borsh from '@coral-xyz/borsh' +import * as borsh from "@coral-xyz/borsh"; + +// Constants for size calculations +const ANCHOR_DISCRIMINATOR = 8; // 8 bytes for the account discriminator used by Anchor +const STRING_LENGTH_SPACE = 4; // 4 bytes to store the length of each string + +// Specific sizes for 'title' and 'description' strings +const TITLE_SIZE = 100; // Allocate 100 bytes for the 'title' +const DESCRIPTION_SIZE = 500; // Allocate 500 bytes for the 'description' + +// Total space calculation for the Movie review structure +const MOVIE_REVIEW_SPACE = + ANCHOR_DISCRIMINATOR + // 8 bytes for the account discriminator + STRING_LENGTH_SPACE + + TITLE_SIZE + // 4 bytes for the title length + 100 bytes for the title + STRING_LENGTH_SPACE + + DESCRIPTION_SIZE + // 4 bytes for the description length + 500 bytes for the description + 1 + // 1 byte for 'variant' + 1; // 1 byte for 'rating' export class Movie { title: string; rating: number; description: string; - ... + constructor(title: string, rating: number, description: string) { + // Enforce specific sizes for title and description + if (title.length > TITLE_SIZE) { + throw new Error(`Title cannot exceed ${TITLE_SIZE} characters.`); + } + if (description.length > DESCRIPTION_SIZE) { + throw new Error( + `Description cannot exceed ${DESCRIPTION_SIZE} characters.`, + ); + } + + this.title = title; + this.rating = rating; + this.description = description; + } borshInstructionSchema = borsh.struct([ - borsh.u8('variant'), - borsh.str('title'), - borsh.u8('rating'), - borsh.str('description'), - ]) + borsh.u8("variant"), + borsh.str("title"), + borsh.u8("rating"), + borsh.str("description"), + ]); serialize(): Buffer { try { - const buffer = Buffer.alloc(1000); + // Allocate a buffer with the exact space needed + const buffer = Buffer.alloc(MOVIE_REVIEW_SPACE); this.borshInstructionSchema.encode({ ...this, variant: 0 }, buffer); return buffer.subarray(0, this.borshInstructionSchema.getSpan(buffer)); } catch (error) { - console.error('Serialization error:', error); + console.error("Serialization error:", error); return Buffer.alloc(0); } } diff --git a/public/assets/courses/movie-review-dapp.png b/public/assets/courses/movie-review-dapp.png new file mode 100644 index 000000000..a770aa715 Binary files /dev/null and b/public/assets/courses/movie-review-dapp.png differ