@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
@@ -0,0 +1,439 @@
1
+ //! OFT - traits and implementations for Omnichain Fungible Tokens.
2
+ //!
3
+ //! This module provides:
4
+ //! - `OFTInternal`: Internal methods NOT exposed as contract entrypoints (`__debit`, `__credit`, etc.)
5
+ //! - `OFTCore`: Public methods exposed as contract entrypoints (using `#[contracttrait]`)
6
+ //! - Core functions: `initialize_oft`, `lz_receive`, and other helpers
7
+ //!
8
+ //! ## Usage
9
+ //!
10
+ //! ```ignore
11
+ //! use oapp_macros::{oapp, oapp_options_type3};
12
+ //! use oft_core::{OFTInternal, OFTCore, initialize_oft};
13
+ //!
14
+ //! #[oapp]
15
+ //! pub struct MyOFT;
16
+ //!
17
+ //! #[contractimpl]
18
+ //! impl MyOFT {
19
+ //! pub fn __constructor(env: &Env, token: &Address, owner: &Address, endpoint: &Address, delegate: &Option<Address>) {
20
+ //! initialize_oft::<Self>(env, owner, token, endpoint, delegate, 6)
21
+ //! }
22
+ //! }
23
+ //!
24
+ //! // Public methods - exposed as contract entrypoints
25
+ //! #[contractimpl(contracttrait)]
26
+ //! impl OFTCore for MyOFT {}
27
+ //!
28
+ //! // Internal methods - NOT exposed as contract entrypoints
29
+ //! // IMPORTANT: Do NOT use #[contractimpl] here to keep methods internal
30
+ //! impl OFTInternal for MyOFT {
31
+ //! fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
32
+ //! // Your debit logic (e.g., burn or lock tokens)
33
+ //! oft_core::oft_types::mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
34
+ //! }
35
+ //!
36
+ //! fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
37
+ //! // Your credit logic (e.g., mint or unlock tokens)
38
+ //! oft_core::oft_types::mint_burn::credit::<Self>(env, to, amount_ld, src_eid)
39
+ //! }
40
+ //! }
41
+ //! ```
42
+
43
+ use crate::{
44
+ self as oft_core,
45
+ codec::{oft_compose_msg_codec::OFTComposeMsg, oft_msg_codec::OFTMessage},
46
+ errors::OFTError,
47
+ events::{self, OFTSent},
48
+ storage::OFTStorage,
49
+ types::{self, OFTFeeDetail, OFTLimit, OFTReceipt, SendParam},
50
+ utils as oft_utils,
51
+ };
52
+ use common_macros::contract_trait;
53
+ use endpoint_v2::{MessagingComposerClient, MessagingFee, MessagingReceipt, Origin};
54
+ use oapp::{
55
+ oapp_core::{initialize_oapp, OAppCore},
56
+ oapp_options_type3::OAppOptionsType3,
57
+ oapp_receiver::OAppReceiver,
58
+ oapp_sender::OAppSenderInternal,
59
+ };
60
+ use soroban_sdk::{assert_with_error, token::TokenClient, vec, Address, Bytes, BytesN, Env, Vec};
61
+ use utils::{option_ext::OptionExt, ownable::OwnableInitializer};
62
+
63
+ // ===========================================================================
64
+ // OFTInternal Trait (NOT exposed as contract entrypoints)
65
+ // ===========================================================================
66
+
67
+ /// Internal OFT trait containing methods that should NOT be exposed as contract entrypoints.
68
+ ///
69
+ /// **IMPORTANT**: Implement this trait WITHOUT the `#[contractimpl]` macro to keep
70
+ /// `__debit`, `__credit`, and other internal methods private to the contract.
71
+ ///
72
+ /// ```ignore
73
+ /// // Correct - methods stay internal
74
+ /// impl OFTInternal for MyOFT { ... }
75
+ ///
76
+ /// // WRONG - would expose methods as entrypoints
77
+ /// #[contractimpl]
78
+ /// impl OFTInternal for MyOFT { ... }
79
+ /// ```
80
+ ///
81
+ /// Internal OFT trait for token debit/credit operations.
82
+ ///
83
+ /// This trait contains only the internal token operations. The OApp supertraits
84
+ /// are on `OFTCore` instead, keeping this trait focused on token logic.
85
+ pub trait OFTInternal: OAppCore + OAppOptionsType3 {
86
+ // =========================================================================
87
+ // Required Methods (no defaults - user MUST implement)
88
+ // =========================================================================
89
+
90
+ /// Debits tokens from the specified address for cross-chain transfer.
91
+ ///
92
+ /// # Arguments
93
+ /// * `from` - The address to debit the tokens from
94
+ /// * `amount_ld` - The amount of tokens to send in local decimals
95
+ /// * `min_amount_ld` - The minimum amount to send in local decimals
96
+ /// * `dst_eid` - The destination chain ID
97
+ ///
98
+ /// # Returns
99
+ /// `OFTReceipt` containing the amount sent and amount received
100
+ fn __debit(env: &Env, from: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt;
101
+
102
+ /// Credits tokens to recipient after receiving cross-chain transfer.
103
+ ///
104
+ /// # Arguments
105
+ /// * `to` - The address to credit tokens to
106
+ /// * `amount_ld` - Amount in local decimals to credit
107
+ /// * `src_eid` - Source endpoint ID
108
+ ///
109
+ /// # Returns
110
+ /// The amount actually credited
111
+ fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128;
112
+
113
+ // =========================================================================
114
+ // Optional Methods (have defaults - override as needed)
115
+ // =========================================================================
116
+
117
+ /// Calculates debit amounts without executing (view function).
118
+ fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
119
+ debit_view(env, amount_ld, min_amount_ld, dst_eid)
120
+ }
121
+
122
+ /// Builds OFT message and combines options for cross-chain transfer.
123
+ fn __build_msg_and_options(
124
+ env: &Env,
125
+ from: &Address,
126
+ send_param: &oft_core::types::SendParam,
127
+ receive_amount_ld: i128,
128
+ ) -> (Bytes, Bytes) {
129
+ build_msg_and_options::<Self>(env, from, send_param, receive_amount_ld)
130
+ }
131
+ }
132
+
133
+ // ===========================================================================
134
+ // OFTCore Trait (exposed as contract entrypoints)
135
+ // ===========================================================================
136
+
137
+ /// The public OFT trait defining the cross-chain token transfer interface.
138
+ ///
139
+ /// This trait is marked with `#[contracttrait]` so all its methods are exposed as
140
+ /// contract entrypoints. Users implement this with `#[contractimpl(contracttrait)]`.
141
+ ///
142
+ /// Internal methods like `__debit`, `__credit`, `__debit_view`, and `__build_msg_and_options`
143
+ /// are in the `OFTInternal` trait and are NOT exposed as contract entrypoints.
144
+ #[contract_trait(client_name = "OFTClient")]
145
+ pub trait OFTCore: OFTInternal + OAppReceiver + OAppSenderInternal + OAppOptionsType3 {
146
+ /// Retrieves the token address associated with this OFT.
147
+ fn token(env: &soroban_sdk::Env) -> soroban_sdk::Address {
148
+ token(env)
149
+ }
150
+
151
+ /// Returns OFT version as (major, minor).
152
+ fn oft_version(_env: &soroban_sdk::Env) -> (u64, u64) {
153
+ (1, 1)
154
+ }
155
+
156
+ /// Retrieves the shared decimals used for cross-chain normalization.
157
+ fn shared_decimals(env: &soroban_sdk::Env) -> u32 {
158
+ shared_decimals(env)
159
+ }
160
+
161
+ /// Retrieves the decimal conversion rate used for cross-chain normalization.
162
+ fn decimal_conversion_rate(env: &soroban_sdk::Env) -> i128 {
163
+ decimal_conversion_rate(env)
164
+ }
165
+
166
+ /// Whether a separate token approval is required before sending.
167
+ ///
168
+ /// Helps wallet implementers determine integration requirements.
169
+ ///
170
+ /// # Returns
171
+ /// - `true` if a separate token approval step is required
172
+ /// - `false` if no separate approval is needed
173
+ fn approval_required(_env: &soroban_sdk::Env) -> bool {
174
+ false
175
+ }
176
+
177
+ /// Quotes an OFT transfer without executing.
178
+ ///
179
+ /// # Returns
180
+ /// (OFTLimit, fee details, receipt with estimated amounts)
181
+ fn quote_oft(
182
+ env: &soroban_sdk::Env,
183
+ send_param: &oft_core::types::SendParam,
184
+ ) -> (oft_core::types::OFTLimit, soroban_sdk::Vec<oft_core::types::OFTFeeDetail>, oft_core::types::OFTReceipt) {
185
+ quote_oft::<Self>(env, send_param)
186
+ }
187
+
188
+ /// Quotes a send operation including LayerZero messaging fees.
189
+ fn quote_send(
190
+ env: &soroban_sdk::Env,
191
+ sender: &soroban_sdk::Address,
192
+ send_param: &oft_core::types::SendParam,
193
+ pay_in_zro: bool,
194
+ ) -> endpoint_v2::MessagingFee {
195
+ quote_send::<Self>(env, sender, send_param, pay_in_zro)
196
+ }
197
+
198
+ /// Sends tokens cross-chain to another endpoint.
199
+ ///
200
+ /// Sender must be authenticated.
201
+ ///
202
+ /// # Returns
203
+ /// (MessagingReceipt, OFTReceipt)
204
+ fn send(
205
+ env: &soroban_sdk::Env,
206
+ sender: &soroban_sdk::Address,
207
+ send_param: &oft_core::types::SendParam,
208
+ fee: &endpoint_v2::MessagingFee,
209
+ refund_address: &soroban_sdk::Address,
210
+ ) -> (endpoint_v2::MessagingReceipt, oft_core::types::OFTReceipt) {
211
+ send::<Self>(env, sender, send_param, fee, refund_address)
212
+ }
213
+ }
214
+
215
+ // ===========================================================================
216
+ // Initialization
217
+ // ===========================================================================
218
+
219
+ /// Initializes the OFT (Omnichain Fungible Token) contract.
220
+ ///
221
+ /// Sets up the OApp infrastructure and configures decimal conversion for cross-chain transfers.
222
+ /// The `shared_decimals` parameter defines the common decimal precision used across all chains,
223
+ /// enabling consistent token amounts regardless of each chain's native token decimals.
224
+ ///
225
+ /// # Arguments
226
+ /// * `owner` - The address that will own this OFT contract
227
+ /// * `token` - The underlying token contract address (must implement SEP-41 token interface)
228
+ /// * `endpoint` - The LayerZero endpoint address for cross-chain messaging
229
+ /// * `delegate` - Optional delegate address for endpoint configuration permissions
230
+ /// * `shared_decimals` - The shared decimal precision for cross-chain compatibility (must be <= local decimals)
231
+ ///
232
+ /// # Panics
233
+ /// * `OFTError::InvalidLocalDecimals` - If the token's local decimals are less than `shared_decimals`
234
+ pub fn initialize_oft<T: OFTCore + OwnableInitializer>(
235
+ env: &Env,
236
+ owner: &Address,
237
+ token: &Address,
238
+ endpoint: &Address,
239
+ delegate: &Option<Address>,
240
+ shared_decimals: u32,
241
+ ) {
242
+ // Initialize OApp (includes owner initialization)
243
+ initialize_oapp::<T>(env, owner, endpoint, delegate);
244
+
245
+ let local_decimals = TokenClient::new(env, token).decimals();
246
+ assert_with_error!(env, local_decimals >= shared_decimals, OFTError::InvalidLocalDecimals);
247
+
248
+ // Initialize OFT storage
249
+ OFTStorage::set_token(env, token);
250
+ OFTStorage::set_decimal_conversion_rate(env, &10_i128.pow(local_decimals - shared_decimals));
251
+ }
252
+
253
+ // ===========================================================================
254
+ // Implementation Functions
255
+ // ===========================================================================
256
+
257
+ /// Simulates a debit operation without executing, used for quoting.
258
+ ///
259
+ /// # Arguments
260
+ /// * `amount_ld` - The amount of tokens to send in local decimals
261
+ /// * `min_amount_ld` - The minimum amount to send in local decimals (slippage protection)
262
+ /// * `dst_eid` - The destination chain ID (unused in default implementation)
263
+ ///
264
+ /// # Returns
265
+ /// `OFTReceipt` containing the amount sent and amount received after dust removal
266
+ pub fn debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, _dst_eid: u32) -> OFTReceipt {
267
+ let conversion_rate = decimal_conversion_rate(env);
268
+ let amount_sent_ld = oft_utils::remove_dust(amount_ld, conversion_rate);
269
+ let amount_received_ld = amount_sent_ld;
270
+
271
+ assert_with_error!(env, amount_received_ld >= min_amount_ld, OFTError::SlippageExceeded);
272
+
273
+ OFTReceipt { amount_sent_ld, amount_received_ld }
274
+ }
275
+
276
+ /// Builds the OFT message payload and combines enforced options for cross-chain transfer.
277
+ ///
278
+ /// # Arguments
279
+ /// * `from` - The address initiating the transfer
280
+ /// * `send_param` - The send parameters including destination, recipient, and options
281
+ /// * `receive_amount_ld` - The amount to be received in local decimals (after dust removal)
282
+ ///
283
+ /// # Returns
284
+ /// A tuple of (encoded message, combined options)
285
+ pub fn build_msg_and_options<T: OAppOptionsType3>(
286
+ env: &Env,
287
+ from: &Address,
288
+ send_param: &SendParam,
289
+ receive_amount_ld: i128,
290
+ ) -> (Bytes, Bytes) {
291
+ let has_compose = !send_param.compose_msg.is_empty();
292
+ let conversion_rate = decimal_conversion_rate(env);
293
+ let (msg, _) = OFTMessage {
294
+ send_to: send_param.to.clone(),
295
+ amount_sd: oft_utils::to_sd(env, receive_amount_ld, conversion_rate),
296
+ compose_from: if has_compose { Some(oft_utils::address_payload(from)) } else { None },
297
+ compose_msg: if has_compose { Some(send_param.compose_msg.clone()) } else { None },
298
+ }
299
+ .encode(env);
300
+ let msg_type = if has_compose { types::SEND_AND_CALL } else { types::SEND };
301
+
302
+ (msg, T::combine_options(env, send_param.dst_eid, msg_type, &send_param.extra_options))
303
+ }
304
+
305
+ /// Quotes an OFT transfer without executing.
306
+ ///
307
+ /// # Arguments
308
+ /// * `send_param` - The send parameters to quote
309
+ ///
310
+ /// # Returns
311
+ /// A tuple of (transfer limits, fee details, estimated receipt)
312
+ pub fn quote_oft<T: OFTInternal>(env: &Env, send_param: &SendParam) -> (OFTLimit, Vec<OFTFeeDetail>, OFTReceipt) {
313
+ let limit = OFTLimit { min_amount_ld: 0, max_amount_ld: i128::MAX }; // No limits in default implementation
314
+ let fee_details = vec![env]; // No fee details in default implementation
315
+ let oft_receipt = T::__debit_view(env, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
316
+ (limit, fee_details, oft_receipt)
317
+ }
318
+
319
+ /// Quotes the LayerZero messaging fee for a send operation.
320
+ ///
321
+ /// # Arguments
322
+ /// * `from` - The address initiating the transfer
323
+ /// * `send_param` - The send parameters to quote
324
+ /// * `pay_in_zro` - Whether to pay the fee in ZRO token
325
+ ///
326
+ /// # Returns
327
+ /// The messaging fee required for the transfer
328
+ pub fn quote_send<T: OFTCore>(env: &Env, from: &Address, send_param: &SendParam, pay_in_zro: bool) -> MessagingFee {
329
+ let OFTReceipt { amount_received_ld, .. } =
330
+ T::__debit_view(env, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
331
+
332
+ let (msg, options) = build_msg_and_options::<T>(env, from, send_param, amount_received_ld);
333
+ T::__quote(env, send_param.dst_eid, &msg, &options, pay_in_zro)
334
+ }
335
+
336
+ /// Executes a cross-chain token transfer via LayerZero.
337
+ ///
338
+ /// Debits tokens from the `from` address, builds the message, and sends via the endpoint.
339
+ /// The `from` address must be authenticated.
340
+ ///
341
+ /// # Arguments
342
+ /// * `from` - The address sending tokens (must authorize)
343
+ /// * `send_param` - The send parameters (destination, recipient, amount, options)
344
+ /// * `fee` - The messaging fee to pay
345
+ /// * `refund_address` - The address to refund excess fees to
346
+ ///
347
+ /// # Returns
348
+ /// A tuple of (messaging receipt, OFT receipt with amounts)
349
+ pub fn send<T: OFTCore>(
350
+ env: &Env,
351
+ from: &Address,
352
+ send_param: &SendParam,
353
+ fee: &MessagingFee,
354
+ refund_address: &Address,
355
+ ) -> (MessagingReceipt, OFTReceipt) {
356
+ from.require_auth();
357
+
358
+ let oft_receipt = T::__debit(env, from, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
359
+
360
+ let (msg, options) = build_msg_and_options::<T>(env, from, send_param, oft_receipt.amount_received_ld);
361
+ let msg_receipt = T::__lz_send(env, send_param.dst_eid, &msg, &options, from, fee, refund_address);
362
+
363
+ OFTSent {
364
+ guid: msg_receipt.guid.clone(),
365
+ dst_eid: send_param.dst_eid,
366
+ from: from.clone(),
367
+ amount_sent_ld: oft_receipt.amount_sent_ld,
368
+ amount_received_ld: oft_receipt.amount_received_ld,
369
+ }
370
+ .publish(env);
371
+
372
+ (msg_receipt, oft_receipt)
373
+ }
374
+
375
+ /// Retrieves the decimal conversion rate used for cross-chain normalization.
376
+ pub fn decimal_conversion_rate(env: &Env) -> i128 {
377
+ OFTStorage::decimal_conversion_rate(env).unwrap_or_panic(env, OFTError::NotInitialized)
378
+ }
379
+
380
+ /// Retrieves the token address associated with this OFT.
381
+ pub fn token(env: &Env) -> Address {
382
+ OFTStorage::token(env).unwrap_or_panic(env, OFTError::NotInitialized)
383
+ }
384
+
385
+ /// Retrieves the shared decimals used for cross-chain normalization.
386
+ pub fn shared_decimals(env: &Env) -> u32 {
387
+ let token = token(env);
388
+ let local_decimals = TokenClient::new(env, &token).decimals();
389
+ let conversion_rate = decimal_conversion_rate(env);
390
+ local_decimals - conversion_rate.ilog10()
391
+ }
392
+
393
+ // ===========================================================================
394
+ // LzReceive Handler (called by OAppReceiver)
395
+ // ===========================================================================
396
+
397
+ /// Handles incoming cross-chain OFT transfer from LayerZero endpoint.
398
+ ///
399
+ /// Credits tokens to the recipient and optionally queues a compose message.
400
+ ///
401
+ /// # Arguments
402
+ /// * `executor` - The address of the executor handling the message (unused in default implementation)
403
+ /// * `origin` - The origin information (source chain, sender, nonce)
404
+ /// * `guid` - The unique message identifier
405
+ /// * `message` - The encoded OFT message payload
406
+ /// * `extra_data` - Additional data (unused in default implementation)
407
+ /// * `value` - The native token value sent with the message (unused in default implementation)
408
+ pub fn lz_receive<T: OFTInternal>(
409
+ env: &Env,
410
+ _executor: &Address,
411
+ origin: &Origin,
412
+ guid: &BytesN<32>,
413
+ message: &Bytes,
414
+ _extra_data: &Bytes,
415
+ _value: i128,
416
+ ) {
417
+ let oft_msg = OFTMessage::decode(message);
418
+ let send_to = oft_utils::resolve_address(env, &oft_msg.send_to);
419
+
420
+ let conversion_rate = decimal_conversion_rate(env);
421
+ let amount_received_ld =
422
+ T::__credit(env, &send_to, oft_utils::to_ld(oft_msg.amount_sd, conversion_rate), origin.src_eid);
423
+
424
+ if oft_msg.is_composed() {
425
+ let compose_msg = OFTComposeMsg {
426
+ nonce: origin.nonce,
427
+ src_eid: origin.src_eid,
428
+ amount_ld: amount_received_ld,
429
+ compose_from: oft_msg.compose_from.unwrap(),
430
+ compose_msg: oft_msg.compose_msg.unwrap(),
431
+ }
432
+ .encode(env);
433
+
434
+ let endpoint_client = MessagingComposerClient::new(env, &T::endpoint(env));
435
+ endpoint_client.send_compose(&env.current_contract_address(), &send_to, guid, &0, &compose_msg);
436
+ }
437
+
438
+ events::OFTReceived { guid: guid.clone(), src_eid: origin.src_eid, to: send_to, amount_received_ld }.publish(env);
439
+ }
@@ -11,5 +11,3 @@ pub mod test_quote_send;
11
11
  pub mod test_resolve_address;
12
12
  pub mod test_send;
13
13
  pub mod test_token;
14
-
15
- pub mod extensions;
@@ -5,7 +5,7 @@ use crate::{
5
5
  create_origin, create_recipient_address, encode_oft_message, encode_oft_message_with_compose,
6
6
  generate_g_address, OFTTestSetupBuilder,
7
7
  },
8
- utils::address_to_bytes32,
8
+ utils::address_payload,
9
9
  };
10
10
  use endpoint_v2::LayerZeroReceiverClient;
11
11
  use soroban_sdk::{testutils::Address as _, Address, Bytes, BytesN, Env};
@@ -26,7 +26,7 @@ fn run_lz_receive_test(setup: &OFTTestSetup, recipient: &Address, amount_sd: u64
26
26
  if setup.is_lock_unlock() { Some(setup.token_client.balance(&setup.oft.address)) } else { None };
27
27
 
28
28
  // Create OFT message
29
- let recipient_bytes32 = address_to_bytes32(recipient);
29
+ let recipient_bytes32 = address_payload(recipient);
30
30
  let message = encode_oft_message(env, &recipient_bytes32, amount_sd);
31
31
 
32
32
  let guid = BytesN::from_array(env, &[1u8; 32]);
@@ -111,8 +111,8 @@ fn test_mint_burn_lz_receive_with_compose() {
111
111
  setup.set_peer(src_eid, &peer);
112
112
 
113
113
  // Create OFT message with compose
114
- let recipient_bytes32 = address_to_bytes32(&recipient);
115
- let compose_from = address_to_bytes32(&sender_on_src);
114
+ let recipient_bytes32 = address_payload(&recipient);
115
+ let compose_from = address_payload(&sender_on_src);
116
116
  let compose_msg = Bytes::from_array(&env, b"test compose payload");
117
117
  let amount_sd = 1000000u64;
118
118
  let message = encode_oft_message_with_compose(&env, &recipient_bytes32, amount_sd, &compose_from, &compose_msg);
@@ -204,8 +204,8 @@ fn test_lock_unlock_lz_receive_with_compose() {
204
204
  setup.set_peer(src_eid, &peer);
205
205
 
206
206
  // Create OFT message with compose
207
- let recipient_bytes32 = address_to_bytes32(&recipient);
208
- let compose_from = address_to_bytes32(&sender_on_src);
207
+ let recipient_bytes32 = address_payload(&recipient);
208
+ let compose_from = address_payload(&sender_on_src);
209
209
  let compose_msg = Bytes::from_array(&env, b"test compose payload");
210
210
  let amount_sd = 1000000u64;
211
211
  let message = encode_oft_message_with_compose(&env, &recipient_bytes32, amount_sd, &compose_from, &compose_msg);
@@ -261,7 +261,7 @@ fn test_lz_receive_without_giving_authorization() {
261
261
  assert_eq!(setup.token_client.balance(&recipient), 0);
262
262
 
263
263
  // Create OFT message
264
- let recipient_bytes32 = address_to_bytes32(&recipient);
264
+ let recipient_bytes32 = address_payload(&recipient);
265
265
  let amount_sd = 1000000u64; // 1 token in shared decimals
266
266
  let message = encode_oft_message(&env, &recipient_bytes32, amount_sd);
267
267
 
@@ -1,4 +1,4 @@
1
- use crate::{codec::oft_msg_codec::OFTMessage, utils::address_to_bytes32};
1
+ use crate::{codec::oft_msg_codec::OFTMessage, utils::address_payload};
2
2
  use soroban_sdk::{testutils::Address as _, Address, Bytes, BytesN, Env};
3
3
 
4
4
  #[test]
@@ -33,7 +33,7 @@ fn test_encode_and_decode_with_compose() {
33
33
  let msg = OFTMessage {
34
34
  send_to: send_to_addr.clone(),
35
35
  amount_sd: amount_sd_val,
36
- compose_from: Some(address_to_bytes32(&sender)),
36
+ compose_from: Some(address_payload(&sender)),
37
37
  compose_msg: Some(compose_msg_val),
38
38
  };
39
39
  let (encoded, has_compose) = msg.encode(&env);
@@ -66,7 +66,7 @@ fn test_is_composed_false_when_only_compose_from() {
66
66
  let msg = OFTMessage {
67
67
  send_to: send_to_addr,
68
68
  amount_sd: amount_sd_val,
69
- compose_from: Some(address_to_bytes32(&sender)),
69
+ compose_from: Some(address_payload(&sender)),
70
70
  compose_msg: None,
71
71
  };
72
72
 
@@ -105,7 +105,7 @@ fn test_is_composed_false_when_compose_msg_empty() {
105
105
  let msg = OFTMessage {
106
106
  send_to: send_to_addr.clone(),
107
107
  amount_sd: amount_sd_val,
108
- compose_from: Some(address_to_bytes32(&sender)),
108
+ compose_from: Some(address_payload(&sender)),
109
109
  compose_msg: Some(empty_compose_msg),
110
110
  };
111
111
 
@@ -1,6 +1,6 @@
1
1
  use crate::{
2
2
  tests::test_utils::{create_recipient_address, generate_g_address},
3
- utils::{address_to_bytes32, resolve_address},
3
+ utils::{address_payload, resolve_address},
4
4
  };
5
5
  use soroban_sdk::Env;
6
6
 
@@ -12,7 +12,7 @@ fn test_resolve_address_contract_exists() {
12
12
  let contract_address = create_recipient_address(&env);
13
13
 
14
14
  // Convert to bytes32
15
- let bytes32 = address_to_bytes32(&contract_address);
15
+ let bytes32 = address_payload(&contract_address);
16
16
 
17
17
  // Resolve back - should return the same contract address
18
18
  let resolved = resolve_address(&env, &bytes32);
@@ -28,7 +28,7 @@ fn test_resolve_address_contract_not_exists_fallback_to_g_address() {
28
28
  let g_address = generate_g_address(&env);
29
29
 
30
30
  // Convert to bytes32
31
- let bytes32 = address_to_bytes32(&g_address);
31
+ let bytes32 = address_payload(&g_address);
32
32
 
33
33
  // Resolve - should fallback to G-address since contract doesn't exist
34
34
  let resolved = resolve_address(&env, &bytes32);