Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serialize and deserialize #14

Open
wants to merge 3 commits into
base: starter
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,64 @@ import { FC } from 'react';
import { Movie } from '../models/Movie';
import { useState } from 'react';
import { Box, Button, FormControl, FormLabel, Input, NumberDecrementStepper, NumberIncrementStepper, NumberInput, NumberInputField, NumberInputStepper, Textarea } from '@chakra-ui/react';
import * as web3 from '@solana/web3.js'
import {useConnection, useWallet} from '@solana/wallet-adapter-react'

const MOVIE_REVIEW_PROGRAM_ID = 'CenYq6bDRB7p73EjsPEpiYN7uveyPUTdXkDkgUduboaN'

export const Form: FC = () => {
const [title, setTitle] = useState('')
const [rating, setRating] = useState(0)
const [message, setMessage] = useState('')

const {connection} = useConnection();
const {publicKey,sendTransaction} = useWallet();
const handleSubmit = (event: any) => {
event.preventDefault()
const movie = new Movie(title, rating, message)
handleTransactionSubmit(movie)
}

const handleTransactionSubmit = async (movie: Movie) => {
console.log(JSON.stringify(movie))
console.log(JSON.stringify(movie));
if (!publicKey)
{
alert('Please connect your wallet!')
return
}
const buffer = movie.serialize()
const transaction = new web3.Transaction();

const [pda] = web3.PublicKey.findProgramAddressSync([publicKey.toBuffer(), new TextEncoder().encode(movie.title)],
new web3.PublicKey(MOVIE_REVIEW_PROGRAM_ID))
console.log(pda)
const instruction = new web3.TransactionInstruction({
keys: [
{
pubkey: publicKey,
isSigner: true,
isWritable: false,
},
{
pubkey: pda,
isSigner: false,
isWritable: true
},
{
pubkey: web3.SystemProgram.programId,
isSigner: false,
isWritable: false
}
],
data: buffer,
programId: new web3.PublicKey(MOVIE_REVIEW_PROGRAM_ID)
})
transaction.add(instruction)
try {
let txid = await sendTransaction(transaction, connection)
console.log(`Transaction submitted: https://explorer.solana.com/tx/${txid}?cluster=devnet`);
} catch (e) {
alert(JSON.stringify(e));
}
}

return (
Expand Down
49 changes: 39 additions & 10 deletions components/MovieList.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
import { Card } from './Card'
import { FC, useEffect, useState } from 'react'
import { FC, useEffect, useMemo, useState } from 'react'
import { Movie } from '../models/Movie'

const MOVIE_REVIEW_PROGRAM_ID = 'CenYq6bDRB7p73EjsPEpiYN7uveyPUTdXkDkgUduboaN'
import * as web3 from '@solana/web3.js'
import { MovieCoordinator } from '../coordinators/MovieCoordinator'
import { Button, Center, HStack, Input, Spacer } from '@chakra-ui/react'

export const MovieList: FC = () => {
const connection = new web3.Connection(web3.clusterApiUrl('devnet'))
const [movies, setMovies] = useState<Movie[]>([])
const [page, setPage] = useState(1)
const [search, setSearch] = useState('')

useEffect(() => {
setMovies(Movie.mocks)
}, [])
MovieCoordinator.fetchPage(
connection,
page,
5,
search,
true
).then(setMovies)
}, [page, search])

return (
<div>
<Center>
<Input
id='search'
color='gray.400'
onChange={event => setSearch(event.currentTarget.value)}
placeholder='Search'
w='97%'
mt={2}
mb={2}
/>
</Center>
{
movies.map((movie, i) => {
return (
<Card key={i} movie={movie} />
)
})
movies.map((movie, i) => <Card key={i} movie={movie} /> )
}
<Center>
<HStack w='full' mt={2} mb={8} ml={4} mr={4}>
{
page > 1 && <Button onClick={() => setPage(page - 1)}>Previous</Button>
}
<Spacer />
{
MovieCoordinator.accounts.length > page * 5 &&
<Button onClick={() => setPage(page + 1)}>Next</Button>
}
</HStack>
</Center>
</div>
)
}
67 changes: 67 additions & 0 deletions coordinators/MovieCoordinator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import bs58 from 'bs58'
import * as web3 from '@solana/web3.js'
import { Movie } from '../models/Movie'

