@layerzerolabs/protocol-stellar-v2 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/.turbo/turbo-build.log +443 -302
  2. package/.turbo/turbo-lint.log +118 -96
  3. package/.turbo/turbo-test.log +853 -731
  4. package/Cargo.lock +120 -37
  5. package/Cargo.toml +8 -5
  6. package/contracts/common-macros/src/contract_impl.rs +44 -0
  7. package/contracts/common-macros/src/lib.rs +86 -40
  8. package/contracts/common-macros/src/ownable.rs +24 -32
  9. package/contracts/common-macros/src/storage.rs +95 -120
  10. package/contracts/common-macros/src/tests/contract_impl.rs +289 -0
  11. package/contracts/common-macros/src/tests/mod.rs +9 -0
  12. package/contracts/common-macros/src/tests/ownable.rs +151 -0
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap +85 -0
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +30 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap +9 -0
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__storage__snapshot_generated_storage_code.snap +1072 -0
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +45 -0
  18. package/contracts/common-macros/src/tests/storage.rs +485 -0
  19. package/contracts/common-macros/src/tests/test_helpers.rs +93 -0
  20. package/contracts/common-macros/src/tests/ttl_configurable.rs +34 -0
  21. package/contracts/common-macros/src/ttl_configurable.rs +31 -14
  22. package/contracts/common-macros/src/utils.rs +27 -0
  23. package/contracts/endpoint-v2/ARCHITECTURE.md +4 -4
  24. package/contracts/endpoint-v2/src/endpoint_v2.rs +18 -15
  25. package/contracts/endpoint-v2/src/interfaces/message_lib.rs +2 -3
  26. package/contracts/endpoint-v2/src/interfaces/message_lib_manager.rs +5 -3
  27. package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +2 -2
  28. package/contracts/endpoint-v2/src/interfaces/messaging_composer.rs +2 -2
  29. package/contracts/endpoint-v2/src/interfaces/send_lib.rs +4 -4
  30. package/contracts/endpoint-v2/src/lib.rs +6 -5
  31. package/contracts/endpoint-v2/src/message_lib_manager.rs +14 -6
  32. package/contracts/endpoint-v2/src/messaging_channel.rs +6 -2
  33. package/contracts/endpoint-v2/src/messaging_composer.rs +6 -2
  34. package/contracts/endpoint-v2/src/storage.rs +10 -7
  35. package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +16 -16
  36. package/contracts/endpoint-v2/src/tests/endpoint_v2/ttl_config.rs +46 -46
  37. package/contracts/endpoint-v2/src/tests/mock.rs +2 -2
  38. package/contracts/endpoint-v2/src/util.rs +8 -2
  39. package/contracts/message-libs/block-message-lib/Cargo.toml +1 -0
  40. package/contracts/message-libs/block-message-lib/src/lib.rs +5 -5
  41. package/contracts/message-libs/message-lib-common/src/errors.rs +8 -8
  42. package/contracts/message-libs/message-lib-common/src/interfaces/dvn.rs +0 -1
  43. package/contracts/message-libs/message-lib-common/src/interfaces/mod.rs +3 -3
  44. package/contracts/message-libs/message-lib-common/src/lib.rs +0 -2
  45. package/contracts/message-libs/message-lib-common/src/packet_codec_v1.rs +4 -6
  46. package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1.rs +2 -2
  47. package/contracts/message-libs/message-lib-common/src/tests/worker_options.rs +11 -11
  48. package/contracts/message-libs/message-lib-common/src/worker_options.rs +10 -16
  49. package/contracts/message-libs/simple-message-lib/src/errors.rs +0 -4
  50. package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +49 -34
  51. package/contracts/message-libs/simple-message-lib/src/storage.rs +3 -7
  52. package/contracts/message-libs/simple-message-lib/src/test.rs +3 -3
  53. package/contracts/message-libs/treasury/src/storage.rs +1 -2
  54. package/contracts/message-libs/treasury/src/tests/setup.rs +3 -2
  55. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +0 -13
  56. package/contracts/message-libs/treasury/src/treasury.rs +18 -21
  57. package/contracts/message-libs/uln-302/Cargo.toml +1 -0
  58. package/contracts/message-libs/uln-302/src/interfaces/mod.rs +4 -4
  59. package/contracts/message-libs/uln-302/src/interfaces/{receive.rs → receive_uln.rs} +3 -3
  60. package/contracts/message-libs/uln-302/src/interfaces/{send.rs → send_uln.rs} +8 -80
  61. package/contracts/message-libs/uln-302/src/lib.rs +5 -4
  62. package/contracts/message-libs/uln-302/src/{receive.rs → receive_uln.rs} +20 -12
  63. package/contracts/message-libs/uln-302/src/{send.rs → send_uln.rs} +19 -13
  64. package/contracts/message-libs/uln-302/src/storage.rs +1 -2
  65. package/contracts/message-libs/uln-302/src/tests/config/uln_config.rs +3 -2
  66. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +30 -30
  67. package/contracts/message-libs/uln-302/src/tests/setup.rs +12 -11
  68. package/contracts/message-libs/uln-302/src/tests/uln302/set_config.rs +1 -1
  69. package/contracts/message-libs/uln-302/src/{config_validation.rs → types.rs} +79 -11
  70. package/contracts/message-libs/uln-302/src/uln302.rs +15 -10
  71. package/contracts/oapp-macros/Cargo.toml +2 -8
  72. package/contracts/oapp-macros/src/lib.rs +57 -311
  73. package/contracts/oapp-macros/src/oapp_core.rs +23 -32
  74. package/contracts/oapp-macros/src/oapp_full.rs +8 -2
  75. package/contracts/oapp-macros/src/oapp_options_type3.rs +21 -36
  76. package/contracts/oapp-macros/src/oapp_receiver.rs +38 -57
  77. package/contracts/oapp-macros/src/oapp_sender.rs +12 -14
  78. package/contracts/oapp-macros/src/util.rs +14 -10
  79. package/contracts/oapps/counter/Cargo.toml +2 -1
  80. package/contracts/oapps/counter/integration_tests/utils.rs +4 -4
  81. package/contracts/oapps/counter/src/codec.rs +8 -9
  82. package/contracts/oapps/counter/src/counter.rs +156 -147
  83. package/contracts/oapps/counter/src/storage.rs +1 -2
  84. package/contracts/oapps/counter/src/tests/test_codec.rs +5 -5
  85. package/contracts/oapps/counter/src/tests/test_counter.rs +11 -13
  86. package/contracts/oapps/oapp/Cargo.toml +1 -0
  87. package/contracts/oapps/oapp/src/errors.rs +1 -1
  88. package/contracts/oapps/oapp/src/lib.rs +3 -0
  89. package/contracts/oapps/oapp/src/macro_tests/mod.rs +1 -0
  90. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +312 -0
  91. package/contracts/oapps/oapp/src/oapp_core.rs +52 -53
  92. package/contracts/oapps/oapp/src/oapp_options_type3.rs +18 -28
  93. package/contracts/oapps/oapp/src/oapp_receiver.rs +82 -31
  94. package/contracts/oapps/oapp/src/oapp_sender.rs +55 -13
  95. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +16 -3
  96. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +33 -8
  97. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +6 -9
  98. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +28 -15
  99. package/contracts/oapps/oft/Cargo.toml +27 -0
  100. package/contracts/oapps/oft/integration-tests/mod.rs +3 -0
  101. package/contracts/oapps/oft/integration-tests/setup.rs +320 -0
  102. package/contracts/oapps/oft/integration-tests/test_with_sml.rs +155 -0
  103. package/contracts/oapps/oft/integration-tests/utils.rs +201 -0
  104. package/contracts/oapps/oft/src/codec/mod.rs +2 -0
  105. package/contracts/oapps/oft/src/codec/oft_compose_msg_codec.rs +55 -0
  106. package/contracts/oapps/oft/src/codec/oft_msg_codec.rs +62 -0
  107. package/contracts/oapps/oft/src/constants.rs +5 -0
  108. package/contracts/oapps/oft/src/errors.rs +8 -0
  109. package/contracts/oapps/oft/src/events.rs +19 -0
  110. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +23 -0
  111. package/contracts/oapps/oft/src/interfaces/mod.rs +3 -0
  112. package/contracts/oapps/oft/src/lib.rs +22 -0
  113. package/contracts/oapps/oft/src/macro_tests/mod.rs +2 -0
  114. package/contracts/oapps/oft/src/macro_tests/test_all_default.rs +41 -0
  115. package/contracts/oapps/oft/src/macro_tests/test_override.rs +83 -0
  116. package/contracts/oapps/oft/src/oft.rs +320 -0
  117. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +50 -0
  118. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +50 -0
  119. package/contracts/oapps/oft/src/oft_types/mod.rs +10 -0
  120. package/contracts/oapps/oft/src/storage.rs +11 -0
  121. package/contracts/oapps/oft/src/tests/mod.rs +13 -0
  122. package/contracts/oapps/oft/src/tests/test_decimals.rs +89 -0
  123. package/contracts/oapps/oft/src/tests/test_lz_receive.rs +282 -0
  124. package/contracts/oapps/oft/src/tests/test_oft_compose_msg_codec.rs +68 -0
  125. package/contracts/oapps/oft/src/tests/test_oft_msg_codec.rs +136 -0
  126. package/contracts/oapps/oft/src/tests/test_oft_version.rs +13 -0
  127. package/contracts/oapps/oft/src/tests/test_quote_oft.rs +159 -0
  128. package/contracts/oapps/oft/src/tests/test_quote_send.rs +195 -0
  129. package/contracts/oapps/oft/src/tests/test_resolve_address.rs +37 -0
  130. package/contracts/oapps/oft/src/tests/test_send.rs +915 -0
  131. package/contracts/oapps/oft/src/tests/test_token.rs +47 -0
  132. package/contracts/oapps/oft/src/tests/test_utils.rs +789 -0
  133. package/contracts/oapps/oft/src/types.rs +38 -0
  134. package/contracts/oapps/oft/src/utils.rs +67 -0
  135. package/contracts/oapps/oft-mint-burn/Cargo.toml +26 -0
  136. package/contracts/oapps/oft-mint-burn/src/lib.rs +3 -0
  137. package/contracts/oapps/oft-mint-burn/src/oft.rs +28 -0
  138. package/contracts/oapps/oft-mint-burn/src/tests/mod.rs +1 -0
  139. package/contracts/utils/src/buffer_reader.rs +8 -9
  140. package/contracts/utils/src/buffer_writer.rs +11 -5
  141. package/contracts/utils/src/errors.rs +5 -5
  142. package/contracts/utils/src/ownable.rs +14 -6
  143. package/contracts/utils/src/testing_utils.rs +11 -1
  144. package/contracts/utils/src/tests/buffer_reader.rs +491 -730
  145. package/contracts/utils/src/tests/buffer_writer.rs +336 -148
  146. package/contracts/utils/src/tests/bytes_ext.rs +125 -40
  147. package/contracts/utils/src/tests/mod.rs +3 -0
  148. package/contracts/utils/src/tests/ownable.rs +379 -27
  149. package/contracts/utils/src/tests/test_helper.rs +47 -0
  150. package/contracts/utils/src/tests/testing_utils.rs +555 -0
  151. package/contracts/utils/src/tests/ttl.rs +421 -0
  152. package/contracts/utils/src/ttl.rs +29 -89
  153. package/contracts/workers/dvn/Cargo.toml +31 -0
  154. package/contracts/workers/dvn/src/auth.rs +66 -0
  155. package/contracts/workers/dvn/src/dvn.rs +143 -0
  156. package/contracts/workers/dvn/src/errors.rs +21 -0
  157. package/contracts/workers/dvn/src/events.rs +19 -0
  158. package/contracts/workers/dvn/src/interfaces/dvn.rs +12 -0
  159. package/contracts/workers/dvn/src/interfaces/mod.rs +5 -0
  160. package/contracts/workers/dvn/src/interfaces/multisig.rs +15 -0
  161. package/contracts/workers/dvn/src/lib.rs +24 -0
  162. package/contracts/workers/dvn/src/multisig.rs +127 -0
  163. package/contracts/workers/dvn/src/storage.rs +35 -0
  164. package/contracts/workers/dvn/src/tests/auth.rs +237 -0
  165. package/contracts/workers/dvn/src/tests/dvn.rs +349 -0
  166. package/contracts/workers/dvn/src/tests/key_pair.rs +66 -0
  167. package/contracts/workers/dvn/src/tests/mod.rs +5 -0
  168. package/contracts/workers/dvn/src/tests/multisig/mod.rs +3 -0
  169. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +133 -0
  170. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +108 -0
  171. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +109 -0
  172. package/contracts/workers/dvn/src/tests/setup.rs +109 -0
  173. package/contracts/workers/dvn/src/types.rs +26 -0
  174. package/contracts/workers/dvn-fee-lib/Cargo.toml +24 -0
  175. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +113 -0
  176. package/contracts/workers/dvn-fee-lib/src/errors.rs +8 -0
  177. package/contracts/workers/dvn-fee-lib/src/lib.rs +17 -0
  178. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +282 -0
  179. package/contracts/workers/dvn-fee-lib/src/tests/mod.rs +1 -0
  180. package/contracts/workers/executor/Cargo.toml +10 -7
  181. package/contracts/workers/executor/src/errors.rs +8 -0
  182. package/contracts/workers/executor/src/events.rs +4 -7
  183. package/contracts/workers/executor/src/interfaces/executor.rs +72 -22
  184. package/contracts/workers/executor/src/interfaces/mod.rs +0 -2
  185. package/contracts/workers/executor/src/lib.rs +16 -7
  186. package/contracts/workers/executor/src/lz_executor.rs +308 -0
  187. package/contracts/workers/executor/src/storage.rs +24 -16
  188. package/contracts/workers/executor-fee-lib/Cargo.toml +22 -0
  189. package/contracts/workers/executor-fee-lib/src/errors.rs +15 -0
  190. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +215 -0
  191. package/contracts/workers/executor-fee-lib/src/executor_option.rs +203 -0
  192. package/contracts/workers/executor-fee-lib/src/lib.rs +7 -0
  193. package/contracts/workers/executor-helper/Cargo.toml +29 -0
  194. package/contracts/workers/executor-helper/src/executor_helper.rs +161 -0
  195. package/contracts/workers/executor-helper/src/lib.rs +11 -0
  196. package/contracts/workers/{worker-common → worker}/Cargo.toml +1 -4
  197. package/contracts/workers/worker/src/errors.rs +24 -0
  198. package/contracts/workers/worker/src/events.rs +62 -0
  199. package/contracts/workers/worker/src/interfaces/dvn_fee_lib.rs +75 -0
  200. package/contracts/workers/worker/src/interfaces/executor_fee_lib.rs +84 -0
  201. package/contracts/workers/{worker-common → worker}/src/interfaces/mod.rs +2 -2
  202. package/contracts/workers/worker/src/interfaces/price_feed.rs +85 -0
  203. package/contracts/workers/worker/src/lib.rs +14 -0
  204. package/contracts/workers/worker/src/storage.rs +63 -0
  205. package/contracts/workers/worker/src/worker.rs +459 -0
  206. package/package.json +3 -3
  207. package/sdk/dist/generated/bml.d.ts +88 -17
  208. package/sdk/dist/generated/bml.js +62 -16
  209. package/sdk/dist/generated/counter.d.ts +281 -102
  210. package/sdk/dist/generated/counter.js +93 -41
  211. package/sdk/dist/generated/endpoint.d.ts +128 -105
  212. package/sdk/dist/generated/endpoint.js +47 -45
  213. package/sdk/dist/generated/sml.d.ts +212 -69
  214. package/sdk/dist/generated/sml.js +103 -53
  215. package/sdk/dist/generated/uln302.d.ts +270 -173
  216. package/sdk/dist/generated/uln302.js +112 -64
  217. package/sdk/package.json +11 -11
  218. package/sdk/test/index.test.ts +147 -42
  219. package/sdk/test/suites/constants.ts +7 -3
  220. package/sdk/test/suites/deploy.ts +65 -42
  221. package/sdk/test/suites/localnet.ts +2 -2
  222. package/sdk/test/suites/scan.ts +28 -25
  223. package/sdk/test/utils.ts +199 -0
  224. package/sdk/tsconfig.json +93 -95
  225. package/tools/ts-bindings-gen/src/main.rs +2 -0
  226. package/contracts/common-macros/src/snapshots/common_macros__tests__tests__snapshot_generated_storage_code.snap +0 -310
  227. package/contracts/common-macros/src/tests.rs +0 -287
  228. package/contracts/oapp-macros/tests/test_macros.rs +0 -522
  229. package/contracts/workers/executor/src/executor.rs +0 -347
  230. package/contracts/workers/executor/src/interfaces/types.rs +0 -51
  231. package/contracts/workers/worker-common/src/constants.rs +0 -17
  232. package/contracts/workers/worker-common/src/errors.rs +0 -6
  233. package/contracts/workers/worker-common/src/events.rs +0 -34
  234. package/contracts/workers/worker-common/src/interfaces/executor_fee_lib.rs +0 -35
  235. package/contracts/workers/worker-common/src/interfaces/price_feed.rs +0 -40
  236. package/contracts/workers/worker-common/src/interfaces/worker.rs +0 -60
  237. package/contracts/workers/worker-common/src/lib.rs +0 -19
  238. package/contracts/workers/worker-common/src/storage.rs +0 -32
  239. package/contracts/workers/worker-common/src/worker_common.rs +0 -166
