diff --git a/basics/create-account/steel/.gitignore b/basics/create-account/steel/.gitignore new file mode 100644 index 000000000..6b34b1f5f --- /dev/null +++ b/basics/create-account/steel/.gitignore @@ -0,0 +1,4 @@ +target +test-ledger +node_modules +pnpm-lock.yaml \ No newline at end of file diff --git a/basics/create-account/steel/Cargo.toml b/basics/create-account/steel/Cargo.toml new file mode 100644 index 000000000..0d2a78376 --- /dev/null +++ b/basics/create-account/steel/Cargo.toml @@ -0,0 +1,22 @@ +[workspace] +resolver = "2" +members = ["api", "program"] + +[workspace.package] +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +homepage = "" +documentation = "" +respository = "" +readme = "./README.md" +keywords = ["solana"] + +[workspace.dependencies] +create-account-api = { path = "./api", version = "0.1.0" } +bytemuck = "1.14" +num_enum = "0.7" +solana-program = "1.18" +steel = "1.3" +thiserror = "1.0" + diff --git a/basics/create-account/steel/README.md b/basics/create-account/steel/README.md new file mode 100644 index 000000000..335507e29 --- /dev/null +++ b/basics/create-account/steel/README.md @@ -0,0 +1,22 @@ +# CreateAccount + +**CreateAccount** is a ... + +## API +- [`Consts`](api/src/consts.rs) – Program constants. +- [`Error`](api/src/error.rs) – Custom program errors. +- [`Event`](api/src/event.rs) – Custom program events. +- [`Instruction`](api/src/instruction.rs) – Declared instructions. + +## Instructions +- [`Hello`](program/src/hello.rs) – Hello ... + +## State +- [`User`](api/src/state/user.rs) – User ... + +## Tests + +To run the test suit, use the Solana toolchain: +``` +cargo test-sbf +``` diff --git a/basics/create-account/steel/api/Cargo.toml b/basics/create-account/steel/api/Cargo.toml new file mode 100644 index 000000000..ffe7e357a --- /dev/null +++ b/basics/create-account/steel/api/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "create-account-api" +version = "0.1.0" +edition = "2021" + +[dependencies] +bytemuck.workspace = true +num_enum.workspace = true +solana-program.workspace = true +steel.workspace = true +thiserror.workspace = true + diff --git a/basics/create-account/steel/api/src/consts.rs b/basics/create-account/steel/api/src/consts.rs new file mode 100644 index 000000000..1b5a765ad --- /dev/null +++ b/basics/create-account/steel/api/src/consts.rs @@ -0,0 +1,2 @@ +/// Seed of the new account PDA. +pub const NEWACCOUNT: &[u8] = b"newaccount"; diff --git a/basics/create-account/steel/api/src/error.rs b/basics/create-account/steel/api/src/error.rs new file mode 100644 index 000000000..628894ce1 --- /dev/null +++ b/basics/create-account/steel/api/src/error.rs @@ -0,0 +1,10 @@ +use steel::*; + +#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)] +#[repr(u32)] +pub enum CreateAccountError { + #[error("This is a dummy error")] + Dummy = 0, +} + +error!(CreateAccountError); diff --git a/basics/create-account/steel/api/src/instruction.rs b/basics/create-account/steel/api/src/instruction.rs new file mode 100644 index 000000000..ceba253b8 --- /dev/null +++ b/basics/create-account/steel/api/src/instruction.rs @@ -0,0 +1,14 @@ +use steel::*; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)] +pub enum CreateAccountInstruction { + InitializeNewAccount = 0, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct InitializeNewAccount {} + +instruction!(CreateAccountInstruction, InitializeNewAccount); + diff --git a/basics/create-account/steel/api/src/lib.rs b/basics/create-account/steel/api/src/lib.rs new file mode 100644 index 000000000..aed2ed84f --- /dev/null +++ b/basics/create-account/steel/api/src/lib.rs @@ -0,0 +1,18 @@ +pub mod consts; +pub mod error; +pub mod instruction; +pub mod sdk; +pub mod state; + +pub mod prelude { + pub use crate::consts::*; + pub use crate::error::*; + pub use crate::instruction::*; + pub use crate::sdk::*; + pub use crate::state::*; +} + +use steel::*; + +// TODO Set program id +declare_id!("z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35"); diff --git a/basics/create-account/steel/api/src/sdk.rs b/basics/create-account/steel/api/src/sdk.rs new file mode 100644 index 000000000..e3af3777a --- /dev/null +++ b/basics/create-account/steel/api/src/sdk.rs @@ -0,0 +1,15 @@ +use steel::*; + +use crate::prelude::*; + +pub fn initializenewaccount(signer: Pubkey) -> Instruction { + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(new_account_pda().0, false), + AccountMeta::new_readonly(system_program::ID, false), + ], + data: InitializeNewAccount {}.to_bytes() + } +} diff --git a/basics/create-account/steel/api/src/state/mod.rs b/basics/create-account/steel/api/src/state/mod.rs new file mode 100644 index 000000000..ae1cb5299 --- /dev/null +++ b/basics/create-account/steel/api/src/state/mod.rs @@ -0,0 +1,18 @@ +mod newaccount; + +pub use newaccount::*; + +use steel::*; + +use crate::consts::*; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] +pub enum CreateAccountAccount { + NewAccount = 0 +} + +/// Fetch PDA of the new account. +pub fn new_account_pda() -> (Pubkey, u8) { + Pubkey::find_program_address(&[NEWACCOUNT], &crate::id()) +} diff --git a/basics/create-account/steel/api/src/state/newaccount.rs b/basics/create-account/steel/api/src/state/newaccount.rs new file mode 100644 index 000000000..bbd7cf7f5 --- /dev/null +++ b/basics/create-account/steel/api/src/state/newaccount.rs @@ -0,0 +1,11 @@ +use steel::*; + +use super::CreateAccountAccount; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] +pub struct NewAccount { + pub userID: u8, +} + +account!(CreateAccountAccount, NewAccount); diff --git a/basics/create-account/steel/package.json b/basics/create-account/steel/package.json new file mode 100644 index 000000000..8bbfceb68 --- /dev/null +++ b/basics/create-account/steel/package.json @@ -0,0 +1,25 @@ +{ + "name": "create-new-account-solana", + "version": "1.0.0", + "description": "Create an Account with steel framework for solana", + "scripts": { + "test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts", + "build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test", + "build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so", + "deploy": "solana program deploy ./program/target/so/create_account_program.so" + }, + "devDependencies": { + "@types/bn.js": "^5.1.0", + "@types/chai": "^4.3.1", + "@types/mocha": "^9.1.1", + "@types/node": "^22.7.4", + "chai": "^4.3.4", + "mocha": "^9.0.3", + "solana-bankrun": "^0.3.0", + "ts-mocha": "^10.0.0", + "typescript": "^4.3.5" + }, + "dependencies": { + "@solana/web3.js": "^1.95.3" + } + } \ No newline at end of file diff --git a/basics/create-account/steel/program/Cargo.toml b/basics/create-account/steel/program/Cargo.toml new file mode 100644 index 000000000..e5fdae227 --- /dev/null +++ b/basics/create-account/steel/program/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "create-account-program" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +create-account-api.workspace = true +solana-program.workspace = true +steel.workspace = true + +[dev-dependencies] +bs64 = "0.1.1" +rand = "0.8.5" +solana-program-test = "1.18" +solana-sdk = "1.18" +tokio = { version = "1.35", features = ["full"] } + + + + + diff --git a/basics/create-account/steel/program/src/initialize.rs b/basics/create-account/steel/program/src/initialize.rs new file mode 100644 index 000000000..9eda6028b --- /dev/null +++ b/basics/create-account/steel/program/src/initialize.rs @@ -0,0 +1,39 @@ +use create_account_api::prelude::*; +use steel::*; +use solana_program::msg; + +pub fn process_initialize(accounts: &[AccountInfo<'_>]) -> ProgramResult { + // Get expected pda bump. + let new_account_bump = new_account_pda().1; + + // Load accounts. + let [signer_info, newaccount, system_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + signer_info.is_signer()?; + newaccount.is_empty()?.is_writable()?.has_seeds( + &[NEWACCOUNT], + new_account_bump, + &create_account_api::ID + )?; + system_program.is_program(&system_program::ID)?; + + // Initialize new account. + create_account::( + newaccount, + &create_account_api::ID, + &[NEWACCOUNT, &[new_account_bump]], + system_program, + signer_info, + )?; + + // fetch new account + + let new_account = newaccount.to_account_mut::(&create_account_api::ID)?; + new_account.userID = 3; + + msg!("Initialized"); + msg!("Initial Value {}", new_account.userID); + + Ok(()) +} diff --git a/basics/create-account/steel/program/src/lib.rs b/basics/create-account/steel/program/src/lib.rs new file mode 100644 index 000000000..d35b70886 --- /dev/null +++ b/basics/create-account/steel/program/src/lib.rs @@ -0,0 +1,20 @@ +mod initialize; +use initialize::*; + +use create_account_api::prelude::*; +use steel::*; + +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + let (ix, data) = parse_instruction(&create_account_api::ID, program_id, data)?; + + match ix { + CreateAccountInstruction::InitializeNewAccount => process_initialize(accounts)? + } + Ok(()) +} + +entrypoint!(process_instruction); diff --git a/basics/create-account/steel/tests/test.ts b/basics/create-account/steel/tests/test.ts new file mode 100644 index 000000000..fe4bf1088 --- /dev/null +++ b/basics/create-account/steel/tests/test.ts @@ -0,0 +1,58 @@ +import { describe, it } from "mocha"; +import { + PublicKey, + Transaction, + TransactionInstruction, + SystemProgram, +} from "@solana/web3.js"; +import { assert } from "chai"; +import { start } from "solana-bankrun"; + +describe("CreateAccountWithSteel", function () { + let context: any; + let client: any; + let payer: any; + const PROGRAM_ID = PublicKey.unique(); + + const NEW_ACCOUNT_SEED = Buffer.from("newaccount"); + + before(async function () { + // load program in solana-bankrun + context = await start( + [{ name: "new_account_program", programId: PROGRAM_ID }], + [] + ); + client = context.banksClient; + payer = context.payer; + }); + + it("It creates new account", async function () { + const blockhash = context.lastBlockhash; + + const [newAccountPDA, bump] = PublicKey.findProgramAddressSync( + [NEW_ACCOUNT_SEED], + PROGRAM_ID + ); + + // set up your instruction first. + const ix = new TransactionInstruction({ + keys: [ + { pubkey: payer.publicKey, isSigner: true, isWritable: true }, + { pubkey: newAccountPDA, isSigner: false, isWritable: true }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + programId: PROGRAM_ID, + data: Buffer.from([0]), + }); + + const tx = new Transaction(); + tx.recentBlockhash = blockhash; + tx.add(ix).sign(payer); + + // Now process the transaction + const transaction = await client.processTransaction(tx); + + assert(transaction.logMessages[3].startsWith(`Program log: new account is created`)); + }); + +}); \ No newline at end of file diff --git a/basics/create-account/steel/tsconfig.json b/basics/create-account/steel/tsconfig.json new file mode 100644 index 000000000..36b290409 --- /dev/null +++ b/basics/create-account/steel/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai", "node"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } + } \ No newline at end of file