@layerzerolabs/protocol-stellar-v2 0.2.18 → 0.2.20

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 (213) hide show
  1. package/.turbo/turbo-build.log +303 -253
  2. package/.turbo/turbo-lint.log +66 -65
  3. package/.turbo/turbo-test.log +1312 -1282
  4. package/Cargo.lock +21 -8
  5. package/Cargo.toml +2 -0
  6. package/contracts/ERROR_SPEC.md +9 -2
  7. package/contracts/common-macros/src/contract_ttl.rs +18 -7
  8. package/contracts/common-macros/src/lib.rs +4 -4
  9. package/contracts/common-macros/src/tests/contract_ttl.rs +1 -1
  10. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +2 -1
  11. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +7 -12
  12. package/contracts/common-macros/src/upgradeable.rs +15 -21
  13. package/contracts/message-libs/uln-302/src/events.rs +4 -0
  14. package/contracts/message-libs/uln-302/src/send_uln.rs +23 -7
  15. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +38 -64
  16. package/contracts/oapps/counter/Cargo.toml +1 -0
  17. package/contracts/oapps/counter/integration_tests/setup_uln.rs +1 -1
  18. package/contracts/oapps/oapp/src/oapp_receiver.rs +1 -1
  19. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +113 -65
  20. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +111 -82
  21. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +293 -65
  22. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +331 -56
  23. package/contracts/oapps/oft/Cargo.toml +10 -7
  24. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
  25. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
  26. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
  27. package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
  28. package/contracts/oapps/oft/integration-tests/setup.rs +28 -127
  29. package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
  30. package/contracts/oapps/oft/src/extensions/oft_fee.rs +23 -8
  31. package/contracts/oapps/oft/src/extensions/pausable.rs +19 -4
  32. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +52 -28
  33. package/contracts/oapps/oft/src/lib.rs +10 -14
  34. package/contracts/oapps/oft/src/oft.rs +143 -193
  35. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -11
  36. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +12 -14
  37. package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
  38. package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
  39. package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
  40. package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +126 -29
  41. package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
  42. package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
  43. package/contracts/oapps/oft-core/src/lib.rs +18 -0
  44. package/contracts/oapps/oft-core/src/oft_core.rs +479 -0
  45. package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
  46. package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
  47. package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -4
  48. package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
  49. package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +46 -27
  50. package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
  51. package/contracts/upgrader/src/lib.rs +30 -57
  52. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  53. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  54. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  55. package/contracts/utils/src/buffer_reader.rs +1 -0
  56. package/contracts/utils/src/errors.rs +8 -2
  57. package/contracts/utils/src/ownable.rs +125 -3
  58. package/contracts/utils/src/tests/option_ext.rs +1 -1
  59. package/contracts/utils/src/tests/ownable.rs +445 -7
  60. package/contracts/utils/src/tests/ttl_configurable.rs +2 -2
  61. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  62. package/contracts/utils/src/ttl_configurable.rs +3 -3
  63. package/contracts/utils/src/upgradeable.rs +48 -23
  64. package/contracts/workers/dvn/Cargo.toml +1 -0
  65. package/contracts/workers/dvn/src/auth.rs +12 -42
  66. package/contracts/workers/dvn/src/dvn.rs +16 -31
  67. package/contracts/workers/dvn/src/errors.rs +0 -1
  68. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  69. package/contracts/workers/dvn/src/lib.rs +4 -3
  70. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  71. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  72. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  73. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  74. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  75. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  76. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -5
  77. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +2 -3
  78. package/contracts/workers/executor/Cargo.toml +1 -0
  79. package/contracts/workers/executor/src/executor.rs +15 -26
  80. package/contracts/workers/executor-fee-lib/Cargo.toml +2 -1
  81. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +63 -5
  82. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  83. package/contracts/workers/executor-fee-lib/src/lib.rs +3 -0
  84. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  85. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  86. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  87. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  88. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  89. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  90. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  91. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  92. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  93. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  94. package/contracts/workers/price-feed/Cargo.toml +2 -1
  95. package/contracts/workers/price-feed/src/events.rs +1 -1
  96. package/contracts/workers/price-feed/src/lib.rs +3 -0
  97. package/contracts/workers/price-feed/src/price_feed.rs +6 -12
  98. package/contracts/workers/price-feed/src/storage.rs +1 -1
  99. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  100. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  101. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  102. package/contracts/workers/price-feed/src/types.rs +1 -1
  103. package/contracts/workers/worker/src/errors.rs +0 -3
  104. package/contracts/workers/worker/src/lib.rs +0 -2
  105. package/contracts/workers/worker/src/storage.rs +32 -29
  106. package/contracts/workers/worker/src/tests/setup.rs +1 -7
  107. package/contracts/workers/worker/src/tests/worker.rs +50 -42
  108. package/contracts/workers/worker/src/worker.rs +49 -58
  109. package/package.json +4 -5
  110. package/sdk/.turbo/turbo-test.log +229 -217
  111. package/sdk/dist/generated/bml.d.ts +39 -1
  112. package/sdk/dist/generated/bml.js +33 -8
  113. package/sdk/dist/generated/counter.d.ts +131 -3
  114. package/sdk/dist/generated/counter.js +41 -10
  115. package/sdk/dist/generated/dvn.d.ts +431 -362
  116. package/sdk/dist/generated/dvn.js +80 -55
  117. package/sdk/dist/generated/dvn_fee_lib.d.ts +327 -251
  118. package/sdk/dist/generated/dvn_fee_lib.js +55 -57
  119. package/sdk/dist/generated/endpoint.d.ts +131 -3
  120. package/sdk/dist/generated/endpoint.js +41 -10
  121. package/sdk/dist/generated/executor.d.ts +503 -339
  122. package/sdk/dist/generated/executor.js +80 -48
  123. package/sdk/dist/generated/executor_fee_lib.d.ts +395 -319
  124. package/sdk/dist/generated/executor_fee_lib.js +54 -56
  125. package/sdk/dist/generated/executor_helper.d.ts +53 -187
  126. package/sdk/dist/generated/executor_helper.js +47 -29
  127. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  128. package/sdk/dist/generated/layerzero_view.js +294 -0
  129. package/sdk/dist/generated/oft.d.ts +1851 -0
  130. package/sdk/dist/generated/oft.js +347 -0
  131. package/sdk/dist/generated/price_feed.d.ts +329 -253
  132. package/sdk/dist/generated/price_feed.js +55 -57
  133. package/sdk/dist/generated/sml.d.ts +131 -3
  134. package/sdk/dist/generated/sml.js +41 -10
  135. package/sdk/dist/generated/treasury.d.ts +131 -3
  136. package/sdk/dist/generated/treasury.js +41 -10
  137. package/sdk/dist/generated/uln302.d.ts +131 -3
  138. package/sdk/dist/generated/uln302.js +43 -12
  139. package/sdk/dist/generated/upgrader.d.ts +201 -15
  140. package/sdk/dist/generated/upgrader.js +99 -1
  141. package/sdk/dist/index.d.ts +2 -2
  142. package/sdk/dist/index.js +3 -3
  143. package/sdk/package.json +3 -2
  144. package/sdk/src/index.ts +3 -3
  145. package/sdk/test/oft-sml.test.ts +20 -20
  146. package/sdk/test/upgrader.test.ts +2 -3
  147. package/sdk/turbo.json +8 -0
  148. package/tools/ts-bindings-gen/Cargo.toml +2 -0
  149. package/tools/ts-bindings-gen/src/main.rs +53 -5
  150. package/turbo.json +0 -2
  151. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
  152. package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
  153. package/contracts/oapps/oft/src/oft_impl.rs +0 -201
  154. package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
  155. package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -917
  156. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -751
  157. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -434
  158. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1080
  159. package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
  160. package/contracts/oapps/oft-std/src/lib.rs +0 -16
  161. package/contracts/oapps/oft-std/src/oft.rs +0 -174
  162. package/sdk/dist/generated/oft_std.d.ts +0 -1722
  163. package/sdk/dist/generated/oft_std.js +0 -316
  164. package/sdk/dist/wasm/blocked-message-lib.d.ts +0 -1
  165. package/sdk/dist/wasm/blocked-message-lib.js +0 -2
  166. package/sdk/dist/wasm/counter.d.ts +0 -1
  167. package/sdk/dist/wasm/counter.js +0 -2
  168. package/sdk/dist/wasm/dvn-fee-lib.d.ts +0 -1
  169. package/sdk/dist/wasm/dvn-fee-lib.js +0 -2
  170. package/sdk/dist/wasm/dvn.d.ts +0 -1
  171. package/sdk/dist/wasm/dvn.js +0 -2
  172. package/sdk/dist/wasm/endpoint-v2.d.ts +0 -1
  173. package/sdk/dist/wasm/endpoint-v2.js +0 -2
  174. package/sdk/dist/wasm/executor-fee-lib.d.ts +0 -1
  175. package/sdk/dist/wasm/executor-fee-lib.js +0 -2
  176. package/sdk/dist/wasm/executor-helper.d.ts +0 -1
  177. package/sdk/dist/wasm/executor-helper.js +0 -2
  178. package/sdk/dist/wasm/executor.d.ts +0 -1
  179. package/sdk/dist/wasm/executor.js +0 -2
  180. package/sdk/dist/wasm/layerzero-views.d.ts +0 -1
  181. package/sdk/dist/wasm/layerzero-views.js +0 -2
  182. package/sdk/dist/wasm/oft-std.d.ts +0 -1
  183. package/sdk/dist/wasm/oft-std.js +0 -2
  184. package/sdk/dist/wasm/price-feed.d.ts +0 -1
  185. package/sdk/dist/wasm/price-feed.js +0 -2
  186. package/sdk/dist/wasm/simple-message-lib.d.ts +0 -1
  187. package/sdk/dist/wasm/simple-message-lib.js +0 -2
  188. package/sdk/dist/wasm/treasury.d.ts +0 -1
  189. package/sdk/dist/wasm/treasury.js +0 -2
  190. package/sdk/dist/wasm/uln302.d.ts +0 -1
  191. package/sdk/dist/wasm/uln302.js +0 -2
  192. package/sdk/dist/wasm/upgrader.d.ts +0 -1
  193. package/sdk/dist/wasm/upgrader.js +0 -2
  194. package/sdk/dist/wasm.d.ts +0 -15
  195. package/sdk/dist/wasm.js +0 -15
  196. /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
  197. /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
  198. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
  199. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
  200. /package/contracts/oapps/{oft → oft-core}/src/errors.rs +0 -0
  201. /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
  202. /package/contracts/oapps/{oft → oft-core}/src/storage.rs +0 -0
  203. /package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +0 -0
  204. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
  205. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
  206. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
  207. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
  208. /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
  209. /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
  210. /package/contracts/oapps/{oft → oft-core}/src/types.rs +0 -0
  211. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  212. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  213. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -0,0 +1,479 @@
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`, `__initialize_oft`, etc.)
5
+ //! - `OFTCore`: Public methods exposed as contract entrypoints (using `#[contracttrait]`)
6
+ //! - `impl_oft_lz_receive!`: Macro to implement `LzReceiveInternal` with default OFT receive logic
7
+ //! - Helper functions: `lz_receive` for handling incoming cross-chain transfers
8
+ //!
9
+ //! ## Usage
10
+ //!
11
+ //! ```ignore
12
+ //! use oapp_macros::oapp;
13
+ //! use oft_core::{OFTInternal, OFTCore, impl_oft_lz_receive};
14
+ //!
15
+ //! #[oapp]
16
+ //! pub struct MyOFT;
17
+ //!
18
+ //! #[contractimpl]
19
+ //! impl MyOFT {
20
+ //! pub fn __constructor(env: &Env, token: &Address, owner: &Address, endpoint: &Address, delegate: &Option<Address>) {
21
+ //! Self::__initialize_oft(env, owner, token, endpoint, delegate, 6)
22
+ //! }
23
+ //! }
24
+ //!
25
+ //! // Public methods - exposed as contract entrypoints
26
+ //! #[contractimpl(contracttrait)]
27
+ //! impl OFTCore for MyOFT {}
28
+ //!
29
+ //! // Internal methods - NOT exposed as contract entrypoints
30
+ //! // IMPORTANT: Do NOT use #[contractimpl] here to keep methods internal
31
+ //! impl OFTInternal for MyOFT {
32
+ //! fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
33
+ //! // Your debit logic (e.g., burn or lock tokens)
34
+ //! oft_core::oft_types::mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
35
+ //! }
36
+ //!
37
+ //! fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
38
+ //! // Your credit logic (e.g., mint or unlock tokens)
39
+ //! oft_core::oft_types::mint_burn::credit::<Self>(env, to, amount_ld, src_eid)
40
+ //! }
41
+ //! }
42
+ //!
43
+ //! // LzReceiveInternal - use the macro for default OFT receive logic
44
+ //! impl_oft_lz_receive!(MyOFT);
45
+ //! ```
46
+
47
+ use crate::{
48
+ self as oft_core,
49
+ codec::{oft_compose_msg_codec::OFTComposeMsg, oft_msg_codec::OFTMessage},
50
+ errors::OFTError,
51
+ events::{self, OFTSent},
52
+ storage::OFTStorage,
53
+ types::{self, OFTFeeDetail, OFTLimit, OFTReceipt, SendParam},
54
+ utils as oft_utils,
55
+ };
56
+ use common_macros::contract_trait;
57
+ use endpoint_v2::{MessagingComposerClient, MessagingFee, MessagingReceipt};
58
+ use oapp::{
59
+ oapp_core::{initialize_oapp, OAppCore},
60
+ oapp_options_type3::OAppOptionsType3,
61
+ oapp_receiver::OAppReceiver,
62
+ oapp_sender::OAppSenderInternal,
63
+ };
64
+ use soroban_sdk::{assert_with_error, token::TokenClient, vec, Address, Bytes, Env, Vec};
65
+ use utils::{option_ext::OptionExt, ownable::OwnableInitializer};
66
+
67
+ // ===========================================================================
68
+ // OFTInternal Trait (NOT exposed as contract entrypoints)
69
+ // ===========================================================================
70
+
71
+ /// Internal OFT trait containing methods that should NOT be exposed as contract entrypoints.
72
+ ///
73
+ /// **IMPORTANT**: Implement this trait WITHOUT the `#[contractimpl]` macro to keep
74
+ /// `__debit`, `__credit`, and other internal methods private to the contract.
75
+ ///
76
+ /// ```ignore
77
+ /// // Correct - methods stay internal
78
+ /// impl OFTInternal for MyOFT { ... }
79
+ ///
80
+ /// // WRONG - would expose methods as entrypoints
81
+ /// #[contractimpl]
82
+ /// impl OFTInternal for MyOFT { ... }
83
+ /// ```
84
+ ///
85
+ /// This trait extends all OApp supertraits and contains both the token operations
86
+ /// and the internal sending logic. `OFTCore` serves only as an entrypoint wrapper.
87
+ pub trait OFTInternal: OAppCore + OAppReceiver + OAppSenderInternal + OAppOptionsType3 + OwnableInitializer {
88
+ // =========================================================================
89
+ // Initialization
90
+ // =========================================================================
91
+
92
+ /// Initializes the OFT (Omnichain Fungible Token) contract.
93
+ ///
94
+ /// Sets up the OApp infrastructure and configures decimal conversion for cross-chain transfers.
95
+ /// The `shared_decimals` parameter defines the common decimal precision used across all chains,
96
+ /// enabling consistent token amounts regardless of each chain's native token decimals.
97
+ ///
98
+ /// # Arguments
99
+ /// * `owner` - The address that will own this OFT contract
100
+ /// * `token` - The underlying token contract address (must implement SEP-41 token interface)
101
+ /// * `endpoint` - The LayerZero endpoint address for cross-chain messaging
102
+ /// * `delegate` - Optional delegate address for endpoint configuration permissions
103
+ /// * `shared_decimals` - The shared decimal precision for cross-chain compatibility (must be <= local decimals)
104
+ ///
105
+ /// # Panics
106
+ /// * `OFTError::InvalidLocalDecimals` - If the token's local decimals are less than `shared_decimals`
107
+ fn __initialize_oft(
108
+ env: &Env,
109
+ owner: &Address,
110
+ token: &Address,
111
+ endpoint: &Address,
112
+ delegate: &Option<Address>,
113
+ shared_decimals: u32,
114
+ ) {
115
+ // Initialize OApp (includes owner initialization)
116
+ initialize_oapp::<Self>(env, owner, endpoint, delegate);
117
+
118
+ let local_decimals = TokenClient::new(env, token).decimals();
119
+ assert_with_error!(env, local_decimals >= shared_decimals, OFTError::InvalidLocalDecimals);
120
+
121
+ // Initialize OFT storage
122
+ OFTStorage::set_token(env, token);
123
+ OFTStorage::set_decimal_conversion_rate(env, &10_i128.pow(local_decimals - shared_decimals));
124
+ }
125
+
126
+ // =========================================================================
127
+ // Required Methods (no defaults - user MUST implement)
128
+ // =========================================================================
129
+
130
+ /// Debits tokens from the specified address for cross-chain transfer.
131
+ ///
132
+ /// # Arguments
133
+ /// * `from` - The address to debit the tokens from
134
+ /// * `amount_ld` - The amount of tokens to send in local decimals
135
+ /// * `min_amount_ld` - The minimum amount to send in local decimals
136
+ /// * `dst_eid` - The destination chain ID
137
+ ///
138
+ /// # Returns
139
+ /// `OFTReceipt` containing the amount sent and amount received
140
+ fn __debit(env: &Env, from: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt;
141
+
142
+ /// Credits tokens to recipient after receiving cross-chain transfer.
143
+ ///
144
+ /// # Arguments
145
+ /// * `to` - The address to credit tokens to
146
+ /// * `amount_ld` - Amount in local decimals to credit
147
+ /// * `src_eid` - Source endpoint ID
148
+ ///
149
+ /// # Returns
150
+ /// The amount actually credited
151
+ fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128;
152
+
153
+ // =========================================================================
154
+ // Optional Methods (have defaults - override as needed)
155
+ // =========================================================================
156
+
157
+ // ----- Quote Methods -----
158
+
159
+ /// Quotes an OFT transfer without executing.
160
+ ///
161
+ /// The default implementation has no send limits (`min_amount_ld: 0`, `max_amount_ld: i128::MAX`)
162
+ /// and no fee details (empty vector). Override this method to implement custom limits or fees.
163
+ ///
164
+ /// # Arguments
165
+ /// * `send_param` - The send parameters to quote
166
+ ///
167
+ /// # Returns
168
+ /// A tuple of (transfer limits, fee details, estimated receipt)
169
+ fn __quote_oft(env: &Env, send_param: &SendParam) -> (OFTLimit, Vec<OFTFeeDetail>, OFTReceipt) {
170
+ let limit = OFTLimit { min_amount_ld: 0, max_amount_ld: i128::MAX };
171
+ let fee_details = vec![env];
172
+ let oft_receipt = Self::__debit_view(env, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
173
+ (limit, fee_details, oft_receipt)
174
+ }
175
+
176
+ /// Quotes the LayerZero messaging fee for a send operation.
177
+ ///
178
+ /// # Arguments
179
+ /// * `from` - The address initiating the transfer
180
+ /// * `send_param` - The send parameters to quote
181
+ /// * `pay_in_zro` - Whether to pay the fee in ZRO token
182
+ ///
183
+ /// # Returns
184
+ /// The messaging fee required for the transfer
185
+ fn __quote_send(env: &Env, from: &Address, send_param: &SendParam, pay_in_zro: bool) -> MessagingFee {
186
+ let OFTReceipt { amount_received_ld, .. } =
187
+ Self::__debit_view(env, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
188
+
189
+ let (msg, options) = Self::__build_msg_and_options(env, from, send_param, amount_received_ld);
190
+ Self::__quote(env, send_param.dst_eid, &msg, &options, pay_in_zro)
191
+ }
192
+
193
+ // ----- Send Method -----
194
+
195
+ /// Executes a cross-chain token transfer via LayerZero.
196
+ ///
197
+ /// Debits tokens from the `from` address, builds the message, and sends via the endpoint.
198
+ /// The `from` address must be authenticated.
199
+ ///
200
+ /// # Arguments
201
+ /// * `from` - The address sending tokens (must authorize)
202
+ /// * `send_param` - The send parameters (destination, recipient, amount, options)
203
+ /// * `fee` - The messaging fee to pay
204
+ /// * `refund_address` - The address to refund excess fees to
205
+ ///
206
+ /// # Returns
207
+ /// A tuple of (messaging receipt, OFT receipt with amounts)
208
+ fn __send(
209
+ env: &Env,
210
+ from: &Address,
211
+ send_param: &SendParam,
212
+ fee: &MessagingFee,
213
+ refund_address: &Address,
214
+ ) -> (MessagingReceipt, OFTReceipt) {
215
+ from.require_auth();
216
+
217
+ let oft_receipt = Self::__debit(env, from, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
218
+
219
+ let (msg, options) = Self::__build_msg_and_options(env, from, send_param, oft_receipt.amount_received_ld);
220
+ let msg_receipt = Self::__lz_send(env, send_param.dst_eid, &msg, &options, from, fee, refund_address);
221
+
222
+ OFTSent {
223
+ guid: msg_receipt.guid.clone(),
224
+ dst_eid: send_param.dst_eid,
225
+ from: from.clone(),
226
+ amount_sent_ld: oft_receipt.amount_sent_ld,
227
+ amount_received_ld: oft_receipt.amount_received_ld,
228
+ }
229
+ .publish(env);
230
+
231
+ (msg_receipt, oft_receipt)
232
+ }
233
+
234
+ // ----- View/Helper Methods -----
235
+
236
+ /// Simulates a debit operation without executing, used for quoting.
237
+ ///
238
+ /// The default implementation does not charge any fee, so `amount_sent_ld` equals
239
+ /// `amount_received_ld` (after dust removal). Override this method to implement
240
+ /// custom fee logic.
241
+ ///
242
+ /// # Arguments
243
+ /// * `amount_ld` - The amount of tokens to send in local decimals
244
+ /// * `min_amount_ld` - The minimum amount to send in local decimals (slippage protection)
245
+ /// * `dst_eid` - The destination chain ID (unused in default implementation)
246
+ ///
247
+ /// # Returns
248
+ /// `OFTReceipt` containing the amount sent and amount received after dust removal
249
+ ///
250
+ /// # Panics
251
+ /// * `OFTError::SlippageExceeded` - If `amount_received_ld` is less than `min_amount_ld`
252
+ fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, _dst_eid: u32) -> OFTReceipt {
253
+ let conversion_rate = Self::__decimal_conversion_rate(env);
254
+ let amount_sent_ld = oft_utils::remove_dust(amount_ld, conversion_rate);
255
+ let amount_received_ld = amount_sent_ld;
256
+
257
+ assert_with_error!(env, amount_received_ld >= min_amount_ld, OFTError::SlippageExceeded);
258
+
259
+ OFTReceipt { amount_sent_ld, amount_received_ld }
260
+ }
261
+
262
+ /// Builds OFT message and combines options for cross-chain transfer.
263
+ ///
264
+ /// # Arguments
265
+ /// * `from` - The address initiating the transfer
266
+ /// * `send_param` - The send parameters including destination, recipient, and options
267
+ /// * `receive_amount_ld` - The amount to be received in local decimals (after dust removal)
268
+ ///
269
+ /// # Returns
270
+ /// A tuple of (encoded message, combined options)
271
+ fn __build_msg_and_options(
272
+ env: &Env,
273
+ from: &Address,
274
+ send_param: &SendParam,
275
+ receive_amount_ld: i128,
276
+ ) -> (Bytes, Bytes) {
277
+ let has_compose = !send_param.compose_msg.is_empty();
278
+ let conversion_rate = Self::__decimal_conversion_rate(env);
279
+ let (msg, _) = OFTMessage {
280
+ send_to: send_param.to.clone(),
281
+ amount_sd: oft_utils::to_sd(env, receive_amount_ld, conversion_rate),
282
+ compose_from: if has_compose { Some(oft_utils::address_payload(from)) } else { None },
283
+ compose_msg: if has_compose { Some(send_param.compose_msg.clone()) } else { None },
284
+ }
285
+ .encode(env);
286
+ let msg_type = if has_compose { types::SEND_AND_CALL } else { types::SEND };
287
+
288
+ (msg, Self::combine_options(env, send_param.dst_eid, msg_type, &send_param.extra_options))
289
+ }
290
+
291
+ // ----- Storage Accessors -----
292
+
293
+ /// Retrieves the token address associated with this OFT.
294
+ fn __token(env: &Env) -> Address {
295
+ OFTStorage::token(env).unwrap_or_panic(env, OFTError::NotInitialized)
296
+ }
297
+
298
+ /// Retrieves the decimal conversion rate used for cross-chain normalization.
299
+ fn __decimal_conversion_rate(env: &Env) -> i128 {
300
+ OFTStorage::decimal_conversion_rate(env).unwrap_or_panic(env, OFTError::NotInitialized)
301
+ }
302
+
303
+ /// Retrieves the shared decimals used for cross-chain normalization.
304
+ fn __shared_decimals(env: &Env) -> u32 {
305
+ let token = Self::__token(env);
306
+ let local_decimals = TokenClient::new(env, &token).decimals();
307
+ let conversion_rate = Self::__decimal_conversion_rate(env);
308
+ local_decimals - conversion_rate.ilog10()
309
+ }
310
+ }
311
+
312
+ // ===========================================================================
313
+ // OFTCore Trait (exposed as contract entrypoints)
314
+ // ===========================================================================
315
+
316
+ /// The public OFT trait defining the cross-chain token transfer interface.
317
+ ///
318
+ /// This trait is marked with `#[contracttrait]` so all its methods are exposed as
319
+ /// contract entrypoints. Users implement this with `#[contractimpl(contracttrait)]`.
320
+ ///
321
+ /// This trait only exposes entrypoints. All internal logic is in `OFTInternal`.
322
+ #[contract_trait(client_name = "OFTClient")]
323
+ pub trait OFTCore: OFTInternal {
324
+ /// Retrieves the token address associated with this OFT.
325
+ fn token(env: &soroban_sdk::Env) -> soroban_sdk::Address {
326
+ Self::__token(env)
327
+ }
328
+
329
+ /// Returns OFT version as (major, minor).
330
+ fn oft_version(_env: &soroban_sdk::Env) -> (u64, u64) {
331
+ (1, 1)
332
+ }
333
+
334
+ /// Retrieves the shared decimals used for cross-chain normalization.
335
+ fn shared_decimals(env: &soroban_sdk::Env) -> u32 {
336
+ Self::__shared_decimals(env)
337
+ }
338
+
339
+ /// Retrieves the decimal conversion rate used for cross-chain normalization.
340
+ fn decimal_conversion_rate(env: &soroban_sdk::Env) -> i128 {
341
+ Self::__decimal_conversion_rate(env)
342
+ }
343
+
344
+ /// Whether a separate token approval is required before sending.
345
+ ///
346
+ /// Helps wallet implementers determine integration requirements.
347
+ ///
348
+ /// # Returns
349
+ /// - `true` if a separate token approval step is required
350
+ /// - `false` if no separate approval is needed
351
+ fn approval_required(_env: &soroban_sdk::Env) -> bool {
352
+ false
353
+ }
354
+
355
+ /// Quotes an OFT transfer without executing.
356
+ ///
357
+ /// # Returns
358
+ /// (OFTLimit, fee details, receipt with estimated amounts)
359
+ fn quote_oft(
360
+ env: &soroban_sdk::Env,
361
+ send_param: &oft_core::types::SendParam,
362
+ ) -> (oft_core::types::OFTLimit, soroban_sdk::Vec<oft_core::types::OFTFeeDetail>, oft_core::types::OFTReceipt) {
363
+ Self::__quote_oft(env, send_param)
364
+ }
365
+
366
+ /// Quotes a send operation including LayerZero messaging fees.
367
+ fn quote_send(
368
+ env: &soroban_sdk::Env,
369
+ from: &soroban_sdk::Address,
370
+ send_param: &oft_core::types::SendParam,
371
+ pay_in_zro: bool,
372
+ ) -> endpoint_v2::MessagingFee {
373
+ Self::__quote_send(env, from, send_param, pay_in_zro)
374
+ }
375
+
376
+ /// Sends tokens cross-chain to another endpoint.
377
+ ///
378
+ /// From must be authenticated.
379
+ ///
380
+ /// # Returns
381
+ /// (MessagingReceipt, OFTReceipt)
382
+ fn send(
383
+ env: &soroban_sdk::Env,
384
+ from: &soroban_sdk::Address,
385
+ send_param: &oft_core::types::SendParam,
386
+ fee: &endpoint_v2::MessagingFee,
387
+ refund_address: &soroban_sdk::Address,
388
+ ) -> (endpoint_v2::MessagingReceipt, oft_core::types::OFTReceipt) {
389
+ Self::__send(env, from, send_param, fee, refund_address)
390
+ }
391
+ }
392
+
393
+ // ===========================================================================
394
+ // LzReceive Handler (called by OAppReceiver)
395
+ // ===========================================================================
396
+
397
+ /// Implements `LzReceiveInternal` for an OFT contract using the default OFT receive logic.
398
+ ///
399
+ /// This macro generates the boilerplate `LzReceiveInternal` implementation that delegates
400
+ /// to `oft_core::lz_receive`, which handles decoding the OFT message, crediting tokens
401
+ /// to the recipient, and optionally queuing compose messages.
402
+ ///
403
+ /// # Usage
404
+ ///
405
+ /// ```ignore
406
+ /// use oft_core::impl_oft_lz_receive;
407
+ ///
408
+ /// #[oapp]
409
+ /// pub struct MyOFT;
410
+ ///
411
+ /// impl OFTInternal for MyOFT {
412
+ /// // ... implement __debit and __credit ...
413
+ /// }
414
+ ///
415
+ /// // Instead of manually implementing LzReceiveInternal:
416
+ /// impl_oft_lz_receive!(MyOFT);
417
+ /// ```
418
+ #[macro_export]
419
+ macro_rules! impl_oft_lz_receive {
420
+ ($contract:ty) => {
421
+ impl oapp::oapp_receiver::LzReceiveInternal for $contract {
422
+ fn __lz_receive(
423
+ env: &soroban_sdk::Env,
424
+ origin: &endpoint_v2::Origin,
425
+ guid: &soroban_sdk::BytesN<32>,
426
+ message: &soroban_sdk::Bytes,
427
+ extra_data: &soroban_sdk::Bytes,
428
+ executor: &soroban_sdk::Address,
429
+ value: i128,
430
+ ) {
431
+ oft_core::lz_receive::<Self>(env, executor, origin, guid, message, extra_data, value)
432
+ }
433
+ }
434
+ };
435
+ }
436
+
437
+ /// Handles incoming cross-chain OFT transfer from LayerZero endpoint.
438
+ ///
439
+ /// Credits tokens to the recipient and optionally queues a compose message.
440
+ ///
441
+ /// # Arguments
442
+ /// * `executor` - The address of the executor handling the message (unused in default implementation)
443
+ /// * `origin` - The origin information (source chain, sender, nonce)
444
+ /// * `guid` - The unique message identifier
445
+ /// * `message` - The encoded OFT message payload
446
+ /// * `extra_data` - Additional data (unused in default implementation)
447
+ /// * `value` - The native token value sent with the message (unused in default implementation)
448
+ pub fn lz_receive<T: OFTInternal>(
449
+ env: &soroban_sdk::Env,
450
+ _executor: &soroban_sdk::Address,
451
+ origin: &endpoint_v2::Origin,
452
+ guid: &soroban_sdk::BytesN<32>,
453
+ message: &soroban_sdk::Bytes,
454
+ _extra_data: &soroban_sdk::Bytes,
455
+ _value: i128,
456
+ ) {
457
+ let oft_msg = OFTMessage::decode(message);
458
+ let send_to = oft_utils::resolve_address(env, &oft_msg.send_to);
459
+
460
+ let conversion_rate = T::__decimal_conversion_rate(env);
461
+ let amount_received_ld =
462
+ T::__credit(env, &send_to, oft_utils::to_ld(oft_msg.amount_sd, conversion_rate), origin.src_eid);
463
+
464
+ if oft_msg.is_composed() {
465
+ let compose_msg = OFTComposeMsg {
466
+ nonce: origin.nonce,
467
+ src_eid: origin.src_eid,
468
+ amount_ld: amount_received_ld,
469
+ compose_from: oft_msg.compose_from.unwrap(),
470
+ compose_msg: oft_msg.compose_msg.unwrap(),
471
+ }
472
+ .encode(env);
473
+
474
+ let endpoint_client = MessagingComposerClient::new(env, &T::endpoint(env));
475
+ endpoint_client.send_compose(&env.current_contract_address(), &send_to, guid, &0, &compose_msg);
476
+ }
477
+
478
+ events::OFTReceived { guid: guid.clone(), src_eid: origin.src_eid, to: send_to, amount_received_ld }.publish(env);
479
+ }
@@ -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);