jpcaulfi 3 years ago
parent
commit
f23262d006

+ 8 - 0
basics/realloc/native/cicd.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# This script is for quick building & deploying of the program.
+# It also serves as a reference for the commands used for building & deploying Solana programs.
+# Run this bad boy with "bash cicd.sh" or "./cicd.sh"
+
+cargo build-bpf --manifest-path=./program/Cargo.toml --bpf-out-dir=./program/target/so
+solana program deploy ./program/target/so/program.so

+ 18 - 0
basics/realloc/native/package.json

@@ -0,0 +1,18 @@
+{
+  "scripts": {
+    "test": "yarn run ts-mocha -p ./tests/tsconfig.test.json -t 1000000 ./tests/realloc.test.ts"
+  },
+  "dependencies": {
+    "@solana/web3.js": "^1.35",
+    "fs": "^0.0.1-security"
+  },
+  "devDependencies": {
+    "@types/bn.js": "^5.1.0",
+    "@types/chai": "^4.3.1",
+    "@types/mocha": "^9.1.1",
+    "chai": "^4.3.4",
+    "mocha": "^9.0.3",
+    "ts-mocha": "^10.0.0",
+    "typescript": "^4.3.5"
+  }
+}

+ 12 - 0
basics/realloc/native/program/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "program"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+borsh = "0.9.3"
+borsh-derive = "0.9.1"
+solana-program = "1.10.12"
+
+[lib]
+crate-type = ["cdylib", "lib"]

+ 45 - 0
basics/realloc/native/program/src/instructions/create.rs

@@ -0,0 +1,45 @@
+use borsh::BorshSerialize;
+use solana_program::{
+    account_info::{ AccountInfo, next_account_info },
+    entrypoint::ProgramResult, 
+    program::invoke,
+    pubkey::Pubkey,
+    rent::Rent,
+    system_instruction,
+    sysvar::Sysvar,
+};
+
+use crate::state::AddressInfo;
+
+
+pub fn create_address_info(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    data: AddressInfo,
+) -> ProgramResult {
+
+    let accounts_iter = &mut accounts.iter();
+    let target_account = next_account_info(accounts_iter)?;
+    let payer = next_account_info(accounts_iter)?;
+    let system_program = next_account_info(accounts_iter)?;
+
+    let account_span = (data.try_to_vec()?).len();
+    let lamports_required = (Rent::get()?).minimum_balance(account_span);
+
+    invoke(
+        &system_instruction::create_account(
+            &payer.key,
+            &target_account.key,
+            lamports_required,
+            account_span as u64,
+            program_id,
+        ),
+        &[
+            payer.clone(), target_account.clone(), system_program.clone()
+        ],
+    )?;
+    
+    data.serialize(&mut &mut target_account.data.borrow_mut()[..])?;
+    Ok(())
+}
+

+ 5 - 0
basics/realloc/native/program/src/instructions/mod.rs

@@ -0,0 +1,5 @@
+pub mod create;
+pub mod reallocate;
+
+pub use create::*;
+pub use reallocate::*;

+ 68 - 0
basics/realloc/native/program/src/instructions/reallocate.rs

