@layerzerolabs/protocol-stellar-v2 0.2.8 → 0.2.10

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,109 @@
1
+ extern crate std;
2
+
3
+ use crate::storage::MultisigStorage;
4
+ use crate::tests::key_pair::KeyPair;
5
+ use crate::tests::setup::TestSetup;
6
+ use soroban_sdk::{vec, Bytes, BytesN, Vec};
7
+
8
+ fn hash(env: &soroban_sdk::Env, label: &[u8]) -> BytesN<32> {
9
+ env.crypto().keccak256(&Bytes::from_slice(env, label)).to_bytes()
10
+ }
11
+
12
+ fn sign(setup: &TestSetup, idx: usize, hash: &BytesN<32>) -> BytesN<65> {
13
+ setup.key_pairs[idx].sign_bytes(&setup.env, hash)
14
+ }
15
+
16
+ fn sorted_signatures(setup: &TestSetup, digest: &BytesN<32>) -> Vec<BytesN<65>> {
17
+ let mut pairs: std::vec::Vec<_> =
18
+ setup.key_pairs.iter().map(|kp| (kp.eth_address, kp.sign_bytes(&setup.env, digest))).collect();
19
+ pairs.sort_by(|a, b| a.0.cmp(&b.0));
20
+ let mut result: Vec<BytesN<65>> = vec![&setup.env];
21
+ for (_, sig) in pairs {
22
+ result.push_back(sig);
23
+ }
24
+ result
25
+ }
26
+
27
+ #[test]
28
+ fn test_verify_signatures() {
29
+ let setup = TestSetup::new(1);
30
+ let digest = hash(&setup.env, b"test");
31
+ let signature = setup.key_pairs[0].sign(&digest.to_array());
32
+ let signatures: Vec<BytesN<65>> = vec![&setup.env, BytesN::from_array(&setup.env, &signature)];
33
+
34
+ setup.multisig_client.verify_signatures(&digest, &signatures);
35
+ }
36
+
37
+ #[test]
38
+ fn test_verify_signatures_multiple_signers() {
39
+ let setup = TestSetup::new(2);
40
+ let digest = hash(&setup.env, b"multisig");
41
+
42
+ let signatures = sorted_signatures(&setup, &digest);
43
+
44
+ setup.multisig_client.verify_signatures(&digest, &signatures);
45
+ }
46
+
47
+ #[test]
48
+ fn test_verify_signatures_raw_v_path() {
49
+ let setup = TestSetup::new(1);
50
+ let digest = hash(&setup.env, b"rawv");
51
+
52
+ // Sign normally (v in 27/28), then force v into raw 0/1 path
53
+ let mut sig_bytes = setup.key_pairs[0].sign(&digest.to_array());
54
+ sig_bytes[64] = sig_bytes[64].saturating_sub(27); // map 27->0 or 28->1
55
+ let signature = BytesN::from_array(&setup.env, &sig_bytes);
56
+ let signatures: Vec<BytesN<65>> = vec![&setup.env, signature];
57
+
58
+ setup.multisig_client.verify_signatures(&digest, &signatures);
59
+ }
60
+
61
+ #[test]
62
+ #[should_panic(expected = "Error(Contract, #5)")] // MultisigError::SignerNotFound
63
+ fn test_verify_signatures_with_wrong_signer() {
64
+ let setup = TestSetup::new(1);
65
+ let digest = hash(&setup.env, b"test");
66
+ let wrong_key_pair = KeyPair::generate();
67
+ let signature = wrong_key_pair.sign(&digest.to_array());
68
+ let signatures: Vec<BytesN<65>> = vec![&setup.env, BytesN::from_array(&setup.env, &signature)];
69
+
70
+ setup.multisig_client.verify_signatures(&digest, &signatures);
71
+ }
72
+
73
+ #[test]
74
+ #[should_panic(expected = "Error(Contract, #7)")] // MultisigError::SignatureError
75
+ fn test_verify_signatures_insufficient_signatures() {
76
+ let setup = TestSetup::new(2);
77
+ let digest = hash(&setup.env, b"insufficient");
78
+
79
+ let signatures: Vec<BytesN<65>> = vec![&setup.env, sign(&setup, 0, &digest)];
80
+
81
+ setup.multisig_client.verify_signatures(&digest, &signatures);
82
+ }
83
+
84
+ #[test]
85
+ #[should_panic(expected = "Error(Contract, #6)")] // MultisigError::UnsortedSigners
86
+ fn test_verify_signatures_duplicate_signer() {
87
+ let setup = TestSetup::new(2);
88
+ let digest = hash(&setup.env, b"duplicate");
89
+
90
+ let sig1 = sign(&setup, 0, &digest);
91
+ let signatures: Vec<BytesN<65>> = vec![&setup.env, sig1.clone(), sig1];
92
+
93
+ setup.multisig_client.verify_signatures(&digest, &signatures);
94
+ }
95
+
96
+ #[test]
97
+ #[should_panic(expected = "Error(Contract, #1)")] // MultisigError::ZeroThreshold
98
+ fn test_verify_signatures_threshold_zero() {
99
+ let setup = TestSetup::new(1);
100
+ // Force threshold to 0 via storage to hit ZeroThreshold branch
101
+ setup.env.as_contract(&setup.contract_id, || {
102
+ MultisigStorage::set_threshold(&setup.env, &0);
103
+ });
104
+ let digest = hash(&setup.env, b"threshold_zero");
105
+
106
+ let signatures: Vec<BytesN<65>> = vec![&setup.env, sign(&setup, 0, &digest)];
107
+
108
+ setup.multisig_client.verify_signatures(&digest, &signatures);
109
+ }
@@ -0,0 +1,109 @@
1
+ //! Test setup utilities for DVN tests
2
+
3
+ extern crate std;
4
+
5
+ use crate::tests::key_pair::KeyPair;
6
+ use crate::{dvn::Dvn, interfaces::MultiSigClient};
7
+ use soroban_sdk::{address_payload::AddressPayload, testutils::Address as _, vec, Address, BytesN, Env, Vec};
8
+ use std::vec::Vec as StdVec;
9
+
10
+ pub const VID: u32 = 1;
11
+ pub const DEFAULT_MULTIPLIER_BPS: u32 = 10000;
12
+
13
+ pub struct TestSetup<'a> {
14
+ pub env: Env,
15
+ pub contract_id: Address,
16
+ pub multisig_client: MultiSigClient<'a>,
17
+ pub key_pairs: StdVec<KeyPair>,
18
+ pub admins: Vec<Address>,
19
+ }
20
+
21
+ impl<'a> TestSetup<'a> {
22
+ /// Create a new test setup with the specified number of signers
23
+ /// Threshold defaults to signer_count
24
+ pub fn new(signer_count: u32) -> Self {
25
+ Self::with_threshold(signer_count, signer_count)
26
+ }
27
+
28
+ /// Create a new test setup with custom threshold
29
+ pub fn with_threshold(signer_count: u32, threshold: u32) -> Self {
30
+ let env = Env::default();
31
+ env.mock_all_auths();
32
+
33
+ let key_pairs: StdVec<KeyPair> = (0..signer_count).map(|_| KeyPair::generate()).collect();
34
+
35
+ let mut signers: Vec<BytesN<20>> = vec![&env];
36
+ for kp in &key_pairs {
37
+ signers.push_back(kp.signer(&env));
38
+ }
39
+
40
+ let admins: Vec<Address> = vec![&env, Address::generate(&env)];
41
+ let supported_msglibs: Vec<Address> = vec![&env, Address::generate(&env)];
42
+ let price_feed: Address = Address::generate(&env);
43
+ let worker_fee_lib: Address = Address::generate(&env);
44
+ let deposit_address: Address = Address::generate(&env);
45
+
46
+ let contract_id = env.register(
47
+ Dvn,
48
+ (
49
+ &VID,
50
+ &signers,
51
+ &threshold,
52
+ &admins,
53
+ &supported_msglibs,
54
+ &price_feed,
55
+ &worker_fee_lib,
56
+ &DEFAULT_MULTIPLIER_BPS,
57
+ &deposit_address,
58
+ ),
59
+ );
60
+
61
+ let multisig_client = MultiSigClient::new(&env, &contract_id);
62
+
63
+ Self { env, contract_id, multisig_client, key_pairs, admins }
64
+ }
65
+
66
+ /// Create a new test setup with additional admins (as Ed25519 public key bytes)
67
+ pub fn with_admin_bytes(signer_count: u32, admin_bytes: StdVec<[u8; 32]>) -> Self {
68
+ let env = Env::default();
69
+ env.mock_all_auths();
70
+
71
+ let key_pairs: StdVec<KeyPair> = (0..signer_count).map(|_| KeyPair::generate()).collect();
72
+
73
+ let mut signers: Vec<BytesN<20>> = vec![&env];
74
+ for kp in &key_pairs {
75
+ signers.push_back(kp.signer(&env));
76
+ }
77
+
78
+ let mut admins: Vec<Address> = vec![&env, Address::generate(&env)];
79
+ for bytes in admin_bytes {
80
+ let bytes_n = BytesN::from_array(&env, &bytes);
81
+ let addr = Address::from_payload(&env, AddressPayload::AccountIdPublicKeyEd25519(bytes_n));
82
+ admins.push_back(addr);
83
+ }
84
+
85
+ let supported_msglibs: Vec<Address> = vec![&env, Address::generate(&env)];
86
+ let price_feed: Address = Address::generate(&env);
87
+ let worker_fee_lib: Address = Address::generate(&env);
88
+ let deposit_address: Address = Address::generate(&env);
89
+
90
+ let contract_id = env.register(
91
+ Dvn,
92
+ (
93
+ &VID,
94
+ &signers,
95
+ &signer_count,
96
+ &admins,
97
+ &supported_msglibs,
98
+ &price_feed,
99
+ &worker_fee_lib,
100
+ &DEFAULT_MULTIPLIER_BPS,
101
+ &deposit_address,
102
+ ),
103
+ );
104
+
105
+ let multisig_client = MultiSigClient::new(&env, &contract_id);
106
+
107
+ Self { env, contract_id, multisig_client, key_pairs, admins }
108
+ }
109
+ }
@@ -0,0 +1,26 @@
1
+ use soroban_sdk::{contracttype, BytesN, Vec};
2
+
3
+ #[contracttype]
4
+ #[derive(Clone, Debug, Eq, PartialEq)]
5
+ pub struct DstConfig {
6
+ pub gas: u128,
7
+ pub multiplier_bps: u32,
8
+ pub floor_margin_usd: u128,
9
+ }
10
+
11
+ #[contracttype]
12
+ #[derive(Clone, Debug, Eq, PartialEq)]
13
+ pub struct DstConfigParam {
14
+ pub dst_eid: u32,
15
+ pub config: DstConfig,
16
+ }
17
+
18
+ #[contracttype]
19
+ #[derive(Clone, Debug, Eq, PartialEq)]
20
+ pub struct TransactionAuthData {
21
+ pub vid: u32,
22
+ pub expiration: u64,
23
+ pub signatures: Vec<BytesN<65>>,
24
+ pub admin: BytesN<32>,
25
+ pub admin_signature: BytesN<64>,
26
+ }
@@ -0,0 +1,24 @@
1
+ [package]
2
+ name = "dvn-fee-lib"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ publish = false
7
+
8
+ [features]
9
+ library = []
10
+ testutils = []
11
+
12
+ [lib]
13
+ crate-type = ["cdylib", "rlib"]
14
+ doctest = false
15
+
16
+ [dependencies]
17
+ cfg-if = "1.0"
18
+ soroban-sdk = { workspace = true }
19
+ message-lib-common = { workspace = true }
20
+ worker = { workspace = true }
21
+ common-macros = { workspace = true }
22
+
23
+ [dev-dependencies]
24
+ soroban-sdk = { workspace = true, features = ["testutils"] }
@@ -0,0 +1,113 @@
1
+ use crate::DvnFeeLibError;
2
+ use soroban_sdk::{assert_with_error, contract, contractimpl, Env};
3
+ use worker::{DvnFeeParams, IDvnFeeLib, LayerZeroPriceFeedClient};
4
+
5
+ // ============================================================================
6
+ // Constants
7
+ // ============================================================================
8
+
9
+ /// Basis points denominator (10000 = 100%).
10
+ pub(crate) const BPS_BASE: i128 = 10000;
11
+
12
+ /// Fixed bytes for execute function call.
13
+ pub(crate) const EXECUTE_FIXED_BYTES: u64 = 260;
14
+
15
+ /// Raw signature bytes length (65 bytes: 64 for signature + 1 for recovery ID).
16
+ pub(crate) const SIGNATURE_RAW_BYTES: u64 = 65;
17
+
18
+ /// Verify function bytes (padded).
19
+ pub(crate) const VERIFY_BYTES: u64 = 288;
20
+
21
+ /// Native token decimal rate for XLM (10^7 stroops per XLM).
22
+ pub(crate) const NATIVE_DECIMALS_RATE: u128 = 10_000_000;
23
+
24
+ /// DVN fee library contract for calculating DVN verification fees.
25
+ ///
26
+ /// Provides fee calculation logic based on quorum size, destination gas costs,
27
+ /// and current gas prices from the price feed. Handles fee multipliers and
28
+ /// floor margins.
29
+ #[contract]
30
+ pub struct DvnFeeLib;
31
+
32
+ #[contractimpl]
33
+ impl IDvnFeeLib for DvnFeeLib {
34
+ fn get_fee(env: &Env, params: &DvnFeeParams) -> i128 {
35
+ assert_with_error!(env, params.gas > 0, DvnFeeLibError::InvalidGas);
36
+ assert_with_error!(env, params.options.is_empty(), DvnFeeLibError::InvalidDVNOptions);
37
+
38
+ let call_data_size = get_call_data_size(params.quorum as u64);
39
+ let call_data_size_u32 =
40
+ u32::try_from(call_data_size).expect("call data size should comfortably fit into u32 for WASM");
41
+
42
+ // Get estimated fee from price feed
43
+ let price_feed_client = LayerZeroPriceFeedClient::new(env, &params.price_feed);
44
+ let fee_result = price_feed_client.estimate_fee_by_eid(
45
+ &env.current_contract_address(),
46
+ &params.dst_eid,
47
+ &call_data_size_u32,
48
+ &params.gas,
49
+ );
50
+ assert_with_error!(env, fee_result.total_gas_fee >= 0, DvnFeeLibError::NegativeFee);
51
+
52
+ apply_premium(
53
+ fee_result.total_gas_fee,
54
+ params.multiplier_bps,
55
+ params.default_multiplier_bps,
56
+ params.floor_margin_usd,
57
+ fee_result.native_price_usd,
58
+ )
59
+ }
60
+ }
61
+
62
+ // ============================================================================
63
+ // Internal Helper Functions
64
+ // ============================================================================
65
+
66
+ /// Applies premium (multiplier and margin) to the base fee.
67
+ ///
68
+ /// Calculates fee with multiplier and floor margin, returning the maximum of both
69
+ /// to ensure profitability.
70
+ ///
71
+ /// # Arguments
72
+ /// * `fee` - Base gas fee
73
+ /// * `multiplier_bps` - Destination-specific multiplier in basis points
74
+ /// * `default_multiplier_bps` - Default multiplier if destination multiplier is 0
75
+ /// * `floor_margin_usd` - Minimum margin in USD (scaled)
76
+ /// * `native_price_usd` - Native token price in USD (scaled)
77
+ ///
78
+ /// # Returns
79
+ /// Fee with premium applied (max of multiplier fee and margin fee).
80
+ pub(crate) fn apply_premium(
81
+ fee: i128,
82
+ multiplier_bps: u32,
83
+ default_multiplier_bps: u32,
84
+ floor_margin_usd: u128,
85
+ native_price_usd: u128,
86
+ ) -> i128 {
87
+ let effective_multiplier_bps = if multiplier_bps == 0 { default_multiplier_bps } else { multiplier_bps };
88
+
89
+ let fee_with_multiplier = fee * (effective_multiplier_bps as i128) / BPS_BASE;
90
+ if native_price_usd == 0 || floor_margin_usd == 0 {
91
+ return fee_with_multiplier;
92
+ }
93
+ let fee_with_floor_margin = fee + (floor_margin_usd * NATIVE_DECIMALS_RATE / native_price_usd) as i128;
94
+ fee_with_floor_margin.max(fee_with_multiplier)
95
+ }
96
+
97
+ /// Calculates the total calldata size for DVN verification.
98
+ ///
99
+ /// Includes execute function overhead, verify function bytes, and padded signature bytes.
100
+ /// Signature bytes are padded to 32-byte boundaries for efficient EVM processing.
101
+ ///
102
+ /// # Arguments
103
+ /// * `quorum` - Number of signatures required
104
+ ///
105
+ /// # Returns
106
+ /// Total calldata size in bytes.
107
+ pub(crate) fn get_call_data_size(quorum: u64) -> u64 {
108
+ let mut total_signature_bytes = quorum * SIGNATURE_RAW_BYTES;
109
+ if !total_signature_bytes.is_multiple_of(32) {
110
+ total_signature_bytes = total_signature_bytes - (total_signature_bytes % 32) + 32;
111
+ }
112
+ EXECUTE_FIXED_BYTES + VERIFY_BYTES + total_signature_bytes + 32
113
+ }
@@ -0,0 +1,8 @@
1
+ use common_macros::contract_error;
2
+
3
+ #[contract_error]
4
+ pub enum DvnFeeLibError {
5
+ InvalidDVNOptions,
6
+ InvalidGas,
7
+ NegativeFee,
8
+ }
@@ -0,0 +1,17 @@
1
+ #![no_std]
2
+ #[cfg(test)]
3
+ extern crate std;
4
+
5
+ mod errors;
6
+
7
+ pub use errors::*;
8
+
9
+ cfg_if::cfg_if! {
10
+ if #[cfg(any(not(feature = "library"), feature = "testutils"))] {
11
+ mod dvn_fee_lib;
12
+ pub use dvn_fee_lib::DvnFeeLib;
13
+ }
14
+ }
15
+
16
+ #[cfg(test)]
17
+ mod tests;
@@ -0,0 +1,282 @@
1
+ use crate::dvn_fee_lib::{
2
+ apply_premium, get_call_data_size, DvnFeeLib, BPS_BASE, EXECUTE_FIXED_BYTES, NATIVE_DECIMALS_RATE, VERIFY_BYTES,
3
+ };
4
+ use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, Bytes, Env};
5
+ use worker::{interfaces::FeeEstimate, DvnFeeParams, IDvnFeeLib};
6
+
7
+ #[test]
8
+ fn get_call_data_size_pads_signatures() {
9
+ let size = get_call_data_size(1);
10
+ assert_eq!(size, EXECUTE_FIXED_BYTES + VERIFY_BYTES + 96 + 32);
11
+
12
+ let size = get_call_data_size(2);
13
+ assert_eq!(size, EXECUTE_FIXED_BYTES + VERIFY_BYTES + 160 + 32);
14
+
15
+ let size = get_call_data_size(32);
16
+ assert_eq!(size, EXECUTE_FIXED_BYTES + VERIFY_BYTES + 2080 + 32);
17
+ }
18
+
19
+ #[test]
20
+ fn apply_premium_multiplier_only_both_zero() {
21
+ let fee = 1_000i128;
22
+ let multiplier_bps = 15_000u32;
23
+ // multiplier_bps is non-zero, so default_multiplier_bps is ignored
24
+ let result = apply_premium(fee, multiplier_bps, 10_000, 0, 0);
25
+ assert_eq!(result, fee * multiplier_bps as i128 / BPS_BASE);
26
+ }
27
+
28
+ #[test]
29
+ fn apply_premium_multiplier_only_native_price_zero() {
30
+ let fee = 1_000i128;
31
+ let multiplier_bps = 15_000u32;
32
+ let result = apply_premium(fee, multiplier_bps, 10_000, 100, 0);
33
+ assert_eq!(result, fee * multiplier_bps as i128 / BPS_BASE);
34
+ }
35
+
36
+ #[test]
37
+ fn apply_premium_multiplier_only_floor_margin_zero() {
38
+ let fee = 1_000i128;
39
+ let multiplier_bps = 15_000u32;
40
+ let result = apply_premium(fee, multiplier_bps, 10_000, 0, NATIVE_DECIMALS_RATE);
41
+ assert_eq!(result, fee * multiplier_bps as i128 / BPS_BASE);
42
+ }
43
+
44
+ #[test]
45
+ fn apply_premium_uses_default_when_multiplier_zero() {
46
+ let fee = 1_000i128;
47
+ let default_bps = 12_000u32;
48
+ // multiplier_bps is 0, so default_multiplier_bps should be used
49
+ let result = apply_premium(fee, 0, default_bps, 0, 0);
50
+ assert_eq!(result, fee * default_bps as i128 / BPS_BASE);
51
+ }
52
+
53
+ #[test]
54
+ fn apply_premium_floor_margin_wins() {
55
+ let fee = 1_000i128;
56
+ let multiplier_bps = 10_000u32;
57
+ let floor_margin_usd = 10u128;
58
+ let native_price_usd = NATIVE_DECIMALS_RATE;
59
+
60
+ let result = apply_premium(fee, multiplier_bps, 10_000, floor_margin_usd, native_price_usd);
61
+ assert_eq!(result, fee + floor_margin_usd as i128);
62
+ }
63
+
64
+ #[test]
65
+ fn apply_premium_multiplier_wins_over_small_floor() {
66
+ let fee = 1_000i128;
67
+ let multiplier_bps = 12_000u32;
68
+ let floor_margin_usd = 1u128;
69
+ let native_price_usd = NATIVE_DECIMALS_RATE;
70
+
71
+ let result = apply_premium(fee, multiplier_bps, 10_000, floor_margin_usd, native_price_usd);
72
+ assert_eq!(result, 1_200);
73
+ }
74
+
75
+ #[test]
76
+ #[should_panic(expected = "Error(Contract, #2)")] // DvnFeeLibError::InvalidGas
77
+ fn get_fee_panics_when_gas_zero() {
78
+ let env = Env::default();
79
+ env.mock_all_auths();
80
+ let params = DvnFeeParams {
81
+ sender: Address::generate(&env),
82
+ dst_eid: 1,
83
+ confirmations: 1,
84
+ options: Bytes::new(&env),
85
+ price_feed: Address::generate(&env),
86
+ default_multiplier_bps: 10_000,
87
+ quorum: 1,
88
+ gas: 0, // Zero gas should panic
89
+ multiplier_bps: 10_000,
90
+ floor_margin_usd: 0,
91
+ };
92
+
93
+ DvnFeeLib::get_fee(&env, &params);
94
+ }
95
+
96
+ #[test]
97
+ #[should_panic(expected = "Error(Contract, #1)")] // DvnFeeLibError::InvalidDVNOptions
98
+ fn get_fee_panics_when_options_not_empty() {
99
+ let env = Env::default();
100
+ env.mock_all_auths();
101
+ let mut options = Bytes::new(&env);
102
+ options.push_back(1u8);
103
+
104
+ let params = DvnFeeParams {
105
+ sender: Address::generate(&env),
106
+ dst_eid: 1,
107
+ confirmations: 1,
108
+ options, // Non-empty options should panic
109
+ price_feed: Address::generate(&env),
110
+ default_multiplier_bps: 10_000,
111
+ quorum: 1,
112
+ gas: 1,
113
+ multiplier_bps: 10_000,
114
+ floor_margin_usd: 0,
115
+ };
116
+
117
+ DvnFeeLib::get_fee(&env, &params);
118
+ }
119
+
120
+ #[contract]
121
+ struct MockPriceFeed;
122
+
123
+ #[contractimpl]
124
+ impl MockPriceFeed {
125
+ pub fn estimate_fee_by_eid(
126
+ _env: &Env,
127
+ _fee_lib: &Address,
128
+ _dst_eid: u32,
129
+ _calldata_size: u32,
130
+ _gas: u128,
131
+ ) -> FeeEstimate {
132
+ FeeEstimate {
133
+ total_gas_fee: 100,
134
+ price_ratio: 0,
135
+ price_ratio_denominator: 0,
136
+ native_price_usd: NATIVE_DECIMALS_RATE,
137
+ }
138
+ }
139
+
140
+ pub fn native_token_price_usd(_env: &Env) -> u128 {
141
+ NATIVE_DECIMALS_RATE
142
+ }
143
+
144
+ pub fn set_native_token_price_usd(_env: &Env, _price_updater: &Address, _native_token_price_usd: u128) {}
145
+ }
146
+
147
+ #[contract]
148
+ struct MockPriceFeedNegative;
149
+
150
+ #[contractimpl]
151
+ impl MockPriceFeedNegative {
152
+ pub fn estimate_fee_by_eid(
153
+ _env: &Env,
154
+ _fee_lib: &Address,
155
+ _dst_eid: u32,
156
+ _calldata_size: u32,
157
+ _gas: u128,
158
+ ) -> FeeEstimate {
159
+ FeeEstimate {
160
+ total_gas_fee: -100,
161
+ price_ratio: 0,
162
+ price_ratio_denominator: 0,
163
+ native_price_usd: NATIVE_DECIMALS_RATE,
164
+ }
165
+ }
166
+ }
167
+
168
+ #[test]
169
+ fn get_fee_success_path() {
170
+ let env = Env::default();
171
+ env.mock_all_auths();
172
+ let fee_lib_addr = env.register(DvnFeeLib, ());
173
+ let params = DvnFeeParams {
174
+ sender: Address::generate(&env),
175
+ dst_eid: 7,
176
+ confirmations: 1,
177
+ options: Bytes::new(&env),
178
+ price_feed: env.register(MockPriceFeed, ()),
179
+ default_multiplier_bps: 10_000,
180
+ quorum: 2,
181
+ gas: 100,
182
+ multiplier_bps: 10_000,
183
+ floor_margin_usd: 0,
184
+ };
185
+
186
+ let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &params));
187
+
188
+ assert_eq!(fee, 100);
189
+ }
190
+
191
+ #[test]
192
+ fn get_fee_uses_default_multiplier_when_zero() {
193
+ let env = Env::default();
194
+ env.mock_all_auths();
195
+ let fee_lib_addr = env.register(DvnFeeLib, ());
196
+
197
+ // multiplier_bps = 0, default_multiplier_bps = 12000 (1.2x)
198
+ // MockPriceFeed returns fee=100, so result = 100 * 12000 / 10000 = 120
199
+ let params = DvnFeeParams {
200
+ sender: Address::generate(&env),
201
+ dst_eid: 7,
202
+ confirmations: 1,
203
+ options: Bytes::new(&env),
204
+ price_feed: env.register(MockPriceFeed, ()),
205
+ default_multiplier_bps: 12_000,
206
+ quorum: 2,
207
+ gas: 100,
208
+ multiplier_bps: 0, // Zero means use default
209
+ floor_margin_usd: 0,
210
+ };
211
+
212
+ let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &params));
213
+
214
+ assert_eq!(fee, 120);
215
+ }
216
+
217
+ #[test]
218
+ fn get_fee_prefers_dst_multiplier_over_default() {
219
+ let env = Env::default();
220
+ env.mock_all_auths();
221
+ let fee_lib_addr = env.register(DvnFeeLib, ());
222
+
223
+ // multiplier_bps = 15000 (1.5x), default_multiplier_bps = 10000 (1.0x)
224
+ // MockPriceFeed returns fee=100, so result = 100 * 15000 / 10000 = 150
225
+ let params = DvnFeeParams {
226
+ sender: Address::generate(&env),
227
+ dst_eid: 7,
228
+ confirmations: 1,
229
+ options: Bytes::new(&env),
230
+ price_feed: env.register(MockPriceFeed, ()),
231
+ default_multiplier_bps: 10_000,
232
+ quorum: 2,
233
+ gas: 100,
234
+ multiplier_bps: 15_000, // Prefer this over default
235
+ floor_margin_usd: 0,
236
+ };
237
+
238
+ let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &params));
239
+
240
+ assert_eq!(fee, 150);
241
+ }
242
+
243
+ #[test]
244
+ #[should_panic(expected = "Error(Contract, #3)")] // DvnFeeLibError::NegativeFee
245
+ fn get_fee_panics_when_price_feed_returns_negative_fee() {
246
+ let env = Env::default();
247
+ env.mock_all_auths();
248
+ let fee_lib_addr = env.register(DvnFeeLib, ());
249
+ let params = DvnFeeParams {
250
+ sender: Address::generate(&env),
251
+ dst_eid: 7,
252
+ confirmations: 1,
253
+ options: Bytes::new(&env),
254
+ price_feed: env.register(MockPriceFeedNegative, ()),
255
+ default_multiplier_bps: 10_000,
256
+ quorum: 2,
257
+ gas: 100,
258
+ multiplier_bps: 10_000,
259
+ floor_margin_usd: 0,
260
+ };
261
+
262
+ env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &params));
263
+ }
264
+
265
+ #[test]
266
+ #[should_panic]
267
+ fn apply_premium_panics_on_overflow() {
268
+ let fee = i128::MAX / 2;
269
+ let multiplier_bps = 20_000u32;
270
+ apply_premium(fee, multiplier_bps, 10_000, u128::MAX, 1);
271
+ }
272
+
273
+ #[test]
274
+ fn apply_premium_floor_margin_truncates() {
275
+ let fee = 0i128;
276
+ let multiplier_bps = 10_000u32;
277
+ let floor_margin_usd = 1u128;
278
+ let native_price_usd = NATIVE_DECIMALS_RATE * 3 / 2;
279
+
280
+ let result = apply_premium(fee, multiplier_bps, 10_000, floor_margin_usd, native_price_usd);
281
+ assert_eq!(result, 0);
282
+ }
@@ -0,0 +1 @@
1
+ mod dvn_fee_lib;