Skip to content

Commit 255735c

Browse files
committed
update example and tests
1 parent 9d38c14 commit 255735c

File tree

5 files changed

+116
-28
lines changed

5 files changed

+116
-28
lines changed

basics/favorites/poseidon/migrations/deploy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
const anchor = require("@coral-xyz/anchor");
66

7-
module.exports = async function (provider) {
7+
module.exports = async (provider) => {
88
// Configure client to use the provider.
99
anchor.setProvider(provider);
1010

basics/favorites/poseidon/programs/poseidon/src/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,40 @@ declare_id!("GsGBeoB6fFTWfUrHhKYTjtXvuiKCC7shhhQqXeQsTLJ2");
33
#[program]
44
pub mod favorites {
55
use super::*;
6-
pub fn initialize(ctx: Context<InitializeContext>, number: u8) -> Result<()> {
6+
pub fn initialize(
7+
ctx: Context<InitializeContext>,
8+
number: u8,
9+
color: String,
10+
hobbies: Vec<String>,
11+
) -> Result<()> {
712
ctx.accounts.state.owner = ctx.accounts.user.key();
813
ctx.accounts.state.number = number;
14+
ctx.accounts.state.color = color;
15+
ctx.accounts.state.hobbies = hobbies;
916
ctx.accounts.state.bump = ctx.bumps.state;
1017
Ok(())
1118
}
1219
}
1320
#[derive(Accounts)]
1421
pub struct InitializeContext<'info> {
22+
#[account(mut)]
23+
pub user: Signer<'info>,
1524
#[account(
1625
init,
1726
payer = user,
18-
space = 42,
27+
space = 130,
1928
seeds = [b"favorites",
2029
user.key().as_ref()],
2130
bump,
2231
)]
2332
pub state: Account<'info, FavoritesState>,
24-
#[account(mut)]
25-
pub user: Signer<'info>,
2633
pub system_program: Program<'info, System>,
2734
}
2835
#[account]
2936
pub struct FavoritesState {
3037
pub owner: Pubkey,
3138
pub number: u8,
39+
pub color: String,
40+
pub hobbies: Vec<String>,
3241
pub bump: u8,
3342
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { describe, it } from 'node:test';
2+
import * as anchor from '@coral-xyz/anchor';
3+
import { PublicKey } from '@solana/web3.js';
4+
import { BankrunProvider } from 'anchor-bankrun';
5+
import { assert } from 'chai';
6+
import { startAnchor } from 'solana-bankrun';
7+
import type { Favorites } from '../target/types/favorites';
8+
9+
const web3 = anchor.web3;
10+
const IDL = require('../target/idl/favorites.json');
11+
const PROGRAM_ID = new PublicKey(IDL.address);
12+
13+
describe('Favorites Bankrun', async () => {
14+
// Use the cluster and the keypair from Anchor.toml
15+
// Load programs into anchor-bankrun
16+
const context = await startAnchor('', [{ name: 'favorites_program', programId: PROGRAM_ID }], []);
17+
const provider = new BankrunProvider(context);
18+
anchor.setProvider(provider);
19+
const user = (provider.wallet as anchor.Wallet).payer;
20+
const someRandomGuy = anchor.web3.Keypair.generate();
21+
22+
const program = new anchor.Program<Favorites>(IDL, provider);
23+
24+
// Here"s what we want to write to the blockchain
25+
const favoriteNumber = 23;
26+
const favoriteColor = 'purple';
27+
const favoriteHobbies = ['skiing', 'skydiving', 'biking'];
28+
29+
// We don"t need to airdrop if we"re using the local cluster
30+
// because the local cluster gives us 1,000,000 SOL
31+
const balance = await context.banksClient.getBalance(user.publicKey);
32+
const balanceInSOL = balance / BigInt(web3.LAMPORTS_PER_SOL);
33+
const formattedBalance = new Intl.NumberFormat().format(balanceInSOL);
34+
console.log(`Balance: ${formattedBalance} SOL`);
35+
36+
it('Writes our favorites to the blockchain', async () => {
37+
await program.methods
38+
// set_favourites in Rust becomes setFavorites in TypeScript
39+
.initialize(favoriteNumber, favoriteColor, favoriteHobbies)
40+
// Sign the transaction
41+
.signers([user])
42+
// Send the transaction to the cluster or RPC
43+
.rpc();
44+
45+
// Find the PDA for the user"s favorites
46+
const favoritesPdaAndBump = web3.PublicKey.findProgramAddressSync([Buffer.from('favorites'), user.publicKey.toBuffer()], program.programId);
47+
const favoritesPda = favoritesPdaAndBump[0];
48+
const dataFromPda = await program.account.favoritesState.fetch(favoritesPda);
49+
// And make sure it matches!
50+
assert.equal(dataFromPda.color, favoriteColor);
51+
// A little extra work to make sure the BNs are equal
52+
assert.equal(dataFromPda.number.toString(), favoriteNumber.toString());
53+
// And check the hobbies too
54+
assert.deepEqual(dataFromPda.hobbies, favoriteHobbies);
55+
});
56+
57+
it('Updates the favorites', async () => {
58+
const newFavoriteHobbies = ['skiing', 'skydiving', 'biking', 'swimming'];
59+
try {
60+
await program.methods.initialize(favoriteNumber, favoriteColor, newFavoriteHobbies).signers([user]).rpc();
61+
} catch (error) {
62+
const errorMessage = (error as Error).message;
63+
assert.isTrue(errorMessage.includes('SendTransactionError'));
64+
}
65+
});
66+
67+
it('Rejects transactions from unauthorized signers', async () => {
68+
try {
69+
await program.methods
70+
// set_favourites in Rust becomes setFavorites in TypeScript
71+
.initialize(favoriteNumber, favoriteColor, favoriteHobbies)
72+
// Sign the transaction
73+
.signers([someRandomGuy])
74+
// Send the transaction to the cluster or RPC
75+
.rpc();
76+
} catch (error) {
77+
const errorMessage = (error as Error).message;
78+
assert.isTrue(errorMessage.includes('unknown signer'));
79+
}
80+
});
81+
});

basics/favorites/poseidon/tests/favourits.test.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ describe("account favorites program", () => {
2727

2828
const favorites = {
2929
number: 2,
30-
// color: "blue",
30+
color: "blue",
31+
hobbies: ["coding", "chess"],
3132
};
3233
await program.methods
33-
.initialize(favorites.number)
34+
.initialize(favorites.number, favorites.color, favorites.hobbies)
3435
.accounts({
3536
user: favoritesAccount.publicKey,
3637
})
@@ -41,12 +42,14 @@ describe("account favorites program", () => {
4142
console.log(`Owner: ${account.owner}`);
4243
console.log(`Number: ${account.number}`);
4344
console.log(`Bump: ${account.bump}`);
44-
// console.log(`Color: ${account.color}`);
45-
45+
console.log(`Color: ${account.color}`);
46+
console.log(`Hobbies: ${account.hobbies}`);
4647
expect(account.bump).to.be.a("number");
4748
expect(account.owner.toBase58()).to.equal(
4849
favoritesAccount.publicKey.toBase58()
4950
);
5051
expect(account.number).to.equal(favorites.number);
52+
expect(account.color).to.equal(favorites.color);
53+
expect(account.hobbies).to.deep.equal(favorites.hobbies);
5154
});
5255
});

basics/favorites/poseidon/ts-programs/src/favourits.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,33 @@ import {
44
Signer,
55
u8,
66
type Result,
7+
// Avoid using `as` for renaming imports in this case, as it can cause issues during the transpilation process.
8+
String,
9+
Vec,
710
} from "@solanaturbine/poseidon";
811

9-
// * This program defines a Favorites class that allows a Solana user to store their favorite number.
10-
// * It uses Program Derived Addresses (PDAs) to securely manage the user's favorite data.
12+
// This program defines a Favorites class that allows a Solana user to store their favorite number.
13+
// It uses Program Derived Addresses (PDAs) to securely manage the user's favorite data.
1114

1215
export default class Favorites {
1316
static PROGRAM_ID = new Pubkey(
1417
"GsGBeoB6fFTWfUrHhKYTjtXvuiKCC7shhhQqXeQsTLJ2"
1518
);
1619

17-
// Note: As of this implementation, the Poseidon framework does not support using arrays of strings
18-
// (e.g., String[] or Array<String>) in the transaction context. Therefore,
19-
// it is not possible to use collections of strings, such as hobbies: String[] or Array<String>.
20-
21-
// Additionally, working with string data types can lead to deserialization issues,
22-
// specifically the `AccountDidNotDeserialize` error. This occurs because the framework may
23-
// not correctly allocate the required space for PDAs that include string fields.
24-
2520
initialize(
26-
user: Signer,
2721
state: FavoritesState,
28-
number: u8
29-
// color: String,
30-
// hobbies: Array<String>
22+
user: Signer,
23+
number: u8,
24+
color: String<10>,
25+
hobbies: Vec<String<10>, 5>
3126
): Result {
3227
// Create a PDA using the seed combination ["favorites", user.key]
33-
state.derive(["favorites", user.key]).init();
28+
state.derive(["favorites", user.key]).init(user);
3429

3530
state.owner = user.key; // Set the owner of the favorites state to the user's public key
3631
state.number = number; // Store the user's favorite number
37-
// state.color = color; // Note: String handling is currently unsupported
38-
// state.hobbies = hobbies; // Note: Array of String is currently unsupported
32+
state.color = color;
33+
state.hobbies = hobbies;
3934
state.bump = state.getBump(); // Retrieve the bump seed for the PDA
4035
}
4136
}
@@ -45,7 +40,7 @@ export interface FavoritesState extends Account {
4540

4641
// PDA properties
4742
number: u8; // The user's favorite number
48-
// color: String; // Note: String handling is currently unsupported
49-
// hobbies: Array<String>; // Note: String handling is currently unsupported
43+
color: String<10>;
44+
hobbies: Vec<String<10>, 5>;
5045
bump: u8; // Bump seed for the PDA
5146
}

0 commit comments

Comments
 (0)