Skip to content
Merged
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
110 changes: 39 additions & 71 deletions basics/account-data/pinocchio/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import {
type Address,
appendTransactionMessageInstruction,
createTransactionMessage,
fixDecoderSize,
fixEncoderSize,
generateKeyPairSigner,
getStructDecoder,
getStructEncoder,
getU8Decoder,
getU8Encoder,
getUtf8Decoder,
getUtf8Encoder,
type KeyPairSigner,
lamports,
pipe,
Expand All @@ -13,67 +21,26 @@ import {
import { SYSTEM_PROGRAM_ADDRESS } from '@solana-program/system';
import { FailedTransactionMetadata, LiteSVM } from 'litesvm';

interface AddressInfo {
name: string;
house_number: number;
street: string;
city: string;
}

function toBytes(addressInfo: AddressInfo): Buffer {
const data: number[] = [];

// Add instruction discriminator
data.push(0);

// Pad name to 16 bytes (data[1..17])
const nameBytes = Buffer.from(addressInfo.name, 'utf-8');
const namePadded = Buffer.alloc(16);
nameBytes.copy(namePadded, 0, 0, Math.min(nameBytes.length, 16));
data.push(...namePadded);

// Add 1 byte padding at index 17
data.push(0);

// Add house_number at index 18
data.push(addressInfo.house_number);

// Pad street to 16 bytes (data[19..35])
const streetBytes = Buffer.from(addressInfo.street, 'utf-8');
const streetPadded = Buffer.alloc(16);
streetBytes.copy(streetPadded, 0, 0, Math.min(streetBytes.length, 16));
data.push(...streetPadded);

// Add 1 byte padding at index 35
data.push(0);

// Pad city to 16 bytes (data[36..52])
const cityBytes = Buffer.from(addressInfo.city, 'utf-8');
const cityPadded = Buffer.alloc(16);
cityBytes.copy(cityPadded, 0, 0, Math.min(cityBytes.length, 16));
data.push(...cityPadded);

return Buffer.from(data);
}

function fromBytes(buffer: Buffer): AddressInfo {
// name: bytes 0..16
const nameBytes = buffer.subarray(0, 16);
const name = nameBytes.toString('utf-8').replace(/\0/g, '');

// house_number: byte 17
const house_number = buffer[17];

// street: bytes 18..34
const streetBytes = buffer.subarray(18, 34);
const street = streetBytes.toString('utf-8').replace(/\0/g, '');

// city: bytes 35..51
const cityBytes = buffer.subarray(35, 51);
const city = cityBytes.toString('utf-8').replace(/\0/g, '');

return { name, house_number, street, city };
}
// The on-chain account stores each field padded to a fixed width, with a single
// alignment byte before `house_number` and before `city`.
const createAddressInfoEncoder = getStructEncoder([
['discriminator', getU8Encoder()],
['name', fixEncoderSize(getUtf8Encoder(), 16)],
['namePadding', getU8Encoder()],
['houseNumber', getU8Encoder()],
['street', fixEncoderSize(getUtf8Encoder(), 16)],
['streetPadding', getU8Encoder()],
['city', fixEncoderSize(getUtf8Encoder(), 16)],
]);

const addressInfoDecoder = getStructDecoder([
['name', fixDecoderSize(getUtf8Decoder(), 16)],
['namePadding', getU8Decoder()],
['houseNumber', getU8Decoder()],
['street', fixDecoderSize(getUtf8Decoder(), 16)],
['streetPadding', getU8Decoder()],
['city', fixDecoderSize(getUtf8Decoder(), 16)],
]);

describe('Account Data!', () => {
const litesvm = new LiteSVM();
Expand All @@ -97,13 +64,6 @@ describe('Account Data!', () => {
console.log(`Payer Address : ${payer.address}`);
console.log(`Address Info Acct : ${addressInfoAccount.address}`);

const addressInfo: AddressInfo = {
name: 'Joe C',
house_number: 136,
street: 'Mile High Dr.',
city: 'Solana Beach',
};

const ix = {
programAddress: programId,
accounts: [
Expand All @@ -115,7 +75,15 @@ describe('Account Data!', () => {
{ address: payer.address, role: AccountRole.WRITABLE_SIGNER, signer: payer },
{ address: SYSTEM_PROGRAM_ADDRESS, role: AccountRole.READONLY },
],
data: new Uint8Array(toBytes(addressInfo)),
data: createAddressInfoEncoder.encode({
discriminator: 0,
name: 'Joe C',
namePadding: 0,
houseNumber: 136,
street: 'Mile High Dr.',
streetPadding: 0,
city: 'Solana Beach',
}),
};

const transactionMessage = pipe(
Expand All @@ -139,10 +107,10 @@ describe('Account Data!', () => {
throw new Error('Account not found');
}

const readAddressInfo = fromBytes(Buffer.from(accountInfo.data));
const readAddressInfo = addressInfoDecoder.decode(accountInfo.data);

console.log(`Name : ${readAddressInfo.name}`);
console.log(`House Num: ${readAddressInfo.house_number}`);
console.log(`House Num: ${readAddressInfo.houseNumber}`);
console.log(`Street : ${readAddressInfo.street}`);
console.log(`City : ${readAddressInfo.city}`);
});
Expand Down
21 changes: 16 additions & 5 deletions basics/close-account/pinocchio/tests/close-account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import {
type Address,
appendTransactionMessageInstruction,
createTransactionMessage,
fixDecoderSize,
fixEncoderSize,
generateKeyPairSigner,
getAddressEncoder,
getProgramDerivedAddress,
getStructEncoder,
getU8Encoder,
getUtf8Decoder,
getUtf8Encoder,
type Instruction,
type KeyPairSigner,
lamports,
Expand All @@ -23,6 +29,14 @@ const USER_ACCOUNT_SIZE = 16;
const CREATE_DISCRIMINATOR = 0;
const CLOSE_DISCRIMINATOR = 1;

const createUserEncoder = getStructEncoder([
['discriminator', getU8Encoder()],
['bump', getU8Encoder()],
['name', fixEncoderSize(getUtf8Encoder(), USER_ACCOUNT_SIZE)],
]);

const userNameDecoder = fixDecoderSize(getUtf8Decoder(), USER_ACCOUNT_SIZE);

describe('Close Account!', () => {
const svm = new LiteSVM();
let programId: Address;
Expand Down Expand Up @@ -62,13 +76,10 @@ describe('Close Account!', () => {
}

it('Create the account', async () => {
const name = Buffer.alloc(USER_ACCOUNT_SIZE);
name.write('Jacob');

const ix = {
programAddress: programId,
accounts: keys,
data: new Uint8Array(Buffer.concat([Buffer.from([CREATE_DISCRIMINATOR, bump]), name])),
data: createUserEncoder.encode({ discriminator: CREATE_DISCRIMINATOR, bump, name: 'Jacob' }),
};

const result = await sendInstruction(ix);
Expand All @@ -78,7 +89,7 @@ describe('Close Account!', () => {
assert(account.exists, 'expected user account to exist');
assert.equal(account.data.length, USER_ACCOUNT_SIZE);
assert.equal(account.programAddress, programId, 'expected user account to be owned by the program');
assert.equal(Buffer.from(account.data.slice(0, 5)).toString(), 'Jacob');
assert.equal(userNameDecoder.decode(account.data), 'Jacob');
});

it('Close the account', async () => {
Expand Down
4 changes: 1 addition & 3 deletions basics/counter/native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"deploy": "solana program deploy ./program/target/so/program.so"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^5.2.3",
"@types/mocha": "^10.0.10",
"chai": "^6.2.2",
Expand All @@ -26,7 +25,6 @@
},
"dependencies": {
"@solana/kit": "^7.0.0",
"@solana-program/system": "^0.13.0",
"bn.js": "^5.2.2"
"@solana-program/system": "^0.13.0"
}
}
18 changes: 0 additions & 18 deletions basics/counter/native/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions basics/counter/native/tests/counter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ describe('Counter Solana Native', () => {

// Deserialize the counter & check count has been incremented
const counterAccount = deserializeCounterAccount(Buffer.from(counterAccountInfo.data));
assert(counterAccount.count.toNumber() === 1, 'Expected count to have been 1');
console.log(`[alloc+increment] count is: ${counterAccount.count.toNumber()}`);
assert(counterAccount.count === 1n, 'Expected count to have been 1');
console.log(`[alloc+increment] count is: ${counterAccount.count}`);
});

it('Test allocate tx and increment tx', async () => {
Expand Down Expand Up @@ -98,8 +98,8 @@ describe('Counter Solana Native', () => {
assert(counterAccountInfo.exists, 'Expected counter account to have been created');

let counterAccount = deserializeCounterAccount(Buffer.from(counterAccountInfo.data));
assert(counterAccount.count.toNumber() === 0, 'Expected count to have been 0');
console.log(`[allocate] count is: ${counterAccount.count.toNumber()}`);
assert(counterAccount.count === 0n, 'Expected count to have been 0');
console.log(`[allocate] count is: ${counterAccount.count}`);

// Check increment tx
const incrementIx = createIncrementInstruction({ counter });
Expand All @@ -118,7 +118,7 @@ describe('Counter Solana Native', () => {
assert(counterAccountInfo.exists, 'Expected counter account to have been created');

counterAccount = deserializeCounterAccount(Buffer.from(counterAccountInfo.data));
assert(counterAccount.count.toNumber() === 1, 'Expected count to have been 1');
console.log(`[increment] count is: ${counterAccount.count.toNumber()}`);
assert(counterAccount.count === 1n, 'Expected count to have been 1');
console.log(`[increment] count is: ${counterAccount.count}`);
});
});
6 changes: 3 additions & 3 deletions basics/counter/native/ts/accounts/counter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import BN from 'bn.js';
import { getU64Decoder } from '@solana/kit';

export type Counter = {
count: BN;
count: bigint;
};

export const COUNTER_ACCOUNT_SIZE = 8;
Expand All @@ -12,6 +12,6 @@ export function deserializeCounterAccount(data: Buffer): Counter {
}

return {
count: new BN(data, 'le'),
count: getU64Decoder().decode(data),
};
}
Loading
Loading