// SPDX-License-Identifier: Apache-2.0 use crate::build_solidity; use indexmap::Equivalent; use soroban_sdk::{testutils::Address as _, Address, FromVal, IntoVal, Val}; #[test] fn get_fields_via_dot() { let runtime = build_solidity( r#" contract locker { struct Lock { uint64 release_time; address beneficiary; uint64 amount; } mapping(address => Lock) locks; function create_lock( uint64 release_time, address beneficiary, uint64 amount ) public returns (uint64) { Lock memory l = Lock({ release_time: release_time, beneficiary: beneficiary, amount: amount }); locks[beneficiary] = l; return l.amount; } function get_lock_amount(address beneficiary) public view returns (uint64) { return locks[beneficiary].amount; } function get_lock_release(address beneficiary) public view returns (uint64) { return locks[beneficiary].release_time; } function get_lock_beneficiary(address key) public view returns (address) { return locks[key].beneficiary; } // Extended functionality: increase amount in-place and return new total function increase_lock_amount(address beneficiary, uint64 delta) public returns (uint64) { locks[beneficiary].amount += delta; return locks[beneficiary].amount; } // Extended functionality: move a lock to a different beneficiary function move_lock(address from, address to) public { Lock memory l = locks[from]; require(l.amount != 0, "no lock"); l.beneficiary = to; locks[to] = l; // emulate delete by zeroing fields locks[from].amount = 0; locks[from].release_time = 0; } // Extended functionality: clear lock for a beneficiary function clear_lock(address beneficiary) public { // emulate delete by zeroing fields locks[beneficiary].amount = 0; locks[beneficiary].release_time = 0; } } "#, |_| {}, ); let addr = runtime.contracts.last().unwrap(); let user1 = Address::generate(&runtime.env); let user2 = Address::generate(&runtime.env); let release_time: Val = 1_000_u64.into_val(&runtime.env); let amount: Val = 500_u64.into_val(&runtime.env); // Create a new lock for user1 let create_args = vec![release_time, user1.clone().into_val(&runtime.env), amount]; let res = runtime.invoke_contract(addr, "create_lock", create_args); assert!(amount.shallow_eq(&res)); // Verify getters let get_amt_args = vec![user1.clone().into_val(&runtime.env)]; let get_rel_args = vec![user1.clone().into_val(&runtime.env)]; let get_ben_args = vec![user1.clone().into_val(&runtime.env)]; let got_amount = runtime.invoke_contract(addr, "get_lock_amount", get_amt_args); let got_release = runtime.invoke_contract(addr, "get_lock_release", get_rel_args); let got_beneficiary = runtime.invoke_contract(addr, "get_lock_beneficiary", get_ben_args); assert!(amount.shallow_eq(&got_amount)); assert!(release_time.shallow_eq(&got_release)); let addr_val = Address::from_val(&runtime.env, &got_beneficiary); assert!(addr_val.equivalent(&user1)); // Increase amount and verify new total let delta: Val = 250_u64.into_val(&runtime.env); let inc_args = vec![user1.clone().into_val(&runtime.env), delta]; let new_total = runtime.invoke_contract(addr, "increase_lock_amount", inc_args); let expected_total: Val = 750_u64.into_val(&runtime.env); assert!(expected_total.shallow_eq(&new_total)); // Move lock from user1 to user2 let move_args = vec![ user1.clone().into_val(&runtime.env), user2.clone().into_val(&runtime.env), ]; let _ = runtime.invoke_contract(addr, "move_lock", move_args); // After moving, user1 should have no lock (amount == 0) let zero: Val = 0_u64.into_val(&runtime.env); let amt_user1 = runtime.invoke_contract( addr, "get_lock_amount", vec![user1.clone().into_val(&runtime.env)], ); assert!(zero.shallow_eq(&amt_user1)); // And user2 should now hold the moved lock with the updated total amount let amt_user2 = runtime.invoke_contract( addr, "get_lock_amount", vec![user2.clone().into_val(&runtime.env)], ); assert!(expected_total.shallow_eq(&amt_user2)); // Beneficiary for user2's lock should be user2 let ben_user2 = runtime.invoke_contract( addr, "get_lock_beneficiary", vec![user2.clone().into_val(&runtime.env)], ); let ben2 = Address::from_val(&runtime.env, &ben_user2); assert!(ben2.equivalent(&user2)); // Clear user2's lock and verify let _ = runtime.invoke_contract( addr, "clear_lock", vec![user2.clone().into_val(&runtime.env)], ); let amt_user2_after_clear = runtime.invoke_contract(addr, "get_lock_amount", vec![user2.into_val(&runtime.env)]); assert!(zero.shallow_eq(&amt_user2_after_clear)); } // Removed: keep only two tests as requested #[test] fn get_whole_struct() { let runtime = build_solidity( r#" contract locker { struct Lock { uint64 release_time; address beneficiary; uint64 amount; } mapping(address => Lock) locks; function create_lock( uint64 release_time, address beneficiary, uint64 amount ) public returns (uint64) { Lock memory l = Lock({ release_time: release_time, beneficiary: beneficiary, amount: amount }); locks[beneficiary] = l; return l.amount; } function get_lock_amount(address beneficiary) public view returns (uint64) { return locks[beneficiary].amount; } function get_lock_release(address beneficiary) public view returns (uint64) { return locks[beneficiary].release_time; } function get_lock_beneficiary(address key) public view returns (address) { return locks[key].beneficiary; } } "#, |_| {}, ); let addr = runtime.contracts.last().unwrap(); let user = Address::generate(&runtime.env); let release_time: Val = 42_u64.into_val(&runtime.env); let amount: Val = 7_u64.into_val(&runtime.env); // Create lock let _ = runtime.invoke_contract( addr, "create_lock", vec![release_time, user.clone().into_val(&runtime.env), amount], ); // Retrieve each field via accessors (no multiple returns) let rt_val = runtime.invoke_contract( addr, "get_lock_release", vec![user.clone().into_val(&runtime.env)], ); let ben_val = runtime.invoke_contract( addr, "get_lock_beneficiary", vec![user.clone().into_val(&runtime.env)], ); let amt_val = runtime.invoke_contract( addr, "get_lock_amount", vec![user.clone().into_val(&runtime.env)], ); let rt: u64 = FromVal::from_val(&runtime.env, &rt_val); let ben = Address::from_val(&runtime.env, &ben_val); let amt: u64 = FromVal::from_val(&runtime.env, &amt_val); assert_eq!(rt, 42); assert!(ben.equivalent(&user)); assert_eq!(amt, 7); }