@layerzerolabs/protocol-stellar-v2 0.2.10 → 0.2.12

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 (129) hide show
  1. package/.turbo/turbo-build.log +273 -219
  2. package/.turbo/turbo-lint.log +79 -107
  3. package/.turbo/turbo-test.log +1016 -840
  4. package/Cargo.lock +14 -6
  5. package/contracts/common-macros/src/contract_impl.rs +6 -3
  6. package/contracts/common-macros/src/error.rs +9 -17
  7. package/contracts/common-macros/src/lib.rs +4 -37
  8. package/contracts/common-macros/src/ownable.rs +9 -5
  9. package/contracts/common-macros/src/tests/contract_impl.rs +178 -86
  10. package/contracts/common-macros/src/tests/error.rs +168 -0
  11. package/contracts/common-macros/src/tests/mod.rs +2 -4
  12. package/contracts/common-macros/src/tests/ownable.rs +37 -60
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap +16 -6
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__error__snapshot_generated_contract_error_code.snap +20 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +3 -1
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap +12 -2
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +5 -1
  18. package/contracts/common-macros/src/tests/utils.rs +267 -0
  19. package/contracts/common-macros/src/ttl_configurable.rs +15 -12
  20. package/contracts/common-macros/src/utils.rs +35 -6
  21. package/contracts/endpoint-v2/src/endpoint_v2.rs +4 -4
  22. package/contracts/endpoint-v2/src/events.rs +40 -22
  23. package/contracts/endpoint-v2/src/interfaces/message_lib.rs +2 -2
  24. package/contracts/endpoint-v2/src/interfaces/message_lib_manager.rs +2 -2
  25. package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +2 -2
  26. package/contracts/endpoint-v2/src/interfaces/messaging_composer.rs +2 -2
  27. package/contracts/endpoint-v2/src/interfaces/send_lib.rs +2 -2
  28. package/contracts/endpoint-v2/src/message_lib_manager.rs +3 -3
  29. package/contracts/endpoint-v2/src/messaging_channel.rs +1 -1
  30. package/contracts/endpoint-v2/src/messaging_composer.rs +1 -1
  31. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +4 -8
  32. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +3 -7
  33. package/contracts/message-libs/{block-message-lib → blocked-message-lib}/Cargo.toml +1 -1
  34. package/contracts/message-libs/treasury/src/events.rs +9 -6
  35. package/contracts/message-libs/uln-302/src/events.rs +19 -11
  36. package/contracts/message-libs/uln-302/src/interfaces/receive_uln.rs +2 -2
  37. package/contracts/message-libs/uln-302/src/interfaces/send_uln.rs +2 -2
  38. package/contracts/message-libs/uln-302/src/receive_uln.rs +2 -2
  39. package/contracts/message-libs/uln-302/src/send_uln.rs +3 -3
  40. package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +5 -5
  41. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +5 -5
  42. package/contracts/message-libs/uln-302/src/tests/setup.rs +3 -3
  43. package/contracts/message-libs/uln-302/src/types.rs +24 -24
  44. package/contracts/message-libs/uln-302/src/uln302.rs +2 -2
  45. package/contracts/oapp-macros/src/oapp_core.rs +1 -1
  46. package/contracts/oapps/counter/integration_tests/utils.rs +1 -1
  47. package/contracts/oapps/oapp/src/oapp_core.rs +4 -3
  48. package/contracts/oapps/oapp/src/oapp_options_type3.rs +4 -3
  49. package/contracts/oapps/oft/integration-tests/setup.rs +4 -3
  50. package/contracts/oapps/oft/integration-tests/utils.rs +1 -1
  51. package/contracts/oapps/oft/src/default_oft_impl.rs +146 -0
  52. package/contracts/oapps/oft/src/events.rs +5 -4
  53. package/contracts/oapps/oft/src/extensions/mod.rs +3 -0
  54. package/contracts/oapps/oft/src/extensions/oft_fee.rs +168 -0
  55. package/contracts/oapps/oft/src/extensions/pausable.rs +50 -0
  56. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +200 -0
  57. package/contracts/oapps/oft/src/lib.rs +2 -3
  58. package/contracts/oapps/oft/src/oft.rs +16 -85
  59. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +1 -1
  60. package/contracts/oapps/oft/src/tests/extensions/mod.rs +11 -0
  61. package/contracts/oapps/oft/src/tests/extensions/setup.rs +888 -0
  62. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +749 -0
  63. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +432 -0
  64. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +1078 -0
  65. package/contracts/oapps/oft/src/tests/mod.rs +2 -0
  66. package/contracts/oapps/oft/src/tests/test_utils.rs +24 -6
  67. package/contracts/oapps/{oft-mint-burn → oft-std}/Cargo.toml +1 -8
  68. package/contracts/oapps/oft-std/src/lib.rs +5 -0
  69. package/contracts/oapps/oft-std/src/oft.rs +59 -0
  70. package/contracts/utils/src/ownable.rs +8 -6
  71. package/contracts/utils/src/tests/ownable.rs +0 -63
  72. package/contracts/utils/src/tests/testing_utils.rs +7 -5
  73. package/contracts/utils/src/ttl.rs +21 -2
  74. package/contracts/workers/dvn/src/auth.rs +108 -30
  75. package/contracts/workers/dvn/src/dvn.rs +103 -33
  76. package/contracts/workers/dvn/src/errors.rs +10 -13
  77. package/contracts/workers/dvn/src/events.rs +7 -5
  78. package/contracts/workers/dvn/src/interfaces/dvn.rs +76 -3
  79. package/contracts/workers/dvn/src/interfaces/multisig.rs +41 -0
  80. package/contracts/workers/dvn/src/lib.rs +6 -8
  81. package/contracts/workers/dvn/src/multisig.rs +98 -72
  82. package/contracts/workers/dvn/src/storage.rs +9 -12
  83. package/contracts/workers/dvn/src/tests/auth.rs +56 -26
  84. package/contracts/workers/dvn/src/tests/dvn.rs +40 -41
  85. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +8 -8
  86. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +9 -9
  87. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +6 -6
  88. package/contracts/workers/dvn/src/tests/setup.rs +5 -5
  89. package/contracts/workers/dvn-fee-lib/Cargo.toml +2 -1
  90. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +4 -3
  91. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +8 -6
  92. package/contracts/workers/executor/src/auth.rs +93 -0
  93. package/contracts/workers/executor/src/events.rs +5 -4
  94. package/contracts/workers/executor/src/{lz_executor.rs → executor.rs} +30 -103
  95. package/contracts/workers/executor/src/interfaces/executor.rs +5 -2
  96. package/contracts/workers/executor/src/interfaces/mod.rs +1 -1
  97. package/contracts/workers/executor/src/lib.rs +6 -5
  98. package/contracts/workers/price-feed/Cargo.toml +21 -0
  99. package/contracts/workers/price-feed/src/errors.rs +9 -0
  100. package/contracts/workers/price-feed/src/events.rs +30 -0
  101. package/contracts/workers/price-feed/src/lib.rs +11 -0
  102. package/contracts/workers/price-feed/src/price_feed.rs +265 -0
  103. package/contracts/workers/price-feed/src/storage.rs +42 -0
  104. package/contracts/workers/price-feed/src/types.rs +59 -0
  105. package/contracts/workers/worker/src/events.rs +23 -13
  106. package/contracts/workers/worker/src/interfaces/dvn_fee_lib.rs +2 -1
  107. package/contracts/workers/worker/src/worker.rs +32 -21
  108. package/package.json +3 -3
  109. package/sdk/dist/generated/bml.js +24 -22
  110. package/sdk/dist/generated/counter.d.ts +102 -0
  111. package/sdk/dist/generated/counter.js +36 -24
  112. package/sdk/dist/generated/endpoint.js +24 -22
  113. package/sdk/dist/generated/sml.js +24 -22
  114. package/sdk/dist/generated/uln302.d.ts +1 -1
  115. package/sdk/dist/generated/uln302.js +34 -32
  116. package/sdk/package.json +1 -1
  117. package/sdk/test/index.test.ts +1 -1
  118. package/sdk/test/oft.test.ts +847 -0
  119. package/sdk/test/suites/scan.ts +20 -4
  120. package/tools/ts-bindings-gen/src/main.rs +2 -1
  121. package/contracts/common-macros/src/event.rs +0 -16
  122. package/contracts/oapps/oft/src/macro_tests/mod.rs +0 -2
  123. package/contracts/oapps/oft/src/macro_tests/test_all_default.rs +0 -41
  124. package/contracts/oapps/oft/src/macro_tests/test_override.rs +0 -83
  125. package/contracts/oapps/oft-mint-burn/src/lib.rs +0 -3
  126. package/contracts/oapps/oft-mint-burn/src/oft.rs +0 -28
  127. package/contracts/oapps/oft-mint-burn/src/tests/mod.rs +0 -1
  128. package/contracts/workers/dvn/src/types.rs +0 -26
  129. /package/contracts/message-libs/{block-message-lib → blocked-message-lib}/src/lib.rs +0 -0
