@layerzerolabs/protocol-stellar-v2 0.2.29 → 0.2.30

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 (205) hide show
  1. package/.turbo/turbo-build.log +371 -321
  2. package/.turbo/turbo-lint.log +211 -202
  3. package/.turbo/turbo-test.log +1766 -1673
  4. package/Cargo.lock +11 -1
  5. package/contracts/common-macros/src/lib.rs +0 -2
  6. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +1 -0
  7. package/contracts/endpoint-v2/src/messaging_channel.rs +32 -3
  8. package/contracts/endpoint-v2/src/tests/endpoint_setup.rs +1 -1
  9. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +1 -1
  10. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +6 -6
  11. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound_payload_hash.rs +1 -1
  12. package/contracts/endpoint-v2/src/tests/messaging_channel/outbound.rs +16 -10
  13. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +10 -10
  14. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +3 -3
  15. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +4 -3
  16. package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +1 -57
  17. package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_missing_internal.stderr +0 -30
  18. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -3
  19. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +6 -4
  20. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -3
  21. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -3
  22. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_auth_trait.stderr +0 -30
  23. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_upgradeable_internal.stderr +0 -30
  24. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/basic.rs +0 -2
  25. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/multisig_contract.rs +0 -2
  26. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_migration.rs +0 -2
  27. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_user_contractimpl.rs +1 -3
  28. package/contracts/message-libs/message-lib-common/src/packet_codec_v1.rs +3 -6
  29. package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +10 -0
  30. package/contracts/message-libs/message-lib-common/src/worker_options.rs +6 -2
  31. package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +3 -3
  32. package/contracts/message-libs/treasury/src/lib.rs +2 -1
  33. package/contracts/message-libs/treasury/src/tests/setup.rs +1 -1
  34. package/contracts/message-libs/treasury/src/treasury.rs +5 -2
  35. package/contracts/message-libs/uln-302/src/errors.rs +2 -0
  36. package/contracts/message-libs/uln-302/src/events.rs +3 -3
  37. package/contracts/message-libs/uln-302/src/interfaces/receive_uln.rs +8 -0
  38. package/contracts/message-libs/uln-302/src/lib.rs +2 -1
  39. package/contracts/message-libs/uln-302/src/receive_uln.rs +16 -13
  40. package/contracts/message-libs/uln-302/src/send_uln.rs +51 -24
  41. package/contracts/message-libs/uln-302/src/storage.rs +2 -2
  42. package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +45 -1
  43. package/contracts/message-libs/uln-302/src/tests/receive_uln302/verifiable.rs +63 -0
  44. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +47 -2
  45. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +50 -1
  46. package/contracts/message-libs/uln-302/src/uln302.rs +0 -8
  47. package/contracts/oapps/counter/Cargo.toml +4 -4
  48. package/contracts/oapps/counter/integration_tests/setup_uln.rs +22 -2
  49. package/contracts/oapps/counter/src/counter.rs +8 -8
  50. package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +33 -10
  51. package/contracts/oapps/oapp/src/lib.rs +6 -2
  52. package/contracts/oapps/oapp/src/oapp_core.rs +49 -24
  53. package/contracts/oapps/oapp/src/oapp_options_type3.rs +21 -14
  54. package/contracts/oapps/oapp/src/oapp_receiver.rs +17 -16
  55. package/contracts/oapps/oapp/src/oapp_sender.rs +66 -15
  56. package/contracts/oapps/oapp/src/tests/oapp_core.rs +5 -5
  57. package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +18 -18
  58. package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +4 -4
  59. package/contracts/oapps/oapp/src/tests/oapp_sender.rs +3 -3
  60. package/contracts/oapps/oapp-macros/Cargo.toml +0 -1
  61. package/contracts/oapps/oapp-macros/src/generators.rs +87 -46
  62. package/contracts/oapps/oapp-macros/src/lib.rs +3 -61
  63. package/contracts/oapps/oapp-macros/src/tests/oapp.rs +9 -23
  64. package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +15 -11
  65. package/contracts/oapps/oft/Cargo.toml +1 -1
  66. package/contracts/oapps/oft/integration-tests/extensions/test_oft_fee.rs +3 -3
  67. package/contracts/oapps/oft/integration-tests/extensions/test_pausable.rs +4 -4
  68. package/contracts/oapps/oft/integration-tests/extensions/test_rate_limiter.rs +144 -8
  69. package/contracts/oapps/oft/integration-tests/setup.rs +4 -2
  70. package/contracts/oapps/oft/integration-tests/utils.rs +25 -11
  71. package/contracts/oapps/oft/src/extensions/oft_fee.rs +65 -63
  72. package/contracts/oapps/oft/src/extensions/pausable.rs +2 -3
  73. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +22 -5
  74. package/contracts/oapps/oft/src/interfaces/mint_burnable.rs +18 -0
  75. package/contracts/oapps/oft/src/interfaces/mod.rs +3 -0
  76. package/contracts/oapps/oft/src/lib.rs +4 -2
  77. package/contracts/oapps/oft/src/oft.rs +35 -36
  78. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +13 -9
  79. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +14 -9
  80. package/contracts/oapps/oft/src/oft_types/mod.rs +14 -12
  81. package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +28 -20
  82. package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +136 -2
  83. package/contracts/oapps/oft/src/tests/oft_types/lock_unlock.rs +12 -8
  84. package/contracts/oapps/oft-core/integration-tests/setup.rs +8 -9
  85. package/contracts/oapps/oft-core/integration-tests/test_with_sml.rs +7 -6
  86. package/contracts/oapps/oft-core/integration-tests/utils.rs +5 -4
  87. package/contracts/oapps/oft-core/src/codec/oft_compose_msg_codec.rs +2 -2
  88. package/contracts/oapps/oft-core/src/codec/oft_msg_codec.rs +33 -37
  89. package/contracts/oapps/oft-core/src/errors.rs +2 -1
  90. package/contracts/oapps/oft-core/src/events.rs +6 -0
  91. package/contracts/oapps/oft-core/src/lib.rs +8 -4
  92. package/contracts/oapps/oft-core/src/oft_core.rs +205 -148
  93. package/contracts/oapps/oft-core/src/storage.rs +4 -2
  94. package/contracts/oapps/oft-core/src/tests/test_decimals.rs +2 -2
  95. package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +6 -6
  96. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +7 -6
  97. package/contracts/oapps/oft-core/src/tests/test_oft_msg_codec.rs +11 -82
  98. package/contracts/oapps/oft-core/src/tests/test_quote_oft.rs +13 -13
  99. package/contracts/oapps/oft-core/src/tests/test_quote_send.rs +1 -1
  100. package/contracts/oapps/oft-core/src/tests/test_resolve_address.rs +2 -2
  101. package/contracts/oapps/oft-core/src/tests/test_send.rs +22 -22
  102. package/contracts/oapps/oft-core/src/tests/test_utils.rs +20 -22
  103. package/contracts/oapps/oft-core/src/utils.rs +12 -8
  104. package/contracts/sac-manager/Cargo.toml +25 -0
  105. package/contracts/sac-manager/src/errors.rs +18 -0
  106. package/contracts/sac-manager/src/extensions/mod.rs +6 -0
  107. package/contracts/sac-manager/src/extensions/redistribution.rs +109 -0
  108. package/contracts/sac-manager/src/extensions/supply_control/mod.rs +488 -0
  109. package/contracts/sac-manager/src/extensions/supply_control/rate_limit.rs +126 -0
  110. package/contracts/sac-manager/src/interfaces/mod.rs +3 -0
  111. package/contracts/sac-manager/src/interfaces/sac_manager.rs +52 -0
  112. package/contracts/sac-manager/src/lib.rs +23 -0
  113. package/contracts/sac-manager/src/sac_manager.rs +193 -0
  114. package/contracts/sac-manager/src/storage.rs +20 -0
  115. package/contracts/sac-manager/src/tests/mod.rs +14 -0
  116. package/contracts/sac-manager/src/tests/redistribution/mod.rs +1 -0
  117. package/contracts/sac-manager/src/tests/redistribution/redistribute_funds.rs +82 -0
  118. package/contracts/sac-manager/src/tests/sac_manager/admin_mint.rs +206 -0
  119. package/contracts/sac-manager/src/tests/sac_manager/burn.rs +215 -0
  120. package/contracts/sac-manager/src/tests/sac_manager/clawback.rs +209 -0
  121. package/contracts/sac-manager/src/tests/sac_manager/mint.rs +252 -0
  122. package/contracts/sac-manager/src/tests/sac_manager/mod.rs +9 -0
  123. package/contracts/sac-manager/src/tests/sac_manager/set_admin.rs +36 -0
  124. package/contracts/sac-manager/src/tests/sac_manager/set_authorized.rs +43 -0
  125. package/contracts/sac-manager/src/tests/sac_manager/set_oft_address.rs +47 -0
  126. package/contracts/sac-manager/src/tests/sac_manager/test_helper.rs +75 -0
  127. package/contracts/sac-manager/src/tests/sac_manager/view_functions.rs +60 -0
  128. package/contracts/sac-manager/src/tests/supply_control/enumerable_set.rs +256 -0
  129. package/contracts/sac-manager/src/tests/supply_control/mod.rs +8 -0
  130. package/contracts/sac-manager/src/tests/supply_control/refill.rs +90 -0
  131. package/contracts/sac-manager/src/tests/supply_control/set_mint_whitelist.rs +245 -0
  132. package/contracts/sac-manager/src/tests/supply_control/set_supply_controller.rs +267 -0
  133. package/contracts/sac-manager/src/tests/supply_control/set_supply_controller_manager.rs +122 -0
  134. package/contracts/sac-manager/src/tests/supply_control/test_helper.rs +38 -0
  135. package/contracts/sac-manager/src/tests/supply_control/update_allow_any_mint_burn.rs +114 -0
  136. package/contracts/sac-manager/src/tests/supply_control/update_limit_config.rs +257 -0
  137. package/contracts/sac-manager/src/tests/test_helper.rs +190 -0
  138. package/contracts/upgrader/src/lib.rs +2 -1
  139. package/contracts/utils/src/errors.rs +0 -1
  140. package/contracts/utils/src/tests/upgradeable.rs +0 -66
  141. package/contracts/utils/src/upgradeable.rs +0 -18
  142. package/contracts/workers/dvn/src/dvn.rs +2 -2
  143. package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
  144. package/contracts/workers/dvn/src/lib.rs +2 -1
  145. package/contracts/workers/dvn-fee-lib/src/lib.rs +3 -1
  146. package/contracts/workers/executor/src/auth.rs +42 -26
  147. package/contracts/workers/executor/src/executor.rs +28 -3
  148. package/contracts/workers/executor/src/lib.rs +4 -2
  149. package/contracts/workers/executor/src/storage.rs +21 -1
  150. package/contracts/workers/executor/src/tests/auth.rs +64 -20
  151. package/contracts/workers/executor/src/tests/executor.rs +1 -1
  152. package/contracts/workers/executor/src/tests/setup.rs +18 -0
  153. package/contracts/workers/executor-fee-lib/src/lib.rs +4 -1
  154. package/contracts/workers/executor-helper/src/executor_helper.rs +24 -10
  155. package/contracts/workers/executor-helper/src/tests/setup.rs +147 -34
  156. package/contracts/workers/price-feed/src/lib.rs +3 -1
  157. package/contracts/workers/worker/src/lib.rs +2 -1
  158. package/contracts/workers/worker/src/worker.rs +31 -17
  159. package/docs/oapp-guide.md +17 -8
  160. package/docs/oft-guide.md +3 -3
  161. package/package.json +3 -3
  162. package/sdk/.turbo/turbo-test.log +512 -351
  163. package/sdk/dist/generated/bml.d.ts +3 -9
  164. package/sdk/dist/generated/bml.js +6 -7
  165. package/sdk/dist/generated/counter.d.ts +22 -28
  166. package/sdk/dist/generated/counter.js +11 -12
  167. package/sdk/dist/generated/dvn.d.ts +36 -54
  168. package/sdk/dist/generated/dvn.js +10 -15
  169. package/sdk/dist/generated/dvn_fee_lib.d.ts +3 -21
  170. package/sdk/dist/generated/dvn_fee_lib.js +6 -11
  171. package/sdk/dist/generated/endpoint.d.ts +3 -9
  172. package/sdk/dist/generated/endpoint.js +6 -7
  173. package/sdk/dist/generated/executor.d.ts +80 -54
  174. package/sdk/dist/generated/executor.js +16 -16
  175. package/sdk/dist/generated/executor_fee_lib.d.ts +3 -21
  176. package/sdk/dist/generated/executor_fee_lib.js +6 -11
  177. package/sdk/dist/generated/executor_helper.d.ts +36 -42
  178. package/sdk/dist/generated/executor_helper.js +9 -10
  179. package/sdk/dist/generated/layerzero_view.d.ts +20 -32
  180. package/sdk/dist/generated/layerzero_view.js +25 -26
  181. package/sdk/dist/generated/oft.d.ts +147 -79
  182. package/sdk/dist/generated/oft.js +47 -54
  183. package/sdk/dist/generated/price_feed.d.ts +20 -38
  184. package/sdk/dist/generated/price_feed.js +15 -20
  185. package/sdk/dist/generated/sac_manager.d.ts +1309 -0
  186. package/sdk/dist/generated/sac_manager.js +484 -0
  187. package/sdk/dist/generated/sml.d.ts +3 -9
  188. package/sdk/dist/generated/sml.js +6 -7
  189. package/sdk/dist/generated/treasury.d.ts +3 -9
  190. package/sdk/dist/generated/treasury.js +8 -9
  191. package/sdk/dist/generated/uln302.d.ts +20 -20
  192. package/sdk/dist/generated/uln302.js +25 -22
  193. package/sdk/dist/generated/upgrader.d.ts +3 -9
  194. package/sdk/dist/generated/upgrader.js +6 -7
  195. package/sdk/dist/index.d.ts +1 -0
  196. package/sdk/dist/index.js +1 -0
  197. package/sdk/package.json +1 -1
  198. package/sdk/src/index.ts +1 -0
  199. package/sdk/test/oft-sml.test.ts +7 -5
  200. package/sdk/test/sac-manager-redistribution.test.ts +578 -0
  201. package/sdk/test/suites/globalSetup.ts +11 -6
  202. package/sdk/test/test_data/test_upgradeable_dvn.wasm +0 -0
  203. package/sdk/test/upgrader.test.ts +75 -202
  204. package/sdk/test/utils.ts +40 -0
  205. package/tools/ts-bindings-gen/src/main.rs +1 -0
