@layerzerolabs/protocol-stellar-v2 0.2.18 → 0.2.20

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 (213) hide show
  1. package/.turbo/turbo-build.log +303 -253
  2. package/.turbo/turbo-lint.log +66 -65
  3. package/.turbo/turbo-test.log +1312 -1282
  4. package/Cargo.lock +21 -8
  5. package/Cargo.toml +2 -0
  6. package/contracts/ERROR_SPEC.md +9 -2
  7. package/contracts/common-macros/src/contract_ttl.rs +18 -7
  8. package/contracts/common-macros/src/lib.rs +4 -4
  9. package/contracts/common-macros/src/tests/contract_ttl.rs +1 -1
  10. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +2 -1
  11. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +7 -12
  12. package/contracts/common-macros/src/upgradeable.rs +15 -21
  13. package/contracts/message-libs/uln-302/src/events.rs +4 -0
  14. package/contracts/message-libs/uln-302/src/send_uln.rs +23 -7
  15. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +38 -64
  16. package/contracts/oapps/counter/Cargo.toml +1 -0
  17. package/contracts/oapps/counter/integration_tests/setup_uln.rs +1 -1
  18. package/contracts/oapps/oapp/src/oapp_receiver.rs +1 -1
  19. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +113 -65
  20. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +111 -82
  21. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +293 -65
  22. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +331 -56
  23. package/contracts/oapps/oft/Cargo.toml +10 -7
  24. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
  25. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
  26. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
  27. package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
  28. package/contracts/oapps/oft/integration-tests/setup.rs +28 -127
  29. package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
  30. package/contracts/oapps/oft/src/extensions/oft_fee.rs +23 -8
  31. package/contracts/oapps/oft/src/extensions/pausable.rs +19 -4
  32. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +52 -28
  33. package/contracts/oapps/oft/src/lib.rs +10 -14
  34. package/contracts/oapps/oft/src/oft.rs +143 -193
  35. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -11
  36. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +12 -14
  37. package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
  38. package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
  39. package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
  40. package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +126 -29
  41. package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
  42. package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
  43. package/contracts/oapps/oft-core/src/lib.rs +18 -0
  44. package/contracts/oapps/oft-core/src/oft_core.rs +479 -0
  45. package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
  46. package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
  47. package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -4
  48. package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
  49. package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +46 -27
  50. package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
  51. package/contracts/upgrader/src/lib.rs +30 -57
  52. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  53. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  54. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  55. package/contracts/utils/src/buffer_reader.rs +1 -0
  56. package/contracts/utils/src/errors.rs +8 -2
  57. package/contracts/utils/src/ownable.rs +125 -3
  58. package/contracts/utils/src/tests/option_ext.rs +1 -1
  59. package/contracts/utils/src/tests/ownable.rs +445 -7
  60. package/contracts/utils/src/tests/ttl_configurable.rs +2 -2
  61. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  62. package/contracts/utils/src/ttl_configurable.rs +3 -3
  63. package/contracts/utils/src/upgradeable.rs +48 -23
  64. package/contracts/workers/dvn/Cargo.toml +1 -0
  65. package/contracts/workers/dvn/src/auth.rs +12 -42
  66. package/contracts/workers/dvn/src/dvn.rs +16 -31
  67. package/contracts/workers/dvn/src/errors.rs +0 -1
  68. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  69. package/contracts/workers/dvn/src/lib.rs +4 -3
  70. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  71. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  72. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  73. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  74. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  75. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  76. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -5
  77. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +2 -3
  78. package/contracts/workers/executor/Cargo.toml +1 -0
  79. package/contracts/workers/executor/src/executor.rs +15 -26
  80. package/contracts/workers/executor-fee-lib/Cargo.toml +2 -1
  81. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +63 -5
  82. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  83. package/contracts/workers/executor-fee-lib/src/lib.rs +3 -0
  84. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  85. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  86. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  87. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  88. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  89. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  90. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  91. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  92. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  93. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  94. package/contracts/workers/price-feed/Cargo.toml +2 -1
  95. package/contracts/workers/price-feed/src/events.rs +1 -1
  96. package/contracts/workers/price-feed/src/lib.rs +3 -0
  97. package/contracts/workers/price-feed/src/price_feed.rs +6 -12
  98. package/contracts/workers/price-feed/src/storage.rs +1 -1
  99. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  100. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  101. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  102. package/contracts/workers/price-feed/src/types.rs +1 -1
  103. package/contracts/workers/worker/src/errors.rs +0 -3
  104. package/contracts/workers/worker/src/lib.rs +0 -2
  105. package/contracts/workers/worker/src/storage.rs +32 -29
  106. package/contracts/workers/worker/src/tests/setup.rs +1 -7
  107. package/contracts/workers/worker/src/tests/worker.rs +50 -42
  108. package/contracts/workers/worker/src/worker.rs +49 -58
  109. package/package.json +4 -5
  110. package/sdk/.turbo/turbo-test.log +229 -217
  111. package/sdk/dist/generated/bml.d.ts +39 -1
  112. package/sdk/dist/generated/bml.js +33 -8
  113. package/sdk/dist/generated/counter.d.ts +131 -3
  114. package/sdk/dist/generated/counter.js +41 -10
  115. package/sdk/dist/generated/dvn.d.ts +431 -362
  116. package/sdk/dist/generated/dvn.js +80 -55
  117. package/sdk/dist/generated/dvn_fee_lib.d.ts +327 -251
  118. package/sdk/dist/generated/dvn_fee_lib.js +55 -57
  119. package/sdk/dist/generated/endpoint.d.ts +131 -3
  120. package/sdk/dist/generated/endpoint.js +41 -10
  121. package/sdk/dist/generated/executor.d.ts +503 -339
  122. package/sdk/dist/generated/executor.js +80 -48
  123. package/sdk/dist/generated/executor_fee_lib.d.ts +395 -319
  124. package/sdk/dist/generated/executor_fee_lib.js +54 -56
  125. package/sdk/dist/generated/executor_helper.d.ts +53 -187
  126. package/sdk/dist/generated/executor_helper.js +47 -29
  127. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  128. package/sdk/dist/generated/layerzero_view.js +294 -0
  129. package/sdk/dist/generated/oft.d.ts +1851 -0
  130. package/sdk/dist/generated/oft.js +347 -0
  131. package/sdk/dist/generated/price_feed.d.ts +329 -253
  132. package/sdk/dist/generated/price_feed.js +55 -57
  133. package/sdk/dist/generated/sml.d.ts +131 -3
  134. package/sdk/dist/generated/sml.js +41 -10
  135. package/sdk/dist/generated/treasury.d.ts +131 -3
  136. package/sdk/dist/generated/treasury.js +41 -10
  137. package/sdk/dist/generated/uln302.d.ts +131 -3
  138. package/sdk/dist/generated/uln302.js +43 -12
  139. package/sdk/dist/generated/upgrader.d.ts +201 -15
  140. package/sdk/dist/generated/upgrader.js +99 -1
  141. package/sdk/dist/index.d.ts +2 -2
  142. package/sdk/dist/index.js +3 -3
  143. package/sdk/package.json +3 -2
  144. package/sdk/src/index.ts +3 -3
  145. package/sdk/test/oft-sml.test.ts +20 -20
  146. package/sdk/test/upgrader.test.ts +2 -3
  147. package/sdk/turbo.json +8 -0
  148. package/tools/ts-bindings-gen/Cargo.toml +2 -0
  149. package/tools/ts-bindings-gen/src/main.rs +53 -5
  150. package/turbo.json +0 -2
  151. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
  152. package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
  153. package/contracts/oapps/oft/src/oft_impl.rs +0 -201
  154. package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
  155. package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -917
  156. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -751
  157. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -434
  158. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1080
  159. package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
  160. package/contracts/oapps/oft-std/src/lib.rs +0 -16
  161. package/contracts/oapps/oft-std/src/oft.rs +0 -174
  162. package/sdk/dist/generated/oft_std.d.ts +0 -1722
  163. package/sdk/dist/generated/oft_std.js +0 -316
  164. package/sdk/dist/wasm/blocked-message-lib.d.ts +0 -1
  165. package/sdk/dist/wasm/blocked-message-lib.js +0 -2
  166. package/sdk/dist/wasm/counter.d.ts +0 -1
  167. package/sdk/dist/wasm/counter.js +0 -2
  168. package/sdk/dist/wasm/dvn-fee-lib.d.ts +0 -1
  169. package/sdk/dist/wasm/dvn-fee-lib.js +0 -2
  170. package/sdk/dist/wasm/dvn.d.ts +0 -1
  171. package/sdk/dist/wasm/dvn.js +0 -2
  172. package/sdk/dist/wasm/endpoint-v2.d.ts +0 -1
  173. package/sdk/dist/wasm/endpoint-v2.js +0 -2
  174. package/sdk/dist/wasm/executor-fee-lib.d.ts +0 -1
  175. package/sdk/dist/wasm/executor-fee-lib.js +0 -2
  176. package/sdk/dist/wasm/executor-helper.d.ts +0 -1
  177. package/sdk/dist/wasm/executor-helper.js +0 -2
  178. package/sdk/dist/wasm/executor.d.ts +0 -1
  179. package/sdk/dist/wasm/executor.js +0 -2
  180. package/sdk/dist/wasm/layerzero-views.d.ts +0 -1
  181. package/sdk/dist/wasm/layerzero-views.js +0 -2
  182. package/sdk/dist/wasm/oft-std.d.ts +0 -1
  183. package/sdk/dist/wasm/oft-std.js +0 -2
  184. package/sdk/dist/wasm/price-feed.d.ts +0 -1
  185. package/sdk/dist/wasm/price-feed.js +0 -2
  186. package/sdk/dist/wasm/simple-message-lib.d.ts +0 -1
  187. package/sdk/dist/wasm/simple-message-lib.js +0 -2
  188. package/sdk/dist/wasm/treasury.d.ts +0 -1
  189. package/sdk/dist/wasm/treasury.js +0 -2
  190. package/sdk/dist/wasm/uln302.d.ts +0 -1
  191. package/sdk/dist/wasm/uln302.js +0 -2
  192. package/sdk/dist/wasm/upgrader.d.ts +0 -1
  193. package/sdk/dist/wasm/upgrader.js +0 -2
  194. package/sdk/dist/wasm.d.ts +0 -15
  195. package/sdk/dist/wasm.js +0 -15
  196. /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
  197. /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
  198. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
  199. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
  200. /package/contracts/oapps/{oft → oft-core}/src/errors.rs +0 -0
  201. /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
  202. /package/contracts/oapps/{oft → oft-core}/src/storage.rs +0 -0
  203. /package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +0 -0
  204. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
  205. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
  206. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
  207. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
  208. /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
  209. /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
  210. /package/contracts/oapps/{oft → oft-core}/src/types.rs +0 -0
  211. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  212. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  213. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -0,0 +1,869 @@
