Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basics/rent/steel #240

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
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);
25 changes: 25 additions & 0 deletions basics/rent/steel/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pub mod error;
pub mod instruction;
pub mod state;
pub mod sdk;

pub mod prelude {
pub use crate::error::*;
pub use crate::instruction::*;
pub use crate::state::*;
pub use crate::sdk::*;
// 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");
28 changes: 28 additions & 0 deletions basics/rent/steel/api/src/sdk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use steel::*;
use crate::prelude::*;

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 steel::*;
use super::RentAccount;

#[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);
83 changes: 83 additions & 0 deletions basics/rent/steel/program/tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
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");
}
Loading