@@ -0,0 +1,888 @@
1
+ //! Test setup for ExtensiveOFT with all extensions (pausable, oft_fee, rate_limiter).
2
+ //!
3
+ //! This module provides a comprehensive test setup similar to `test_utils.rs` but
4
+ //! specifically for testing ExtensiveOFT with extension functionality.
5
+
6
+ extern crate self as oft;
7
+
8
+ use crate::default_oft_impl::{default_quote_oft, default_quote_send};
9
+ use crate::errors::OFTError;
10
+ use crate::extensions::oft_fee::{OFTFee, OFTFeeInternal};
11
+ use crate::extensions::pausable::{OFTPausable, OFTPausableInternal};
12
+ use crate::extensions::rate_limiter::{Direction, RateLimiter, RateLimiterInternal};
13
+ use crate::oft::{oft_initialize, OFTInner, OFT};
14
+ use crate::storage::OFTStorage;
15
+ use crate::tests::test_utils::{
16
+ create_recipient_address, DummyToken, MockEndpointWithCompose, MockEndpointWithComposeClient, DEFAULT_NATIVE_FEE,
17
+ DEFAULT_SHARED_DECIMALS, DEFAULT_ZRO_FEE, INITIAL_MINT_AMOUNT,
18
+ };
19
+ use crate::types::{OFTFeeDetail, OFTLimit, OFTReceipt, SendParam};
20
+ use crate::utils::remove_dust;
21
+ use endpoint_v2::{LayerZeroReceiverClient, MessagingFee, MessagingReceipt, Origin};
22
+ use oapp::oapp_core::OAppCoreClient;
23
+ use soroban_sdk::{
24
+ assert_with_error, bytes, contractimpl, log,
25
+ testutils::{Ledger, MockAuth, MockAuthInvoke},
26
+ token::{StellarAssetClient, TokenClient},
27
+ Address, Bytes, BytesN, Env, IntoVal,
28
+ };
29
+
30
+ // ==================== ExtensiveOFT Contract ====================
31
+
32
+ /// OFT contract with all three extensions enabled: pausable, oft_fee, rate_limiter.
33
+ #[oapp_macros::oapp]
34
+ pub struct ExtensiveOFT;
35
+
36
+ #[contractimpl]
37
+ impl ExtensiveOFT {
38
+ pub fn __constructor(
39
+ env: &Env,
40
+ token: &Address,
41
+ owner: &Address,
42
+ endpoint: &Address,
43
+ delegate: &Option<Address>,
44
+ shared_decimals: u32,
45
+ ) {
46
+ oft_initialize::<Self>(env, owner, token, endpoint, delegate, shared_decimals);
47
+ }
48
+ }
49
+
50
+ // Implement OFT trait with custom overrides for extensions
51
+ #[contractimpl(contracttrait)]
52
+ impl OFT for ExtensiveOFT {
53
+ fn quote_oft(
54
+ env: &Env,
55
+ send_param: &crate::types::SendParam,
56
+ ) -> (OFTLimit, soroban_sdk::Vec<OFTFeeDetail>, OFTReceipt) {
57
+ Self::__assert_not_paused(env);
58
+ let (_, fee_details, oft_receipt) = default_quote_oft::<Self>(env, send_param);
59
+ let capacity = Self::rate_limit_capacity(env, &Direction::Outbound, send_param.dst_eid);
60
+ let oft_limit = OFTLimit { min_amount_ld: 0, max_amount_ld: capacity };
61
+ (oft_limit, fee_details, oft_receipt)
62
+ }
63
+
64
+ fn quote_send(
65
+ env: &Env,
66
+ sender: &Address,
67
+ send_param: &crate::types::SendParam,
68
+ pay_in_zro: bool,
69
+ ) -> endpoint_v2::MessagingFee {
70
+ Self::__assert_not_paused(env);
71
+ default_quote_send::<Self>(env, sender, send_param, pay_in_zro)
72
+ }
73
+ }
74
+
75
+ // Internal OFT implementation - NOT exposed as contract endpoints
76
+ impl OFTInner for ExtensiveOFT {
77
+ fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
78
+ Self::__assert_not_paused(env);
79
+ Self::__try_consume_rate_limit_capacity(env, &Direction::Outbound, dst_eid, amount_ld);
80
+ Self::__release_rate_limit_capacity(env, &Direction::Inbound, dst_eid, amount_ld);
81
+ let oft_receipt = crate::oft_types::mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid);
82
+ Self::__transfer_fee(
83
+ env,
84
+ &Self::token(env),
85
+ sender,
86
+ oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld,
87
+ );
88
+ oft_receipt
89
+ }
90
+
91
+ fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
92
+ Self::__assert_not_paused(env);
93
+ Self::__try_consume_rate_limit_capacity(env, &Direction::Inbound, src_eid, amount_ld);
94
+ Self::__release_rate_limit_capacity(env, &Direction::Outbound, src_eid, amount_ld);
95
+ crate::oft_types::mint_burn::credit::<Self>(env, to, amount_ld, src_eid)
96
+ }
97
+
98
+ fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
99
+ let conversion_rate = OFTStorage::decimal_conversion_rate(env).unwrap();
100
+ let amount_after_fee = Self::__apply_fee(env, dst_eid, amount_ld);
101
+ let amount_received_ld = remove_dust(amount_after_fee, conversion_rate);
102
+ // Note: when no fee is applied, amount_send_ld is equal to amount_received_ld
103
+ // this is to align the behavior with the fee extensions on other VMs
104
+ let amount_sent_ld = if amount_after_fee == amount_ld { amount_received_ld } else { amount_ld };
105
+ assert_with_error!(env, amount_received_ld >= min_amount_ld, OFTError::SlippageExceeded);
106
+
107
+ OFTReceipt { amount_sent_ld, amount_received_ld }
108
+ }
109
+ }
110
+
111
+ // Extension traits - exposed as contract endpoints
112
+ #[contractimpl(contracttrait)]
113
+ impl OFTFee for ExtensiveOFT {}
114
+
115
+ impl OFTFeeInternal for ExtensiveOFT {}
116
+
117
+ #[contractimpl(contracttrait)]
118
+ impl OFTPausable for ExtensiveOFT {}
119
+
120
+ impl OFTPausableInternal for ExtensiveOFT {}
121
+
122
+ #[contractimpl(contracttrait)]
123
+ impl RateLimiter for ExtensiveOFT {}
124
+
125
+ impl RateLimiterInternal for ExtensiveOFT {}
126
+
127
+ // ==================== ExtensiveOFT Test Setup ====================
128
+
129
+ /// Test setup for ExtensiveOFT with all three extensions enabled.
130
+ pub struct ExtensiveOFTTestSetup<'a> {
131
+ pub env: &'a Env,
132
+ pub oft: ExtensiveOFTClient<'a>,
133
+ pub endpoint_client: MockEndpointWithComposeClient<'a>,
134
+ pub token: Address,
135
+ pub token_client: TokenClient<'a>,
136
+ pub native_token: Address,
137
+ pub zro_token: Address,
138
+ pub owner: Address,
139
+ pub fee_collector: Address,
140
+ pub native_fee: i128,
141
+ pub zro_fee: i128,
142
+ pub token_decimals: u32,
143
+ pub shared_decimals: u32,
144
+ }
145
+
146
+ /// Builder for ExtensiveOFTTestSetup
147
+ pub struct ExtensiveOFTTestSetupBuilder<'a> {
148
+ env: &'a Env,
149
+ native_fee: i128,
150
+ zro_fee: i128,
151
+ token_decimals: u32,
152
+ shared_decimals: u32,
153
+ }
154
+
155
+ impl<'a> ExtensiveOFTTestSetupBuilder<'a> {
156
+ pub fn new(env: &'a Env) -> Self {
157
+ Self {
158
+ env,
159
+ native_fee: DEFAULT_NATIVE_FEE,
160
+ zro_fee: DEFAULT_ZRO_FEE,
161
+ token_decimals: 7,
162
+ shared_decimals: DEFAULT_SHARED_DECIMALS,
163
+ }
164
+ }
165
+
166
+ pub fn with_token_decimals(mut self, decimals: u32) -> Self {
167
+ self.token_decimals = decimals;
168
+ self
169
+ }
170
+
171
+ pub fn with_shared_decimals(mut self, decimals: u32) -> Self {
172
+ self.shared_decimals = decimals;
173
+ self
174
+ }
175
+
176
+ pub fn with_fees(mut self, native_fee: i128, zro_fee: i128) -> Self {
177
+ self.native_fee = native_fee;
178
+ self.zro_fee = zro_fee;
179
+ self
180
+ }
181
+
182
+ pub fn build(self) -> ExtensiveOFTTestSetup<'a> {
183
+ let env = self.env;
184
+ let native_fee = self.native_fee;
185
+ let zro_fee = self.zro_fee;
186
+
187
+ let owner = create_recipient_address(env);
188
+ let fee_collector = create_recipient_address(env);
189
+
190
+ // Create native token for fees
191
+ let native_sac = env.register_stellar_asset_contract_v2(owner.clone());
192
+ let native_token = native_sac.address();
193
+
194
+ // Create ZRO token
195
+ let zro_sac = env.register_stellar_asset_contract_v2(owner.clone());
196
+ let zro_token = zro_sac.address();
197
+
198
+ // Create OFT token (DummyToken with mint/burn capabilities)
199
+ let token = env.register(DummyToken, (&owner, self.token_decimals));
200
+ let token_client = TokenClient::new(env, &token);
201
+
202
+ // Register mock endpoint
203
+ let endpoint_address =
204
+ env.register(MockEndpointWithCompose, (&native_fee, &zro_fee, &native_token, &zro_token));
205
+ let endpoint_client = MockEndpointWithComposeClient::new(env, &endpoint_address);
206
+
207
+ // Register ExtensiveOFT
208
+ let delegate: Option<Address> = Some(owner.clone());
209
+ let shared_decimals = self.shared_decimals;
210
+ let oft_address = env.register(ExtensiveOFT, (&token, &owner, &endpoint_address, &delegate, &shared_decimals));
211
+ let oft = ExtensiveOFTClient::new(env, &oft_address);
212
+
213
+ // Pre-mint large amounts to owner
214
+ ExtensiveOFTTestSetup::mint_to(env, &owner, &token, &owner, INITIAL_MINT_AMOUNT);
215
+ ExtensiveOFTTestSetup::mint_to(env, &owner, &native_token, &owner, INITIAL_MINT_AMOUNT);
216
+ ExtensiveOFTTestSetup::mint_to(env, &owner, &zro_token, &owner, INITIAL_MINT_AMOUNT);
217
+
218
+ // Transfer token ownership to OFT so it can mint/burn tokens
219
+ ExtensiveOFTTestSetup::transfer_token_ownership(env, &owner, &token, &oft_address);
220
+
221
+ log!(&env, "token decimals: {}", self.token_decimals);
222
+ log!(&env, "token address: {}", token);
223
+ log!(&env, "native token address: {}", native_token);
224
+ log!(&env, "zro token address: {}", zro_token);
225
+ log!(&env, "owner: {}", owner);
226
+ log!(&env, "fee_collector: {}", fee_collector);
227
+ log!(&env, "native fee: {}", native_fee);
228
+ log!(&env, "zro fee: {}", zro_fee);
229
+ log!(&env, "oft address: {}", oft_address);
230
+
231
+ ExtensiveOFTTestSetup {
232
+ env,
233
+ oft,
234
+ endpoint_client,
235
+ token,
236
+ token_client,
237
+ native_token,
238
+ zro_token,
239
+ owner,
240
+ fee_collector,
241
+ native_fee,
242
+ zro_fee,
243
+ token_decimals: self.token_decimals,
244
+ shared_decimals: self.shared_decimals,
245
+ }
246
+ }
247
+ }
248
+
249
+ impl<'a> ExtensiveOFTTestSetup<'a> {
250
+ /// Create a new test setup with default configuration
251
+ pub fn new(env: &'a Env) -> Self {
252
+ ExtensiveOFTTestSetupBuilder::new(env).build()
253
+ }
254
+
255
+ /// Create a builder for customized test setup
256
+ pub fn builder(env: &'a Env) -> ExtensiveOFTTestSetupBuilder<'a> {
257
+ ExtensiveOFTTestSetupBuilder::new(env)
258
+ }
259
+
260
+ // ==================== Token Helpers ====================
261
+
262
+ pub fn mint_to(env: &Env, owner: &Address, token: &Address, to: &Address, amount: i128) {
263
+ env.mock_auths(&[MockAuth {
264
+ address: owner,
265
+ invoke: &MockAuthInvoke {
266
+ contract: token,
267
+ fn_name: "mint",
268
+ args: (to, amount).into_val(env),
269
+ sub_invokes: &[],
270
+ },
271
+ }]);
272
+ StellarAssetClient::new(env, token).mint(to, &amount);
273
+ }
274
+
275
+ pub fn transfer_token_ownership(env: &Env, owner: &Address, token: &Address, new_admin: &Address) {
276
+ env.mock_auths(&[MockAuth {
277
+ address: owner,
278
+ invoke: &MockAuthInvoke {
279
+ contract: token,
280
+ fn_name: "set_admin",
281
+ args: (new_admin,).into_val(env),
282
+ sub_invokes: &[],
283
+ },
284
+ }]);
285
+ StellarAssetClient::new(env, token).set_admin(new_admin);
286
+ }
287
+
288
+ /// Fund an account with native fees (transfers from owner)
289
+ pub fn fund_native_fees(&self, to: &Address, amount: i128) {
290
+ self.env.mock_auths(&[MockAuth {
291
+ address: &self.owner,
292
+ invoke: &MockAuthInvoke {
293
+ contract: &self.native_token,
294
+ fn_name: "transfer",
295
+ args: (&self.owner, to, amount).into_val(self.env),
296
+ sub_invokes: &[],
297
+ },
298
+ }]);
299
+ TokenClient::new(self.env, &self.native_token).transfer(&self.owner, to, &amount);
300
+ }
301
+
302
+ /// Fund an account with ZRO fees (transfers from owner)
303
+ pub fn fund_zro_fees(&self, to: &Address, amount: i128) {
304
+ self.env.mock_auths(&[MockAuth {
305
+ address: &self.owner,
306
+ invoke: &MockAuthInvoke {
307
+ contract: &self.zro_token,
308
+ fn_name: "transfer",
309
+ args: (&self.owner, to, amount).into_val(self.env),
310
+ sub_invokes: &[],
311
+ },
312
+ }]);
313
+ TokenClient::new(self.env, &self.zro_token).transfer(&self.owner, to, &amount);
314
+ }
315
+
316
+ /// Fund an account with OFT tokens (transfers from owner)
317
+ pub fn fund_tokens(&self, to: &Address, amount: i128) {
318
+ self.env.mock_auths(&[MockAuth {
319
+ address: &self.owner,
320
+ invoke: &MockAuthInvoke {
321
+ contract: &self.token,
322
+ fn_name: "transfer",
323
+ args: (&self.owner, to, amount).into_val(self.env),
324
+ sub_invokes: &[],
325
+ },
326
+ }]);
327
+ self.token_client.transfer(&self.owner, to, &amount);
328
+ }
329
+
330
+ // ==================== OApp Helpers ====================
331
+
332
+ pub fn set_peer(&self, eid: u32, peer: &BytesN<32>) {
333
+ self.env.mock_auths(&[MockAuth {
334
+ address: &self.owner,
335
+ invoke: &MockAuthInvoke {
336
+ contract: &self.oft.address,
337
+ fn_name: "set_peer",
338
+ args: (&eid, peer).into_val(self.env),
339
+ sub_invokes: &[],
340
+ },
341
+ }]);
342
+ OAppCoreClient::new(self.env, &self.oft.address).set_peer(&eid, &Some(peer.clone()));
343
+ }
344
+
345
+ // ==================== Pausable Extension Helpers ====================
346
+
347
+ /// Set the paused state (owner only)
348
+ pub fn set_paused(&self, paused: bool) {
349
+ self.env.mock_auths(&[MockAuth {
350
+ address: &self.owner,
351
+ invoke: &MockAuthInvoke {
352
+ contract: &self.oft.address,
353
+ fn_name: "set_paused",
354
+ args: (paused,).into_val(self.env),
355
+ sub_invokes: &[],
356
+ },
357
+ }]);
358
+ self.oft.set_paused(&paused);
359
+ }
360
+
361
+ /// Try to set paused state (returns Result)
362
+ pub fn try_set_paused(
363
+ &self,
364
+ paused: bool,
365
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
366
+ self.env.mock_auths(&[MockAuth {
367
+ address: &self.owner,
368
+ invoke: &MockAuthInvoke {
369
+ contract: &self.oft.address,
370
+ fn_name: "set_paused",
371
+ args: (paused,).into_val(self.env),
372
+ sub_invokes: &[],
373
+ },
374
+ }]);
375
+ self.oft.try_set_paused(&paused)
376
+ }
377
+
378
+ /// Check if paused
379
+ pub fn is_paused(&self) -> bool {
380
+ self.oft.is_paused()
381
+ }
382
+
383
+ // ==================== OFT Fee Extension Helpers ====================
384
+
385
+ /// Set the default fee in basis points (owner only)
386
+ pub fn set_default_fee_bps(&self, fee_bps: u64) {
387
+ self.env.mock_auths(&[MockAuth {
388
+ address: &self.owner,
389
+ invoke: &MockAuthInvoke {
390
+ contract: &self.oft.address,
391
+ fn_name: "set_default_fee_bps",
392
+ args: (fee_bps,).into_val(self.env),
393
+ sub_invokes: &[],
394
+ },
395
+ }]);
396
+ self.oft.set_default_fee_bps(&fee_bps);
397
+ }
398
+
399
+ /// Try to set default fee bps (returns Result)
400
+ pub fn try_set_default_fee_bps(
401
+ &self,
402
+ fee_bps: u64,
403
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
404
+ self.env.mock_auths(&[MockAuth {
405
+ address: &self.owner,
406
+ invoke: &MockAuthInvoke {
407
+ contract: &self.oft.address,
408
+ fn_name: "set_default_fee_bps",
409
+ args: (fee_bps,).into_val(self.env),
410
+ sub_invokes: &[],
411
+ },
412
+ }]);
413
+ self.oft.try_set_default_fee_bps(&fee_bps)
414
+ }
415
+
416
+ /// Set fee bps for a specific destination (owner only)
417
+ pub fn set_fee_bps(&self, dst_eid: u32, fee_bps: u64) {
418
+ self.env.mock_auths(&[MockAuth {
419
+ address: &self.owner,
420
+ invoke: &MockAuthInvoke {
421
+ contract: &self.oft.address,
422
+ fn_name: "set_fee_bps",
423
+ args: (dst_eid, fee_bps).into_val(self.env),
424
+ sub_invokes: &[],
425
+ },
426
+ }]);
427
+ self.oft.set_fee_bps(&dst_eid, &fee_bps);
428
+ }
429
+
430
+ /// Try to set fee bps (returns Result)
431
+ pub fn try_set_fee_bps(
432
+ &self,
433
+ dst_eid: u32,
434
+ fee_bps: u64,
435
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
436
+ self.env.mock_auths(&[MockAuth {
437
+ address: &self.owner,
438
+ invoke: &MockAuthInvoke {
439
+ contract: &self.oft.address,
440
+ fn_name: "set_fee_bps",
441
+ args: (dst_eid, fee_bps).into_val(self.env),
442
+ sub_invokes: &[],
443
+ },
444
+ }]);
445
+ self.oft.try_set_fee_bps(&dst_eid, &fee_bps)
446
+ }
447
+
448
+ /// Unset fee bps for a specific destination (owner only)
449
+ pub fn unset_fee_bps(&self, dst_eid: u32) {
450
+ self.env.mock_auths(&[MockAuth {
451
+ address: &self.owner,
452
+ invoke: &MockAuthInvoke {
453
+ contract: &self.oft.address,
454
+ fn_name: "unset_fee_bps",
455
+ args: (dst_eid,).into_val(self.env),
456
+ sub_invokes: &[],
457
+ },
458
+ }]);
459
+ self.oft.unset_fee_bps(&dst_eid);
460
+ }
461
+
462
+ /// Try to unset fee bps (returns Result)
463
+ pub fn try_unset_fee_bps(
464
+ &self,
465
+ dst_eid: u32,
466
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
467
+ self.env.mock_auths(&[MockAuth {
468
+ address: &self.owner,
469
+ invoke: &MockAuthInvoke {
470
+ contract: &self.oft.address,
471
+ fn_name: "unset_fee_bps",
472
+ args: (dst_eid,).into_val(self.env),
473
+ sub_invokes: &[],
474
+ },
475
+ }]);
476
+ self.oft.try_unset_fee_bps(&dst_eid)
477
+ }
478
+
479
+ /// Set the fee deposit address (owner only)
480
+ pub fn set_fee_deposit_address(&self, address: &Address) {
481
+ self.env.mock_auths(&[MockAuth {
482
+ address: &self.owner,
483
+ invoke: &MockAuthInvoke {
484
+ contract: &self.oft.address,
485
+ fn_name: "set_fee_deposit_address",
486
+ args: (address,).into_val(self.env),
487
+ sub_invokes: &[],
488
+ },
489
+ }]);
490
+ self.oft.set_fee_deposit_address(address);
491
+ }
492
+
493
+ /// Try to set fee deposit address (returns Result)
494
+ pub fn try_set_fee_deposit_address(
495
+ &self,
496
+ address: &Address,
497
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
498
+ self.env.mock_auths(&[MockAuth {
499
+ address: &self.owner,
500
+ invoke: &MockAuthInvoke {
501
+ contract: &self.oft.address,
502
+ fn_name: "set_fee_deposit_address",
503
+ args: (address,).into_val(self.env),
504
+ sub_invokes: &[],
505
+ },
506
+ }]);
507
+ self.oft.try_set_fee_deposit_address(address)
508
+ }
509
+
510
+ /// Get the default fee bps
511
+ pub fn default_fee_bps(&self) -> u64 {
512
+ self.oft.default_fee_bps()
513
+ }
514
+
515
+ /// Get the fee bps for a specific destination
516
+ pub fn fee_bps(&self, dst_eid: u32) -> u64 {
517
+ self.oft.fee_bps(&dst_eid).unwrap_or(0)
518
+ }
519
+
520
+ /// Get the effective fee bps for a specific destination
521
+ pub fn effective_fee_bps(&self, dst_eid: u32) -> u64 {
522
+ self.oft.effective_fee_bps(&dst_eid)
523
+ }
524
+
525
+ /// Check if fee bps is set for a specific destination
526
+ pub fn has_fee_bps(&self, dst_eid: u32) -> bool {
527
+ self.oft.has_fee_bps(&dst_eid)
528
+ }
529
+
530
+ /// Get the fee deposit address
531
+ pub fn fee_deposit_address(&self) -> Address {
532
+ self.oft.fee_deposit_address()
533
+ }
534
+
535
+ // ==================== Rate Limiter Extension Helpers ====================
536
+
537
+ /// Set rate limit for a direction and eid (owner only)
538
+ pub fn set_rate_limit(&self, direction: &Direction, eid: u32, limit: i128, window_seconds: u64) {
539
+ self.env.mock_auths(&[MockAuth {
540
+ address: &self.owner,
541
+ invoke: &MockAuthInvoke {
542
+ contract: &self.oft.address,
543
+ fn_name: "set_rate_limit",
544
+ args: (direction, eid, limit, window_seconds).into_val(self.env),
545
+ sub_invokes: &[],
546
+ },
547
+ }]);
548
+ self.oft.set_rate_limit(direction, &eid, &limit, &window_seconds);
549
+ }
550
+
551
+ /// Try to set rate limit (returns Result)
552
+ pub fn try_set_rate_limit(
553
+ &self,
554
+ direction: &Direction,
555
+ eid: u32,
556
+ limit: i128,
557
+ window_seconds: u64,
558
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
559
+ self.env.mock_auths(&[MockAuth {
560
+ address: &self.owner,
561
+ invoke: &MockAuthInvoke {
562
+ contract: &self.oft.address,
563
+ fn_name: "set_rate_limit",
564
+ args: (direction, eid, limit, window_seconds).into_val(self.env),
565
+ sub_invokes: &[],
566
+ },
567
+ }]);
568
+ self.oft.try_set_rate_limit(direction, &eid, &limit, &window_seconds)
569
+ }
570
+
571
+ /// Unset rate limit for a direction and eid (owner only)
572
+ pub fn unset_rate_limit(&self, direction: &Direction, eid: u32) {
573
+ self.env.mock_auths(&[MockAuth {
574
+ address: &self.owner,
575
+ invoke: &MockAuthInvoke {
576
+ contract: &self.oft.address,
577
+ fn_name: "unset_rate_limit",
578
+ args: (direction, eid).into_val(self.env),
579
+ sub_invokes: &[],
580
+ },
581
+ }]);
582
+ self.oft.unset_rate_limit(direction, &eid);
583
+ }
584
+
585
+ /// Try to unset rate limit (returns Result)
586
+ pub fn try_unset_rate_limit(
587
+ &self,
588
+ direction: &Direction,
589
+ eid: u32,
590
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
591
+ self.env.mock_auths(&[MockAuth {
592
+ address: &self.owner,
593
+ invoke: &MockAuthInvoke {
594
+ contract: &self.oft.address,
595
+ fn_name: "unset_rate_limit",
596
+ args: (direction, eid).into_val(self.env),
597
+ sub_invokes: &[],
598
+ },
599
+ }]);
600
+ self.oft.try_unset_rate_limit(direction, &eid)
601
+ }
602
+
603
+ /// Get rate limit config (limit, window_seconds)
604
+ pub fn rate_limit_config(&self, direction: &Direction, eid: u32) -> (i128, u64) {
605
+ self.oft.rate_limit_config(direction, &eid)
606
+ }
607
+
608
+ /// Get rate limit in flight
609
+ pub fn rate_limit_in_flight(&self, direction: &Direction, eid: u32) -> i128 {
610
+ self.oft.rate_limit_in_flight(direction, &eid)
611
+ }
612
+
613
+ /// Try to get rate limit in-flight (returns Result for testing error cases)
614
+ pub fn try_rate_limit_in_flight(
615
+ &self,
616
+ direction: &Direction,
617
+ eid: u32,
618
+ ) -> Result<Result<i128, soroban_sdk::Error>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
619
+ self.oft.try_rate_limit_in_flight(direction, &eid)
620
+ }
621
+
622
+ /// Get rate limit capacity
623
+ pub fn rate_limit_capacity(&self, direction: &Direction, eid: u32) -> i128 {
624
+ self.oft.rate_limit_capacity(direction, &eid)
625
+ }
626
+
627
+ // ==================== Time Helpers ====================
628
+
629
+ /// Advance the ledger timestamp by the given number of seconds
630
+ pub fn advance_time(&self, seconds: u64) {
631
+ let current = self.env.ledger().timestamp();
632
+ self.env.ledger().set_timestamp(current + seconds);
633
+ }
634
+
635
+ /// Set the ledger timestamp to a specific value
636
+ pub fn set_timestamp(&self, timestamp: u64) {
637
+ self.env.ledger().set_timestamp(timestamp);
638
+ }
639
+
640
+ // ==================== OFT Core Operations ====================
641
+
642
+ /// Create a SendParam for testing
643
+ pub fn create_send_param(&self, dst_eid: u32, amount_ld: i128, min_amount_ld: i128) -> SendParam {
644
+ SendParam {
645
+ dst_eid,
646
+ to: BytesN::from_array(self.env, &[1u8; 32]),
647
+ amount_ld,
648
+ min_amount_ld,
649
+ extra_options: bytes!(self.env),
650
+ compose_msg: bytes!(self.env),
651
+ oft_cmd: bytes!(self.env),
652
+ }
653
+ }
654
+
655
+ /// Quote OFT to get the receipt for authorization
656
+ pub fn quote_oft(&self, send_param: &SendParam) -> OFTReceipt {
657
+ let (_, _, receipt) = self.oft.quote_oft(send_param);
658
+ receipt
659
+ }
660
+
661
+ /// Try quote OFT (returns Result)
662
+ pub fn try_quote_oft(
663
+ &self,
664
+ send_param: &SendParam,
665
+ ) -> Result<
666
+ Result<(crate::types::OFTLimit, soroban_sdk::Vec<crate::types::OFTFeeDetail>, OFTReceipt), soroban_sdk::Error>,
667
+ Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
668
+ > {
669
+ self.oft.try_quote_oft(send_param)
670
+ }
671
+
672
+ /// Quote send to get messaging fees
673
+ pub fn quote_send(&self, sender: &Address, send_param: &SendParam, pay_in_zro: bool) -> MessagingFee {
674
+ self.oft.quote_send(sender, send_param, &pay_in_zro)
675
+ }
676
+
677
+ /// Try quote send (returns Result)
678
+ pub fn try_quote_send(
679
+ &self,
680
+ sender: &Address,
681
+ send_param: &SendParam,
682
+ pay_in_zro: bool,
683
+ ) -> Result<Result<MessagingFee, soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>>
684
+ {
685
+ self.oft.try_quote_send(sender, send_param, &pay_in_zro)
686
+ }
687
+
688
+ /// Send tokens cross-chain with proper sender authentication and fee transfer
689
+ /// Use this when OFT fee extension is configured and fee > 0
690
+ pub fn send_with_fee(
691
+ &self,
692
+ sender: &Address,
693
+ send_param: &SendParam,
694
+ fee: &MessagingFee,
695
+ refund_address: &Address,
696
+ oft_receipt: &OFTReceipt,
697
+ fee_deposit_address: &Address,
698
+ ) -> (MessagingReceipt, OFTReceipt) {
699
+ let fee_amount = oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld;
700
+
701
+ // Mock auth order must match contract execution order:
702
+ // 1. Transfer fee (__transfer_fee) - happens before burn
703
+ // 2. Burn tokens (amount_received_ld, not amount_sent_ld)
704
+ // 3. Transfer native/zro fees (__lz_send)
705
+ self.env.mock_auths(&[MockAuth {
706
+ address: sender,
707
+ invoke: &MockAuthInvoke {
708
+ contract: &self.oft.address,
709
+ fn_name: "send",
710
+ args: (sender, send_param, fee, refund_address).into_val(self.env),
711
+ sub_invokes: &[
712
+ // 1. Transfer fee to fee deposit address - happens in __transfer_fee (BEFORE burn)
713
+ MockAuthInvoke {
714
+ contract: &self.token,
715
+ fn_name: "transfer",
716
+ args: (sender, fee_deposit_address, &fee_amount).into_val(self.env),
717
+ sub_invokes: &[],
718
+ },
719
+ // 2. Burn tokens (amount_received_ld, not amount_sent_ld)
720
+ MockAuthInvoke {
721
+ contract: &self.token,
722
+ fn_name: "burn",
723
+ args: (sender, &oft_receipt.amount_received_ld).into_val(self.env),
724
+ sub_invokes: &[],
725
+ },
726
+ // 3. Transfer native fee - happens in __lz_send
727
+ MockAuthInvoke {
728
+ contract: &self.native_token,
729
+ fn_name: "transfer",
730
+ args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
731
+ sub_invokes: &[],
732
+ },
733
+ // 4. Transfer zro fee - happens in __lz_send
734
+ MockAuthInvoke {
735
+ contract: &self.zro_token,
736
+ fn_name: "transfer",
737
+ args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
738
+ sub_invokes: &[],
739
+ },
740
+ ],
741
+ },
742
+ }]);
743
+ self.oft.send(sender, send_param, fee, refund_address)
744
+ }
745
+
746
+ /// Send tokens cross-chain without fee transfer (for cases where no fee is configured)
747
+ pub fn send(
748
+ &self,
749
+ sender: &Address,
750
+ send_param: &SendParam,
751
+ fee: &MessagingFee,
752
+ refund_address: &Address,
753
+ oft_receipt: &OFTReceipt,
754
+ ) -> (MessagingReceipt, OFTReceipt) {
755
+ // When no fee is configured, amount_sent_ld == amount_received_ld
756
+ // Mock auth order must match contract execution order:
757
+ // 1. Burn tokens (amount_received_ld)
758
+ // 2. Transfer native/zro fees (__lz_send)
759
+ self.env.mock_auths(&[MockAuth {
760
+ address: sender,
761
+ invoke: &MockAuthInvoke {
762
+ contract: &self.oft.address,
763
+ fn_name: "send",
764
+ args: (sender, send_param, fee, refund_address).into_val(self.env),
765
+ sub_invokes: &[
766
+ // 1. Burn tokens (mint-burn strategy) - amount_received_ld == amount_sent_ld when no fee
767
+ MockAuthInvoke {
768
+ contract: &self.token,
769
+ fn_name: "burn",
770
+ args: (sender, &oft_receipt.amount_received_ld).into_val(self.env),
771
+ sub_invokes: &[],
772
+ },
773
+ // 2. Transfer native fee - happens in __lz_send
774
+ MockAuthInvoke {
775
+ contract: &self.native_token,
776
+ fn_name: "transfer",
777
+ args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
778
+ sub_invokes: &[],
779
+ },
780
+ // 3. Transfer zro fee - happens in __lz_send
781
+ MockAuthInvoke {
782
+ contract: &self.zro_token,
783
+ fn_name: "transfer",
784
+ args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
785
+ sub_invokes: &[],
786
+ },
787
+ ],
788
+ },
789
+ }]);
790
+ self.oft.send(sender, send_param, fee, refund_address)
791
+ }
792
+
793
+ /// Try send tokens cross-chain without fee (returns Result)
794
+ pub fn try_send(
795
+ &self,
796
+ sender: &Address,
797
+ send_param: &SendParam,
798
+ fee: &MessagingFee,
799
+ refund_address: &Address,
800
+ oft_receipt: &OFTReceipt,
801
+ ) -> Result<
802
+ Result<(MessagingReceipt, OFTReceipt), soroban_sdk::Error>,
803
+ Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
804
+ > {
805
+ // When no fee is configured, amount_sent_ld == amount_received_ld
806
+ // Mock auth order must match contract execution order:
807
+ // 1. Burn tokens (amount_received_ld)
808
+ // 2. Transfer native/zro fees (__lz_send)
809
+ self.env.mock_auths(&[MockAuth {
810
+ address: sender,
811
+ invoke: &MockAuthInvoke {
812
+ contract: &self.oft.address,
813
+ fn_name: "send",
814
+ args: (sender, send_param, fee, refund_address).into_val(self.env),
815
+ sub_invokes: &[
816
+ // 1. Burn tokens (mint-burn strategy) - amount_received_ld == amount_sent_ld when no fee
817
+ MockAuthInvoke {
818
+ contract: &self.token,
819
+ fn_name: "burn",
820
+ args: (sender, &oft_receipt.amount_received_ld).into_val(self.env),
821
+ sub_invokes: &[],
822
+ },
823
+ // 2. Transfer native fee - happens in __lz_send
824
+ MockAuthInvoke {
825
+ contract: &self.native_token,
826
+ fn_name: "transfer",
827
+ args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
828
+ sub_invokes: &[],
829
+ },
830
+ // 3. Transfer zro fee - happens in __lz_send
831
+ MockAuthInvoke {
832
+ contract: &self.zro_token,
833
+ fn_name: "transfer",
834
+ args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
835
+ sub_invokes: &[],
836
+ },
837
+ ],
838
+ },
839
+ }]);
840
+ self.oft.try_send(sender, send_param, fee, refund_address)
841
+ }
842
+
843
+ /// Execute lz_receive with proper executor authentication
844
+ pub fn lz_receive(
845
+ &self,
846
+ executor: &Address,
847
+ origin: &Origin,
848
+ guid: &BytesN<32>,
849
+ message: &Bytes,
850
+ extra_data: &Bytes,
851
+ value: i128,
852
+ ) {
853
+ self.env.mock_auths(&[MockAuth {
854
+ address: executor,
855
+ invoke: &MockAuthInvoke {
856
+ contract: &self.oft.address,
857
+ fn_name: "lz_receive",
858
+ args: (executor, origin, guid, message, extra_data, value).into_val(self.env),
859
+ sub_invokes: &[],
860
+ },
861
+ }]);
862
+ LayerZeroReceiverClient::new(self.env, &self.oft.address)
863
+ .lz_receive(executor, origin, guid, message, extra_data, &value);
864
+ }
865
+
866
+ /// Try lz_receive (returns Result)
867
+ pub fn try_lz_receive(
868
+ &self,
869
+ executor: &Address,
870
+ origin: &Origin,
871
+ guid: &BytesN<32>,
872
+ message: &Bytes,
873
+ extra_data: &Bytes,
874
+ value: i128,
875
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
876
+ self.env.mock_auths(&[MockAuth {
877
+ address: executor,
878
+ invoke: &MockAuthInvoke {
879
+ contract: &self.oft.address,
880
+ fn_name: "lz_receive",
881
+ args: (executor, origin, guid, message, extra_data, value).into_val(self.env),
882
+ sub_invokes: &[],
883
+ },
884
+ }]);
885
+ LayerZeroReceiverClient::new(self.env, &self.oft.address)
886
+ .try_lz_receive(executor, origin, guid, message, extra_data, &value)
887
+ }
888
+ }