@@ -0,0 +1,68 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+use solana_program::{
+    account_info::{ AccountInfo, next_account_info }, 
+    entrypoint::ProgramResult, 
+    program::invoke,
+    rent::Rent,
+    system_instruction,
+    sysvar::Sysvar,
+};
+
+use crate::state::{
+    AddressInfo,
+    EnhancedAddressInfo,
+    EnhancedAddressInfoExtender,
+    WorkInfo,
+};
+
+
+pub fn reallocate_without_zero_init(
+    accounts: &[AccountInfo],
+    args: EnhancedAddressInfoExtender,
+) -> ProgramResult {
+    
+    let accounts_iter = &mut accounts.iter();
+    let target_account = next_account_info(accounts_iter)?;
+    let payer = next_account_info(accounts_iter)?;
+    let system_program = next_account_info(accounts_iter)?;
+
+    let address_info_data = AddressInfo::try_from_slice(&target_account.data.borrow())?;
+    let enhanced_address_info_data = EnhancedAddressInfo::from_address_info(
+        address_info_data,
+        args.state,
+        args.zip,
+    );
+    
+    let account_span = (enhanced_address_info_data.try_to_vec()?).len();
+    let lamports_required = (Rent::get()?).minimum_balance(account_span);
+    
+    let diff = lamports_required - target_account.lamports();
+    invoke(
+        &system_instruction::transfer(payer.key, target_account.key, diff),
+        &[payer.clone(), target_account.clone(), system_program.clone()],
+    )?;
+
+    target_account.realloc(account_span, false)?;
+    
+    enhanced_address_info_data.serialize(&mut &mut target_account.data.borrow_mut()[..])?;
+    
+    Ok(())
+}
+
+
+pub fn reallocate_zero_init(
+    accounts: &[AccountInfo],
+    data: WorkInfo,
+) -> ProgramResult {
+
+    let accounts_iter = &mut accounts.iter();
+    let target_account = next_account_info(accounts_iter)?;
+
+    let account_span = (data.try_to_vec()?).len();
+
+    target_account.realloc(account_span, true)?;
+
+    data.serialize(&mut &mut target_account.data.borrow_mut()[..])?;
+
+    Ok(())
+}

+ 11 - 0
basics/realloc/native/program/src/lib.rs

@@ -0,0 +1,11 @@
+
+pub mod instructions;
+pub mod processor;
+pub mod state;
+
+use {
+    solana_program::entrypoint,
+    crate::processor::process_instruction,
+};
+
+entrypoint!(process_instruction);

+ 34 - 0
basics/realloc/native/program/src/processor.rs

@@ -0,0 +1,34 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+use solana_program::{
+    account_info::AccountInfo, 
+    entrypoint::ProgramResult, 
+    pubkey::Pubkey,
+};
+use crate::instructions::*;
+use crate::state::*;
+
+
+#[derive(BorshSerialize, BorshDeserialize, Debug)]
+pub enum ReallocInstruction {
+    Create(AddressInfo),
+    ReallocateWithoutZeroInit(EnhancedAddressInfoExtender),
+    ReallocateZeroInit(WorkInfo),
+}
+
+
+pub fn process_instruction(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    input: &[u8],
+) -> ProgramResult {
+
+    let instruction = ReallocInstruction::try_from_slice(&input)?;
+    match instruction {
+        ReallocInstruction::Create(
+            data) => create_address_info(program_id, accounts, data),
+        ReallocInstruction::ReallocateWithoutZeroInit(
+            data) => reallocate_without_zero_init(accounts, data),
+        ReallocInstruction::ReallocateZeroInit(
+            data) => reallocate_zero_init(accounts, data),
+    }
+}

+ 28 - 0
basics/realloc/native/program/src/state/address_info.rs

@@ -0,0 +1,28 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+
+
+#[derive(BorshDeserialize, BorshSerialize, Debug)]
+pub struct AddressInfo {
+    pub name: String,
+    pub house_number: u8,
+    pub street: String,
+    pub city: String,
+}
+
+impl AddressInfo {
+
+    pub fn new(
+        name: String,
+        house_number: u8,
+        street: String,
+        city: String,
+    ) -> Self {
+        
+        AddressInfo {
+            name,
+            house_number,
+            street,
+            city,
+        }
+    }
+}

+ 39 - 0
basics/realloc/native/program/src/state/enhanced_address_info.rs

@@ -0,0 +1,39 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+
+use crate::state::AddressInfo;
+
+
+#[derive(BorshDeserialize, BorshSerialize, Debug)]
+pub struct EnhancedAddressInfoExtender {
+    pub state: String,
+    pub zip: u32,
+}
+
+#[derive(BorshDeserialize, BorshSerialize, Debug)]
+pub struct EnhancedAddressInfo {
+    pub name: String,
+    pub house_number: u8,
+    pub street: String,
+    pub city: String,
+    pub state: String,
+    pub zip: u32,
+}
+
+impl EnhancedAddressInfo {
+
+    pub fn from_address_info(
+        address_info: AddressInfo,
+        state: String,
+        zip: u32,
+    ) -> Self {
+        
+        EnhancedAddressInfo {
+            name: address_info.name,
+            house_number: address_info.house_number,
+            street: address_info.street,
+            city: address_info.city,
+            state,
+            zip,
+        }
+    }
+}