@@ -3,43 +3,48 @@
3
3
  //! This OFT type burns tokens on debit (send) and mints tokens on credit (receive).
4
4
  //! Used when the OFT contract has mint/burn authority over the token.
5
5
 
6
- use oft_core::{types::OFTReceipt, OFTCore};
7
- use soroban_sdk::{token::StellarAssetClient, Address, Env};
6
+ use crate::interfaces::MintBurnableClient;
7
+ use oft_core::OFTCore;
8
+ use soroban_sdk::{Address, Env};
8
9
 
9
10
  /// Debit tokens using MintBurn OFT type (burns tokens from sender).
10
11
  ///
11
12
  /// # Parameters
12
13
  /// * `env` - The Soroban environment
14
+ /// * `burnable` - Address of the contract responsible for burning tokens
13
15
  /// * `sender` - Address of the token sender
14
16
  /// * `amount_ld` - Amount to debit in local decimals
15
17
  /// * `min_amount_ld` - Minimum amount that must be received (for slippage protection)
16
18
  /// * `dst_eid` - Destination endpoint ID
17
19
  ///
18
20
  /// # Returns
19
- /// `OFTReceipt` containing the amount sent and amount received
21
+ /// * `amount_sent_ld` - The amount sent in local decimals
22
+ /// * `amount_received_ld` - The amount received in local decimals on the remote
20
23
  pub fn debit<T: OFTCore>(
21
24
  env: &Env,
25
+ burnable: &Address,
22
26
  sender: &Address,
23
27
  amount_ld: i128,
24
28
  min_amount_ld: i128,
25
29
  dst_eid: u32,
26
- ) -> OFTReceipt {
27
- let receipt = T::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
28
- StellarAssetClient::new(env, &T::token(env)).burn(sender, &receipt.amount_received_ld);
29
- receipt
30
+ ) -> (i128, i128) {
31
+ let (amount_sent_ld, amount_received_ld) = T::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
32
+ MintBurnableClient::new(env, burnable).burn(sender, &amount_received_ld);
33
+ (amount_sent_ld, amount_received_ld)
30
34
  }
