use neo_vm_core::{
NativeContract, NativeRegistry, NeoVM, StackItem, StorageBackend, StorageContext,
TrackedStorage, VMState,
};
fn main() {
println!("=== Neo zkVM Token Contract Example ===\n");
let mut storage = TrackedStorage::new();
let ctx = StorageContext::default();
let owner = b"owner_address_1234";
let alice = b"alice_address_1234";
let bob = b"bob_address_1234";
println!("--- Part 1: Token Initialization ---\n");
let initial_supply: u64 = 1_000_000_000 * 10u64.pow(8); storage.put(
&ctx,
&[b"balance:", owner.as_slice()].concat(),
&initial_supply.to_le_bytes(),
);
storage.put(&ctx, b"total_supply", &initial_supply.to_le_bytes());
storage.put(&ctx, b"symbol", b"NEOX");
storage.put(&ctx, b"decimals", &[8u8]);
println!("Token: NEOX (8 decimals)");
println!("Initial supply: {} NEOX", format_tokens(initial_supply, 8));
println!("Minted to: {:?}", String::from_utf8_lossy(owner));
println!("\n--- Part 2: Token Transfer ---\n");
let transfer_amount: u64 = 10 * 10u64.pow(8);
let owner_balance = get_balance(&storage, &ctx, owner);
println!(
"Owner balance before: {} NEOX",
format_tokens(owner_balance, 8)
);
let new_owner_balance = owner_balance
.checked_sub(transfer_amount)
.expect("Insufficient balance");
let alice_balance = get_balance(&storage, &ctx, alice)
.checked_add(transfer_amount)
.expect("Balance overflow");
storage.put(
&ctx,
&[b"balance:", owner.as_slice()].concat(),
&new_owner_balance.to_le_bytes(),
);
storage.put(
&ctx,
&[b"balance:", alice.as_slice()].concat(),
&alice_balance.to_le_bytes(),
);
record_transfer(&mut storage, &ctx, owner, alice, transfer_amount);
println!("Transferred: {} NEOX", format_tokens(transfer_amount, 8));
println!(" From: {:?}", String::from_utf8_lossy(owner));
println!(" To: {:?}", String::from_utf8_lossy(alice));
println!("\n--- Part 3: Balance Check ---\n");
println!(
"Owner balance: {} NEOX",
format_tokens(get_balance(&storage, &ctx, owner), 8)
);
println!(
"Alice balance: {} NEOX",
format_tokens(get_balance(&storage, &ctx, alice), 8)
);
println!(
"Bob balance: {} NEOX",
format_tokens(get_balance(&storage, &ctx, bob), 8)
);
println!("\n--- Part 4: VM Contract Execution ---\n");
let mut vm = NeoVM::new(1_000_000);
let verification_script = vec![
0x15, 0x12, 0xB8, 0x40, ];
vm.load_script(verification_script).unwrap();
while !matches!(vm.state, VMState::Halt | VMState::Fault) {
vm.execute_next().unwrap();
}
if let Some(StackItem::Boolean(valid)) = vm.eval_stack.pop() {
println!(
"Transfer validation: {}",
if valid { "VALID ✓" } else { "INVALID ✗" }
);
}
println!("\n--- Part 5: State Verification ---\n");
let merkle_root = storage.merkle_root();
println!("State Merkle root: 0x{}", hex::encode(&merkle_root[..8]));
println!("\nRecorded changes:");
for (i, change) in storage.changes().iter().enumerate() {
let key = String::from_utf8_lossy(&change.key);
println!(" {}. Key: {}", i + 1, key);
if let Some(old) = &change.old_value {
println!(" Old: {} bytes", old.len());
}
if let Some(new) = &change.new_value {
println!(" New: {} bytes", new.len());
}
}
println!("\n--- Part 6: Crypto Verification ---\n");
let registry = NativeRegistry::new();
let stdlib_hash = registry.get_stdlib_hash();
let cryptolib_hash = registry.get_cryptolib_hash();
println!("StdLib contract: 0x{}", hex::encode(stdlib_hash));
println!("CryptoLib contract: 0x{}", hex::encode(cryptolib_hash));
let test_data = b"transfer_signature_data";
let _hash_result = neo_vm_core::CryptoLib::new()
.invoke("sha256", vec![StackItem::ByteString(test_data.to_vec())]);
println!("Signature verification: ready (data hashed)");
println!("\n=== Token Contract Example Complete ===");
}
fn get_balance(storage: &TrackedStorage, ctx: &StorageContext, address: &[u8]) -> u64 {
let key = [b"balance:", address].concat();
match storage.get(ctx, &key) {
Some(bytes) if bytes.len() == 8 => u64::from_le_bytes(bytes.try_into().unwrap()),
_ => 0,
}
}
fn record_transfer(
storage: &mut TrackedStorage,
ctx: &StorageContext,
from: &[u8],
to: &[u8],
amount: u64,
) {
let transfer_key = b"last_transfer".to_vec();
let transfer_data = [
from,
b"->".as_slice(),
to,
b":".as_slice(),
&amount.to_le_bytes(),
]
.concat();
storage.put(ctx, &transfer_key, &transfer_data);
}
fn format_tokens(amount: u64, decimals: u32) -> String {
let divisor = 10u64.pow(decimals);
let whole = amount / divisor;
let frac = amount % divisor;
format!("{}.{:08}", whole, frac)
}
pub trait NativeRegistryExt {
fn get_stdlib_hash(&self) -> [u8; 20];
fn get_cryptolib_hash(&self) -> [u8; 20];
}
impl NativeRegistryExt for NativeRegistry {
fn get_stdlib_hash(&self) -> [u8; 20] {
[
0xac, 0xce, 0x6f, 0xd8, 0x0d, 0x44, 0xe1, 0xa3, 0x92, 0x6d, 0xe2, 0x1c, 0xcf, 0x30,
0x96, 0x9a, 0x22, 0x4b, 0xc0, 0x6b,
]
}
fn get_cryptolib_hash(&self) -> [u8; 20] {
[
0x72, 0x6c, 0xb6, 0xe0, 0xcd, 0x8b, 0x0a, 0xc3, 0x3c, 0xe1, 0xde, 0xc0, 0xd4, 0x7e,
0x5c, 0x3c, 0x4a, 0x6b, 0x8a, 0x0d,
]
}
}