+ 7 - 0
basics/realloc/native/program/src/state/mod.rs

@@ -0,0 +1,7 @@
+pub mod address_info;
+pub mod enhanced_address_info;
+pub mod work_info;
+
+pub use address_info::*;
+pub use enhanced_address_info::*;
+pub use work_info::*;

+ 28 - 0
basics/realloc/native/program/src/state/work_info.rs

@@ -0,0 +1,28 @@
+use borsh::{ BorshDeserialize, BorshSerialize };
+
+
+#[derive(BorshDeserialize, BorshSerialize, Debug)]
+pub struct WorkInfo {
+    pub name: String,
+    pub position: String,
+    pub company: String,
+    pub years_employed: u8,
+}
+
+impl WorkInfo {
+
+    pub fn new(
+        name: String,
+        position: String,
+        company: String,
+        years_employed: u8,
+    ) -> Self {
+        
+        WorkInfo {
+            name,
+            position,
+            company,
+            years_employed,
+        }
+    }
+}

+ 126 - 0
basics/realloc/native/tests/realloc.test.ts

@@ -0,0 +1,126 @@
+import {
+    Connection,
+    Keypair,
+    PublicKey,
+    sendAndConfirmTransaction,
+    Transaction,
+} from '@solana/web3.js';
+import {
+    createCreateInstruction,
+    createKeypairFromFile,
+    createReallocateWithoutZeroInitInstruction,
+    createReallocateZeroInitInstruction,
+    AddressInfo,
+    EnhancedAddressInfo,
+    WorkInfo,
+} from '../ts';
+
+
+
+describe("Realloc!", async () => {
+
+    const connection = new Connection(`https://api.devnet.solana.com`, 'confirmed');
+    const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
+    const program = createKeypairFromFile('./program/target/so/program-keypair.json');
+
+    const testAccount = Keypair.generate();
+
+    it("Create the account with data", async () => {
+        console.log(`${testAccount.publicKey}`);
+        const ix = createCreateInstruction(
+            testAccount.publicKey,
+            payer.publicKey, 
+            program.publicKey,
+            "Jacob",
+            123,
+            "Main St.",
+            "Chicago",
+        );
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer, testAccount]
+        );
+        await printAddressInfo(testAccount.publicKey);
+    });
+    
+    it("Reallocate WITHOUT zero init", async () => {
+        const ix = createReallocateWithoutZeroInitInstruction(
+            testAccount.publicKey,
+            payer.publicKey, 
+            program.publicKey,
+            "Illinois",
+            12345,
+        );
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer]
+        );
+        await printEnhancedAddressInfo(testAccount.publicKey);
+    });
+
+    it("Reallocate WITH zero init", async () => {
+        const ix = createReallocateZeroInitInstruction(
+            testAccount.publicKey,
+            payer.publicKey, 
+            program.publicKey,
+            "Pete",
+            "Engineer",
+            "Solana Labs",
+            2,
+        );
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix),
+            [payer]
+        );
+        await printWorkInfo(testAccount.publicKey);
+    });
+
+    async function printAddressInfo(pubkey: PublicKey): Promise<void> {
+        await delay(2);
+        const data = (await connection.getAccountInfo(pubkey))?.data;
+        if (data) {
+            const addressInfo = AddressInfo.fromBuffer(data);
+            console.log("Address info:");
+            console.log(`   Name:       ${addressInfo.name}`);
+            console.log(`   House Num:  ${addressInfo.house_number}`);
+            console.log(`   Street:     ${addressInfo.street}`);
+            console.log(`   City:       ${addressInfo.city}`);
+        };
+    }
+
+    async function printEnhancedAddressInfo(pubkey: PublicKey): Promise<void> {
+        await delay(2);
+        const data = (await connection.getAccountInfo(pubkey))?.data;
+        if (data) {
+            const enhancedAddressInfo = EnhancedAddressInfo.fromBuffer(data);
+            console.log("Enhanced Address info:");
+            console.log(`   Name:       ${enhancedAddressInfo.name}`);
+            console.log(`   House Num:  ${enhancedAddressInfo.house_number}`);
+            console.log(`   Street:     ${enhancedAddressInfo.street}`);
+            console.log(`   City:       ${enhancedAddressInfo.city}`);
+            console.log(`   State:      ${enhancedAddressInfo.state}`);
+            console.log(`   Zip:        ${enhancedAddressInfo.zip}`);
+        };
+    }
+
+    async function printWorkInfo(pubkey: PublicKey): Promise<void> {
+        await delay(2);
+        const data = (await connection.getAccountInfo(pubkey))?.data;
+        if (data) {
+            const workInfo = WorkInfo.fromBuffer(data);
+            console.log("Work info:");
+            console.log(`   Name:       ${workInfo.name}`);
+            console.log(`   Position:   ${workInfo.position}`);
+            console.log(`   Company:    ${workInfo.company}`);
+            console.log(`   Years:      ${workInfo.years_employed}`);
+        };
+    }
+
+    function delay(s: number) {
+        return new Promise( resolve => setTimeout(resolve, s * 1000) );
+    }
+  });
+  