31
35
 
32
36
  /// Credit tokens using MintBurn OFT type (mints tokens to recipient).
33
37
  ///
34
38
  /// # Parameters
35
39
  /// * `env` - The Soroban environment
40
+ /// * `mintable` - Address of the contract responsible for minting tokens
36
41
  /// * `to` - Address of the token recipient
37
42
  /// * `amount_ld` - Amount to credit in local decimals
38
43
  /// * `_src_eid` - Source endpoint ID (unused)
39
44
  ///
40
45
  /// # Returns
41
46
  /// The amount credited
42
- pub fn credit<T: OFTCore>(env: &Env, to: &Address, amount_ld: i128, _src_eid: u32) -> i128 {
43
- StellarAssetClient::new(env, &T::token(env)).mint(to, &amount_ld);
47
+ pub fn credit<T: OFTCore>(env: &Env, mintable: &Address, to: &Address, amount_ld: i128, _src_eid: u32) -> i128 {
48
+ MintBurnableClient::new(env, mintable).mint(to, &amount_ld);
44
49
  amount_ld
45
50
  }
@@ -1,23 +1,25 @@
1
- //! OFT types implementations.
1
+ //! OFT mode implementations.
2
2
  //!
3
3
  //! This module provides reference implementations for the two main OFT types:
4
4
  //!
5
- //! - **MintBurn**: Burns tokens on send, mints on receive. Approval not required.
6
- //! - **LockUnlock**: Locks tokens on send, unlocks on receive. Approval not required.
7
- //!
5
+ //! - **LockUnlock**: Locks tokens on send, unlocks on receive. Operates directly on the
6
+ //! token via standard SEP-41 `transfer`.
7
+ //! - **MintBurn**: Burns tokens on send, mints on receive. Operates on any contract
8
+ //! that implements [`MintBurnable`](crate::interfaces::MintBurnable) —
9
+ //! whether it's a raw SAC or a token wrapper (e.g. SAC Manager).
8
10
 
9
- use soroban_sdk::contracttype;
11
+ use soroban_sdk::{contracttype, Address};
10
12
 
11
13
  pub mod lock_unlock;
12
14
  pub mod mint_burn;
13
15
 
14
- /// The type of OFT operation mode
16
+ /// The OFT operation type.
15
17
  #[contracttype]
16
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
17
- #[repr(u8)]
18
+ #[derive(Clone, Debug, Eq, PartialEq)]
18
19
  pub enum OftType {
19
- /// Lock tokens on send, unlock on receive
20
- LockUnlock = 0,
21
- /// Burn tokens on send, mint on receive
22
- MintBurn = 1,
20
+ /// Lock tokens on send, unlock on receive.
21
+ LockUnlock,
22
+ /// Burn tokens on send, mint on receive.
23
+ /// The address is the contract responsible for minting and burning tokens.
24
+ MintBurn(Address),
23
25
  }
@@ -86,18 +86,17 @@ fn test_fee_view_zero_fee_returns_zero() {
86
86
  }
87
87
 
88
88
  #[test]
89
- fn test_fee_deposit_address_errors_when_unset() {
89
+ fn test_fee_deposit_address_returns_none_when_unset() {
90
90
  let TestSetup { client, .. } = setup();
91
91
 
92
- let res = client.try_fee_deposit_address();
93
- assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeDepositAddress.into());
92
+ assert_eq!(client.fee_deposit_address(), None);
94
93
  }
95
94
 
96
95
  #[test]
97
96
  fn test_fee_view_nonzero_fee_errors_when_deposit_address_unset() {
98
97
  let TestSetup { client, .. } = setup();
99
98
 
100
- client.set_default_fee_bps(&100u64);
99
+ client.set_default_fee_bps(&Some(100u32));
101
100
 
102
101
  let res = client.try_fee_view(&7u32, &1_000_000i128);
103
102
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeDepositAddress.into());
@@ -111,7 +110,12 @@ fn test_fee_view_nonzero_fee_errors_when_deposit_address_unset() {
111
110
  fn test_set_default_fee_bps_rejects_invalid_value() {
112
111
  let TestSetup { client, .. } = setup();
113
112
 
114
- let res = client.try_set_default_fee_bps(&10_001u64);
113
+ // Zero is not a valid default fee (use None to remove instead)
114
+ let res = client.try_set_default_fee_bps(&Some(0u32));
115
+ assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeBps.into());
116
+
117
+ // Exceeds maximum
118
+ let res = client.try_set_default_fee_bps(&Some(10_001u32));
115
119
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeBps.into());
116
120
  }
