structs.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::build_solidity;
  3. use indexmap::Equivalent;
  4. use soroban_sdk::{testutils::Address as _, Address, FromVal, IntoVal, Val};
  5. #[test]
  6. fn get_fields_via_dot() {
  7. let runtime = build_solidity(
  8. r#"
  9. contract locker {
  10. struct Lock {
  11. uint64 release_time;
  12. address beneficiary;
  13. uint64 amount;
  14. }
  15. mapping(address => Lock) locks;
  16. function create_lock(
  17. uint64 release_time,
  18. address beneficiary,
  19. uint64 amount
  20. ) public returns (uint64) {
  21. Lock memory l = Lock({
  22. release_time: release_time,
  23. beneficiary: beneficiary,
  24. amount: amount
  25. });
  26. locks[beneficiary] = l;
  27. return l.amount;
  28. }
  29. function get_lock_amount(address beneficiary) public view returns (uint64) {
  30. return locks[beneficiary].amount;
  31. }
  32. function get_lock_release(address beneficiary) public view returns (uint64) {
  33. return locks[beneficiary].release_time;
  34. }
  35. function get_lock_beneficiary(address key) public view returns (address) {
  36. return locks[key].beneficiary;
  37. }
  38. // Extended functionality: increase amount in-place and return new total
  39. function increase_lock_amount(address beneficiary, uint64 delta) public returns (uint64) {
  40. locks[beneficiary].amount += delta;
  41. return locks[beneficiary].amount;
  42. }
  43. // Extended functionality: move a lock to a different beneficiary
  44. function move_lock(address from, address to) public {
  45. Lock memory l = locks[from];
  46. require(l.amount != 0, "no lock");
  47. l.beneficiary = to;
  48. locks[to] = l;
  49. // emulate delete by zeroing fields
  50. locks[from].amount = 0;
  51. locks[from].release_time = 0;
  52. }
  53. // Extended functionality: clear lock for a beneficiary
  54. function clear_lock(address beneficiary) public {
  55. // emulate delete by zeroing fields
  56. locks[beneficiary].amount = 0;
  57. locks[beneficiary].release_time = 0;
  58. }
  59. }
  60. "#,
  61. |_| {},
  62. );
  63. let addr = runtime.contracts.last().unwrap();
  64. let user1 = Address::generate(&runtime.env);
  65. let user2 = Address::generate(&runtime.env);
  66. let release_time: Val = 1_000_u64.into_val(&runtime.env);
  67. let amount: Val = 500_u64.into_val(&runtime.env);
  68. // Create a new lock for user1
  69. let create_args = vec![release_time, user1.clone().into_val(&runtime.env), amount];
  70. let res = runtime.invoke_contract(addr, "create_lock", create_args);
  71. assert!(amount.shallow_eq(&res));
  72. // Verify getters
  73. let get_amt_args = vec![user1.clone().into_val(&runtime.env)];
  74. let get_rel_args = vec![user1.clone().into_val(&runtime.env)];
  75. let get_ben_args = vec![user1.clone().into_val(&runtime.env)];
  76. let got_amount = runtime.invoke_contract(addr, "get_lock_amount", get_amt_args);
  77. let got_release = runtime.invoke_contract(addr, "get_lock_release", get_rel_args);
  78. let got_beneficiary = runtime.invoke_contract(addr, "get_lock_beneficiary", get_ben_args);
  79. assert!(amount.shallow_eq(&got_amount));
  80. assert!(release_time.shallow_eq(&got_release));
  81. let addr_val = Address::from_val(&runtime.env, &got_beneficiary);
  82. assert!(addr_val.equivalent(&user1));
  83. // Increase amount and verify new total
  84. let delta: Val = 250_u64.into_val(&runtime.env);
  85. let inc_args = vec![user1.clone().into_val(&runtime.env), delta];
  86. let new_total = runtime.invoke_contract(addr, "increase_lock_amount", inc_args);
  87. let expected_total: Val = 750_u64.into_val(&runtime.env);
  88. assert!(expected_total.shallow_eq(&new_total));
  89. // Move lock from user1 to user2
  90. let move_args = vec![
  91. user1.clone().into_val(&runtime.env),
  92. user2.clone().into_val(&runtime.env),
  93. ];
  94. let _ = runtime.invoke_contract(addr, "move_lock", move_args);
  95. // After moving, user1 should have no lock (amount == 0)
  96. let zero: Val = 0_u64.into_val(&runtime.env);
  97. let amt_user1 = runtime.invoke_contract(
  98. addr,
  99. "get_lock_amount",
  100. vec![user1.clone().into_val(&runtime.env)],
  101. );
  102. assert!(zero.shallow_eq(&amt_user1));
  103. // And user2 should now hold the moved lock with the updated total amount
  104. let amt_user2 = runtime.invoke_contract(
  105. addr,
  106. "get_lock_amount",
  107. vec![user2.clone().into_val(&runtime.env)],
  108. );
  109. assert!(expected_total.shallow_eq(&amt_user2));
  110. // Beneficiary for user2's lock should be user2
  111. let ben_user2 = runtime.invoke_contract(
  112. addr,
  113. "get_lock_beneficiary",
  114. vec![user2.clone().into_val(&runtime.env)],
  115. );
  116. let ben2 = Address::from_val(&runtime.env, &ben_user2);
  117. assert!(ben2.equivalent(&user2));
  118. // Clear user2's lock and verify
  119. let _ = runtime.invoke_contract(
  120. addr,
  121. "clear_lock",
  122. vec![user2.clone().into_val(&runtime.env)],
  123. );
  124. let amt_user2_after_clear =
  125. runtime.invoke_contract(addr, "get_lock_amount", vec![user2.into_val(&runtime.env)]);
  126. assert!(zero.shallow_eq(&amt_user2_after_clear));
  127. }
  128. // Removed: keep only two tests as requested
  129. #[test]
  130. fn get_whole_struct() {
  131. let runtime = build_solidity(
  132. r#"
  133. contract locker {
  134. struct Lock {
  135. uint64 release_time;
  136. address beneficiary;
  137. uint64 amount;
  138. }
  139. mapping(address => Lock) locks;
  140. function create_lock(
  141. uint64 release_time,
  142. address beneficiary,
  143. uint64 amount
  144. ) public returns (uint64) {
  145. Lock memory l = Lock({
  146. release_time: release_time,
  147. beneficiary: beneficiary,
  148. amount: amount
  149. });
  150. locks[beneficiary] = l;
  151. return l.amount;
  152. }
  153. function get_lock_amount(address beneficiary) public view returns (uint64) {
  154. return locks[beneficiary].amount;
  155. }
  156. function get_lock_release(address beneficiary) public view returns (uint64) {
  157. return locks[beneficiary].release_time;
  158. }
  159. function get_lock_beneficiary(address key) public view returns (address) {
  160. return locks[key].beneficiary;
  161. }
  162. }
  163. "#,
  164. |_| {},
  165. );
  166. let addr = runtime.contracts.last().unwrap();
  167. let user = Address::generate(&runtime.env);
  168. let release_time: Val = 42_u64.into_val(&runtime.env);
  169. let amount: Val = 7_u64.into_val(&runtime.env);
  170. // Create lock
  171. let _ = runtime.invoke_contract(
  172. addr,
  173. "create_lock",
  174. vec![release_time, user.clone().into_val(&runtime.env), amount],
  175. );
  176. // Retrieve each field via accessors (no multiple returns)
  177. let rt_val = runtime.invoke_contract(
  178. addr,
  179. "get_lock_release",
  180. vec![user.clone().into_val(&runtime.env)],
  181. );
  182. let ben_val = runtime.invoke_contract(
  183. addr,
  184. "get_lock_beneficiary",
  185. vec![user.clone().into_val(&runtime.env)],
  186. );
  187. let amt_val = runtime.invoke_contract(
  188. addr,
  189. "get_lock_amount",
  190. vec![user.clone().into_val(&runtime.env)],
  191. );
  192. let rt: u64 = FromVal::from_val(&runtime.env, &rt_val);
  193. let ben = Address::from_val(&runtime.env, &ben_val);
  194. let amt: u64 = FromVal::from_val(&runtime.env, &amt_val);
  195. assert_eq!(rt, 42);
  196. assert!(ben.equivalent(&user));
  197. assert_eq!(amt, 7);
  198. }