+ 10 - 0
basics/realloc/native/tests/tsconfig.test.json

@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "types": ["mocha", "chai"],
+    "typeRoots": ["./node_modules/@types"],
+    "lib": ["es2015"],
+    "module": "commonjs",
+    "target": "es6",
+    "esModuleInterop": true
+  }
+}

+ 8 - 0
basics/realloc/native/ts/index.ts

@@ -0,0 +1,8 @@
+
+export * from './instructions/create';
+export * from './instructions/instruction';
+export * from './instructions/reallocate';
+export * from './state/address-info';
+export * from './state/enhanced-address-info';
+export * from './state/work-info';
+export * from './util/util';

+ 84 - 0
basics/realloc/native/ts/instructions/create.ts

@@ -0,0 +1,84 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+import { 
+    PublicKey, 
+    SystemProgram,
+    TransactionInstruction 
+} from '@solana/web3.js';
+import { ReallocInstruction } from './instruction';
+
+
+export class Create {
+
+    instruction: ReallocInstruction;
+    name: string;
+    house_number: number;
+    street: string;
+    city: string;
+
+    constructor(props: {
+        instruction: ReallocInstruction,
+        name: string,
+        house_number: number,
+        street: string,
+        city: string,
+    }) {
+        this.instruction = props.instruction;
+        this.name = props.name;
+        this.house_number = props.house_number;
+        this.street = props.street;
+        this.city = props.city;
+    }
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(CreateSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(CreateSchema, Create, buffer);
+    };
+};
+
+export const CreateSchema = new Map([
+    [ Create, { 
+        kind: 'struct', 
+        fields: [ 
+            ['instruction', 'u8'],
+            ['name', 'string'], 
+            ['house_number', 'u8'], 
+            ['street', 'string'], 
+            ['city', 'string'], 
+        ],
+    }]
+]);
+
+export function createCreateInstruction(
+    target: PublicKey,
+    payer: PublicKey,
+    programId: PublicKey,
+    name: string,
+    house_number: number,
+    street: string,
+    city: string,
+): TransactionInstruction {
+
+    const instructionObject = new Create({
+        instruction: ReallocInstruction.Create,
+        name,
+        house_number,
+        street,
+        city,
+    });
+
+    const ix = new TransactionInstruction({
+        keys: [
+            {pubkey: target, isSigner: false, isWritable: true},
+            {pubkey: payer, isSigner: true, isWritable: true},
+            {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
+        ],
+        programId: programId,
+        data: instructionObject.toBuffer(),
+    });
+
+    return ix;
+}

+ 3 - 0
basics/realloc/native/ts/instructions/index.ts

@@ -0,0 +1,3 @@
+export * from './create';
+export * from './instruction';
+export * from './reallocate';

+ 5 - 0
basics/realloc/native/ts/instructions/instruction.ts

@@ -0,0 +1,5 @@
+export enum ReallocInstruction {
+    Create,
+    ReallocateWithoutZeroInit,
+    ReallocateZeroInit,
+}

+ 147 - 0
basics/realloc/native/ts/instructions/reallocate.ts

@@ -0,0 +1,147 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+import { 
+    PublicKey, 
+    SystemProgram,
+    TransactionInstruction 
+} from '@solana/web3.js';
+import { ReallocInstruction } from './instruction';
+
+
+export class ReallocateWithoutZeroInit {
+
+    instruction: ReallocInstruction;
+    state: string;
+    zip: number;
+
+    constructor(props: {
+        instruction: ReallocInstruction,
+        state: string,
+        zip: number,
+    }) {
+        this.instruction = props.instruction;
+        this.state = props.state;
+        this.zip = props.zip;
+    }
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(ReallocateWithoutZeroInitSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(ReallocateWithoutZeroInitSchema, ReallocateWithoutZeroInit, buffer);
+    };
+};
+
+export const ReallocateWithoutZeroInitSchema = new Map([
+    [ ReallocateWithoutZeroInit, { 
+        kind: 'struct', 
+        fields: [ 
+            ['instruction', 'u8'],
+            ['state', 'string'], 
+            ['zip', 'u32'], 
+        ],
+    }]
+]);
+
+export function createReallocateWithoutZeroInitInstruction(
+    target: PublicKey,
+    payer: PublicKey,
+    programId: PublicKey,
+    state: string,
+    zip: number,
+): TransactionInstruction {
+
+    const instructionObject = new ReallocateWithoutZeroInit({
+        instruction: ReallocInstruction.ReallocateWithoutZeroInit,
+        state,
+        zip,
+    });
+
+    const ix = new TransactionInstruction({
+        keys: [
+            {pubkey: target, isSigner: false, isWritable: true},
+            {pubkey: payer, isSigner: true, isWritable: true},
+            {pubkey: SystemProgram.programId, isSigner: false, isWritable: false}
+        ],
+        programId: programId,
+        data: instructionObject.toBuffer(),
+    });
+
+    return ix;
+}
+
+
+
+export class ReallocateZeroInit {
+
+    instruction: ReallocInstruction;
+    name: string;
+    position: string;
+    company: string;
+    years_employed: number;
+
+    constructor(props: {
+        instruction: ReallocInstruction,
+        name: string,
+        position: string,
+        company: string,
+        years_employed: number,
+    }) {
+        this.instruction = props.instruction;
+        this.name = props.name;
+        this.position = props.position;
+        this.company = props.company;
+        this.years_employed = props.years_employed;
+    }
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(ReallocateZeroInitSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(ReallocateZeroInitSchema, ReallocateZeroInit, buffer);
+    };
+};
+
+export const ReallocateZeroInitSchema = new Map([
+    [ ReallocateZeroInit, { 
+        kind: 'struct', 
+        fields: [ 
+            ['instruction', 'u8'],
+            ['name', 'string'], 
+            ['position', 'string'], 
+            ['company', 'string'], 
+            ['years_employed', 'u8'], 
+        ],
+    }]
+]);
+
+export function createReallocateZeroInitInstruction(
+    target: PublicKey,
+    payer: PublicKey,
+    programId: PublicKey,
+    name: string,
+    position: string,
+    company: string,
+    years_employed: number,
+): TransactionInstruction {
+
+    const instructionObject = new ReallocateZeroInit({
+        instruction: ReallocInstruction.ReallocateZeroInit,
+        name,
+        position,
+        company,
+        years_employed,
+    });
+
+    const ix = new TransactionInstruction({
+        keys: [
+            {pubkey: target, isSigner: false, isWritable: true},
+        ],
+        programId: programId,
+        data: instructionObject.toBuffer(),
+    });
+
+    return ix;
+}

+ 47 - 0
basics/realloc/native/ts/state/address-info.ts

@@ -0,0 +1,47 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+
+
+export class AddressInfo {
+
+    name: string;
+    house_number: number;
+    street: string;
+    city: string;
+
+    constructor(props: {
+        name: string,
+        house_number: number,
+        street: string,
+        city: string,
+    }) {
+        this.name = props.name;
+        this.house_number = props.house_number;
+        this.street = props.street;
+        this.city = props.city;
+    }
+
+    toBase58() {
+        return borsh.serialize(AddressInfoSchema, this).toString()
+    };
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(AddressInfoSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(AddressInfoSchema, AddressInfo, buffer);
+    };
+};
+
+export const AddressInfoSchema = new Map([
+    [ AddressInfo, { 
+        kind: 'struct', 
+        fields: [ 
+            ['name', 'string'],
+            ['house_number', 'u8'],
+            ['street', 'string'],
+            ['city', 'string'],
+        ],
+    }]
+]);

+ 55 - 0
basics/realloc/native/ts/state/enhanced-address-info.ts

@@ -0,0 +1,55 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+
+
+export class EnhancedAddressInfo {
+
+    name: string;
+    house_number: number;
+    street: string;
+    city: string;
+    state: string;
+    zip: number;
+
+    constructor(props: {
+        name: string,
+        house_number: number,
+        street: string,
+        city: string,
+        state: string,
+        zip: number,
+    }) {
+        this.name = props.name;
+        this.house_number = props.house_number;
+        this.street = props.street;
+        this.city = props.city;
+        this.state = props.state;
+        this.zip = props.zip;
+    }
+
+    toBase58() {
+        return borsh.serialize(EnhancedAddressInfoSchema, this).toString()
+    };
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(EnhancedAddressInfoSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(EnhancedAddressInfoSchema, EnhancedAddressInfo, buffer);
+    };
+};
+
+export const EnhancedAddressInfoSchema = new Map([
+    [ EnhancedAddressInfo, { 
+        kind: 'struct', 
+        fields: [ 
+            ['name', 'string'],
+            ['house_number', 'u8'],
+            ['street', 'string'],
+            ['city', 'string'],
+            ['state', 'string'],
+            ['zip', 'u32'],
+        ],
+    }]
+]);

+ 3 - 0
basics/realloc/native/ts/state/index.ts

@@ -0,0 +1,3 @@
+export * from './address-info';
+export * from './enhanced-address-info';
+export * from './work-info';

+ 47 - 0
basics/realloc/native/ts/state/work-info.ts

@@ -0,0 +1,47 @@
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+
+
+export class WorkInfo {
+
+    name: string;
+    position: string;
+    company: string;
+    years_employed: number;
+
+    constructor(props: {
+        name: string,
+        position: string,
+        company: string,
+        years_employed: number,
+    }) {
+        this.name = props.name;
+        this.position = props.position;
+        this.company = props.company;
+        this.years_employed = props.years_employed;
+    }
+
+    toBase58() {
+        return borsh.serialize(WorkInfoSchema, this).toString()
+    };
+
+    toBuffer() { 
+        return Buffer.from(borsh.serialize(WorkInfoSchema, this)) 
+    };
+    
+    static fromBuffer(buffer: Buffer) {
+        return borsh.deserialize(WorkInfoSchema, WorkInfo, buffer);
+    };
+};
+
+export const WorkInfoSchema = new Map([
+    [ WorkInfo, { 
+        kind: 'struct', 
+        fields: [ 
+            ['name', 'string'],
+            ['position', 'string'],
+            ['company', 'string'],
+            ['years_employed', 'u8'],
+        ],
+    }]
+]);

+ 1 - 0
basics/realloc/native/ts/util/index.ts

@@ -0,0 +1 @@
+export * from './util';

+ 9 - 0
basics/realloc/native/ts/util/util.ts

@@ -0,0 +1,9 @@
+import { Keypair } from '@solana/web3.js';
+
+
+export function createKeypairFromFile(path: string): Keypair {
+    return Keypair.fromSecretKey(
+        Buffer.from(JSON.parse(require('fs').readFileSync(path, "utf-8")))
+    )
+};
+