Skip to content

Commit

Permalink
add steel example to close account (#208)
Browse files Browse the repository at this point in the history
  • Loading branch information
448-OG authored Jan 4, 2025
1 parent 5bdafb4 commit 2f244bf
Show file tree
Hide file tree
Showing 25 changed files with 1,630 additions and 2 deletions.
2 changes: 2 additions & 0 deletions basics/close-account/steel/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
test-ledger
21 changes: 21 additions & 0 deletions basics/close-account/steel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[workspace]
members = ["api", "program"]
resolver = "2"

[workspace.package]
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
homepage = "https://github.com/solana-developers/program-examples"
documentation = "https://github.com/solana-developers/program-examples"
respository = "https://github.com/solana-developers/program-examples"
readme = "./README.md"
keywords = ["solana"]

[workspace.dependencies]
close-account-api = { path = "./api", version = "0.1.0" }
bytemuck = "1.14"
num_enum = "0.7"
solana-program = "1.18"
steel = "2.1.0"
thiserror = "1.0"
22 changes: 22 additions & 0 deletions basics/close-account/steel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# CloseAccount

**CloseAccount** 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
```
12 changes: 12 additions & 0 deletions basics/close-account/steel/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "close-account-api"
version = "0.1.0"
edition = "2021"

[dependencies]
bytemuck.workspace = true
jzon = "0.12.5"
num_enum.workspace = true
solana-program.workspace = true
steel.workspace = true
thiserror.workspace = true
18 changes: 18 additions & 0 deletions basics/close-account/steel/api/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use steel::*;

/// A [Result] type representing `Result<T, CloseAccountError>`
pub type CloseAccountResult<T> = Result<T, CloseAccountError>;

/// Error handling enum for this create
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)]
#[repr(u32)]
pub enum CloseAccountError {
/// A name can only be 64 bytes in length when converted to bytes
#[error("Invalid Name Length. The maximum length of the string is 64 bytes.")]
MaxNameLengthExceeded = 0,
/// Only UTF-8 String types are supported
#[error("Only UTF-8 String encoding is supported")]
OnlyUtf8IsSupported = 1,
}

error!(CloseAccountError);
25 changes: 25 additions & 0 deletions basics/close-account/steel/api/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use steel::*;

/// Used in generating the discriminats for instructions
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
pub enum MyInstruction {
/// Create account discriminant represented by `0`
CreateAccount = 0,
/// Close account discriminant represented by `1`
CloseAccount = 1,
}

/// Create account struct with the name
/// as an array of 64 bytes
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct CreateAccount(pub [u8; 64]);

/// UsedClose Account
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct CloseAccount;

instruction!(MyInstruction, CreateAccount);
instruction!(MyInstruction, CloseAccount);
18 changes: 18 additions & 0 deletions basics/close-account/steel/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#![forbid(unsafe_code)]

pub mod error;
pub mod instruction;
pub mod sdk;
pub mod state;

pub mod prelude {
pub use crate::error::*;
pub use crate::instruction::*;
pub use crate::sdk::*;
pub use crate::state::*;
}

use steel::*;

// Set program id
declare_id!("z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35");
31 changes: 31 additions & 0 deletions basics/close-account/steel/api/src/sdk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use steel::*;

use crate::prelude::*;

/// Create an PDA and store a String in it
pub fn create_account(signer: Pubkey, user: CreateAccount) -> Instruction {
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(User::pda(signer).0, false),
AccountMeta::new_readonly(system_program::ID, false),
],
data: user.to_bytes(),
}
}

/// Creates an instruction to close the account,
/// in our case the PDA. The PDA address is derived from
/// the `payer` public key
pub fn close_account(signer: Pubkey) -> Instruction {
Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(User::pda(signer).0, false),
AccountMeta::new_readonly(system_program::ID, false),
],
data: CloseAccount.to_bytes(),
}
}
2 changes: 2 additions & 0 deletions basics/close-account/steel/api/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod user;
pub use user::*;
75 changes: 75 additions & 0 deletions basics/close-account/steel/api/src/state/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use steel::*;

use crate::error::{CloseAccountError, CloseAccountResult};

/// An enum which is used to derive a discriminator for the user account.
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
pub enum UserAccount {
/// The user is represented by a discriminator of `0`
User = 0,
}

/// The user Account structure which stores a
/// `name` as bytes with max array length of u64 due to the
/// requirement for memory alignment since 64 is a factor of 8.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct User {
/// The name string stored as bytes.
/// The `&str` is converted into bytes and copied upto
/// the length of the bytes, if the bytes are not 64, it
/// pads with zeroes upto 64, if it is more than 64 an error
/// is returned.
pub name: [u8; 64],
}

impl User {
/// Seed for the [User] used to in PDA generation
pub const SEED_PREFIX: &'static str = "USER";

/// Create a new user, convert the name into bytes
/// and add those bytes to a 64 byte array
pub fn new(name: &str) -> CloseAccountResult<Self> {
let name_bytes = name.as_bytes();

Self::check_length(name_bytes)?;

let mut name = [0u8; 64];
name[0..name_bytes.len()].copy_from_slice(name_bytes);

Ok(Self { name })
}

/// Converts the byte array into a UTF-8 [str]
/// using the `trim_end_matches("\0")` of [str] method
/// to remove padded zeroes if any. Padded zeroes are
/// represented by `\0`
pub fn to_string(&self) -> CloseAccountResult<String> {
let value =
core::str::from_utf8(&self.name).map_err(|_| CloseAccountError::OnlyUtf8IsSupported)?;

Ok(value.trim_end_matches("\0").to_string())
}

fn check_length(bytes: &[u8]) -> CloseAccountResult<()> {
if bytes.len() > 64 {
return Err(CloseAccountError::MaxNameLengthExceeded);
}

Ok(())
}

/// Generate a PDA from the [Self::SEED_PREFIX] constant
/// and the payer public key. This returns a tuple struct
/// ([Pubkey], [u8])
pub fn pda(payer: Pubkey) -> (Pubkey, u8) {
Pubkey::try_find_program_address(
&[Self::SEED_PREFIX.as_bytes(), payer.as_ref()],
&crate::id(),
)
.unwrap()
}
}

account!(UserAccount, User);
8 changes: 8 additions & 0 deletions basics/close-account/steel/cicd.sh
Original file line number Diff line number Diff line change
@@ -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-sbf --manifest-path=./program/Cargo.toml --bpf-out-dir=./program/target/so
solana program deploy ./program/target/so/program.so
18 changes: 18 additions & 0 deletions basics/close-account/steel/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"scripts": {
"test": "pnpm ts-mocha -p ./tests/tsconfig.test.json -t 1000000 ./tests/close-account.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=./target/so",
"deploy": "solana program deploy ./target/so/close_account_program.so"
},
"dependencies": {
"@coral-xyz/borsh": "^0.30.1",
"@solana/web3.js": "^1.35"
},
"devDependencies": {
"solana-bankrun": "^0.4.0",
"typescript": "^5.6.3",
"ts-mocha": "^10.0.0"
},
"packageManager": "[email protected]+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1"
}
Loading

0 comments on commit 2f244bf

Please sign in to comment.