@layerzerolabs/protocol-stellar-v2 0.2.64 → 0.2.66

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 (48) hide show
  1. package/.turbo/turbo-build.log +230 -307
  2. package/.turbo/turbo-lint.log +175 -175
  3. package/.turbo/turbo-test.log +2047 -1975
  4. package/Cargo.lock +0 -16
  5. package/Cargo.toml +0 -1
  6. package/contracts/oapps/oft/integration-tests/extensions/test_oft_fee.rs +22 -0
  7. package/contracts/oapps/oft/integration-tests/extensions/test_pausable.rs +9 -2
  8. package/contracts/oapps/oft/integration-tests/extensions/test_rate_limiter.rs +27 -2
  9. package/contracts/oapps/oft/integration-tests/setup.rs +22 -18
  10. package/contracts/oapps/oft/integration-tests/utils.rs +81 -34
  11. package/contracts/oapps/oft/src/extensions/oft_fee.rs +13 -0
  12. package/contracts/oapps/oft/src/oft.rs +10 -2
  13. package/package.json +5 -5
  14. package/sdk/.turbo/turbo-test.log +284 -291
  15. package/sdk/dist/generated/oft.d.ts +3 -3
  16. package/sdk/dist/generated/oft.js +3 -3
  17. package/sdk/node_modules/.bin/vitest +2 -2
  18. package/sdk/package.json +2 -2
  19. package/contracts/oapps/console-oft/Cargo.toml +0 -30
  20. package/contracts/oapps/console-oft/integration-tests/extensions/mod.rs +0 -5
  21. package/contracts/oapps/console-oft/integration-tests/extensions/test_combined.rs +0 -90
  22. package/contracts/oapps/console-oft/integration-tests/extensions/test_oft_fee.rs +0 -186
  23. package/contracts/oapps/console-oft/integration-tests/extensions/test_ownership.rs +0 -161
  24. package/contracts/oapps/console-oft/integration-tests/extensions/test_pausable.rs +0 -154
  25. package/contracts/oapps/console-oft/integration-tests/extensions/test_rate_limiter.rs +0 -479
  26. package/contracts/oapps/console-oft/integration-tests/mod.rs +0 -3
  27. package/contracts/oapps/console-oft/integration-tests/setup.rs +0 -303
  28. package/contracts/oapps/console-oft/integration-tests/utils.rs +0 -685
  29. package/contracts/oapps/console-oft/src/errors.rs +0 -7
  30. package/contracts/oapps/console-oft/src/extensions/mod.rs +0 -3
  31. package/contracts/oapps/console-oft/src/extensions/oft_fee.rs +0 -239
  32. package/contracts/oapps/console-oft/src/extensions/pausable.rs +0 -185
  33. package/contracts/oapps/console-oft/src/extensions/rate_limiter.rs +0 -478
  34. package/contracts/oapps/console-oft/src/interfaces/mintable.rs +0 -14
  35. package/contracts/oapps/console-oft/src/interfaces/mod.rs +0 -3
  36. package/contracts/oapps/console-oft/src/lib.rs +0 -26
  37. package/contracts/oapps/console-oft/src/oft.rs +0 -208
  38. package/contracts/oapps/console-oft/src/oft_access_control.rs +0 -93
  39. package/contracts/oapps/console-oft/src/oft_types/lock_unlock.rs +0 -50
  40. package/contracts/oapps/console-oft/src/oft_types/mint_burn.rs +0 -50
  41. package/contracts/oapps/console-oft/src/oft_types/mod.rs +0 -24
  42. package/contracts/oapps/console-oft/src/tests/extensions/mod.rs +0 -3
  43. package/contracts/oapps/console-oft/src/tests/extensions/oft_fee.rs +0 -255
  44. package/contracts/oapps/console-oft/src/tests/extensions/pausable.rs +0 -212
  45. package/contracts/oapps/console-oft/src/tests/extensions/rate_limiter.rs +0 -992
  46. package/contracts/oapps/console-oft/src/tests/mod.rs +0 -2
  47. package/contracts/oapps/console-oft/src/tests/oft_types/lock_unlock.rs +0 -185
  48. package/contracts/oapps/console-oft/src/tests/oft_types/mod.rs +0 -1
