Forráskód Böngészése

add basics/rent/steel (#240)

Sabir Khan 9 hónapja
szülő
commit
85082a332b

+ 2 - 0
basics/rent/steel/.gitignore

@@ -0,0 +1,2 @@
+target
+test-ledger

+ 21 - 0
basics/rent/steel/Cargo.toml

@@ -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 - 0
basics/rent/steel/README.md

@@ -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 - 0
basics/rent/steel/api/Cargo.toml

@@ -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 - 0
basics/rent/steel/api/src/error.rs

@@ -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 - 0
basics/rent/steel/api/src/instruction.rs

@@ -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 - 0
basics/rent/steel/api/src/lib.rs

@@ -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 - 0
basics/rent/steel/api/src/sdk.rs

@@ -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 - 0
basics/rent/steel/api/src/state/address.rs

@@ -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 - 0
basics/rent/steel/api/src/state/mod.rs

@@ -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 - 0
basics/rent/steel/package.json

@@ -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 - 0
basics/rent/steel/program/Cargo.toml

@@ -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 - 0
basics/rent/steel/program/src/create_account.rs

@@ -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 - 0
basics/rent/steel/program/src/lib.rs

@@ -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 - 0
basics/rent/steel/program/tests/test.rs

@@ -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");
+}