Skip to content

Commit

Permalink
add basics/rent/steel (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
simplysabir authored Jan 2, 2025
1 parent dd771c7 commit 85082a3
Show file tree
Hide file tree
Showing 15 changed files with 359 additions and 0 deletions.
2 changes: 2 additions & 0 deletions basics/rent/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/rent/steel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[workspace]
resolver = "2"
members = ["api", "program"]

[workspace.package]
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
homepage = ""
documentation = ""
repository = ""
readme = "./README.md"
keywords = ["solana"]

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

**RentExample** 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
- [`Add`](program/src/add.rs) – Add ...
- [`Initialize`](program/src/initialize.rs) – Initialize ...

## State
- [`Counter`](api/src/state/counter.rs) – Counter ...

## Get started

Compile your program:
```sh
steel build
```

Run unit and integration tests:
```sh
steel test
```
18 changes: 18 additions & 0 deletions basics/rent/steel/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "rent_example-api"
description = "API for interacting with the RentExample program"
version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
documentation.workspace = true
repository.workspace = true
readme.workspace = true
keywords.workspace = true

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

#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)]
#[repr(u32)]
pub enum RentExampleError {
#[error("This is a dummy error")]
Dummy = 0,
}

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

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
pub enum RentInstruction {
CreateSystemAccount = 0,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct CreateSystemAccount {
pub name: [u8; 32],
pub address: [u8; 64],
}

instruction!(RentInstruction, CreateSystemAccount);
21 changes: 21 additions & 0 deletions basics/rent/steel/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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::*;
// Re-export common solana dependencies
pub use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey, rent::Rent,
system_program,
};
}

use steel::*;

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

pub fn create_system_account(
payer: Pubkey,
new_account: Pubkey,
name: String,
address: String,
) -> Instruction {
let mut name_bytes = [0u8; 32];
let mut address_bytes = [0u8; 64];

name_bytes[..name.len()].copy_from_slice(name.as_bytes());
address_bytes[..address.len()].copy_from_slice(address.as_bytes());

Instruction {
program_id: crate::ID,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new(new_account, true),
AccountMeta::new_readonly(system_program::ID, false),
],
data: CreateSystemAccount {
name: name_bytes,
address: address_bytes,
}
.to_bytes(),
}
}
11 changes: 11 additions & 0 deletions basics/rent/steel/api/src/state/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use super::RentAccount;
use steel::*;

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct Address {
pub name: [u8; 32],
pub address: [u8; 64],
}

account!(RentAccount, Address);
11 changes: 11 additions & 0 deletions basics/rent/steel/api/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod address;

pub use address::*;

use steel::*;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
pub enum RentAccount {
Address = 0,
}
14 changes: 14 additions & 0 deletions basics/rent/steel/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "rent-example",
"version": "1.0.0",
"description": "rent example with steel framework for solana",
"scripts": {
"test": "cargo test-sbf",
"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/rent_example_program.so"
},
"keywords": [],
"author": "Sabir Khan",
"license": "ISC"
}
26 changes: 26 additions & 0 deletions basics/rent/steel/program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "rent_example-program"
description = ""
version.workspace = true
edition.workspace = true
license.workspace = true
homepage.workspace = true
documentation.workspace = true
repository.workspace = true
readme.workspace = true
keywords.workspace = true

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
rent_example-api.workspace = true
solana-program.workspace = true
steel.workspace = true

[dev-dependencies]
base64 = "0.21"
rand = "0.8.5"
solana-program-test = "1.18"
solana-sdk = "1.18"
tokio = { version = "1.35", features = ["full"] }
36 changes: 36 additions & 0 deletions basics/rent/steel/program/src/create_account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use rent_example_api::prelude::*;
use solana_program::msg;
use steel::*;

pub fn process_create_account(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
let args = CreateSystemAccount::try_from_bytes(data)?;

let [payer_info, new_account_info, system_program] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};

payer_info.is_signer()?;
new_account_info.is_signer()?;

let account_size = std::mem::size_of::<Address>();
let rent = Rent::get()?;
let lamports_required = rent.minimum_balance(account_size);

msg!("Account size: {}", account_size);
msg!("Lamports required: {}", lamports_required);

// Create account with correct type argument
create_account::<Address>(
new_account_info,
system_program,
payer_info,
&rent_example_api::ID,
&[],
)?;

let address = new_account_info.as_account_mut::<Address>(&rent_example_api::ID)?;
address.name = args.name;
address.address = args.address;

Ok(())
}
21 changes: 21 additions & 0 deletions basics/rent/steel/program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
mod create_account;

use create_account::*;
use rent_example_api::prelude::*;
use steel::*;

pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let (ix, data) = parse_instruction::<RentInstruction>(&rent_example_api::ID, program_id, data)?;

match ix {
RentInstruction::CreateSystemAccount => process_create_account(accounts, data)?,
}

Ok(())
}

entrypoint!(process_instruction);
95 changes: 95 additions & 0 deletions basics/rent/steel/program/tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use rent_example_api::prelude::*;
use solana_program::hash::Hash;
use solana_program_test::{processor, BanksClient, ProgramTest};
use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction};
use steel::*;

async fn setup() -> (BanksClient, Keypair, Hash) {
let mut program_test = ProgramTest::new(
"rent_example_program",
rent_example_api::ID,
processor!(rent_example_program::process_instruction),
);
program_test.prefer_bpf(true);
program_test.start().await
}

#[tokio::test]
async fn test_create_system_account() {
// Setup test environment
let (mut banks_client, payer, recent_blockhash) = setup().await;

// Generate a new keypair for the account we'll create
let new_account = Keypair::new();

// Test data
let name = "John Doe";
let address = "123 Blockchain Street";

// Create the instruction
let ix = create_system_account(
payer.pubkey(),
new_account.pubkey(),
name.to_string(),
address.to_string(),
);

// Create and send transaction
let mut transaction = Transaction::new_signed_with_payer(
&[ix],
Some(&payer.pubkey()),
&[&payer, &new_account],
recent_blockhash,
);

// Process transaction
let result = banks_client.process_transaction(transaction).await;
assert!(
result.is_ok(),
"Failed to process transaction: {:?}",
result
);

// Fetch and verify the created account
let account = banks_client
.get_account(new_account.pubkey())
.await
.expect("Failed to get account")
.expect("Account not found");

// Verify account owner
assert_eq!(
account.owner,
rent_example_api::ID,
"Incorrect account owner"
);

// Deserialize and verify account data
let address_account =
Address::try_from_bytes(&account.data).expect("Failed to deserialize account data");

// Convert stored bytes back to strings for comparison
let stored_name = String::from_utf8(
address_account
.name
.iter()
.take_while(|&&b| b != 0)
.cloned()
.collect::<Vec<u8>>(),
)
.unwrap();

let stored_address = String::from_utf8(
address_account
.address
.iter()
.take_while(|&&b| b != 0)
.cloned()
.collect::<Vec<u8>>(),
)
.unwrap();

// Verify the stored data matches what we sent
assert_eq!(stored_name, name, "Stored name doesn't match");
assert_eq!(stored_address, address, "Stored address doesn't match");
}

0 comments on commit 85082a3

Please sign in to comment.