@@ -0,0 +1,349 @@
1
+ use crate::tests::setup::TestSetup;
2
+ use crate::{dvn::Dvn, DVNClient, DstConfig, DstConfigParam, IDVN};
3
+ use endpoint_v2::FeeRecipient;
4
+ use message_lib_common::interfaces::ILayerZeroDVN;
5
+ use soroban_sdk::{
6
+ contract, contractimpl,
7
+ testutils::{Address as _, AuthorizedFunction},
8
+ vec, Address, Bytes, BytesN, Env, IntoVal, Symbol,
9
+ };
10
+ use worker::{DvnFeeParams, IDvnFeeLib, Worker};
11
+
12
+ fn with_contract<F, R>(setup: &TestSetup, f: F) -> R
13
+ where
14
+ F: FnOnce() -> R,
15
+ {
16
+ setup.env.as_contract(&setup.contract_id, f)
17
+ }
18
+
19
+ fn new_addr(env: &Env) -> Address {
20
+ Address::generate(env)
21
+ }
22
+
23
+ fn grant_allowlist(setup: &TestSetup, oapp: &Address) {
24
+ with_contract(setup, || {
25
+ Dvn::set_allowlist(&setup.env, oapp, true);
26
+ });
27
+ }
28
+
29
+ fn configure_dst_config(setup: &TestSetup, dst_eid: u32, config: DstConfig) {
30
+ let admin = setup.admins.get(0).unwrap();
31
+ let params = vec![&setup.env, DstConfigParam { dst_eid, config }];
32
+ with_contract(setup, || {
33
+ Dvn::set_dst_config(&setup.env, &admin, &params);
34
+ });
35
+ }
36
+
37
+ fn configure_fee_lib(setup: &TestSetup, fee_lib: &Address, default_multiplier: u32) {
38
+ let admin = setup.admins.get(0).unwrap();
39
+ with_contract(setup, || {
40
+ Dvn::set_worker_fee_lib(&setup.env, &admin, fee_lib);
41
+ });
42
+ with_contract(setup, || {
43
+ Dvn::set_default_multiplier_bps(&setup.env, &admin, default_multiplier);
44
+ });
45
+ }
46
+
47
+ #[test]
48
+ fn test_vid_returns_configured_value() {
49
+ let setup = TestSetup::new(1);
50
+ let client = DVNClient::new(&setup.env, &setup.contract_id);
51
+ assert_eq!(client.vid(), crate::tests::setup::VID);
52
+ }
53
+
54
+ #[test]
55
+ #[should_panic(expected = "Error(Contract, #5)")] // DvnError::EidNotSupported
56
+ fn test_dst_config_not_set_panics() {
57
+ let setup = TestSetup::new(1);
58
+ let client = DVNClient::new(&setup.env, &setup.contract_id);
59
+ client.dst_config(&999);
60
+ }
61
+
62
+ #[test]
63
+ #[should_panic(expected = "Error(Contract, #5)")] // DvnError::EidNotSupported
64
+ fn test_get_fee_missing_dst_config_panics() {
65
+ let setup = TestSetup::new(1);
66
+ let send_lib = new_addr(&setup.env);
67
+ let sender = new_addr(&setup.env);
68
+ let packet_header = Bytes::new(&setup.env);
69
+ let payload_hash = BytesN::from_array(&setup.env, &[0u8; 32]);
70
+ let options = Bytes::new(&setup.env);
71
+
72
+ with_contract(&setup, || {
73
+ Dvn::get_fee(&setup.env, &send_lib, &sender, 999, &packet_header, &payload_hash, 1, &options);
74
+ });
75
+ }
76
+
77
+ #[test]
78
+ #[should_panic(expected = "Error(Contract, #1209)")] // WorkerError::NotAllowed
79
+ fn test_get_fee_not_allowed_due_to_allowlist() {
80
+ let setup = TestSetup::new(1);
81
+ let allowed = new_addr(&setup.env);
82
+ grant_allowlist(&setup, &allowed);
83
+
84
+ let send_lib = new_addr(&setup.env);
85
+ let sender = new_addr(&setup.env);
86
+ let packet_header = Bytes::new(&setup.env);
87
+ let payload_hash = BytesN::from_array(&setup.env, &[0u8; 32]);
88
+ let options = Bytes::new(&setup.env);
89
+
90
+ with_contract(&setup, || {
91
+ Dvn::get_fee(&setup.env, &send_lib, &sender, 1, &packet_header, &payload_hash, 1, &options);
92
+ });
93
+ }
94
+
95
+ #[test]
96
+ fn test_get_fee_uses_default_multiplier_when_dst_multiplier_zero() {
97
+ let setup = TestSetup::new(1);
98
+ let fee_lib = setup.env.register(MockFeeLib, ());
99
+ configure_fee_lib(&setup, &fee_lib, 15_000);
100
+ configure_dst_config(&setup, 42, DstConfig { gas: 1, multiplier_bps: 0, floor_margin_usd: 0 });
101
+
102
+ let fee = with_contract(&setup, || {
103
+ Dvn::get_fee(
104
+ &setup.env,
105
+ &new_addr(&setup.env),
106
+ &new_addr(&setup.env),
107
+ 42,
108
+ &Bytes::new(&setup.env),
109
+ &BytesN::from_array(&setup.env, &[0; 32]),
110
+ 1,
111
+ &Bytes::new(&setup.env),
112
+ )
113
+ });
114
+
115
+ assert_eq!(fee, 15_000);
116
+ }
117
+
118
+ #[test]
119
+ fn test_get_fee_prefers_dst_multiplier_when_nonzero() {
120
+ let setup = TestSetup::new(1);
121
+ let fee_lib = setup.env.register(MockFeeLib, ());
122
+ configure_fee_lib(&setup, &fee_lib, 15_000);
123
+ configure_dst_config(&setup, 43, DstConfig { gas: 1, multiplier_bps: 9_000, floor_margin_usd: 0 });
124
+
125
+ let fee = with_contract(&setup, || {
126
+ Dvn::get_fee(
127
+ &setup.env,
128
+ &new_addr(&setup.env),
129
+ &new_addr(&setup.env),
130
+ 43,
131
+ &Bytes::new(&setup.env),
132
+ &BytesN::from_array(&setup.env, &[0; 32]),
133
+ 1,
134
+ &Bytes::new(&setup.env),
135
+ )
136
+ });
137
+
138
+ assert_eq!(fee, 9_000);
139
+ }
140
+
141
+ #[test]
142
+ fn test_pause_and_setters_happy_paths() {
143
+ let setup = TestSetup::new(1);
144
+ let admin = setup.admins.get(0).unwrap();
145
+ let other = new_addr(&setup.env);
146
+
147
+ with_contract(&setup, || {
148
+ Dvn::set_paused(&setup.env, true);
149
+ });
150
+ with_contract(&setup, || {
151
+ assert!(Dvn::paused(&setup.env));
152
+ });
153
+ with_contract(&setup, || {
154
+ Dvn::set_paused(&setup.env, false);
155
+ });
156
+ with_contract(&setup, || {
157
+ assert!(!Dvn::paused(&setup.env));
158
+ });
159
+
160
+ with_contract(&setup, || {
161
+ Dvn::set_default_multiplier_bps(&setup.env, &admin, 1234);
162
+ });
163
+ with_contract(&setup, || {
164
+ assert_eq!(Dvn::default_multiplier_bps(&setup.env), 1234);
165
+ });
166
+
167
+ with_contract(&setup, || {
168
+ Dvn::set_deposit_address(&setup.env, &admin, &other);
169
+ });
170
+ with_contract(&setup, || {
171
+ assert_eq!(Dvn::deposit_address(&setup.env), other);
172
+ });
173
+
174
+ let pf = new_addr(&setup.env);
175
+ with_contract(&setup, || {
176
+ Dvn::set_price_feed(&setup.env, &admin, &pf);
177
+ });
178
+ with_contract(&setup, || {
179
+ assert_eq!(Dvn::price_feed(&setup.env), pf);
180
+ });
181
+
182
+ let opts = Bytes::from_array(&setup.env, &[1, 2, 3]);
183
+ with_contract(&setup, || {
184
+ Dvn::set_supported_option_types(&setup.env, &admin, 77, opts.clone());
185
+ });
186
+ with_contract(&setup, || {
187
+ assert_eq!(Dvn::get_supported_option_types(&setup.env, 77), Some(opts));
188
+ });
189
+
190
+ let fee_lib = new_addr(&setup.env);
191
+ with_contract(&setup, || {
192
+ Dvn::set_worker_fee_lib(&setup.env, &admin, &fee_lib);
193
+ });
194
+ with_contract(&setup, || {
195
+ assert_eq!(Dvn::worker_fee_lib(&setup.env), fee_lib);
196
+ });
197
+ }
198
+
199
+ #[test]
200
+ fn test_acl_and_allowlist_size_reads() {
201
+ let setup = TestSetup::new(1);
202
+ let addr = setup.admins.get(0).unwrap();
203
+
204
+ let has_acl = with_contract(&setup, || Dvn::has_acl(&setup.env, &addr));
205
+ assert!(has_acl);
206
+ let allowlist_size = with_contract(&setup, || Dvn::allowlist_size(&setup.env));
207
+ assert_eq!(allowlist_size, 0);
208
+ }
209
+
210
+ #[test]
211
+ fn test_set_dst_config_auth_verification() {
212
+ let setup = TestSetup::new(1);
213
+ let admin = setup.admins.get(0).unwrap();
214
+ let dst_eid = 100u32;
215
+ let client = DVNClient::new(&setup.env, &setup.contract_id);
216
+ let params = vec![
217
+ &setup.env,
218
+ DstConfigParam { dst_eid, config: DstConfig { gas: 1000, multiplier_bps: 10000, floor_margin_usd: 0 } },
219
+ ];
220
+
221
+ client.set_dst_config(&admin, &params);
222
+
223
+ let auths = setup.env.auths();
224
+ assert_eq!(auths.len(), 1);
225
+ let (auth_addr, auth_invocation) = &auths[0];
226
+ assert_eq!(auth_addr, &admin);
227
+ match &auth_invocation.function {
228
+ AuthorizedFunction::Contract((contract_id, fn_name, args)) => {
229
+ assert_eq!(contract_id, &setup.contract_id);
230
+ assert_eq!(fn_name, &Symbol::new(&setup.env, "set_dst_config"));
231
+ assert_eq!(args, &(admin.clone(), params.clone()).into_val(&setup.env));
232
+ }
233
+ _ => panic!("Expected Contract auth"),
234
+ }
235
+ }
236
+
237
+ #[test]
238
+ fn test_assign_job_auth_verification() {
239
+ let setup = TestSetup::new(1);
240
+ let admin = setup.admins.get(0).unwrap();
241
+ let fee_lib = setup.env.register(MockFeeLib, ());
242
+ let dst_eid = 50u32;
243
+ let deposit_addr = new_addr(&setup.env);
244
+ let send_lib = new_addr(&setup.env);
245
+ let sender = new_addr(&setup.env);
246
+ let packet_header = Bytes::new(&setup.env);
247
+ let payload_hash = BytesN::from_array(&setup.env, &[0u8; 32]);
248
+ let options = Bytes::new(&setup.env);
249
+
250
+ configure_fee_lib(&setup, &fee_lib, 10_000);
251
+ configure_dst_config(&setup, dst_eid, DstConfig { gas: 1, multiplier_bps: 10_000, floor_margin_usd: 0 });
252
+
253
+ with_contract(&setup, || {
254
+ Dvn::set_deposit_address(&setup.env, &admin, &deposit_addr);
255
+ });
256
+ with_contract(&setup, || {
257
+ Dvn::set_supported_message_lib(&setup.env, &send_lib, true);
258
+ });
259
+
260
+ let result = setup.env.as_contract(&setup.contract_id, || {
261
+ Dvn::assign_job(&setup.env, &send_lib, &sender, dst_eid, &packet_header, &payload_hash, 1, &options)
262
+ });
263
+
264
+ assert_eq!(result, FeeRecipient { amount: 10_000, to: deposit_addr.clone() });
265
+
266
+ let auths = setup.env.auths();
267
+ // send_lib auth + admin auths from previous configuration steps
268
+ assert!(auths.iter().any(|(addr, _)| addr == &send_lib));
269
+ }
270
+
271
+ #[test]
272
+ fn test_set_admin_add() {
273
+ let setup = TestSetup::new(1);
274
+ let existing_admin = setup.admins.get(0).unwrap();
275
+ let new_admin = new_addr(&setup.env);
276
+
277
+ // Verify new_admin is not an admin initially
278
+ let is_admin = with_contract(&setup, || Dvn::is_admin(&setup.env, &new_admin));
279
+ assert!(!is_admin);
280
+
281
+ // Add new admin by existing admin
282
+ with_contract(&setup, || {
283
+ Dvn::set_admin(&setup.env, &existing_admin, &new_admin, true);
284
+ });
285
+
286
+ // Verify new_admin is now an admin
287
+ let is_admin = with_contract(&setup, || Dvn::is_admin(&setup.env, &new_admin));
288
+ assert!(is_admin);
289
+ }
290
+
291
+ #[test]
292
+ fn test_set_admin_remove() {
293
+ let setup = TestSetup::new(1);
294
+ let existing_admin = setup.admins.get(0).unwrap();
295
+ let new_admin = new_addr(&setup.env);
296
+
297
+ // Add new admin first
298
+ with_contract(&setup, || {
299
+ Dvn::set_admin(&setup.env, &existing_admin, &new_admin, true);
300
+ });
301
+
302
+ // Remove the new admin
303
+ with_contract(&setup, || {
304
+ Dvn::set_admin(&setup.env, &existing_admin, &new_admin, false);
305
+ });
306
+
307
+ // Verify new_admin is no longer an admin
308
+ let is_admin = with_contract(&setup, || Dvn::is_admin(&setup.env, &new_admin));
309
+ assert!(!is_admin);
310
+ }
311
+
312
+ #[test]
313
+ #[should_panic(expected = "Error(Contract, #1215)")] // WorkerError::Unauthorized
314
+ fn test_set_admin_unauthorized() {
315
+ let setup = TestSetup::new(1);
316
+ let non_admin = new_addr(&setup.env);
317
+ let new_admin = new_addr(&setup.env);
318
+
319
+ // Attempt to add admin by non-admin should fail
320
+ with_contract(&setup, || {
321
+ Dvn::set_admin(&setup.env, &non_admin, &new_admin, true);
322
+ });
323
+ }
324
+
325
+ #[test]
326
+ #[should_panic(expected = "Error(Contract, #1204)")] // WorkerError::AttemptingToRemoveOnlyAdmin
327
+ fn test_set_admin_cannot_remove_last_admin() {
328
+ let setup = TestSetup::new(1);
329
+ let existing_admin = setup.admins.get(0).unwrap();
330
+
331
+ // Attempt to remove the only admin should fail
332
+ with_contract(&setup, || {
333
+ Dvn::set_admin(&setup.env, &existing_admin, &existing_admin, false);
334
+ });
335
+ }
336
+
337
+ #[contract]
338
+ struct MockFeeLib;
339
+
340
+ #[contractimpl]
341
+ impl IDvnFeeLib for MockFeeLib {
342
+ fn get_fee(_env: &Env, params: &DvnFeeParams) -> i128 {
343
+ if params.multiplier_bps == 0 {
344
+ params.default_multiplier_bps as i128
345
+ } else {
346
+ params.multiplier_bps as i128
347
+ }
348
+ }
349
+ }
@@ -0,0 +1,66 @@
1
+ //! Test utilities for DVN multisig testing
2
+ //!
3
+ //! Provides key pair generation and signing for secp256k1 ECDSA signatures.
4
+
5
+ extern crate std;
6
+
7
+ use k256::ecdsa::{SigningKey, VerifyingKey};
8
+ use sha3::{Digest, Keccak256};
9
+ use soroban_sdk::{BytesN, Env};
10
+
11
+ /// A secp256k1 key pair with private key and derived Ethereum-style address
12
+ #[derive(Clone)]
13
+ pub struct KeyPair {
14
+ /// The secp256k1 signing key (private key)
15
+ signing_key: SigningKey,
16
+ /// The derived Ethereum-style address (last 20 bytes of keccak256(pubkey))
17
+ pub eth_address: [u8; 20],
18
+ }
19
+
20
+ impl KeyPair {
21
+ pub fn generate() -> Self {
22
+ let signing_key = SigningKey::random(&mut rand::thread_rng());
23
+ let eth_address = Self::derive_eth_address(&signing_key);
24
+ Self { signing_key, eth_address }
25
+ }
26
+
27
+ fn derive_eth_address(signing_key: &SigningKey) -> [u8; 20] {
28
+ let verifying_key: &VerifyingKey = signing_key.verifying_key();
29
+ let pubkey_bytes = verifying_key.to_encoded_point(false);
30
+ let pubkey_uncompressed = pubkey_bytes.as_bytes();
31
+
32
+ let mut hasher = Keccak256::new();
33
+ hasher.update(&pubkey_uncompressed[1..65]);
34
+ let hash = hasher.finalize();
35
+
36
+ let mut eth_address = [0u8; 20];
37
+ eth_address.copy_from_slice(&hash[12..32]);
38
+ eth_address
39
+ }
40
+
41
+ pub fn signer(&self, env: &Env) -> BytesN<20> {
42
+ BytesN::from_array(env, &self.eth_address)
43
+ }
44
+
45
+ /// Sign a 32-byte digest and return a 65-byte signature (r || s || v)
46
+ pub fn sign(&self, digest: &[u8; 32]) -> [u8; 65] {
47
+ let (signature, recovery_id) = self.signing_key.sign_prehash_recoverable(digest).expect("Signing failed");
48
+
49
+ let r = signature.r().to_bytes();
50
+ let s = signature.s().to_bytes();
51
+ let v = 27 + recovery_id.to_byte();
52
+
53
+ let mut result = [0u8; 65];
54
+ result[0..32].copy_from_slice(&r);
55
+ result[32..64].copy_from_slice(&s);
56
+ result[64] = v;
57
+
58
+ result
59
+ }
60
+
61
+ /// Sign a digest and return as BytesN<65> for use in Soroban
62
+ pub fn sign_bytes(&self, env: &Env, digest: &BytesN<32>) -> BytesN<65> {
63
+ let signature = self.sign(&digest.to_array());
64
+ BytesN::from_array(env, &signature)
65
+ }
66
+ }
@@ -0,0 +1,5 @@
1
+ pub mod auth;
2
+ pub mod dvn;
3
+ pub mod key_pair;
4
+ pub mod multisig;
5
+ pub mod setup;
@@ -0,0 +1,3 @@
1
+ pub mod set_signer;
2
+ pub mod set_threshold;
3
+ pub mod verify_signatures;
@@ -0,0 +1,133 @@
1
+ use crate::errors::MultisigError;
2
+ use crate::storage::MultisigStorage;
3
+ use crate::tests::setup::TestSetup;
4
+ use soroban_sdk::{
5
+ testutils::{AuthorizedFunction, BytesN as _},
6
+ BytesN, Env, IntoVal, Symbol,
7
+ };
8
+
9
+ fn random_signer(env: &Env) -> BytesN<20> {
10
+ BytesN::random(env)
11
+ }
12
+
13
+ fn clear_signers(setup: &TestSetup) {
14
+ setup.env.as_contract(&setup.contract_id, || {
15
+ MultisigStorage::remove_signers(&setup.env);
16
+ });
17
+ }
18
+
19
+ #[test]
20
+ fn test_set_signer() {
21
+ let setup = TestSetup::new(1);
22
+ let new_signer = random_signer(&setup.env);
23
+
24
+ setup.multisig_client.set_signer(&new_signer, &true);
25
+
26
+ let auths = setup.env.auths();
27
+ assert_eq!(auths.len(), 1);
28
+ let (auth_addr, auth_invocation) = &auths[0];
29
+ assert_eq!(auth_addr, &setup.contract_id);
30
+ match &auth_invocation.function {
31
+ AuthorizedFunction::Contract((contract_id, fn_name, args)) => {
32
+ assert_eq!(contract_id, &setup.contract_id);
33
+ assert_eq!(fn_name, &Symbol::new(&setup.env, "set_signer"));
34
+ assert_eq!(args, &(new_signer.clone(), true).into_val(&setup.env));
35
+ }
36
+ _ => panic!("Expected Contract auth"),
37
+ }
38
+
39
+ assert!(setup.multisig_client.is_signer(&new_signer));
40
+ assert_eq!(setup.multisig_client.get_signers().len(), 2);
41
+ }
42
+
43
+ #[test]
44
+ fn test_set_signer_duplicate_fails() {
45
+ let setup = TestSetup::new(1);
46
+ let signer = random_signer(&setup.env);
47
+
48
+ setup.multisig_client.set_signer(&signer, &true);
49
+ let res = setup.multisig_client.try_set_signer(&signer, &true);
50
+
51
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::SignerAlreadyExists.into());
52
+ }
53
+
54
+ #[test]
55
+ fn test_set_signer_remove_success() {
56
+ let setup = TestSetup::new(1);
57
+ let signer = random_signer(&setup.env);
58
+
59
+ setup.multisig_client.set_signer(&signer, &true);
60
+ assert!(setup.multisig_client.is_signer(&signer));
61
+
62
+ setup.multisig_client.set_signer(&signer, &false);
63
+ assert!(!setup.multisig_client.is_signer(&signer));
64
+ }
65
+
66
+ #[test]
67
+ fn test_set_signer_remove_not_found() {
68
+ let setup = TestSetup::new(1);
69
+ let signer = random_signer(&setup.env);
70
+
71
+ let res = setup.multisig_client.try_set_signer(&signer, &false);
72
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::SignerNotFound.into());
73
+ }
74
+
75
+ #[test]
76
+ fn test_set_signer_invalid_signer_zero_address() {
77
+ let setup = TestSetup::new(1);
78
+ let zero = BytesN::from_array(&setup.env, &[0u8; 20]);
79
+
80
+ let res = setup.multisig_client.try_set_signer(&zero, &true);
81
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::InvalidSigner.into());
82
+ }
83
+
84
+ #[test]
85
+ fn test_get_signers_empty_map() {
86
+ let setup = TestSetup::new(1);
87
+
88
+ clear_signers(&setup);
89
+
90
+ let signers = setup.multisig_client.get_signers();
91
+ assert_eq!(signers.len(), 0);
92
+ let non_signer = random_signer(&setup.env);
93
+ assert!(!setup.multisig_client.is_signer(&non_signer));
94
+ }
95
+
96
+ #[test]
97
+ fn test_total_signers() {
98
+ let setup = TestSetup::new(2);
99
+
100
+ assert_eq!(setup.multisig_client.total_signers(), 2);
101
+
102
+ let new_signer = random_signer(&setup.env);
103
+ setup.multisig_client.set_signer(&new_signer, &true);
104
+ assert_eq!(setup.multisig_client.total_signers(), 3);
105
+
106
+ setup.multisig_client.set_signer(&new_signer, &false);
107
+ assert_eq!(setup.multisig_client.total_signers(), 2);
108
+ }
109
+
110
+ #[test]
111
+ fn test_remove_signer_when_no_signers() {
112
+ let setup = TestSetup::new(1);
113
+
114
+ clear_signers(&setup);
115
+
116
+ let signer = random_signer(&setup.env);
117
+ let res = setup.multisig_client.try_set_signer(&signer, &false);
118
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::SignerNotFound.into());
119
+ }
120
+
121
+ #[test]
122
+ fn test_remove_signer_violates_threshold() {
123
+ // Create DVN with 2 signers and threshold 2
124
+ let setup = TestSetup::new(2);
125
+
126
+ // Get one of the existing signers
127
+ let signers = setup.multisig_client.get_signers();
128
+ let signer_to_remove = signers.get(0).unwrap();
129
+
130
+ // Removing a signer should fail because it would leave only 1 signer with threshold 2
131
+ let res = setup.multisig_client.try_set_signer(&signer_to_remove, &false);
132
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::TotalSignersLessThanThreshold.into());
133
+ }
@@ -0,0 +1,108 @@
1
+ use crate::tests::setup::{TestSetup, DEFAULT_MULTIPLIER_BPS, VID};
2
+ use crate::{dvn::Dvn, errors::MultisigError, storage::MultisigStorage};
3
+ use soroban_sdk::{
4
+ testutils::{Address as _, AuthorizedFunction, BytesN as _},
5
+ vec, Address, BytesN, Env, IntoVal, Symbol, Vec,
6
+ };
7
+
8
+ fn random_signer(env: &Env) -> BytesN<20> {
9
+ BytesN::random(env)
10
+ }
11
+
12
+ fn register_dvn(env: &Env, signer_count: usize, threshold: u32) {
13
+ let signers: Vec<BytesN<20>> = (0..signer_count).map(|_| random_signer(env)).fold(vec![env], |mut acc, s| {
14
+ acc.push_back(s);
15
+ acc
16
+ });
17
+ let admins: Vec<Address> = vec![env, Address::generate(env)];
18
+ let supported_msglibs: Vec<Address> = vec![env, Address::generate(env)];
19
+ let price_feed: Address = Address::generate(env);
20
+ let worker_fee_lib: Address = Address::generate(env);
21
+ let deposit_address: Address = Address::generate(env);
22
+
23
+ let _ = env.register(
24
+ Dvn,
25
+ (
26
+ &VID,
27
+ &signers,
28
+ &threshold,
29
+ &admins,
30
+ &supported_msglibs,
31
+ &price_feed,
32
+ &worker_fee_lib,
33
+ &DEFAULT_MULTIPLIER_BPS,
34
+ &deposit_address,
35
+ ),
36
+ );
37
+ }
38
+
39
+ #[test]
40
+ fn test_set_threshold_success() {
41
+ let setup = TestSetup::new(2);
42
+
43
+ setup.multisig_client.set_threshold(&2);
44
+
45
+ let auths = setup.env.auths();
46
+ assert_eq!(auths.len(), 1);
47
+ let (auth_addr, auth_invocation) = &auths[0];
48
+ assert_eq!(auth_addr, &setup.contract_id);
49
+ match &auth_invocation.function {
50
+ AuthorizedFunction::Contract((contract_id, fn_name, args)) => {
51
+ assert_eq!(contract_id, &setup.contract_id);
52
+ assert_eq!(fn_name, &Symbol::new(&setup.env, "set_threshold"));
53
+ assert_eq!(args, &(2u32,).into_val(&setup.env));
54
+ }
55
+ _ => panic!("Expected Contract auth"),
56
+ }
57
+
58
+ assert_eq!(setup.multisig_client.threshold(), 2);
59
+
60
+ setup.multisig_client.set_threshold(&1);
61
+ assert_eq!(setup.multisig_client.threshold(), 1);
62
+ }
63
+
64
+ #[test]
65
+ fn test_set_threshold_zero_fails() {
66
+ let setup = TestSetup::new(1);
67
+
68
+ let res = setup.multisig_client.try_set_threshold(&0);
69
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::ZeroThreshold.into());
70
+ }
71
+
72
+ #[test]
73
+ fn test_set_threshold_above_signers_fails() {
74
+ let setup = TestSetup::new(2);
75
+
76
+ let res = setup.multisig_client.try_set_threshold(&3);
77
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::TotalSignersLessThanThreshold.into());
78
+ }
79
+
80
+ #[test]
81
+ fn test_set_threshold_with_no_signers_fails() {
82
+ let setup = TestSetup::new(1);
83
+
84
+ setup.env.as_contract(&setup.contract_id, || {
85
+ MultisigStorage::remove_signers(&setup.env);
86
+ });
87
+
88
+ let res = setup.multisig_client.try_set_threshold(&1);
89
+ assert_eq!(res.err().unwrap().ok().unwrap(), MultisigError::TotalSignersLessThanThreshold.into());
90
+ }
91
+
92
+ #[test]
93
+ #[should_panic(expected = "Error(Contract, #1)")] // MultisigError::ZeroThreshold
94
+ fn test_constructor_threshold_zero_panics() {
95
+ let env = Env::default();
96
+ env.mock_all_auths();
97
+
98
+ register_dvn(&env, 1, 0);
99
+ }
100
+
101
+ #[test]
102
+ #[should_panic(expected = "Error(Contract, #2)")] // MultisigError::TotalSignersLessThanThreshold
103
+ fn test_constructor_signers_less_than_threshold_panics() {
104
+ let env = Env::default();
105
+ env.mock_all_auths();
106
+
107
+ register_dvn(&env, 1, 2);
108
+ }