package/Cargo.lock CHANGED
@@ -292,22 +292,6 @@ dependencies = [
292
292
  "windows-sys",
293
293
  ]
294
294
 
295
- [[package]]
296
- name = "console-oft"
297
- version = "0.0.1"
298
- dependencies = [
299
- "cfg-if",
300
- "common-macros",
301
- "endpoint-v2",
302
- "message-lib-common",
303
- "oapp",
304
- "oapp-macros",
305
- "oft-core",
306
- "simple-message-lib",
307
- "soroban-sdk",
308
- "utils",
309
- ]
310
-
311
295
  [[package]]
312
296
  name = "const-oid"
313
297
  version = "0.9.6"
package/Cargo.toml CHANGED
@@ -50,7 +50,6 @@ blocked-message-lib = { path = "contracts/message-libs/blocked-message-lib" }
50
50
  oapp = { path = "contracts/oapps/oapp" }
51
51
  oapp-macros = { path = "contracts/oapps/oapp-macros" }
52
52
  oft = { path = "contracts/oapps/oft" }
53
- console-oft = { path = "contracts/oapps/console-oft" }
54
53
  oft-core = { path = "contracts/oapps/oft-core" }
55
54
  price-feed = { path = "contracts/workers/price-feed" }
56
55
  simple-message-lib = { path = "contracts/message-libs/simple-message-lib" }
