use std::fs;
use std::path::Path;
use clap::Parser;
use hiero_sdk::{
AccountCreateTransaction, AccountId, Client, ContractCreateTransaction, ContractId, EvmHook, EvmHookSpec, EvmHookStorageSlot, EvmHookStorageUpdate, Hbar, HookCreationDetails, HookEntityId, HookExtensionPoint, HookId, HookStoreTransaction, PrivateKey
};
#[derive(Parser, Debug)]
struct Args {
#[clap(long, env, default_value = "0.0.2")]
operator_account_id: AccountId,
#[clap(
long,
env,
default_value = "302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137"
)]
operator_key: PrivateKey,
#[clap(long, env, default_value = "testnet")]
hedera_network: String,
}
const HOOK_BYTECODE_PATH: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/examples/lambda_sstore_hook.hex"
);
fn load_hook_bytecode() -> anyhow::Result<Vec<u8>> {
let bytecode_hex = fs::read_to_string(Path::new(HOOK_BYTECODE_PATH))?;
let cleaned_hex: String = bytecode_hex.split_whitespace().collect();
let bytecode = hex::decode(cleaned_hex)?;
Ok(bytecode)
}
async fn create_hook_contract(
client: &Client,
operator_key: &PrivateKey,
) -> anyhow::Result<ContractId> {
let bytecode = load_hook_bytecode()?;
let receipt = ContractCreateTransaction::new()
.admin_key(operator_key.public_key())
.gas(500_000)
.bytecode(bytecode)
.freeze_with(client)?
.sign(operator_key.clone())
.execute(client)
.await?
.get_receipt(client)
.await?;
Ok(receipt.contract_id.unwrap())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let _ = dotenvy::dotenv();
let args = Args::parse();
let client = Client::for_name(&args.hedera_network)?;
client.set_operator(args.operator_account_id, args.operator_key.clone());
println!("Lambda SStore Example Start!");
println!("Setting up prerequisites...");
println!("Creating hook contract...");
let contract_id = create_hook_contract(&client, &args.operator_key).await?;
println!("Hook contract created with ID: {contract_id}");
println!("Creating account with lambda hook...");
let account_key = PrivateKey::generate_ed25519();
let account_public_key = account_key.public_key();
let spec = EvmHookSpec::new(Some(contract_id));
let lambda_hook = EvmHook::new(spec, vec![]);
let admin_key = client.get_operator_public_key().unwrap();
let mut hook_details = HookCreationDetails::new(
HookExtensionPoint::AccountAllowanceHook,
1,
Some(lambda_hook),
);
hook_details.admin_key = Some(admin_key.into());
let account_receipt = AccountCreateTransaction::new()
.set_key_without_alias(account_public_key)
.initial_balance(Hbar::new(1))
.add_hook(hook_details)
.freeze_with(&client)?
.sign(args.operator_key.clone())
.execute(&client)
.await?
.get_receipt(&client)
.await?;
let account_id = account_receipt.account_id.unwrap();
println!("account id = {account_id}");
println!("Successfully created account with lambda hook!");
println!("\n=== HookStoreTransaction Example ===");
let mut storage_key = vec![0u8; 1];
storage_key.fill(1);
let mut storage_value = vec![0u8; 32];
storage_value.fill(200);
let storage_slot = EvmHookStorageSlot::new(storage_key.clone(), storage_value.clone());
let storage_update = EvmHookStorageUpdate::StorageSlot(storage_slot);
let hook_entity_id = HookEntityId::new(Some(account_id));
let hook_id = HookId::new(Some(hook_entity_id.clone()), 1);
println!("Storage update created:");
println!(" Storage Key: {:?}", storage_key);
println!(" Storage Value: {:?}", storage_value);
println!(" Hook ID: {}", hook_id.hook_id);
if let Some(entity_id) = &hook_id.entity_id {
if let Some(acc_id) = &entity_id.account_id {
println!(" Hook Entity ID: {acc_id}");
}
}
println!("Executing HookStoreTransaction...");
let lambda_store_receipt = HookStoreTransaction::new()
.set_hook_id(hook_id)
.add_storage_update(storage_update)
.freeze_with(&client)?
.sign(account_key)
.execute(&client)
.await?
.get_receipt(&client)
.await?;
println!("Successfully updated lambda hook storage!");
println!("Transaction completed successfully!");
println!("Receipt status: {:?}", lambda_store_receipt.status);
println!("\nLambda SStore Example Complete!");
Ok(())
}