@layerzerolabs/protocol-stellar-v2 0.2.18 → 0.2.19

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