@@ -29,6 +29,14 @@ fn test_cross_chain_with_zero_fee() {
29
29
  mint_oft_token_to(&env, &chain_a, &sender, 10_000_000);
30
30
  mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
31
31
 
32
+ // Default fee state on both OFTs
33
+ assert_eq!(chain_a.oft.default_fee_bps(), None);
34
+ assert_eq!(chain_a.oft.fee_deposit_address(), None);
35
+ assert_eq!(chain_a.oft.effective_fee_bps(&chain_b.eid), 0);
36
+ assert_eq!(chain_b.oft.default_fee_bps(), None);
37
+ assert_eq!(chain_b.oft.fee_deposit_address(), None);
38
+ assert_eq!(chain_b.oft.effective_fee_bps(&chain_a.eid), 0);
39
+
32
40
  let to = address_to_peer_bytes32(&receiver);
33
41
  let send_param = create_send_param(&env, chain_b.eid, 1_000_000, 0, &to);
34
42
  let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
@@ -67,6 +75,12 @@ fn test_cross_chain_with_fee() {
67
75
  // Enable 1% fee (100 bps)
68
76
  set_fee_deposit_address(&env, &chain_a, &chain_a.fee_collector);
69
77
  set_default_fee_bps(&env, &chain_a, 100);
78
+ assert_eq!(chain_a.oft.default_fee_bps(), Some(100));
79
+ assert_eq!(chain_a.oft.fee_deposit_address(), Some(chain_a.fee_collector.clone()));
80
+ assert_eq!(chain_a.oft.effective_fee_bps(&chain_b.eid), 100);
81
+ assert_eq!(chain_b.oft.default_fee_bps(), None);
82
+ assert_eq!(chain_b.oft.fee_deposit_address(), None);
83
+ assert_eq!(chain_b.oft.effective_fee_bps(&chain_a.eid), 0);
70
84
 
71
85
  let to = address_to_peer_bytes32(&receiver);
72
86
  let amount_ld = 1_000_000i128;
@@ -119,6 +133,14 @@ fn test_cross_chain_with_destination_specific_fee() {
119
133
  set_fee_deposit_address(&env, &chain_a, &chain_a.fee_collector);
120
134
  set_default_fee_bps(&env, &chain_a, 100); // 1%
121
135
  set_fee_bps(&env, &chain_a, chain_b.eid, 200); // 2% for chain_b
136
+ assert_eq!(chain_a.oft.default_fee_bps(), Some(100));
137
+ assert_eq!(chain_a.oft.fee_bps(&chain_b.eid), Some(200));
138
+ assert_eq!(chain_a.oft.fee_deposit_address(), Some(chain_a.fee_collector.clone()));
139
+ assert_eq!(chain_a.oft.effective_fee_bps(&chain_b.eid), 200);
140
+ assert_eq!(chain_b.oft.default_fee_bps(), None);
141
+ assert_eq!(chain_b.oft.fee_bps(&chain_a.eid), None);
142
+ assert_eq!(chain_b.oft.fee_deposit_address(), None);
143
+ assert_eq!(chain_b.oft.effective_fee_bps(&chain_a.eid), 0);
122
144
 
123
145
  let to = address_to_peer_bytes32(&receiver);
124
146
  let amount_ld = 1_000_000i128;
@@ -26,8 +26,9 @@ fn test_send_succeeds_when_unpaused() {
26
26
  mint_oft_token_to(&env, &chain_a, &sender, 10_000_000);
27
27
  mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
28
28
 
29
- // Default state should be unpaused
29
+ // Default state should be unpaused on both OFTs
30
30
  assert!(!is_paused(&chain_a));
31
+ assert!(!is_paused(&chain_b));
31
32
 
32
33
  let to = address_to_peer_bytes32(&receiver);
33
34
  let send_param = create_send_param(&env, chain_b.eid, 1_000_000, 0, &to);
@@ -74,6 +75,7 @@ fn test_send_fails_when_paused() {
74
75
  // Pause the OFT
75
76
  set_paused(&env, &chain_a, true);
76
77
  assert!(is_paused(&chain_a));
78
+ assert!(!is_paused(&chain_b));
77
79
 
78
80
  // Send should fail when paused
79
81
  let success = try_send(&env, &chain_a, &sender, &send_param, &fee, &sender, &oft_receipt);
@@ -108,6 +110,7 @@ fn test_receive_fails_when_paused() {
108
110
  // Pause chain_b's OFT before receiving
109
111
  set_paused(&env, &chain_b, true);
110
112
  assert!(is_paused(&chain_b));
113
+ assert!(!is_paused(&chain_a));
111
114
 
112
115
  // Receive should fail on paused destination
113
116
  let success = try_lz_receive(&env, &chain_b, &executor, &packet, &receiver, 0);
@@ -128,11 +131,15 @@ fn test_cross_chain_succeeds_after_unpause() {
128
131
  mint_oft_token_to(&env, &chain_a, &sender, 10_000_000);
129
132
  mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
130
133
 
131
- // Pause and then unpause
134
+ // Pause and then unpause on chain_a; chain_b remains unpaused throughout
135
+ assert!(!is_paused(&chain_a));
136
+ assert!(!is_paused(&chain_b));
132
137
  set_paused(&env, &chain_a, true);
133
138
  assert!(is_paused(&chain_a));
139
+ assert!(!is_paused(&chain_b));
134
140
  set_paused(&env, &chain_a, false);
135
141
  assert!(!is_paused(&chain_a));
142
+ assert!(!is_paused(&chain_b));
136
143
 
137
144
  // Send should work after unpause
138
145
  let to = address_to_peer_bytes32(&receiver);
@@ -31,6 +31,9 @@ fn test_send_without_rate_limit() {
31
31
  // Default capacity should be i128::MAX (unlimited)
32
32
  let capacity = rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid);
33
33
  assert_eq!(capacity, i128::MAX);
34
+ assert_eq!(rate_limit_in_flight(&chain_a, &Direction::Outbound, chain_b.eid), 0);
35
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
36
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
34
37
 
35
38
  let to = address_to_peer_bytes32(&receiver);
36
39
  let send_param = create_send_param(&env, chain_b.eid, 50_000_000, 0, &to);
@@ -66,8 +69,9 @@ fn test_send_within_rate_limit() {
66
69
  // Set rate limit: 10M per 3600 seconds (1 hour)
67
70
  set_rate_limit(&env, &chain_a, &Direction::Outbound, chain_b.eid, 10_000_000, 3600);
68
71
 
69
- let capacity = rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid);
70
- assert_eq!(capacity, 10_000_000);
72
+ assert_eq!(rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid), 10_000_000);
73
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
74
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
71
75
 
72
76
  // Send 5M (within limit)
73
77
  let to = address_to_peer_bytes32(&receiver);
@@ -113,6 +117,9 @@ fn test_send_exceeds_rate_limit() {
113
117
 
114
118
  // Set rate limit: 1M per 3600 seconds
115
119
  set_rate_limit(&env, &chain_a, &Direction::Outbound, chain_b.eid, 1_000_000, 3600);
120
+ assert_eq!(rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid), 1_000_000);
121
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
122
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
116
123
 
117
124
  // Try to send 5M (exceeds limit)
118
125
  let to = address_to_peer_bytes32(&receiver);
@@ -122,6 +129,8 @@ fn test_send_exceeds_rate_limit() {
122
129
 
123
130
  let success = try_send(&env, &chain_a, &sender, &send_param, &fee, &sender, &oft_receipt);
124
131
  assert!(!success);
132
+ assert_eq!(rate_limit_in_flight(&chain_a, &Direction::Outbound, chain_b.eid), 0);
133
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
125
134
  }
126
135
 
127
136
  /// Test rate limit decay over time
@@ -140,6 +149,9 @@ fn test_rate_limit_decay() {
140
149
 
141
150
  // Set rate limit: 10M per 1000 seconds
142
151
  set_rate_limit(&env, &chain_a, &Direction::Outbound, chain_b.eid, 10_000_000, 1000);
152
+ assert_eq!(rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid), 10_000_000);
153
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
154
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
143
155
 
144
156
  let to = address_to_peer_bytes32(&receiver);
145
157
 
@@ -176,6 +188,9 @@ fn test_rate_limit_decay() {
176
188
  validate_packet(&env, &chain_b, &packet_event2);
177
189
  let packet2 = decode_packet(&env, &packet_event2.0);
178
190
  lz_receive(&env, &chain_b, &executor, &packet2, &receiver, 0);
191
+ assert!(rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid) < 10_000_000);
192
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
193
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
179
194
  }
180
195
 
181
196
  /// Test gross mode: round-trip shows gross mode doesn't release outbound capacity
@@ -199,6 +214,9 @@ fn test_gross_mode_does_not_release() {
199
214
 
200
215
  // Set Gross mode rate limit on chain_a outbound: 10M per 3600 seconds
201
216
  set_rate_limit_with_mode(&env, &chain_a, &Direction::Outbound, chain_b.eid, 10_000_000, 3600, Mode::Gross);
217
+ assert_eq!(rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid), 10_000_000);
218
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
219
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
202
220
 
203
221
  // Step 1: Send 8M from chain_a to chain_b
204
222
  let to_b = address_to_peer_bytes32(&receiver_b);
@@ -242,6 +260,8 @@ fn test_gross_mode_does_not_release() {
242
260
  // Should only have ~2M capacity left, NOT 8M (if it were Net mode)
243
261
  let capacity_after_return = rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid);
244
262
  assert!(capacity_after_return < 3_000_000, "Gross mode: capacity should remain low");
263
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
264
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
245
265
  }
246
266
 
247
267
  /// Test net mode (default): round-trip shows net mode releases outbound capacity
@@ -265,6 +285,9 @@ fn test_net_mode_does_release() {
265
285
 
266
286
  // Set Net mode rate limit on chain_a outbound: 10M per 3600 seconds
267
287
  set_rate_limit_with_mode(&env, &chain_a, &Direction::Outbound, chain_b.eid, 10_000_000, 3600, Mode::Net);
288
+ assert_eq!(rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid), 10_000_000);
289
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
290
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
268
291
 
269
292
  // Step 1: Send 8M from chain_a to chain_b
270
293
  let to_b = address_to_peer_bytes32(&receiver_b);
@@ -308,4 +331,6 @@ fn test_net_mode_does_release() {
308
331
  // Should have much more capacity now (8M sent - 6M received back = ~2M net)
309
332
  let capacity_after_return = rate_limit_capacity(&chain_a, &Direction::Outbound, chain_b.eid);
310
333
  assert!(capacity_after_return > 7_000_000, "Net mode: capacity should increase after receiving");
334
+ assert_eq!(rate_limit_capacity(&chain_b, &Direction::Outbound, chain_a.eid), i128::MAX);
335
+ assert_eq!(rate_limit_in_flight(&chain_b, &Direction::Outbound, chain_a.eid), 0);
311
336
  }
@@ -89,7 +89,7 @@ pub struct TestSetup<'a> {
89
89
  pub chain_b: ChainSetup<'a>,
90
90
  }
91
91
 
92
- fn setup_chain<'a>(env: &Env) -> ChainSetup<'a> {
92
+ fn setup_chain<'a>(env: &Env, use_mint_burn: bool) -> ChainSetup<'a> {
93
93
  let owner = Address::generate(env);
94
94
 
95
95
  // Create native token FIRST - this must match the endpoint's NATIVE_TOKEN constant
@@ -107,26 +107,31 @@ fn setup_chain<'a>(env: &Env) -> ChainSetup<'a> {
107
107
  let oft_sac = env.register_stellar_asset_contract_v2(owner.clone());
108
108
  let oft_token = oft_sac.address();
109
109
  let sac_wrapper_address = env.register(MockSacWrapper, (&oft_token,));
110
- env.mock_auths(&[MockAuth {
111
- address: &owner,
112
- invoke: &MockAuthInvoke {
113
- contract: &oft_token,
114
- fn_name: "set_admin",
115
- args: (&sac_wrapper_address,).into_val(env),
116
- sub_invokes: &[],
117
- },
118
- }]);
119
- StellarAssetClient::new(env, &oft_token).set_admin(&sac_wrapper_address);
110
+
111
+ let mode = if use_mint_burn {
112
+ env.mock_auths(&[MockAuth {
113
+ address: &owner,
114
+ invoke: &MockAuthInvoke {
115
+ contract: &oft_token,
116
+ fn_name: "set_admin",
117
+ args: (&sac_wrapper_address,).into_val(env),
118
+ sub_invokes: &[],
119
+ },
120
+ }]);
121
+ StellarAssetClient::new(env, &oft_token).set_admin(&sac_wrapper_address);
122
+ OftType::MintBurn(sac_wrapper_address.clone())
123
+ } else {
124
+ OftType::LockUnlock
125
+ };
120
126
 
121
127
  let eid: u32 = 30400; // Test EID
122
128
  let endpoint_address = env.register(EndpointV2, (&owner, eid, &native_token));
123
129
  let fee_recipient = Address::generate(env);
124
130
  let sml_address = env.register(SimpleMessageLib, (&owner, &endpoint_address, &fee_recipient));
125
131
  let delegate: Option<Address> = Some(owner.clone());
126
- let shared_decimals: u32 = 6; // Default shared decimals
127
- // MintBurn with SAC wrapper: OFT uses wrapper for mint on credit; burn is on token directly.
128
- let mode = OftType::MintBurn(sac_wrapper_address.clone());
129
- let oft_address = env.register(OFT, (&oft_token, &shared_decimals, &mode, &endpoint_address, delegate.as_ref().unwrap()));
132
+ let shared_decimals: u32 = 6;
133
+ let oft_address =
134
+ env.register(OFT, (&oft_token, &shared_decimals, &mode, &endpoint_address, delegate.as_ref().unwrap()));
130
135
 
131
136
  let endpoint = EndpointV2Client::new(env, &endpoint_address);
132
137
  let sml = SimpleMessageLibClient::new(env, &sml_address);
@@ -161,8 +166,8 @@ fn setup_chain<'a>(env: &Env) -> ChainSetup<'a> {
161
166
 
162
167
  pub fn setup<'a>() -> TestSetup<'a> {
163
168
  let env = Env::default();
164
- let chain_a = setup_chain(&env);
165
- let chain_b = setup_chain(&env);
169
+ let chain_a = setup_chain(&env, false);
170
+ let chain_b = setup_chain(&env, true);
166
171
 
167
172
  let chain_a_oft_token_decimals = TokenClient::new(&env, &chain_a.oft_token).decimals();
168
173
  let chain_b_oft_token_decimals = TokenClient::new(&env, &chain_b.oft_token).decimals();
@@ -214,7 +219,6 @@ pub fn wire_oft(env: &Env, chains: &[&ChainSetup<'_>]) {
214
219
  }
215
220
 
216
221
  pub fn set_peer(env: &Env, owner: &Address, oft: &OFTClient<'_>, dst_eid: u32, peer: &BytesN<32>) {
217
-
218
222
  let peer_option = Some(peer.clone());
219
223
  env.mock_auths(&[MockAuth {
220
224
  address: owner,
@@ -1,9 +1,10 @@
1
1
  //! Utility functions for OFT-STD integration tests.
2
2
 
3
- use crate::extensions::rate_limiter::{Direction, Mode, RateLimitConfig, RATE_LIMITER_MANAGER_ROLE};
4
3
  use crate::extensions::oft_fee::FEE_CONFIG_MANAGER_ROLE;
5
4
  use crate::extensions::pausable::{PAUSER_ROLE, UNPAUSER_ROLE};
5
+ use crate::extensions::rate_limiter::{Direction, Mode, RateLimitConfig, RATE_LIMITER_MANAGER_ROLE};
6
6
  use crate::integration_tests::setup::{decode_packet, ChainSetup};
7
+ use crate::oft_types::OftType;
7
8
  use crate::MintableClient;
8
9
  use endpoint_v2::{MessagingFee, Origin, OutboundPacket};
9
10
  use message_lib_common::packet_codec_v1;
@@ -78,6 +79,21 @@ pub fn send(
78
79
  refund_address: &Address,
79
80
  oft_receipt: &OFTReceipt,
80
81
  ) {
82
+ let token_sub_invoke = match chain.oft.oft_type() {
83
+ OftType::LockUnlock => MockAuthInvoke {
84
+ contract: &chain.oft_token,
85
+ fn_name: "transfer",
86
+ args: (sender, &chain.oft.address, &oft_receipt.amount_received_ld).into_val(env),
87
+ sub_invokes: &[],
88
+ },
89
+ OftType::MintBurn(_) => MockAuthInvoke {
90
+ contract: &chain.oft_token,
91
+ fn_name: "burn",
92
+ args: (sender, &oft_receipt.amount_received_ld).into_val(env),
93
+ sub_invokes: &[],
94
+ },
95
+ };
96
+
81
97
  env.mock_auths(&[MockAuth {
82
98
  address: sender,
83
99
  invoke: &MockAuthInvoke {
@@ -85,18 +101,13 @@ pub fn send(
85
101
  fn_name: "send",
86
102
  args: (sender, send_param, fee, refund_address).into_val(env),
87
103
  sub_invokes: &[
104
+ token_sub_invoke,
88
105
  MockAuthInvoke {
89
106
  contract: &chain.native_token,
90
107
  fn_name: "transfer",
91
108
  args: (sender, &chain.endpoint.address, &fee.native_fee).into_val(env),
92
109
  sub_invokes: &[],
93
110
  },
94
- MockAuthInvoke {
95
- contract: &chain.oft_token,
96
- fn_name: "burn",
97
- args: (sender, &oft_receipt.amount_received_ld).into_val(env),
98
- sub_invokes: &[],
99
- },
100
111
  ],
101
112
  },
102
113
  }]);
@@ -116,6 +127,21 @@ pub fn send_with_fee(
116
127
  fee_deposit_address: &Address,
117
128
  ) {
118
129
  let fee_amount = oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld;
130
+ let token_sub_invoke = match chain.oft.oft_type() {
131
+ OftType::LockUnlock => MockAuthInvoke {
132
+ contract: &chain.oft_token,
133
+ fn_name: "transfer",
134
+ args: (sender, &chain.oft.address, &oft_receipt.amount_received_ld).into_val(env),
135
+ sub_invokes: &[],
136
+ },
137
+ OftType::MintBurn(_) => MockAuthInvoke {
138
+ contract: &chain.oft_token,
139
+ fn_name: "burn",
140
+ args: (sender, &oft_receipt.amount_received_ld).into_val(env),
141
+ sub_invokes: &[],
142
+ },
143
+ };
144
+
119
145
  env.mock_auths(&[MockAuth {
120
146
  address: sender,
121
147
  invoke: &MockAuthInvoke {
@@ -123,18 +149,13 @@ pub fn send_with_fee(
123
149
  fn_name: "send",
124
150
  args: (sender, send_param, fee, refund_address).into_val(env),
125
151
  sub_invokes: &[
152
+ token_sub_invoke,
126
153
  MockAuthInvoke {
127
154
  contract: &chain.oft_token,
128
155
  fn_name: "transfer",
129
156
  args: (sender, fee_deposit_address, &fee_amount).into_val(env),
130
157
  sub_invokes: &[],
131
158
  },
132
- MockAuthInvoke {
133
- contract: &chain.oft_token,
134
- fn_name: "burn",
135
- args: (sender, &oft_receipt.amount_received_ld).into_val(env),
136
- sub_invokes: &[],
137
- },
138
159
  MockAuthInvoke {
139
160
  contract: &chain.native_token,
140
161
  fn_name: "transfer",
@@ -156,6 +177,21 @@ pub fn try_send(
156
177
  refund_address: &Address,
157
178
  oft_receipt: &OFTReceipt,
158
179
  ) -> bool {
180
+ let token_sub_invoke = match chain.oft.oft_type() {
181
+ OftType::LockUnlock => MockAuthInvoke {
182
+ contract: &chain.oft_token,
183
+ fn_name: "transfer",
184
+ args: (sender, &chain.oft.address, &oft_receipt.amount_received_ld).into_val(env),
185
+ sub_invokes: &[],
186
+ },
187
+ OftType::MintBurn(_) => MockAuthInvoke {
188
+ contract: &chain.oft_token,
189
+ fn_name: "burn",
190
+ args: (sender, &oft_receipt.amount_received_ld).into_val(env),
191
+ sub_invokes: &[],
192
+ },
193
+ };
194
+
159
195
  env.mock_auths(&[MockAuth {
160
196
  address: sender,
161
197
  invoke: &MockAuthInvoke {
@@ -163,18 +199,13 @@ pub fn try_send(
163
199
  fn_name: "send",
164
200
  args: (sender, send_param, fee, refund_address).into_val(env),
165
201
  sub_invokes: &[
202
+ token_sub_invoke,
166
203
  MockAuthInvoke {
167
204
  contract: &chain.native_token,
168
205
  fn_name: "transfer",
169
206
  args: (sender, &chain.endpoint.address, &fee.native_fee).into_val(env),
170
207
  sub_invokes: &[],
171
208
  },
172
- MockAuthInvoke {
173
- contract: &chain.oft_token,
174
- fn_name: "burn",
175
- args: (sender, &oft_receipt.amount_received_ld).into_val(env),
176
- sub_invokes: &[],
177
- },
178
209
  ],
179
210
  },
180
211
  }]);
@@ -318,21 +349,37 @@ pub fn mint_to(env: &Env, owner: &Address, token: &Address, to: &Address, amount
318
349
  /// Mints the OFT token (via the Mintable wrapper) to the given address.
319
350
  /// Use when OFT is MintBurn; the wrapper calls the underlying SAC mint.
320
351
  pub fn mint_oft_token_to(env: &Env, chain: &ChainSetup<'_>, to: &Address, amount: i128) {
321
- env.mock_auths(&[MockAuth {
322
- address: &chain.owner,
323
- invoke: &MockAuthInvoke {
324
- contract: &chain.sac_wrapper,
325
- fn_name: "mint",
326
- args: (to, &amount, &chain.owner).into_val(env),
327
- sub_invokes: &[MockAuthInvoke {
328
- contract: &chain.oft_token,
329
- fn_name: "mint",
330
- args: (to, &amount).into_val(env),
331
- sub_invokes: &[],
332
- }],
333
- },
334
- }]);
335
- MintableClient::new(env, &chain.sac_wrapper).mint(to, &amount, &chain.owner);
352
+ match chain.oft.oft_type() {
353
+ OftType::LockUnlock => {
354
+ env.mock_auths(&[MockAuth {
355
+ address: &chain.owner,
356
+ invoke: &MockAuthInvoke {
357
+ contract: &chain.oft_token,
358
+ fn_name: "mint",
359
+ args: (to, &amount).into_val(env),
360
+ sub_invokes: &[],
361
+ },
362
+ }]);
363
+ StellarAssetClient::new(env, &chain.oft_token).mint(to, &amount);
364
+ }
365
+ OftType::MintBurn(_) => {
366
+ env.mock_auths(&[MockAuth {
367
+ address: &chain.owner,
368
+ invoke: &MockAuthInvoke {
369
+ contract: &chain.sac_wrapper,
370
+ fn_name: "mint",
371
+ args: (to, &amount, &chain.owner).into_val(env),
372
+ sub_invokes: &[MockAuthInvoke {
373
+ contract: &chain.oft_token,
374
+ fn_name: "mint",
375
+ args: (to, &amount).into_val(env),
376
+ sub_invokes: &[],
377
+ }],
378
+ },
379
+ }]);
380
+ MintableClient::new(env, &chain.sac_wrapper).mint(to, &amount, &chain.owner);
381
+ }
382
+ }
336
383
  }
337
384
 
338
385
  pub fn transfer_sac_admin(env: &Env, owner: &Address, token: &Address, new_admin: &Address) {
@@ -265,4 +265,17 @@ pub trait OFTFeeInternal {
265
265
  fn __fee_deposit_address(env: &Env) -> Option<Address> {
266
266
  OFTFeeStorage::fee_deposit_address(env)
267
267
  }
268
+
269
+ /// Back-calculates the pre-fee amount given a post-fee amount.
270
+ /// Returns the smallest `x` such that `x - fee(x) >= amount_after_fee`.
271
+ fn __amount_before_fee(env: &Env, dst_eid: u32, amount_after_fee: i128) -> i128 {
272
+ let fee_bps = Self::__effective_fee_bps(env, dst_eid);
273
+ if fee_bps == 0 {
274
+ return amount_after_fee;
275
+ }
276
+ if fee_bps == BASE_FEE_BPS {
277
+ return 0;
278
+ }
279
+ (amount_after_fee * BASE_FEE_BPS as i128) / (BASE_FEE_BPS - fee_bps) as i128
280
+ }
268
281
  }
@@ -68,8 +68,16 @@ impl OFTCore for OFT {
68
68
  fee_details.push_back(OFTFeeDetail { fee_amount_ld, description: Bytes::from_slice(env, b"OFT Fee") });
69
69
  };
70
70
 
71
- // rate limit capacity
72
- limit.max_amount_ld = Self::rate_limit_capacity(env, &Direction::Outbound, send_param.dst_eid);
71
+ // Rate limit capacity, capped to u64::MAX (the safe ceiling from __quote_oft default)
72
+ let mut max_amount_ld = Self::rate_limit_capacity(env, &Direction::Outbound, send_param.dst_eid);
73
+ if max_amount_ld != i128::MAX {
74
+ // Back-calculate pre-fee amount so max_amount_ld represents the largest input to send()
75
+ max_amount_ld = Self::__amount_before_fee(env, send_param.dst_eid, max_amount_ld);
76
+ } else {
77
+ // No rate limit configured — use the bounded default
78
+ max_amount_ld = u64::MAX as i128;
79
+ }
80
+ limit.max_amount_ld = max_amount_ld;
73
81
 
74
82
  (limit, fee_details, receipt)
75
83
  }
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@layerzerolabs/protocol-stellar-v2",
3
- "version": "0.2.64",
3
+ "version": "0.2.66",
4
4
  "private": false,
5
5
  "license": "LZBL-1.2",
6
6
  "devDependencies": {
7
7
  "@types/node": "^22.18.6",
8
8
  "tsx": "^4.19.3",
9
9
  "typescript": "^5.8.2",
10
- "@layerzerolabs/vm-tooling-stellar": "0.2.64",
11
- "@layerzerolabs/stellar-ts-bindings-gen": "0.2.64",
12
- "@layerzerolabs/common-node-utils": "0.2.64"
10
+ "@layerzerolabs/common-node-utils": "0.2.66",
11
+ "@layerzerolabs/vm-tooling-stellar": "0.2.66",
12
+ "@layerzerolabs/stellar-ts-bindings-gen": "0.2.66"
13
13
  },
14
14
  "publishConfig": {
15
15
  "access": "restricted",
@@ -18,7 +18,7 @@
18
18
  "externalRepoConfig": {
19
19
  "targets": [
20
20
  "audit-external",
21
- "everdawn-asset0"
21
+ "onesig-client"
22
22
  ]
23
23
  },
24
24
  "scripts": {