117
121
 
@@ -119,11 +123,14 @@ fn test_set_default_fee_bps_rejects_invalid_value() {
119
123
  fn test_set_default_fee_bps_rejects_same_value() {
120
124
  let TestSetup { client, .. } = setup();
121
125
 
122
- let res = client.try_set_default_fee_bps(&0u64);
126
+ // None when already None (not set)
127
+ let none: Option<u32> = None;
128
+ let res = client.try_set_default_fee_bps(&none);
123
129
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
124
130
 
125
- client.set_default_fee_bps(&123u64);
126
- let res2 = client.try_set_default_fee_bps(&123u64);
131
+ // Same value when already set
132
+ client.set_default_fee_bps(&Some(123u32));
133
+ let res2 = client.try_set_default_fee_bps(&Some(123u32));
127
134
  assert_eq!(res2.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
128
135
  }
129
136
 
@@ -139,7 +146,7 @@ fn test_set_fee_bps_rejects_invalid_and_same_value() {
139
146
  let res = client.try_set_fee_bps(&dst_eid, &None);
140
147
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
141
148
 
142
- let res2 = client.try_set_fee_bps(&dst_eid, &Some(10_001u64));
149
+ let res2 = client.try_set_fee_bps(&dst_eid, &Some(10_001u32));
143
150
  assert_eq!(res2.err().unwrap().ok().unwrap(), OFTFeeError::InvalidFeeBps.into());
144
151
  }
145
152
 
@@ -148,14 +155,14 @@ fn test_set_fee_bps_set_and_remove() {
148
155
  let TestSetup { client, .. } = setup();
149
156
  let dst_eid = 101u32;
150
157
 
151
- client.set_fee_bps(&dst_eid, &Some(200u64));
152
- assert_eq!(client.fee_bps(&dst_eid), Some(200u64));
153
- assert_eq!(client.effective_fee_bps(&dst_eid), 200u64);
158
+ client.set_fee_bps(&dst_eid, &Some(200u32));
159
+ assert_eq!(client.fee_bps(&dst_eid), Some(200u32));
160
+ assert_eq!(client.effective_fee_bps(&dst_eid), 200u32);
154
161
 
155
- client.set_default_fee_bps(&111u64);
162
+ client.set_default_fee_bps(&Some(111u32));
156
163
  client.set_fee_bps(&dst_eid, &None);
157
164
  assert_eq!(client.fee_bps(&dst_eid), None);
158
- assert_eq!(client.effective_fee_bps(&dst_eid), 111u64);
165
+ assert_eq!(client.effective_fee_bps(&dst_eid), 111u32);
159
166
  }
160
167
 
161
168
  // ============================================================================
@@ -190,8 +197,9 @@ fn test_charge_fee_errors_without_deposit_address() {
190
197
  fn test_set_fee_deposit_address_same_value() {
191
198
  let TestSetup { client, fee_deposit, .. } = setup();
192
199
 
193
- client.set_fee_deposit_address(&fee_deposit);
194
- let res = client.try_set_fee_deposit_address(&fee_deposit);
200
+ let fee_deposit_opt = Some(fee_deposit);
201
+ client.set_fee_deposit_address(&fee_deposit_opt);
202
+ let res = client.try_set_fee_deposit_address(&fee_deposit_opt);
195
203
  assert_eq!(res.err().unwrap().ok().unwrap(), OFTFeeError::SameValue.into());
196
204
  }
197
205
 
@@ -199,7 +207,7 @@ fn test_set_fee_deposit_address_same_value() {
199
207
  fn test_charge_fee_zero_amount_no_transfer() {
200
208
  let TestSetup { env, client, token, from, fee_deposit, .. } = setup();
201
209
 
202
- client.set_fee_deposit_address(&fee_deposit);
210
+ client.set_fee_deposit_address(&Some(fee_deposit.clone()));
203
211
 
204
212
  let token_client = TokenClient::new(&env, &token);
205
213
  let from_before = token_client.balance(&from);
@@ -216,7 +224,7 @@ fn test_charge_fee_zero_amount_no_transfer() {
216
224
  fn test_charge_fee_transfers() {
217
225
  let TestSetup { env, client, token, from, fee_deposit, .. } = setup();
218
226
 
219
- client.set_fee_deposit_address(&fee_deposit);
227
+ client.set_fee_deposit_address(&Some(fee_deposit.clone()));
220
228
 
221
229
  let token_client = TokenClient::new(&env, &token);
222
230
  let from_before = token_client.balance(&from);
@@ -232,8 +240,8 @@ fn test_charge_fee_transfers() {
232
240
  fn test_fee_view_computes_correct_fee() {
233
241
  let TestSetup { client, fee_deposit, .. } = setup();
234
242
 
235
- client.set_fee_deposit_address(&fee_deposit);
236
- client.set_default_fee_bps(&100u64); // 1%
243
+ client.set_fee_deposit_address(&Some(fee_deposit));
244
+ client.set_default_fee_bps(&Some(100u32)); // 1%
237
245
 
238
246
  let fee = client.fee_view(&999u32, &10_000i128);
239
247
  assert_eq!(fee, 100); // 1% of 10,000
@@ -1,7 +1,7 @@
1
1
  extern crate std;
2
2
 
3
3
  use crate as oft;
4
- use crate::extensions::rate_limiter::{Direction, RateLimitConfig, RateLimitError, RateLimiter, RateLimiterInternal};
4
+ use crate::extensions::rate_limiter::{Direction, Mode, RateLimitConfig, RateLimitError, RateLimiter, RateLimiterInternal};
5
5
  use soroban_sdk::{contract, contractimpl, testutils::Ledger as _, Address, Env};
6
6
  use utils::auth::Auth;
7
7
 
@@ -55,7 +55,11 @@ fn setup() -> TestSetup {
55
55
  }
56
56
 
57
57
  fn config(limit: i128, window_seconds: u64) -> RateLimitConfig {
58
- RateLimitConfig { limit, window_seconds }
58
+ RateLimitConfig { limit, window_seconds, mode: Mode::Net }
59
+ }
60
+
61
+ fn config_with_mode(limit: i128, window_seconds: u64, mode: Mode) -> RateLimitConfig {
62
+ RateLimitConfig { limit, window_seconds, mode }
59
63
  }
60
64
 
61
65
  // ============================================================================
@@ -349,6 +353,136 @@ fn test_different_eids_are_independent() {
349
353
  assert_eq!(client.rate_limit_in_flight(&direction, &2u32), 0);
350
354
  }
351
355
 
356
+ // ============================================================================
357
+ // Gross Mode Tests
358
+ // ============================================================================
359
+
360
+ #[test]
361
+ fn test_gross_mode_does_not_release_capacity() {
362
+ let TestSetup { client, .. } = setup();
363
+ let direction = Direction::Outbound;
364
+ let eid = 77u32;
365
+
366
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 100, Mode::Gross)));
367
+ client.consume(&direction, &eid, &60i128);
368
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 60);
369
+
370
+ // Release should be a no-op in Gross mode
371
+ client.release(&direction, &eid, &30i128);
372
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 60, "Gross mode should not release");
373
+ }
374
+
375
+ #[test]
376
+ fn test_net_mode_does_release_capacity() {
377
+ let TestSetup { client, .. } = setup();
378
+ let direction = Direction::Outbound;
379
+ let eid = 77u32;
380
+
381
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 100, Mode::Net)));
382
+ client.consume(&direction, &eid, &60i128);
383
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 60);
384
+
385
+ // Release should reduce in-flight in Net mode
386
+ client.release(&direction, &eid, &30i128);
387
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 30, "Net mode should release");
388
+ }
389
+
390
+ #[test]
391
+ fn test_gross_mode_enforces_absolute_limit() {
392
+ let TestSetup { client, .. } = setup();
393
+ let direction = Direction::Outbound;
394
+ let eid = 1u32;
395
+
396
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 100, Mode::Gross)));
397
+
398
+ // Consume 80
399
+ client.consume(&direction, &eid, &80i128);
400
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 80);
401
+ assert_eq!(client.rate_limit_capacity(&direction, &eid), 20);
402
+
403
+ // Try to release 50 (should be no-op)
404
+ client.release(&direction, &eid, &50i128);
405
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 80);
406
+ assert_eq!(client.rate_limit_capacity(&direction, &eid), 20);
407
+
408
+ // Should only be able to consume 20 more
409
+ client.consume(&direction, &eid, &20i128);
410
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 100);
411
+
412
+ // Should fail to consume more
413
+ let res = client.try_consume(&direction, &eid, &1i128);
414
+ assert_eq!(res.err().unwrap().ok().unwrap(), RateLimitError::ExceededRateLimit.into());
415
+ }
416
+
417
+ #[test]
418
+ fn test_gross_mode_decay_still_works() {
419
+ let TestSetup { env, client } = setup();
420
+ let direction = Direction::Outbound;
421
+ let eid = 1u32;
422
+
423
+ env.ledger().set_timestamp(1_000);
424
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 10, Mode::Gross)));
425
+
426
+ // Consume full capacity
427
+ client.consume(&direction, &eid, &100i128);
428
+ assert_eq!(client.rate_limit_capacity(&direction, &eid), 0);
429
+
430
+ // Release should not affect in-flight in Gross mode
431
+ client.release(&direction, &eid, &50i128);
432
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 100);
433
+
434
+ // Advance time by 5 seconds => decay 50
435
+ env.ledger().set_timestamp(1_005);
436
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 50);
437
+ assert_eq!(client.rate_limit_capacity(&direction, &eid), 50);
438
+ }
439
+
440
+ #[test]
441
+ fn test_switching_from_net_to_gross_mode() {
442
+ let TestSetup { client, .. } = setup();
443
+ let direction = Direction::Outbound;
444
+ let eid = 1u32;
445
+
446
+ // Start with Net mode
447
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 100, Mode::Net)));
448
+ client.consume(&direction, &eid, &60i128);
449
+ client.release(&direction, &eid, &20i128);
450
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 40, "Net mode should release");
451
+
452
+ // Switch to Gross mode
453
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 100, Mode::Gross)));
454
+
455
+ // in-flight should be checkpointed
456
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 40);
457
+
458
+ // Now releases should be no-op
459
+ client.release(&direction, &eid, &20i128);
460
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 40, "Gross mode should not release");
461
+ }
462
+
463
+ #[test]
464
+ fn test_switching_from_gross_to_net_mode() {
465
+ let TestSetup { client, .. } = setup();
466
+ let direction = Direction::Outbound;
467
+ let eid = 1u32;
468
+
469
+ // Start with Gross mode
470
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 100, Mode::Gross)));
471
+ client.consume(&direction, &eid, &60i128);
472
+ client.release(&direction, &eid, &20i128);
473
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 60, "Gross mode should not release");
474
+
475
+ // Switch to Net mode
476
+ client.set_rate_limit(&direction, &eid, &Some(config_with_mode(100, 100, Mode::Net)));
477
+
478
+ // in-flight should be checkpointed
479
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 60);
480
+
481
+ // Now releases should work
482
+ client.release(&direction, &eid, &20i128);
483
+ assert_eq!(client.rate_limit_in_flight(&direction, &eid), 40, "Net mode should release");
484
+ }
485
+
352
486
  // ============================================================================