1
+ use super::setup::TestSetup;
2
+ use crate::errors::PriceFeedError;
3
+ use crate::types::{ArbitrumPriceExt, ModelType, SetEidToModelTypeParam, UpdatePrice, UpdatePriceExt};
4
+ use fee_lib_interfaces::Price;
5
+ use soroban_sdk::{testutils::Address as _, vec, Address};
6
+
7
+ // =============================================================================
8
+ // Construction
9
+ // =============================================================================
10
+
11
+ #[test]
12
+ fn test_constructor_sets_owner_and_price_updater() {
13
+ let setup = TestSetup::new();
14
+
15
+ assert_eq!(setup.client.owner(), Some(setup.owner.clone()));
16
+ assert_eq!(setup.client.is_price_updater(&setup.price_updater), true);
17
+ }
18
+
19
+ #[test]
20
+ fn test_constructor_sets_default_values() {
21
+ let setup = TestSetup::new();
22
+
23
+ // Default price ratio denominator is 1e20
24
+ assert_eq!(setup.client.get_price_ratio_denominator(), 10u128.pow(20));
25
+
26
+ // Default Arbitrum compression percent is 47
27
+ assert_eq!(setup.client.arbitrum_compression_percent(), 47);
28
+
29
+ // Default native price USD is 0
30
+ assert_eq!(setup.client.native_token_price_usd(), 0);
31
+
32
+ // Default Arbitrum price ext
33
+ let arb_ext = setup.client.arbitrum_price_ext();
34
+ assert_eq!(arb_ext.gas_per_l2_tx, 0);
35
+ assert_eq!(arb_ext.gas_per_l1_calldata_byte, 0);
36
+ }
37
+
38
+ // =============================================================================
39
+ // ILayerZeroPriceFeed (Trait Impl) - view fns
40
+ // =============================================================================
41
+
42
+ #[test]
43
+ fn test_get_price_returns_none_for_unconfigured_eid() {
44
+ let setup = TestSetup::new();
45
+ assert_eq!(setup.client.get_price(&999), None);
46
+ }
47
+
48
+ // =============================================================================
49
+ // ILayerZeroPriceFeed (Trait Impl) - estimate_fee_by_eid
50
+ // =============================================================================
51
+
52
+ #[test]
53
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
54
+ fn test_estimate_fee_by_eid_requires_fee_lib_auth() {
55
+ let setup = TestSetup::new();
56
+ let fee_lib = Address::generate(&setup.env);
57
+
58
+ // Set up price so only fee_lib.require_auth() can fail.
59
+ let price = setup.default_test_price();
60
+ setup.setup_default_price(1, &price);
61
+
62
+ // No mock_auths for fee_lib.require_auth()
63
+ setup.client.estimate_fee_by_eid(&fee_lib, &1, &100, &100_000);
64
+ }
65
+
66
+ #[test]
67
+ fn test_estimate_fee_by_eid_default_model() {
68
+ let setup = TestSetup::new();
69
+ let fee_lib = Address::generate(&setup.env);
70
+
71
+ // Reference values:
72
+ // - EID: 101
73
+ // - calldata_size: 1000
74
+ // - gas: 500
75
+ let price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 1_000_000_000, gas_per_byte: 16 };
76
+ setup.setup_default_price(101, &price);
77
+
78
+ // Authorize fee_lib
79
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 101u32, 1000u32, 500u128));
80
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &101, &1000, &500);
81
+
82
+ // Expected fee (default model):
83
+ // gas_for_calldata = calldata_size * gas_per_byte
84
+ // = 1000 * 16
85
+ // = 16_000
86
+ // remote_fee = (gas_for_calldata + gas) * gas_price_in_unit
87
+ // = (16_000 + 500) * 1_000_000_000
88
+ // = 16_500 * 1_000_000_000
89
+ // = 16_500_000_000_000
90
+ // fee = (remote_fee * price_ratio) / price_ratio_denominator
91
+ // = (16_500_000_000_000 * 1e20) / 1e20
92
+ // = 16_500_000_000_000
93
+
94
+ assert_eq!(estimate.total_gas_fee, 16_500_000_000_000 as i128);
95
+ assert_eq!(estimate.price_ratio, price.price_ratio);
96
+ assert_eq!(estimate.price_ratio_denominator, 10u128.pow(20));
97
+ assert_eq!(estimate.native_price_usd, 0);
98
+ }
99
+
100
+ #[test]
101
+ fn test_estimate_fee_by_eid_includes_native_price_usd_and_denominator() {
102
+ let setup = TestSetup::new();
103
+ let fee_lib = Address::generate(&setup.env);
104
+
105
+ // Change denominator from the default and ensure it's reflected in FeeEstimate.
106
+ let denom = 10u128.pow(18);
107
+ setup.mock_owner_auth("set_price_ratio_denominator", (denom,));
108
+ setup.client.set_price_ratio_denominator(&denom);
109
+
110
+ // Set native token USD price and ensure it's reflected in FeeEstimate.
111
+ let price_usd = 1234 * 10u128.pow(18);
112
+ setup.mock_price_updater_auth("set_native_token_price_usd", (&setup.price_updater, price_usd));
113
+ setup.client.set_native_token_price_usd(&setup.price_updater, &price_usd);
114
+
115
+ // Use price_ratio == denom so fee math stays 1:1 and easy to validate.
116
+ let price = Price { price_ratio: denom, gas_price_in_unit: 1_000_000, gas_per_byte: 16 };
117
+ setup.setup_default_price(1, &price);
118
+
119
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 1u32, 10u32, 1_000u128));
120
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &1, &10, &1_000);
121
+
122
+ assert_eq!(estimate.price_ratio_denominator, denom);
123
+ assert_eq!(estimate.native_price_usd, price_usd);
124
+ }
125
+
126
+ #[test]
127
+ fn test_estimate_fee_by_eid_with_different_price_ratio() {
128
+ let setup = TestSetup::new();
129
+ let fee_lib = Address::generate(&setup.env);
130
+
131
+ // Set up price with 2:1 price ratio (destination token worth 2x source token)
132
+ let price = Price {
133
+ price_ratio: 2 * 10u128.pow(20), // 2x ratio
134
+ gas_price_in_unit: 1_000_000,
135
+ gas_per_byte: 16,
136
+ };
137
+ setup.setup_default_price(1, &price);
138
+
139
+ // Authorize fee_lib
140
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 1u32, 100u32, 100_000u128));
141
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &1, &100, &100_000);
142
+
143
+ // Fee should be 2x compared to 1:1 ratio:
144
+ // gas_for_calldata = calldata_size * gas_per_byte
145
+ // = 100 * 16
146
+ // = 1_600
147
+ // remote_fee = (gas_for_calldata + gas) * gas_price_in_unit
148
+ // = (1_600 + 100_000) * 1_000_000
149
+ // = 101_600 * 1_000_000
150
+ // = 101_600_000_000
151
+ // fee = (remote_fee * price_ratio) / price_ratio_denominator
152
+ // = (101_600_000_000 * (2 * 1e20)) / 1e20
153
+ // = 203_200_000_000
154
+ let gas_for_calldata = 100u128 * 16;
155
+ let remote_fee = (gas_for_calldata + 100_000) * 1_000_000;
156
+ let expected_fee = (remote_fee * price.price_ratio) / 10u128.pow(20);
157
+
158
+ assert_eq!(estimate.total_gas_fee, expected_fee as i128);
159
+ assert_eq!(estimate.price_ratio, price.price_ratio);
160
+ }
161
+
162
+ #[test]
163
+ fn test_estimate_fee_by_eid_rejects_missing_price() {
164
+ let setup = TestSetup::new();
165
+ let fee_lib = Address::generate(&setup.env);
166
+
167
+ // No price set for EID 999
168
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 999u32, 100u32, 100_000u128));
169
+ assert_eq!(
170
+ setup.client.try_estimate_fee_by_eid(&fee_lib, &999, &100, &100_000).unwrap_err().unwrap(),
171
+ PriceFeedError::NoPrice.into()
172
+ );
173
+ }
174
+
175
+ // =============================================================================
176
+ // ILayerZeroPriceFeed - estimate_fee_by_eid (Arbitrum Model)
177
+ // =============================================================================
178
+
179
+ #[test]
180
+ fn test_estimate_fee_by_eid_arbitrum_model_hardcoded_eid() {
181
+ let setup = TestSetup::new();
182
+ let fee_lib = Address::generate(&setup.env);
183
+
184
+ // EID 110 is hardcoded as Arbitrum
185
+ let arb_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 10_000_000, gas_per_byte: 16 };
186
+ setup.setup_default_price(110, &arb_price);
187
+
188
+ // Set Arbitrum-specific parameters
189
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 4176, gas_per_l1_calldata_byte: 29 };
190
+ let update = UpdatePriceExt { eid: 110, price: arb_price.clone(), extend: arb_ext.clone() };
191
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &update));
192
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &update);
193
+
194
+ // Authorize fee_lib
195
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 110u32, 1000u32, 500u128));
196
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &110, &1000, &500);
197
+
198
+ // Expected fee (Arbitrum model):
199
+ // compressed_size = floor(calldata_size * compression_percent / 100)
200
+ // = floor((1000 * 47) / 100)
201
+ // = floor(470)
202
+ // = 470
203
+ // l1_calldata_gas = compressed_size * gas_per_l1_calldata_byte
204
+ // = 470 * 29
205
+ // = 13_630
206
+ // l2_calldata_gas = calldata_size * gas_per_byte
207
+ // = 1000 * 16
208
+ // = 16_000
209
+ // total_gas = gas + gas_per_l2_tx + l1_calldata_gas + l2_calldata_gas
210
+ // = 500 + 4_176 + 13_630 + 16_000
211
+ // = 34_306
212
+ // fee = (total_gas * gas_price_in_unit * price_ratio) / price_ratio_denominator
213
+ // = (34_306 * 10_000_000 * 1e20) / 1e20
214
+ // = 34_306 * 10_000_000
215
+ // = 343_060_000_000
216
+
217
+ assert_eq!(estimate.total_gas_fee, 343_060_000_000 as i128);
218
+
219
+ assert_eq!(estimate.price_ratio, arb_price.price_ratio);
220
+ }
221
+
222
+ #[test]
223
+ fn test_estimate_fee_by_eid_arbitrum_model_hardcoded_eid_10143() {
224
+ let setup = TestSetup::new();
225
+ let fee_lib = Address::generate(&setup.env);
226
+
227
+ // EID 10143 is hardcoded as Arbitrum (same model selection path as 110).
228
+ // Use the same inputs as `test_estimate_fee_by_eid_arbitrum_model_hardcoded_eid`
229
+ // so the computed fee should be identical.
230
+ let arb_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 10_000_000, gas_per_byte: 16 };
231
+
232
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 4176, gas_per_l1_calldata_byte: 29 };
233
+ let update = UpdatePriceExt { eid: 10143, price: arb_price.clone(), extend: arb_ext };
234
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &update));
235
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &update);
236
+
237
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 10143u32, 1000u32, 500u128));
238
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &10143, &1000, &500);
239
+
240
+ assert_eq!(estimate.total_gas_fee, 343_060_000_000 as i128);
241
+ assert_eq!(estimate.price_ratio, arb_price.price_ratio);
242
+ }
243
+
244
+ #[test]
245
+ fn test_estimate_fee_by_eid_arbitrum_model_uses_updated_compression_percent() {
246
+ let setup = TestSetup::new();
247
+ let fee_lib = Address::generate(&setup.env);
248
+
249
+ // Choose parameters so fee is driven purely by compression percent.
250
+ // gas=0, gas_per_l2_tx=0, gas_per_byte=0, gas_price_in_unit=1
251
+ let denom = setup.client.get_price_ratio_denominator();
252
+ let arb_price = Price { price_ratio: denom, gas_price_in_unit: 1, gas_per_byte: 0 };
253
+ setup.setup_default_price(110, &arb_price);
254
+
255
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 0, gas_per_l1_calldata_byte: 100 };
256
+ let update = UpdatePriceExt { eid: 110, price: arb_price.clone(), extend: arb_ext };
257
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &update));
258
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &update);
259
+
260
+ // compression=0 => L1 calldata fee is 0 => total fee is 0
261
+ setup.mock_owner_auth("set_arbitrum_compression_percent", (0u128,));
262
+ setup.client.set_arbitrum_compression_percent(&0u128);
263
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 110u32, 100u32, 0u128));
264
+ let est0 = setup.client.estimate_fee_by_eid(&fee_lib, &110, &100, &0);
265
+ assert_eq!(est0.total_gas_fee, 0);
266
+
267
+ // compression=100 => L1 calldata gas = (100 * 100 / 100) * 100 = 10_000
268
+ setup.mock_owner_auth("set_arbitrum_compression_percent", (100u128,));
269
+ setup.client.set_arbitrum_compression_percent(&100u128);
270
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 110u32, 100u32, 0u128));
271
+ let est100 = setup.client.estimate_fee_by_eid(&fee_lib, &110, &100, &0);
272
+ assert_eq!(est100.total_gas_fee, 10_000);
273
+ }
274
+
275
+ #[test]
276
+ fn test_estimate_fee_by_eid_arbitrum_model_configured_eid() {
277
+ let setup = TestSetup::new();
278
+ let fee_lib = Address::generate(&setup.env);
279
+
280
+ // Configure EID 200 as ArbStack
281
+ let params = vec![&setup.env, SetEidToModelTypeParam { dst_eid: 200, model_type: ModelType::ArbStack }];
282
+ setup.mock_owner_auth("set_eid_to_model_type", (&params,));
283
+ setup.client.set_eid_to_model_type(&params);
284
+
285
+ // Set price for EID 200
286
+ // Use the same parameters as `test_estimate_fee_by_eid_arbitrum_model_hardcoded_eid`
287
+ // so the expected fee is deterministic and validates the Arbitrum-model math path.
288
+ let arb_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 10_000_000, gas_per_byte: 16 };
289
+ setup.setup_default_price(200, &arb_price);
290
+
291
+ // Set Arbitrum-specific parameters
292
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 4176, gas_per_l1_calldata_byte: 29 };
293
+ let update = UpdatePriceExt { eid: 200, price: arb_price.clone(), extend: arb_ext };
294
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &update));
295
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &update);
296
+
297
+ // Authorize fee_lib
298
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 200u32, 1000u32, 500u128));
299
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &200, &1000, &500);
300
+
301
+ // Should use Arbitrum model (same expected fee as the hardcoded Arbitrum test)
302
+ assert_eq!(estimate.total_gas_fee, 343_060_000_000 as i128);
303
+ assert_eq!(estimate.price_ratio, arb_price.price_ratio);
304
+ }
305
+
306
+ // =============================================================================
307
+ // ILayerZeroPriceFeed - estimate_fee_by_eid (Optimism Model)
308
+ // =============================================================================
309
+
310
+ #[test]
311
+ fn test_estimate_fee_by_eid_optimism_model_hardcoded_eid() {
312
+ let setup = TestSetup::new();
313
+ let fee_lib = Address::generate(&setup.env);
314
+
315
+ // EID 111 is hardcoded as Optimism
316
+ // Need to set up both L1 (Ethereum) and L2 (Optimism) prices
317
+
318
+ let eth_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 646_718_991, gas_per_byte: 8 };
319
+ setup.setup_default_price(101, &eth_price);
320
+
321
+ // Optimism price
322
+ let op_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 2_231_118, gas_per_byte: 16 };
323
+ setup.setup_default_price(111, &op_price);
324
+
325
+ // Authorize fee_lib
326
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 111u32, 1000u32, 500u128));
327
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &111, &1000, &500);
328
+
329
+ // Expected fee (Optimism model):
330
+ // l1_fee = ((calldata_size * eth_gas_per_byte) + 3188) * eth_gas_price
331
+ // = ((1000 * 8) + 3188) * 646_718_991
332
+ // = (8_000 + 3_188) * 646_718_991
333
+ // = 11_188 * 646_718_991
334
+ // = 7_235_492_071_308
335
+ // l2_fee = ((calldata_size * op_gas_per_byte) + gas) * op_gas_price
336
+ // = ((1000 * 16) + 500) * 2_231_118
337
+ // = (16_000 + 500) * 2_231_118
338
+ // = 16_500 * 2_231_118
339
+ // = 36_813_447_000
340
+ // total_fee = (l1_fee * eth_price_ratio / denom) + (l2_fee * op_price_ratio / denom)
341
+ // = (7_235_492_071_308 * 1e20 / 1e20) + (36_813_447_000 * 1e20 / 1e20)
342
+ // = 7_235_492_071_308 + 36_813_447_000
343
+ // = 7_272_305_518_308
344
+
345
+ assert_eq!(estimate.total_gas_fee, 7_272_305_518_308 as i128);
346
+ assert_eq!(estimate.price_ratio, op_price.price_ratio);
347
+ }
348
+
349
+ #[test]
350
+ fn test_estimate_fee_by_eid_optimism_model_goerli() {
351
+ let setup = TestSetup::new();
352
+ let fee_lib = Address::generate(&setup.env);
353
+
354
+ // EID 10132 is hardcoded as Optimism Goerli
355
+ // L1 lookup should be 10121 (Ethereum Goerli)
356
+
357
+ // Ethereum Goerli price
358
+ let eth_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 646_718_991, gas_per_byte: 8 };
359
+ setup.setup_default_price(10121, &eth_price);
360
+
361
+ // Optimism Goerli price
362
+ let op_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 2_231_118, gas_per_byte: 16 };
363
+ setup.setup_default_price(10132, &op_price);
364
+
365
+ // Authorize fee_lib
366
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 10132u32, 1000u32, 500u128));
367
+ let estimate = setup.client.estimate_fee_by_eid(&fee_lib, &10132, &1000, &500);
368
+
369
+ // Deterministic expected fee (Optimism model):
370
+ // L1 uses Ethereum Goerli (10121) with +3188 overhead, L2 uses Optimism Goerli (10132).
371
+ assert_eq!(estimate.total_gas_fee, 7_272_305_518_308 as i128);
372
+ assert_eq!(estimate.price_ratio, op_price.price_ratio);
373
+ }
374
+ #[test]
375
+ fn test_estimate_fee_by_eid_optimism_model_configured_eid() {
376
+ let setup = TestSetup::new();
377
+ let fee_lib = Address::generate(&setup.env);
378
+
379
+ // Configure EID 300 as OpStack
380
+ let params = vec![&setup.env, SetEidToModelTypeParam { dst_eid: 300, model_type: ModelType::OpStack }];
381
+ setup.mock_owner_auth("set_eid_to_model_type", (&params,));
382
+ setup.client.set_eid_to_model_type(&params);
383
+
384
+ // Set up Ethereum price (EID 101 used for mainnet L1 lookup)
385
+ let eth_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 646_718_991, gas_per_byte: 8 };
386
+ setup.setup_default_price(101, &eth_price);
387
+
388
+ // Set up Optimism price for EID 300
389
+ let op_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 2_231_118, gas_per_byte: 16 };
390
+ setup.setup_default_price(300, &op_price);
391
+
392
+ // Compare configured OpStack EID (300) to hardcoded Optimism EID (111).
393
+ // With identical L1/L2 prices and inputs, the fee must match if we are on the Optimism path.
394
+ setup.setup_default_price(111, &op_price);
395
+
396
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 300u32, 1000u32, 500u128));
397
+ let estimate_300 = setup.client.estimate_fee_by_eid(&fee_lib, &300, &1000, &500);
398
+
399
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 111u32, 1000u32, 500u128));
400
+ let estimate_111 = setup.client.estimate_fee_by_eid(&fee_lib, &111, &1000, &500);
401
+
402
+ assert_eq!(estimate_300.total_gas_fee, estimate_111.total_gas_fee);
403
+ assert_eq!(estimate_300.price_ratio, estimate_111.price_ratio);
404
+ assert_eq!(estimate_300.price_ratio, op_price.price_ratio);
405
+ assert_eq!(estimate_300.total_gas_fee, 7_272_305_518_308 as i128);
406
+ }
407
+
408
+ #[test]
409
+ fn test_estimate_fee_by_eid_uses_modulo_for_model_type_and_price_lookup_non_hardcoded() {
410
+ let setup = TestSetup::new();
411
+ let fee_lib = Address::generate(&setup.env);
412
+
413
+ // Pick a non-hardcoded EID so the "configured model type" branch is used.
414
+ // 42_345 % 30_000 == 12_345.
415
+ let base_eid: u32 = 12_345;
416
+ let dst_eid: u32 = 42_345;
417
+
418
+ // Configure base EID as OpStack.
419
+ let params = vec![&setup.env, SetEidToModelTypeParam { dst_eid: base_eid, model_type: ModelType::OpStack }];
420
+ setup.mock_owner_auth("set_eid_to_model_type", (&params,));
421
+ setup.client.set_eid_to_model_type(&params);
422
+
423
+ // For 12_345 (>= 10_000 and < 20_000), L1 lookup id is 10_161 per contract logic.
424
+ let l1_lookup: u32 = 10_161;
425
+ let eth_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 10_000_000_000, gas_per_byte: 16 };
426
+ setup.setup_default_price(l1_lookup, &eth_price);
427
+
428
+ // L2 price for base_eid (the modulo'd EID that gets used for lookup).
429
+ let l2_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 1_000_000, gas_per_byte: 16 };
430
+ setup.setup_default_price(base_eid, &l2_price);
431
+
432
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, dst_eid, 100u32, 100_000u128));
433
+ let estimate_dst = setup.client.estimate_fee_by_eid(&fee_lib, &dst_eid, &100, &100_000);
434
+
435
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, base_eid, 100u32, 100_000u128));
436
+ let estimate_base = setup.client.estimate_fee_by_eid(&fee_lib, &base_eid, &100, &100_000);
437
+
438
+ // The contract reduces dst_eid modulo 30_000 before model selection and price lookup.
439
+ assert_eq!(estimate_dst.total_gas_fee, estimate_base.total_gas_fee);
440
+ assert_eq!(estimate_dst.price_ratio, estimate_base.price_ratio);
441
+ assert_eq!(estimate_dst.price_ratio, l2_price.price_ratio);
442
+ }
443
+
444
+ // =============================================================================
445
+ // ILayerZeroPriceFeed - estimate_fee_by_eid (EID modulo 30000)
446
+ // =============================================================================
447
+
448
+ #[test]
449
+ fn test_estimate_fee_by_eid_modulo_30000() {
450
+ let setup = TestSetup::new();
451
+ let fee_lib = Address::generate(&setup.env);
452
+
453
+ // EID 30110 is treated as 110 (Arbitrum) after modulo 30000
454
+ // The price lookup uses the modulo'd EID (110), so we set price for EID 110
455
+ let arb_price = Price { price_ratio: 10u128.pow(20), gas_price_in_unit: 100_000, gas_per_byte: 8 };
456
+ setup.setup_default_price(110, &arb_price);
457
+
458
+ // Set Arbitrum-specific parameters (stored globally, not per-EID)
459
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 50_000, gas_per_l1_calldata_byte: 16 };
460
+ let update = UpdatePriceExt { eid: 110, price: arb_price.clone(), extend: arb_ext };
461
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &update));
462
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &update);
463
+
464
+ // Authorize fee_lib for EID 30110 (the original EID in the call)
465
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 30110u32, 100u32, 100_000u128));
466
+ let estimate_30110 = setup.client.estimate_fee_by_eid(&fee_lib, &30110, &100, &100_000);
467
+
468
+ setup.mock_auth(&fee_lib, "estimate_fee_by_eid", (&fee_lib, 110u32, 100u32, 100_000u128));
469
+ let estimate_110 = setup.client.estimate_fee_by_eid(&fee_lib, &110, &100, &100_000);
470
+
471
+ // Should use Arbitrum model because 30110 % 30000 = 110
472
+ assert_eq!(estimate_30110.total_gas_fee, estimate_110.total_gas_fee);
473
+ assert_eq!(estimate_30110.price_ratio, estimate_110.price_ratio);
474
+ assert_eq!(estimate_30110.price_ratio, arb_price.price_ratio);
475
+ }
476
+
477
+ #[test]
478
+ fn test_estimate_fee_by_eid_rejects_when_fee_exceeds_i128_max() {
479
+ let setup = TestSetup::new();
480
+ let fee_lib = Address::generate(&setup.env);
481
+
482
+ // Avoid auth-mock brittleness for this pure arithmetic/overflow test.
483
+ setup.env.mock_all_auths();
484
+
485
+ // Use denominator=1 and price_ratio=1 so we avoid u128 overflow in
486
+ // `remote_fee * price_ratio` while still being able to exceed i128::MAX.
487
+ setup.client.set_price_ratio_denominator(&1u128);
488
+
489
+ let price = Price { price_ratio: 1u128, gas_price_in_unit: 1u64, gas_per_byte: 0 };
490
+ let prices = vec![&setup.env, UpdatePrice { eid: 1, price: price.clone() }];
491
+ setup.client.set_price(&setup.price_updater, &prices);
492
+
493
+ // With calldata_size=0 and gas_per_byte=0:
494
+ // remote_fee = gas * 1 == gas, and fee == remote_fee (since denom=1, ratio=1).
495
+ let gas: u128 = (i128::MAX as u128) + 1;
496
+ assert_eq!(
497
+ setup.client.try_estimate_fee_by_eid(&fee_lib, &1, &0, &gas).unwrap_err().unwrap(),
498
+ PriceFeedError::Overflow.into()
499
+ );
500
+ }
501
+
502
+ // =============================================================================
503
+ // Owner Functions
504
+ // =============================================================================
505
+
506
+ #[test]
507
+ fn test_set_price_updater_add_and_remove() {
508
+ let setup = TestSetup::new();
509
+ let new_updater = Address::generate(&setup.env);
510
+
511
+ // Add new price updater
512
+ setup.mock_owner_auth("set_price_updater", (&new_updater, true));
513
+ setup.client.set_price_updater(&new_updater, &true);
514
+ assert_eq!(setup.client.is_price_updater(&new_updater), true);
515
+
516
+ // Remove price updater
517
+ setup.mock_owner_auth("set_price_updater", (&new_updater, false));
518
+ setup.client.set_price_updater(&new_updater, &false);
519
+ assert_eq!(setup.client.is_price_updater(&new_updater), false);
520
+ }
521
+
522
+ #[test]
523
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
524
+ fn test_set_price_updater_requires_owner_auth() {
525
+ let setup = TestSetup::new();
526
+ let new_updater = Address::generate(&setup.env);
527
+
528
+ // No mock_auths -> owner.require_auth() must fail
529
+ setup.client.set_price_updater(&new_updater, &true);
530
+ }
531
+
532
+ #[test]
533
+ fn test_set_price_ratio_denominator_success() {
534
+ let setup = TestSetup::new();
535
+ let new_denominator = 100_000_000u128;
536
+
537
+ setup.mock_owner_auth("set_price_ratio_denominator", (new_denominator,));
538
+ setup.client.set_price_ratio_denominator(&new_denominator);
539
+
540
+ assert_eq!(setup.client.get_price_ratio_denominator(), new_denominator);
541
+ }
542
+
543
+ #[test]
544
+ fn test_set_price_ratio_denominator_rejects_zero() {
545
+ let setup = TestSetup::new();
546
+
547
+ setup.mock_owner_auth("set_price_ratio_denominator", (0u128,));
548
+ assert_eq!(
549
+ setup.client.try_set_price_ratio_denominator(&0).unwrap_err().unwrap(),
550
+ PriceFeedError::InvalidDenominator.into()
551
+ );
552
+ }
553
+
554
+ #[test]
555
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
556
+ fn test_set_price_ratio_denominator_requires_owner_auth() {
557
+ let setup = TestSetup::new();
558
+ let new_denominator = 10u128.pow(18);
559
+
560
+ // No mock_auths
561
+ setup.client.set_price_ratio_denominator(&new_denominator);
562
+ }
563
+
564
+ #[test]
565
+ fn test_set_arbitrum_compression_percent_success() {
566
+ let setup = TestSetup::new();
567
+ let new_percent = 50u128;
568
+
569
+ setup.mock_owner_auth("set_arbitrum_compression_percent", (new_percent,));
570
+ setup.client.set_arbitrum_compression_percent(&new_percent);
571
+
572
+ assert_eq!(setup.client.arbitrum_compression_percent(), new_percent);
573
+ }
574
+
575
+ #[test]
576
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
577
+ fn test_set_arbitrum_compression_percent_requires_owner_auth() {
578
+ let setup = TestSetup::new();
579
+ let new_percent = 50u128;
580
+
581
+ // No mock_auths
582
+ setup.client.set_arbitrum_compression_percent(&new_percent);
583
+ }
584
+
585
+ #[test]
586
+ fn test_set_eid_to_model_type_success() {
587
+ let setup = TestSetup::new();
588
+
589
+ let params = vec![
590
+ &setup.env,
591
+ SetEidToModelTypeParam { dst_eid: 100, model_type: ModelType::OpStack },
592
+ SetEidToModelTypeParam { dst_eid: 200, model_type: ModelType::ArbStack },
593
+ ];
594
+
595
+ setup.mock_owner_auth("set_eid_to_model_type", (&params,));
596
+ setup.client.set_eid_to_model_type(&params);
597
+
598
+ assert_eq!(setup.client.eid_to_model_type(&100), ModelType::OpStack);
599
+ assert_eq!(setup.client.eid_to_model_type(&200), ModelType::ArbStack);
600
+ // Unconfigured EID returns Default
601
+ assert_eq!(setup.client.eid_to_model_type(&300), ModelType::Default);
602
+ }
603
+
604
+ #[test]
605
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
606
+ fn test_set_eid_to_model_type_requires_owner_auth() {
607
+ let setup = TestSetup::new();
608
+
609
+ let params = vec![&setup.env, SetEidToModelTypeParam { dst_eid: 100, model_type: ModelType::OpStack }];
610
+
611
+ // No mock_auths
612
+ setup.client.set_eid_to_model_type(&params);
613
+ }
614
+
615
+ // =============================================================================
616
+ // Price Updater Functions
617
+ // =============================================================================
618
+
619
+ #[test]
620
+ fn test_set_price_with_price_updater() {
621
+ let setup = TestSetup::new();
622
+
623
+ let price = setup.new_price(10u128.pow(20), 1_000_000, 16);
624
+ let prices = vec![&setup.env, UpdatePrice { eid: 1, price: price.clone() }];
625
+
626
+ setup.mock_price_updater_auth("set_price", (&setup.price_updater, &prices));
627
+ setup.client.set_price(&setup.price_updater, &prices);
628
+
629
+ assert_eq!(setup.client.get_price(&1), Some(price));
630
+ }
631
+
632
+ #[test]
633
+ fn test_set_price_with_owner() {
634
+ let setup = TestSetup::new();
635
+
636
+ let price = setup.new_price(10u128.pow(20), 2_000_000, 32);
637
+ let prices = vec![&setup.env, UpdatePrice { eid: 2, price: price.clone() }];
638
+
639
+ // Owner can also set prices
640
+ setup.mock_owner_auth("set_price", (&setup.owner, &prices));
641
+ setup.client.set_price(&setup.owner, &prices);
642
+
643
+ assert_eq!(setup.client.get_price(&2), Some(price));
644
+ }
645
+
646
+ #[test]
647
+ fn test_set_price_multiple_eids() {
648
+ let setup = TestSetup::new();
649
+
650
+ let price1 = setup.new_price(10u128.pow(20), 1_000_000_000, 16);
651
+ let price2 = setup.new_price(2 * 10u128.pow(20), 2_000_000_000, 32);
652
+ let prices = vec![
653
+ &setup.env,
654
+ UpdatePrice { eid: 101, price: price1.clone() },
655
+ UpdatePrice { eid: 102, price: price2.clone() },
656
+ ];
657
+
658
+ setup.mock_price_updater_auth("set_price", (&setup.price_updater, &prices));
659
+ setup.client.set_price(&setup.price_updater, &prices);
660
+
661
+ assert_eq!(setup.client.get_price(&101), Some(price1));
662
+ assert_eq!(setup.client.get_price(&102), Some(price2));
663
+ }
664
+
665
+ #[test]
666
+ fn test_set_price_rejects_non_price_updater() {
667
+ let setup = TestSetup::new();
668
+
669
+ let non_updater = Address::generate(&setup.env);
670
+ let price = setup.default_test_price();
671
+ let prices = vec![&setup.env, UpdatePrice { eid: 1, price }];
672
+
673
+ setup.mock_auth(&non_updater, "set_price", (&non_updater, &prices));
674
+ assert_eq!(
675
+ setup.client.try_set_price(&non_updater, &prices).unwrap_err().unwrap(),
676
+ PriceFeedError::OnlyPriceUpdater.into()
677
+ );
678
+ }
679
+
680
+ #[test]
681
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
682
+ fn test_set_price_requires_caller_auth_even_if_price_updater() {
683
+ let setup = TestSetup::new();
684
+
685
+ let price = setup.default_test_price();
686
+ let prices = vec![&setup.env, UpdatePrice { eid: 1, price }];
687
+
688
+ // price_updater is active, but no mock_auths -> caller.require_auth() must fail.
689
+ setup.client.set_price(&setup.price_updater, &prices);
690
+ }
691
+
692
+ #[test]
693
+ fn test_set_price_for_arbitrum_success() {
694
+ let setup = TestSetup::new();
695
+
696
+ let price = setup.new_price(10u128.pow(20), 500_000, 8);
697
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 100_000, gas_per_l1_calldata_byte: 16 };
698
+ let update = UpdatePriceExt { eid: 110, price: price.clone(), extend: arb_ext.clone() };
699
+
700
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &update));
701
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &update);
702
+
703
+ assert_eq!(setup.client.get_price(&110), Some(price));
704
+ assert_eq!(setup.client.arbitrum_price_ext(), arb_ext);
705
+ }
706
+
707
+ #[test]
708
+ fn test_set_price_for_arbitrum_overwrites_arbitrum_price_ext() {
709
+ let setup = TestSetup::new();
710
+
711
+ let price = setup.default_test_price();
712
+ let ext1 = ArbitrumPriceExt { gas_per_l2_tx: 1, gas_per_l1_calldata_byte: 2 };
713
+ let upd1 = UpdatePriceExt { eid: 110, price: price.clone(), extend: ext1.clone() };
714
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &upd1));
715
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &upd1);
716
+ assert_eq!(setup.client.arbitrum_price_ext(), ext1);
717
+
718
+ let ext2 = ArbitrumPriceExt { gas_per_l2_tx: 999, gas_per_l1_calldata_byte: 888 };
719
+ let upd2 = UpdatePriceExt { eid: 110, price, extend: ext2.clone() };
720
+ setup.mock_price_updater_auth("set_price_for_arbitrum", (&setup.price_updater, &upd2));
721
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &upd2);
722
+ assert_eq!(setup.client.arbitrum_price_ext(), ext2);
723
+ }
724
+
725
+ #[test]
726
+ fn test_set_price_for_arbitrum_rejects_non_price_updater() {
727
+ let setup = TestSetup::new();
728
+
729
+ let non_updater = Address::generate(&setup.env);
730
+ let price = setup.default_test_price();
731
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 100_000, gas_per_l1_calldata_byte: 16 };
732
+ let update = UpdatePriceExt { eid: 110, price, extend: arb_ext };
733
+
734
+ setup.mock_auth(&non_updater, "set_price_for_arbitrum", (&non_updater, &update));
735
+ assert_eq!(
736
+ setup.client.try_set_price_for_arbitrum(&non_updater, &update).unwrap_err().unwrap(),
737
+ PriceFeedError::OnlyPriceUpdater.into()
738
+ );
739
+ }
740
+
741
+ #[test]
742
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
743
+ fn test_set_price_for_arbitrum_requires_caller_auth_even_if_price_updater() {
744
+ let setup = TestSetup::new();
745
+
746
+ let price = setup.default_test_price();
747
+ let arb_ext = ArbitrumPriceExt { gas_per_l2_tx: 100_000, gas_per_l1_calldata_byte: 16 };
748
+ let update = UpdatePriceExt { eid: 110, price, extend: arb_ext };
749
+
750
+ // price_updater is active, but no mock_auths -> caller.require_auth() must fail.
751
+ setup.client.set_price_for_arbitrum(&setup.price_updater, &update);
752
+ }
753
+
754
+ #[test]
755
+ fn test_set_native_token_price_usd_success() {
756
+ let setup = TestSetup::new();
757
+ let price_usd = 2500 * 10u128.pow(18); // $2500 scaled
758
+
759
+ setup.mock_price_updater_auth("set_native_token_price_usd", (&setup.price_updater, price_usd));
760
+ setup.client.set_native_token_price_usd(&setup.price_updater, &price_usd);
761
+
762
+ assert_eq!(setup.client.native_token_price_usd(), price_usd);
763
+ }
764
+
765
+ #[test]
766
+ fn test_set_native_token_price_usd_with_owner() {
767
+ let setup = TestSetup::new();
768
+ let price_usd = 3000 * 10u128.pow(18);
769
+
770
+ setup.mock_owner_auth("set_native_token_price_usd", (&setup.owner, price_usd));
771
+ setup.client.set_native_token_price_usd(&setup.owner, &price_usd);
772
+
773
+ assert_eq!(setup.client.native_token_price_usd(), price_usd);
774
+ }
775
+
776
+ #[test]
777
+ fn test_set_native_token_price_usd_rejects_non_price_updater() {
778
+ let setup = TestSetup::new();
779
+
780
+ let non_updater = Address::generate(&setup.env);
781
+ let price_usd = 2500 * 10u128.pow(18);
782
+
783
+ setup.mock_auth(&non_updater, "set_native_token_price_usd", (&non_updater, price_usd));
784
+ assert_eq!(
785
+ setup.client.try_set_native_token_price_usd(&non_updater, &price_usd).unwrap_err().unwrap(),
786
+ PriceFeedError::OnlyPriceUpdater.into()
787
+ );
788
+ }
789
+
790
+ #[test]
791
+ #[should_panic(expected = "Error(Auth, InvalidAction)")]
792
+ fn test_set_native_token_price_usd_requires_caller_auth_even_if_price_updater() {
793
+ let setup = TestSetup::new();
794
+ let price_usd = 2500 * 10u128.pow(18);
795
+
796
+ // price_updater is active, but no mock_auths -> caller.require_auth() must fail.
797
+ setup.client.set_native_token_price_usd(&setup.price_updater, &price_usd);
798
+ }
799
+
800
+ // =============================================================================
801
+ // View Functions
802
+ // =============================================================================
803
+
804
+ #[test]
805
+ fn test_is_price_updater_returns_false_for_unknown_address() {
806
+ let setup = TestSetup::new();
807
+ let unknown = Address::generate(&setup.env);
808
+
809
+ assert_eq!(setup.client.is_price_updater(&unknown), false);
810
+ }
811
+
812
+ // =============================================================================
813
+ // Edge Cases
814
+ // =============================================================================
815
+
816
+ #[test]
817
+ fn test_price_update_overwrites_previous() {
818
+ let setup = TestSetup::new();
819
+
820
+ let price1 = setup.new_price(10u128.pow(20), 1_000_000, 16);
821
+ setup.setup_default_price(1, &price1);
822
+ assert_eq!(setup.client.get_price(&1), Some(price1));
823
+
824
+ // Update to new price
825
+ let price2 = setup.new_price(2 * 10u128.pow(20), 2_000_000, 32);
826
+ setup.setup_default_price(1, &price2);
827
+ assert_eq!(setup.client.get_price(&1), Some(price2));
828
+ }
829
+
830
+ #[test]
831
+ fn test_multiple_price_updaters() {
832
+ let setup = TestSetup::new();
833
+
834
+ // Add a second price updater
835
+ let updater2 = Address::generate(&setup.env);
836
+ setup.mock_owner_auth("set_price_updater", (&updater2, true));
837
+ setup.client.set_price_updater(&updater2, &true);
838
+
839
+ // Both updaters should be able to set prices
840
+ let price1 = setup.new_price(10u128.pow(20), 1_000_000, 16);
841
+ let prices1 = vec![&setup.env, UpdatePrice { eid: 1, price: price1.clone() }];
842
+ setup.mock_price_updater_auth("set_price", (&setup.price_updater, &prices1));
843
+ setup.client.set_price(&setup.price_updater, &prices1);
844
+ assert_eq!(setup.client.get_price(&1), Some(price1));
845
+
846
+ let price2 = setup.new_price(2 * 10u128.pow(20), 2_000_000, 32);
847
+ let prices2 = vec![&setup.env, UpdatePrice { eid: 2, price: price2.clone() }];
848
+ setup.mock_auth(&updater2, "set_price", (&updater2, &prices2));
849
+ setup.client.set_price(&updater2, &prices2);
850
+ assert_eq!(setup.client.get_price(&2), Some(price2));
851
+ }
852
+
853
+ #[test]
854
+ fn test_removed_price_updater_cannot_set_price() {
855
+ let setup = TestSetup::new();
856
+
857
+ // Remove the price updater
858
+ setup.mock_owner_auth("set_price_updater", (&setup.price_updater, false));
859
+ setup.client.set_price_updater(&setup.price_updater, &false);
860
+
861
+ // Try to set price - should fail
862
+ let price = setup.default_test_price();
863
+ let prices = vec![&setup.env, UpdatePrice { eid: 1, price }];
864
+ setup.mock_price_updater_auth("set_price", (&setup.price_updater, &prices));
865
+ assert_eq!(
866
+ setup.client.try_set_price(&setup.price_updater, &prices).unwrap_err().unwrap(),
867
+ PriceFeedError::OnlyPriceUpdater.into()
868
+ );
869
+ }