const MOVIE_REVIEW_PROGRAM_ID = 'CenYq6bDRB7p73EjsPEpiYN7uveyPUTdXkDkgUduboaN'

export class MovieCoordinator {
static accounts: web3.PublicKey[] = []

static async prefetchAccounts(connection: web3.Connection, search: string) {
const accounts = await connection.getProgramAccounts(
new web3.PublicKey(MOVIE_REVIEW_PROGRAM_ID),
{
dataSlice: { offset: 2, length: 18 },
filters: search === '' ? [] : [
{
memcmp:
{
offset: 6,
bytes: bs58.encode(Buffer.from(search))
}
}
]
}
)
console.log(accounts);
const accountsf = accounts.slice(0,10)
accountsf.sort( (a, b) => {
const lengthA = a.account.data.readUInt32LE(0)
const lengthB = b.account.data.readUInt32LE(0)
console.log(` ${lengthA} ${lengthB}`)
const dataA = a.account.data.slice(4, 4+ lengthA)
const dataB = b.account.data.slice(4, 4+ lengthB)
return dataA.compare(dataB)
})

this.accounts = accounts.map(account => account.pubkey)
}

static async fetchPage(connection: web3.Connection, page: number, perPage: number, search: string, reload: boolean = false): Promise<Movie[]> {
if (this.accounts.length === 0 || reload) {
await this.prefetchAccounts(connection, search)
}

const paginatedPublicKeys = this.accounts.slice(
(page - 1) * perPage,
page * perPage,
)

if (paginatedPublicKeys.length === 0) {
return []
}

const accounts = await connection.getMultipleAccountsInfo(paginatedPublicKeys)

const movies = accounts.reduce((accum: Movie[], account) => {
const movie = Movie.deserialize(account?.data)
if (!movie) {
return accum
}

return [...accum, movie]
}, [])

return movies
}
}
33 changes: 32 additions & 1 deletion models/Movie.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

import * as borsh from '@coral-xyz/borsh'
export class Movie {
title: string;
rating: number;
Expand All @@ -16,4 +16,35 @@ export class Movie {
new Movie('The Godfather: Part II', 4, `The Godfather: Part II is a continuation of the saga of the late Italian-American crime boss, Francis Ford Coppola, and his son, Vito Corleone. The story follows the continuing saga of the Corleone family as they attempt to successfully start a new life for themselves after years of crime and corruption.`),
new Movie('The Dark Knight', 5, `The Dark Knight is a 2008 superhero film directed, produced, and co-written by Christopher Nolan. Batman, in his darkest hour, faces his greatest challenge yet: he must become the symbol of the opposite of the Batmanian order, the League of Shadows.`),
]
borshInstructionSchema = borsh.struct([
borsh.u8('varient'),
borsh.str('title'),
borsh.u8('rating'),
borsh.str('description')
]);
serialize(): Buffer {
const buffer = Buffer.alloc(1000)
this.borshInstructionSchema.encode({...this,varient:0}, buffer)
return buffer.slice(0, this.borshInstructionSchema.getSpan(buffer))
}
static borshAccountSchema = borsh.struct([
borsh.bool('initialized'),
borsh.u8('rating'),
borsh.str('title'),
borsh.str('description'),
])

static deserialize(buffer?: Buffer): Movie|null {
if (!buffer) {
return null
}

try {
const { title, rating, description } = this.borshAccountSchema.decode(buffer)
return new Movie(title, rating, description)
} catch(error) {
console.log('Deserialization error:', error)
return null
}
}
}
Loading