353
487
  // Edge Case Tests
354
488
  // ============================================================================
@@ -3,7 +3,7 @@ extern crate std;
3
3
  use crate::oft_types::lock_unlock;
4
4
  use endpoint_v2::Origin;
5
5
  use oapp::oapp_receiver::{LzReceiveInternal, OAppReceiver};
6
- use oft_core::{types::OFTReceipt, OFTCore, OFTInternal};
6
+ use oft_core::{OFTCore, OFTInternal, OFTReceipt};
7
7
  use soroban_sdk::{
8
8
  contract, contractimpl, contracttype,
9
9
  testutils::Address as _,
@@ -49,14 +49,16 @@ impl LockUnlockHarnessOFT {
49
49
  token: &Address,
50
50
  owner: &Address,
51
51
  endpoint: &Address,
52
- delegate: &Option<Address>,
52
+ delegate: &Address,
53
53
  shared_decimals: u32,
54
54
  ) {
55
- Self::__initialize_oft(env, owner, token, endpoint, delegate, shared_decimals);
55
+ Self::__initialize_oft(env, token, shared_decimals, owner, endpoint, delegate);
56
56
  }
57
57
 
58
58
  pub fn debit(env: Env, sender: Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
59
- <Self as OFTInternal>::__debit(&env, &sender, amount_ld, min_amount_ld, dst_eid)
59
+ let (amount_sent_ld, amount_received_ld) =
60
+ <Self as OFTInternal>::__debit(&env, &sender, amount_ld, min_amount_ld, dst_eid);
61
+ OFTReceipt { amount_sent_ld, amount_received_ld }
60
62
  }
61
63
 
62
64
  pub fn credit(env: Env, to: Address, amount_ld: i128, src_eid: u32) -> i128 {
@@ -85,12 +87,14 @@ impl LzReceiveInternal for LockUnlockHarnessOFT {
85
87
  impl OAppReceiver for LockUnlockHarnessOFT {}
86
88
 
87
89
  impl OFTInternal for LockUnlockHarnessOFT {
88
- fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
89
- lock_unlock::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
90
+ fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> (i128, i128) {
91
+ let target = Self::token(env);
92
+ lock_unlock::debit::<Self>(env, &target, sender, amount_ld, min_amount_ld, dst_eid)
90
93
  }
91
94
 
92
95
  fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
93
- lock_unlock::credit::<Self>(env, to, amount_ld, src_eid)
96
+ let target = Self::token(env);
97
+ lock_unlock::credit::<Self>(env, &target, to, amount_ld, src_eid)
94
98
  }
95
99
  }
96
100
 
@@ -117,7 +121,7 @@ fn setup() -> TestSetup {
117
121
  let endpoint = env.register(DummyEndpoint, ());
118
122
 
119
123
  let owner = Address::generate(&env);
120
- let delegate: Option<Address> = None;
124
+ let delegate = owner.clone();
121
125
  let shared_decimals: u32 = 6;
122
126
 
123
127
  let oft_address = env.register(LockUnlockHarnessOFT, (&token, &owner, &endpoint, &delegate, &shared_decimals));
@@ -9,7 +9,6 @@ use crate::{
9
9
  integration_tests::utils::{address_to_peer_bytes32, peer_bytes32_to_address},
10
10
  oft_core::{OFTClient, OFTCore, OFTInternal},
11
11
  storage::OFTStorage,
12
- types::OFTReceipt,
13
12
  };
14
13
  use common_macros::contract_impl;
15
14
  use endpoint_v2::{EndpointV2, EndpointV2Client, ILayerZeroComposer, Origin};
@@ -50,10 +49,10 @@ impl TestOFT {
50
49
  token: &Address,
51
50
  owner: &Address,
52
51
  endpoint: &Address,
53
- delegate: &Option<Address>,
52
+ delegate: &Address,
54
53
  shared_decimals: u32,
55
54
  ) {
56
- Self::__initialize_oft(env, owner, token, endpoint, delegate, shared_decimals)
55
+ Self::__initialize_oft(env, token, shared_decimals, owner, endpoint, delegate)
57
56
  }
58
57
  }
59
58
 
@@ -61,12 +60,12 @@ impl TestOFT {
61
60
  impl OFTCore for TestOFT {}
62
61
 
63
62
  impl OFTInternal for TestOFT {
64
- fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
65
- // Get the receipt (handles decimal conversion, fees, etc.)
66
- let receipt = Self::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
63
+ fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> (i128, i128) {
64
+ // Get the amounts (handles decimal conversion, fees, etc.)
65
+ let (amount_sent_ld, amount_received_ld) = Self::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
67
66
  // Actually burn tokens from sender
68
- StellarAssetClient::new(env, &OFTStorage::token(env).unwrap()).burn(sender, &receipt.amount_sent_ld);
69
- receipt
67
+ StellarAssetClient::new(env, &OFTStorage::token(env).unwrap()).burn(sender, &amount_sent_ld);
68
+ (amount_sent_ld, amount_received_ld)
70
69
  }
71
70
 
72
71
  fn __credit(env: &Env, to: &Address, amount_ld: i128, _src_eid: u32) -> i128 {
@@ -176,7 +175,7 @@ fn setup_chain<'a>(env: &Env) -> ChainSetup<'a> {
176
175
  let endpoint_address = env.register(EndpointV2, (&owner, eid, &native_token));
177
176
  let fee_recipient = Address::generate(env);
178
177
  let sml_address = env.register(SimpleMessageLib, (&owner, &endpoint_address, &fee_recipient));
179
- let delegate: Option<Address> = Some(owner.clone());
178
+ let delegate = owner.clone();
180
179
  let shared_decimals: u32 = 6; // Default shared decimals
181
180
  let oft_address = env.register(TestOFT, (&oft_token, &owner, &endpoint_address, &delegate, &shared_decimals));
182
181
  let composer_address = env.register(DummyComposer, (&endpoint_address,));
@@ -43,7 +43,7 @@ fn test_send_vanilla() {
43
43
 
44
44
  let send_param = SendParam {
45
45
  dst_eid: chain_b.eid,
46
- to: address_payload(&receiver),
46
+ to: address_payload(&env, &receiver),
47
47
  amount_ld: 10e7 as i128,
48
48
  min_amount_ld: 10e7 as i128,
49
49
  extra_options: bytes!(&env),
@@ -51,7 +51,7 @@ fn test_send_vanilla() {
51
51
  oft_cmd: bytes!(&env),
52
52
  };
53
53
  log!(&env, "send_param: {:?}", send_param);
54
- let (_, _, oft_receipt) = quote_oft(&chain_a, &send_param);
54
+ let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
55
55
  log!(&env, "oft_receipt: {:?}", oft_receipt);
56
56
 
57
57
  let fee = quote_send(&env, &chain_a, &sender, &send_param, false);
@@ -99,7 +99,7 @@ fn test_send_composed() {
99
99
 
100
100
  let send_param = SendParam {
101
101
  dst_eid: chain_b.eid,
102
- to: address_payload(&receiver),
102
+ to: address_payload(&env, &receiver),
103
103
  amount_ld: 10e7 as i128,
104
104
  min_amount_ld: 10e7 as i128,
105
105
  extra_options: bytes!(&env),
@@ -107,7 +107,7 @@ fn test_send_composed() {
107
107
  oft_cmd: bytes!(&env),
108
108
  };
109
109
  log!(&env, "send_param: {:?}", send_param);
110
- let (_, _, oft_receipt) = quote_oft(&chain_a, &send_param);
110
+ let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
111
111
  log!(&env, "oft_receipt: {:?}", oft_receipt);
112
112
 
113
113
  let fee = quote_send(&env, &chain_a, &sender, &send_param, false);
@@ -131,12 +131,13 @@ fn test_send_composed() {
131
131
  // execute lz_compose
132
132
  let extra_data = Bytes::from_array(&env, b"extra data");
133
133
  let oft_msg = oft_msg_codec::OFTMessage::decode(&packet.message);
134
+ let compose = oft_msg.compose.unwrap();
134
135
  let oft_compose_msg = OFTComposeMsg {
135
136
  nonce: packet.nonce,
136
137
  src_eid: packet.src_eid,
137
138
  amount_ld: (oft_msg.amount_sd as i128).mul(chain_b.oft.decimal_conversion_rate()),
138
- compose_from: oft_msg.compose_from.unwrap(),
139
- compose_msg: oft_msg.compose_msg.unwrap(),
139
+ compose_from: compose.from,
140
+ compose_msg: compose.msg,
140
141
  }
141
142
  .encode(&env);
142
143
  let compose_value = 100;
@@ -26,8 +26,8 @@ pub fn peer_bytes32_to_address(env: &Env, bytes32: &BytesN<32>) -> Address {
26
26
  AddressPayload::ContractIdHash(bytes32.clone()).to_address(env)
27
27
  }
28
28
 
29
- pub fn quote_oft(chain: &ChainSetup<'_>, send_param: &SendParam) -> (OFTLimit, Vec<OFTFeeDetail>, OFTReceipt) {
30
- chain.oft.quote_oft(send_param)
29
+ pub fn quote_oft(chain: &ChainSetup<'_>, from: &Address, send_param: &SendParam) -> (OFTLimit, Vec<OFTFeeDetail>, OFTReceipt) {
30
+ chain.oft.quote_oft(from, send_param)
31
31
  }
32
32
 
33
33
  pub fn quote_send(
@@ -141,12 +141,13 @@ pub fn lz_compose(
141
141
  value: i128,
142
142
  ) {
143
143
  let oft_msg = oft_msg_codec::OFTMessage::decode(&packet.message);
144
+ let compose = oft_msg.compose.unwrap();
144
145
  let oft_compose_msg = OFTComposeMsg {
145
146
  nonce: packet.nonce,
146
147
  src_eid: packet.src_eid,
147
148
  amount_ld: (oft_msg.amount_sd as i128).mul(chain.oft.decimal_conversion_rate()),
148
- compose_from: oft_msg.compose_from.unwrap(),
149
- compose_msg: oft_msg.compose_msg.unwrap(),
149
+ compose_from: compose.from,
150
+ compose_msg: compose.msg,
150
151
  }
151
152
  .encode(&env);
152
153
 
@@ -31,8 +31,8 @@ impl OFTComposeMsg {
31
31
  .write_u32(self.src_eid)
32
32
  .write_i128(self.amount_ld)
33
33
  .write_bytes_n(&self.compose_from)
34
- .write_bytes(&self.compose_msg);
35
- writer.to_bytes()
34
+ .write_bytes(&self.compose_msg)
35
+ .to_bytes()
36
36
  }
37
37
 
38
38
  /// Decodes Bytes into an OFTComposeMsg struct.