@layerzerolabs/protocol-stellar-v2 0.2.19 → 0.2.21

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 (249) hide show
  1. package/.turbo/turbo-build.log +795 -791
  2. package/.turbo/turbo-lint.log +325 -155
  3. package/.turbo/turbo-test.log +1398 -1277
  4. package/Cargo.lock +122 -111
  5. package/Cargo.toml +32 -16
  6. package/contracts/common-macros/Cargo.toml +7 -7
  7. package/contracts/common-macros/src/auth.rs +18 -37
  8. package/contracts/common-macros/src/contract_ttl.rs +18 -7
  9. package/contracts/common-macros/src/lib.rs +31 -14
  10. package/contracts/common-macros/src/lz_contract.rs +38 -7
  11. package/contracts/common-macros/src/storage.rs +251 -292
  12. package/contracts/common-macros/src/tests/contract_ttl.rs +1 -1
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +6 -12
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +12 -17
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +2 -1
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +2 -7
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +20 -14
  18. package/contracts/common-macros/src/tests/upgradeable.rs +26 -4
  19. package/contracts/common-macros/src/ttl_configurable.rs +2 -10
  20. package/contracts/common-macros/src/ttl_extendable.rs +2 -10
  21. package/contracts/common-macros/src/upgradeable.rs +61 -26
  22. package/contracts/common-macros/src/utils.rs +0 -9
  23. package/contracts/endpoint-v2/src/lib.rs +3 -2
  24. package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +2 -2
  25. package/contracts/endpoint-v2/src/tests/endpoint_v2/lz_receive_alert.rs +3 -3
  26. package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +4 -4
  27. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_delegate.rs +17 -5
  28. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
  29. package/contracts/endpoint-v2/src/tests/endpoint_v2/verify.rs +2 -2
  30. package/contracts/endpoint-v2/src/tests/message_lib_manager/register_library.rs +2 -2
  31. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +6 -6
  32. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +67 -37
  33. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_send_library.rs +5 -5
  34. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library.rs +44 -54
  35. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library_timeout.rs +7 -7
  36. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_send_library.rs +8 -8
  37. package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +3 -3
  38. package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +4 -4
  39. package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +3 -3
  40. package/contracts/endpoint-v2/src/tests/messaging_composer/clear_compose.rs +2 -2
  41. package/contracts/endpoint-v2/src/tests/messaging_composer/lz_compose_alert.rs +3 -3
  42. package/contracts/endpoint-v2/src/tests/messaging_composer/send_compose.rs +2 -2
  43. package/contracts/layerzero-views/Cargo.toml +0 -1
  44. package/contracts/layerzero-views/src/layerzero_view.rs +1 -13
  45. package/contracts/macro-integration-tests/Cargo.toml +5 -15
  46. package/contracts/macro-integration-tests/tests/runtime/oapp/mod.rs +48 -0
  47. package/contracts/macro-integration-tests/tests/runtime/oapp/oapp_core.rs +170 -0
  48. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +154 -0
  49. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +338 -0
  50. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +435 -0
  51. package/contracts/macro-integration-tests/tests/runtime.rs +1 -0
  52. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.rs +8 -0
  53. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -0
  54. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.rs +8 -0
  55. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.stderr +71 -0
  56. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.rs +10 -0
  57. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +5 -0
  58. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.rs +8 -0
  59. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -0
  60. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.rs +8 -0
  61. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -0
  62. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_all.rs +38 -0
  63. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_single_trait.rs +96 -0
  64. package/contracts/macro-integration-tests/tests/ui/oapp/pass/minimal_contract.rs +64 -0
  65. package/contracts/macro-integration-tests/tests/ui/oapp/pass/struct_with_fields.rs +46 -0
  66. package/contracts/macro-integration-tests/tests/ui/ownable/fail/only_auth_missing_env.stderr +8 -0
  67. package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +1 -1
  68. package/contracts/macro-integration-tests/tests/ui/ownable/pass/only_auth_env_param_variants.rs +1 -1
  69. package/contracts/macro-integration-tests/tests/ui_oapp.rs +11 -0
  70. package/contracts/message-libs/message-lib-common/Cargo.toml +0 -1
  71. package/contracts/message-libs/message-lib-common/src/errors.rs +1 -1
  72. package/contracts/message-libs/treasury/Cargo.toml +0 -2
  73. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +2 -2
  74. package/contracts/message-libs/uln-302/src/events.rs +4 -0
  75. package/contracts/message-libs/uln-302/src/send_uln.rs +22 -6
  76. package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +2 -2
  77. package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +2 -2
  78. package/contracts/message-libs/uln-302/src/tests/receive_uln302/verify.rs +2 -2
  79. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +2 -2
  80. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +2 -2
  81. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +21 -67
  82. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_executor_configs.rs +2 -2
  83. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +2 -2
  84. package/contracts/oapps/counter/Cargo.toml +5 -6
  85. package/contracts/oapps/counter/integration_tests/setup_uln.rs +1 -1
  86. package/contracts/oapps/counter/integration_tests/utils.rs +19 -12
  87. package/contracts/oapps/oapp/src/errors.rs +1 -1
  88. package/contracts/oapps/oapp/src/interfaces/mod.rs +3 -0
  89. package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +47 -0
  90. package/contracts/oapps/oapp/src/lib.rs +1 -0
  91. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +4 -4
  92. package/contracts/oapps/oapp/src/oapp_core.rs +5 -5
  93. package/contracts/oapps/oapp/src/oapp_options_type3.rs +12 -4
  94. package/contracts/oapps/oapp/src/oapp_receiver.rs +14 -9
  95. package/contracts/oapps/oapp/src/tests/mod.rs +4 -4
  96. package/contracts/oapps/oapp/src/tests/oapp_core.rs +223 -0
  97. package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +240 -0
  98. package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +381 -0
  99. package/contracts/oapps/oapp/src/tests/oapp_sender.rs +569 -0
  100. package/contracts/oapps/oapp-macros/Cargo.toml +8 -4
  101. package/contracts/oapps/oapp-macros/src/generators.rs +9 -34
  102. package/contracts/oapps/oapp-macros/src/lib.rs +3 -0
  103. package/contracts/oapps/oapp-macros/src/tests/mod.rs +2 -0
  104. package/contracts/oapps/oapp-macros/src/tests/oapp.rs +88 -0
  105. package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +86 -0
  106. package/contracts/oapps/oapp-macros/src/tests/snapshots/oapp_macros__tests__oapp__snapshot_generate_oapp.snap +103 -0
  107. package/contracts/oapps/oft/integration-tests/utils.rs +28 -8
  108. package/contracts/oapps/oft/src/extensions/oft_fee.rs +153 -75
  109. package/contracts/oapps/oft/src/extensions/pausable.rs +61 -12
  110. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +198 -134
  111. package/contracts/oapps/oft/src/oft.rs +45 -50
  112. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +1 -1
  113. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +4 -26
  114. package/contracts/oapps/oft-core/Cargo.toml +1 -4
  115. package/contracts/oapps/oft-core/integration-tests/setup.rs +3 -3
  116. package/contracts/oapps/oft-core/integration-tests/utils.rs +21 -3
  117. package/contracts/oapps/oft-core/src/errors.rs +3 -2
  118. package/contracts/oapps/oft-core/src/events.rs +6 -0
  119. package/contracts/oapps/oft-core/src/lib.rs +1 -1
  120. package/contracts/oapps/oft-core/src/oft_core.rs +341 -246
  121. package/contracts/oapps/oft-core/src/storage.rs +7 -3
  122. package/contracts/oapps/oft-core/src/tests/mod.rs +1 -0
  123. package/contracts/oapps/oft-core/src/tests/test_decimals.rs +37 -2
  124. package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +2 -2
  125. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +323 -0
  126. package/contracts/oapps/oft-core/src/tests/test_send.rs +2 -2
  127. package/contracts/oapps/oft-core/src/tests/test_utils.rs +61 -16
  128. package/contracts/upgrader/src/lib.rs +30 -57
  129. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  130. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  131. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  132. package/contracts/utils/Cargo.toml +0 -1
  133. package/contracts/utils/src/buffer_reader.rs +1 -0
  134. package/contracts/utils/src/errors.rs +4 -2
  135. package/contracts/utils/src/multisig.rs +17 -8
  136. package/contracts/utils/src/ownable.rs +6 -6
  137. package/contracts/utils/src/testing_utils.rs +124 -54
  138. package/contracts/utils/src/tests/multisig.rs +12 -12
  139. package/contracts/utils/src/tests/ownable.rs +6 -6
  140. package/contracts/utils/src/tests/testing_utils.rs +50 -167
  141. package/contracts/utils/src/tests/ttl_configurable.rs +5 -5
  142. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  143. package/contracts/utils/src/ttl_configurable.rs +13 -7
  144. package/contracts/utils/src/upgradeable.rs +48 -23
  145. package/contracts/workers/dvn/Cargo.toml +6 -6
  146. package/contracts/workers/dvn/src/auth.rs +12 -42
  147. package/contracts/workers/dvn/src/dvn.rs +15 -40
  148. package/contracts/workers/dvn/src/errors.rs +0 -1
  149. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  150. package/contracts/workers/dvn/src/lib.rs +4 -3
  151. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  152. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  153. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  154. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  155. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  156. package/contracts/workers/dvn-fee-lib/Cargo.toml +2 -2
  157. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +38 -22
  158. package/contracts/workers/dvn-fee-lib/src/lib.rs +12 -2
  159. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +17 -16
  160. package/contracts/workers/executor/Cargo.toml +4 -0
  161. package/contracts/workers/executor/src/executor.rs +15 -36
  162. package/contracts/workers/executor/src/lib.rs +2 -2
  163. package/contracts/workers/executor/src/tests/auth.rs +394 -0
  164. package/contracts/workers/executor/src/tests/executor.rs +410 -0
  165. package/contracts/workers/executor/src/tests/mod.rs +3 -0
  166. package/contracts/workers/executor/src/tests/setup.rs +250 -0
  167. package/contracts/workers/executor-fee-lib/Cargo.toml +7 -1
  168. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +62 -15
  169. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  170. package/contracts/workers/executor-fee-lib/src/lib.rs +11 -2
  171. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  172. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  173. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  174. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  175. package/contracts/workers/executor-helper/Cargo.toml +0 -1
  176. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  177. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  178. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  179. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  180. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  181. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  182. package/contracts/workers/price-feed/Cargo.toml +7 -1
  183. package/contracts/workers/price-feed/src/events.rs +1 -1
  184. package/contracts/workers/price-feed/src/lib.rs +12 -4
  185. package/contracts/workers/price-feed/src/price_feed.rs +5 -21
  186. package/contracts/workers/price-feed/src/storage.rs +1 -1
  187. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  188. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  189. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  190. package/contracts/workers/price-feed/src/types.rs +1 -1
  191. package/contracts/workers/worker/src/errors.rs +1 -4
  192. package/contracts/workers/worker/src/lib.rs +0 -2
  193. package/contracts/workers/worker/src/storage.rs +32 -29
  194. package/contracts/workers/worker/src/tests/setup.rs +2 -8
  195. package/contracts/workers/worker/src/tests/worker.rs +96 -74
  196. package/contracts/workers/worker/src/worker.rs +75 -75
  197. package/docs/error-spec.md +55 -0
  198. package/docs/layerzero-v2-on-stellar.md +447 -0
  199. package/docs/oapp-guide.md +212 -0
  200. package/docs/oft-guide.md +314 -0
  201. package/package.json +3 -3
  202. package/sdk/.turbo/turbo-test.log +268 -263
  203. package/sdk/dist/generated/bml.d.ts +12 -4
  204. package/sdk/dist/generated/bml.js +9 -7
  205. package/sdk/dist/generated/counter.d.ts +306 -298
  206. package/sdk/dist/generated/counter.js +48 -46
  207. package/sdk/dist/generated/dvn.d.ts +450 -411
  208. package/sdk/dist/generated/dvn.js +66 -64
  209. package/sdk/dist/generated/dvn_fee_lib.d.ts +294 -338
  210. package/sdk/dist/generated/dvn_fee_lib.js +33 -64
  211. package/sdk/dist/generated/endpoint.d.ts +108 -100
  212. package/sdk/dist/generated/endpoint.js +21 -19
  213. package/sdk/dist/generated/executor.d.ts +414 -370
  214. package/sdk/dist/generated/executor.js +58 -55
  215. package/sdk/dist/generated/executor_fee_lib.d.ts +333 -377
  216. package/sdk/dist/generated/executor_fee_lib.js +34 -65
  217. package/sdk/dist/generated/executor_helper.d.ts +26 -190
  218. package/sdk/dist/generated/executor_helper.js +23 -28
  219. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  220. package/sdk/dist/generated/layerzero_view.js +294 -0
  221. package/sdk/dist/generated/oft.d.ts +408 -385
  222. package/sdk/dist/generated/oft.js +89 -92
  223. package/sdk/dist/generated/price_feed.d.ts +385 -429
  224. package/sdk/dist/generated/price_feed.js +50 -81
  225. package/sdk/dist/generated/sml.d.ts +108 -100
  226. package/sdk/dist/generated/sml.js +21 -19
  227. package/sdk/dist/generated/treasury.d.ts +108 -100
  228. package/sdk/dist/generated/treasury.js +21 -19
  229. package/sdk/dist/generated/uln302.d.ts +108 -100
  230. package/sdk/dist/generated/uln302.js +23 -21
  231. package/sdk/dist/generated/upgrader.d.ts +189 -18
  232. package/sdk/dist/generated/upgrader.js +84 -4
  233. package/sdk/dist/index.d.ts +1 -0
  234. package/sdk/dist/index.js +2 -0
  235. package/sdk/package.json +1 -1
  236. package/sdk/src/index.ts +3 -0
  237. package/sdk/test/oft-sml.test.ts +4 -4
  238. package/sdk/test/suites/localnet.ts +84 -20
  239. package/sdk/test/upgrader.test.ts +2 -3
  240. package/tools/ts-bindings-gen/src/main.rs +2 -1
  241. package/contracts/ERROR_SPEC.md +0 -44
  242. package/contracts/endpoint-v2/ARCHITECTURE.md +0 -233
  243. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +0 -175
  244. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +0 -212
  245. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +0 -153
  246. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +0 -294
  247. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  248. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  249. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -1,15 +1,15 @@
1
1
  //! OFT - traits and implementations for Omnichain Fungible Tokens.
2
2
  //!
3
3
  //! This module provides:
4
- //! - `OFTInternal`: Internal methods NOT exposed as contract entrypoints (`__debit`, `__credit`, etc.)
4
+ //! - `OFTInternal`: Internal methods NOT exposed as contract entrypoints (`__debit`, `__credit`, `__initialize_oft`, `__receive`, etc.)
5
5
  //! - `OFTCore`: Public methods exposed as contract entrypoints (using `#[contracttrait]`)
6
- //! - Core functions: `initialize_oft`, `lz_receive`, and other helpers
6
+ //! - `impl_oft_lz_receive!`: Macro to implement `LzReceiveInternal` with default OFT receive logic
7
7
  //!
8
8
  //! ## Usage
9
9
  //!
10
10
  //! ```ignore
11
- //! use oapp_macros::{oapp, oapp_options_type3};
12
- //! use oft_core::{OFTInternal, OFTCore, initialize_oft};
11
+ //! use oapp_macros::oapp;
12
+ //! use oft_core::{OFTInternal, OFTCore, impl_oft_lz_receive};
13
13
  //!
14
14
  //! #[oapp]
15
15
  //! pub struct MyOFT;
@@ -17,7 +17,7 @@
17
17
  //! #[contractimpl]
18
18
  //! impl MyOFT {
19
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)
20
+ //! Self::__initialize_oft(env, owner, token, endpoint, delegate, 6)
21
21
  //! }
22
22
  //! }
23
23
  //!
@@ -38,26 +38,30 @@
38
38
  //! oft_core::oft_types::mint_burn::credit::<Self>(env, to, amount_ld, src_eid)
39
39
  //! }
40
40
  //! }
41
+ //!
42
+ //! // LzReceiveInternal - use the macro for default OFT receive logic
43
+ //! impl_oft_lz_receive!(MyOFT);
41
44
  //! ```
42
45
 
43
46
  use crate::{
44
47
  self as oft_core,
45
48
  codec::{oft_compose_msg_codec::OFTComposeMsg, oft_msg_codec::OFTMessage},
46
49
  errors::OFTError,
47
- events::{self, OFTSent},
50
+ events::{self, MsgInspectorSet, OFTSent},
48
51
  storage::OFTStorage,
49
52
  types::{self, OFTFeeDetail, OFTLimit, OFTReceipt, SendParam},
50
53
  utils as oft_utils,
51
54
  };
52
- use common_macros::contract_trait;
53
- use endpoint_v2::{MessagingComposerClient, MessagingFee, MessagingReceipt, Origin};
55
+ use common_macros::{contract_trait, only_auth};
56
+ use endpoint_v2::{MessagingComposerClient, MessagingFee, MessagingReceipt};
54
57
  use oapp::{
58
+ interfaces::OAppMsgInspectorClient,
55
59
  oapp_core::{initialize_oapp, OAppCore},
56
60
  oapp_options_type3::OAppOptionsType3,
57
61
  oapp_receiver::OAppReceiver,
58
62
  oapp_sender::OAppSenderInternal,
59
63
  };
60
- use soroban_sdk::{assert_with_error, token::TokenClient, vec, Address, Bytes, BytesN, Env, Vec};
64
+ use soroban_sdk::{assert_with_error, token::TokenClient, vec, Address, Bytes, Env, Vec};
61
65
  use utils::{option_ext::OptionExt, ownable::OwnableInitializer};
62
66
 
63
67
  // ===========================================================================
@@ -78,11 +82,47 @@ use utils::{option_ext::OptionExt, ownable::OwnableInitializer};
78
82
  /// impl OFTInternal for MyOFT { ... }
79
83
  /// ```
80
84
  ///
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 {
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_decimals_diff(env, &(local_decimals - shared_decimals));
124
+ }
125
+
86
126
  // =========================================================================
87
127
  // Required Methods (no defaults - user MUST implement)
88
128
  // =========================================================================
@@ -114,19 +154,239 @@ pub trait OFTInternal: OAppCore + OAppOptionsType3 {
114
154
  // Optional Methods (have defaults - override as needed)
115
155
  // =========================================================================
116
156
 
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)
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
+ assert_with_error!(env, send_param.amount_ld >= 0, OFTError::InvalidAmount);
171
+
172
+ let limit = OFTLimit { min_amount_ld: 0, max_amount_ld: i128::MAX };
173
+ let fee_details = vec![env];
174
+ let oft_receipt = Self::__debit_view(env, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
175
+ (limit, fee_details, oft_receipt)
176
+ }
177
+
178
+ /// Quotes the LayerZero messaging fee for a send operation.
179
+ ///
180
+ /// # Arguments
181
+ /// * `from` - The address initiating the transfer
182
+ /// * `send_param` - The send parameters to quote
183
+ /// * `pay_in_zro` - Whether to pay the fee in ZRO token
184
+ ///
185
+ /// # Returns
186
+ /// The messaging fee required for the transfer
187
+ fn __quote_send(env: &Env, from: &Address, send_param: &SendParam, pay_in_zro: bool) -> MessagingFee {
188
+ assert_with_error!(env, send_param.amount_ld >= 0, OFTError::InvalidAmount);
189
+
190
+ let OFTReceipt { amount_received_ld, .. } =
191
+ Self::__debit_view(env, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
192
+
193
+ let (msg, options) = Self::__build_msg_and_options(env, from, send_param, amount_received_ld);
194
+ Self::__quote(env, send_param.dst_eid, &msg, &options, pay_in_zro)
195
+ }
196
+
197
+ // ----- Send Method -----
198
+
199
+ /// Executes a cross-chain token transfer via LayerZero.
200
+ ///
201
+ /// Debits tokens from the `from` address, builds the message, and sends via the endpoint.
202
+ /// The `from` address must be authenticated.
203
+ ///
204
+ /// # Arguments
205
+ /// * `from` - The address sending tokens (must authorize)
206
+ /// * `send_param` - The send parameters (destination, recipient, amount, options)
207
+ /// * `fee` - The messaging fee to pay
208
+ /// * `refund_address` - The address to refund excess fees to
209
+ ///
210
+ /// # Returns
211
+ /// A tuple of (messaging receipt, OFT receipt with amounts)
212
+ fn __send(
213
+ env: &Env,
214
+ from: &Address,
215
+ send_param: &SendParam,
216
+ fee: &MessagingFee,
217
+ refund_address: &Address,
218
+ ) -> (MessagingReceipt, OFTReceipt) {
219
+ from.require_auth();
220
+
221
+ assert_with_error!(env, send_param.amount_ld >= 0, OFTError::InvalidAmount);
222
+
223
+ let oft_receipt = Self::__debit(env, from, send_param.amount_ld, send_param.min_amount_ld, send_param.dst_eid);
224
+
225
+ let (msg, options) = Self::__build_msg_and_options(env, from, send_param, oft_receipt.amount_received_ld);
226
+ let msg_receipt = Self::__lz_send(env, send_param.dst_eid, &msg, &options, from, fee, refund_address);
227
+
228
+ OFTSent {
229
+ guid: msg_receipt.guid.clone(),
230
+ dst_eid: send_param.dst_eid,
231
+ from: from.clone(),
232
+ amount_sent_ld: oft_receipt.amount_sent_ld,
233
+ amount_received_ld: oft_receipt.amount_received_ld,
234
+ }
235
+ .publish(env);
236
+
237
+ (msg_receipt, oft_receipt)
238
+ }
239
+
240
+ // ----- View/Helper Methods -----
241
+
242
+ /// Simulates a debit operation without executing, used for quoting.
243
+ ///
244
+ /// The default implementation does not charge any fee, so `amount_sent_ld` equals
245
+ /// `amount_received_ld` (after dust removal). Override this method to implement
246
+ /// custom fee logic.
247
+ ///
248
+ /// # Arguments
249
+ /// * `amount_ld` - The amount of tokens to send in local decimals
250
+ /// * `min_amount_ld` - The minimum amount to send in local decimals (slippage protection)
251
+ /// * `dst_eid` - The destination chain ID (unused in default implementation)
252
+ ///
253
+ /// # Returns
254
+ /// `OFTReceipt` containing the amount sent and amount received after dust removal
255
+ ///
256
+ /// # Panics
257
+ /// * `OFTError::SlippageExceeded` - If `amount_received_ld` is less than `min_amount_ld`
258
+ fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, _dst_eid: u32) -> OFTReceipt {
259
+ let conversion_rate = Self::__decimal_conversion_rate(env);
260
+ let amount_sent_ld = oft_utils::remove_dust(amount_ld, conversion_rate);
261
+ let amount_received_ld = amount_sent_ld;
262
+
263
+ assert_with_error!(env, amount_received_ld >= min_amount_ld, OFTError::SlippageExceeded);
264
+
265
+ OFTReceipt { amount_sent_ld, amount_received_ld }
120
266
  }
121
267
 
122
268
  /// Builds OFT message and combines options for cross-chain transfer.
269
+ ///
270
+ /// If a message inspector is set, it will be called to validate the message and options.
271
+ /// The inspector should panic if the message or options are invalid.
272
+ ///
273
+ /// # Arguments
274
+ /// * `from` - The address initiating the transfer
275
+ /// * `send_param` - The send parameters including destination, recipient, and options
276
+ /// * `amount_receive_ld` - The amount to be received in local decimals (after dust removal)
277
+ ///
278
+ /// # Returns
279
+ /// A tuple of (encoded message, combined options)
123
280
  fn __build_msg_and_options(
124
281
  env: &Env,
125
282
  from: &Address,
126
- send_param: &oft_core::types::SendParam,
127
- receive_amount_ld: i128,
283
+ send_param: &SendParam,
284
+ amount_receive_ld: i128,
128
285
  ) -> (Bytes, Bytes) {
129
- build_msg_and_options::<Self>(env, from, send_param, receive_amount_ld)
286
+ let has_compose = !send_param.compose_msg.is_empty();
287
+ let conversion_rate = Self::__decimal_conversion_rate(env);
288
+ let (msg, _) = OFTMessage {
289
+ send_to: send_param.to.clone(),
290
+ amount_sd: oft_utils::to_sd(env, amount_receive_ld, conversion_rate),
291
+ compose_from: if has_compose { Some(oft_utils::address_payload(from)) } else { None },
292
+ compose_msg: if has_compose { Some(send_param.compose_msg.clone()) } else { None },
293
+ }
294
+ .encode(env);
295
+ let msg_type = if has_compose { types::SEND_AND_CALL } else { types::SEND };
296
+ let options = Self::combine_options(env, send_param.dst_eid, msg_type, &send_param.extra_options);
297
+
298
+ // Optionally inspect message and options if inspector is set
299
+ if let Some(inspector) = Self::__msg_inspector(env) {
300
+ OAppMsgInspectorClient::new(env, &inspector).inspect(&env.current_contract_address(), &msg, &options);
301
+ }
302
+
303
+ (msg, options)
304
+ }
305
+
306
+ // ----- Storage Accessors -----
307
+
308
+ /// Retrieves the token address associated with this OFT.
309
+ fn __token(env: &Env) -> Address {
310
+ OFTStorage::token(env).unwrap_or_panic(env, OFTError::NotInitialized)
311
+ }
312
+
313
+ /// Retrieves the decimal conversion rate used for cross-chain normalization.
314
+ fn __decimal_conversion_rate(env: &Env) -> i128 {
315
+ let decimals_diff = OFTStorage::decimals_diff(env).unwrap_or_panic(env, OFTError::NotInitialized);
316
+ 10_i128.pow(decimals_diff)
317
+ }
318
+
319
+ /// Retrieves the shared decimals used for cross-chain normalization.
320
+ fn __shared_decimals(env: &Env) -> u32 {
321
+ let local_decimals = TokenClient::new(env, &Self::__token(env)).decimals();
322
+ let decimals_diff = OFTStorage::decimals_diff(env).unwrap_or_panic(env, OFTError::NotInitialized);
323
+ local_decimals - decimals_diff
324
+ }
325
+
326
+ /// Returns the message inspector address if set.
327
+ fn __msg_inspector(env: &Env) -> Option<Address> {
328
+ OFTStorage::msg_inspector(env)
329
+ }
330
+
331
+ /// Sets or removes the message inspector address.
332
+ ///
333
+ /// The message inspector is an optional contract that validates outgoing messages
334
+ /// and options before they are sent cross-chain. If set, the inspector's `inspect`
335
+ /// method will be called during `send` and `quote_send` operations.
336
+ ///
337
+ /// # Arguments
338
+ /// * `inspector` - The address of the inspector contract, or None to remove
339
+ fn __set_msg_inspector(env: &Env, inspector: &Option<Address>) {
340
+ OFTStorage::set_or_remove_msg_inspector(env, inspector);
341
+ MsgInspectorSet { inspector: inspector.clone() }.publish(env);
342
+ }
343
+
344
+ // ----- Receive Handler -----
345
+
346
+ /// Handles incoming cross-chain OFT transfer from LayerZero endpoint.
347
+ ///
348
+ /// Credits tokens to the recipient and optionally queues a compose message.
349
+ /// Override this method to implement custom receive logic (e.g., pausable, rate limiting).
350
+ ///
351
+ /// # Arguments
352
+ /// * `origin` - The origin information (source chain, sender, nonce)
353
+ /// * `guid` - The unique message identifier
354
+ /// * `message` - The encoded OFT message payload
355
+ /// * `extra_data` - Additional data (unused in default implementation)
356
+ /// * `executor` - The address of the executor handling the message (unused in default implementation)
357
+ /// * `value` - The native token value sent with the message (unused in default implementation)
358
+ fn __receive(
359
+ env: &Env,
360
+ origin: &endpoint_v2::Origin,
361
+ guid: &soroban_sdk::BytesN<32>,
362
+ message: &Bytes,
363
+ _extra_data: &Bytes,
364
+ _executor: &Address,
365
+ _value: i128,
366
+ ) {
367
+ let oft_msg = OFTMessage::decode(message);
368
+ let send_to = oft_utils::resolve_address(env, &oft_msg.send_to);
369
+
370
+ let conversion_rate = Self::__decimal_conversion_rate(env);
371
+ let amount_received_ld =
372
+ Self::__credit(env, &send_to, oft_utils::to_ld(oft_msg.amount_sd, conversion_rate), origin.src_eid);
373
+
374
+ if oft_msg.is_composed() {
375
+ let compose_msg = OFTComposeMsg {
376
+ nonce: origin.nonce,
377
+ src_eid: origin.src_eid,
378
+ amount_ld: amount_received_ld,
379
+ compose_from: oft_msg.compose_from.unwrap(),
380
+ compose_msg: oft_msg.compose_msg.unwrap(),
381
+ }
382
+ .encode(env);
383
+
384
+ let endpoint_client = MessagingComposerClient::new(env, &Self::endpoint(env));
385
+ endpoint_client.send_compose(&env.current_contract_address(), &send_to, guid, &0, &compose_msg);
386
+ }
387
+
388
+ events::OFTReceived { guid: guid.clone(), src_eid: origin.src_eid, to: send_to, amount_received_ld }
389
+ .publish(env);
130
390
  }
131
391
  }
132
392
 
@@ -139,13 +399,12 @@ pub trait OFTInternal: OAppCore + OAppOptionsType3 {
139
399
  /// This trait is marked with `#[contracttrait]` so all its methods are exposed as
140
400
  /// contract entrypoints. Users implement this with `#[contractimpl(contracttrait)]`.
141
401
  ///
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.
402
+ /// This trait only exposes entrypoints. All internal logic is in `OFTInternal`.
144
403
  #[contract_trait(client_name = "OFTClient")]
145
- pub trait OFTCore: OFTInternal + OAppReceiver + OAppSenderInternal + OAppOptionsType3 {
404
+ pub trait OFTCore: OFTInternal {
146
405
  /// Retrieves the token address associated with this OFT.
147
406
  fn token(env: &soroban_sdk::Env) -> soroban_sdk::Address {
148
- token(env)
407
+ Self::__token(env)
149
408
  }
150
409
 
151
410
  /// Returns OFT version as (major, minor).
@@ -155,12 +414,12 @@ pub trait OFTCore: OFTInternal + OAppReceiver + OAppSenderInternal + OAppOptions
155
414
 
156
415
  /// Retrieves the shared decimals used for cross-chain normalization.
157
416
  fn shared_decimals(env: &soroban_sdk::Env) -> u32 {
158
- shared_decimals(env)
417
+ Self::__shared_decimals(env)
159
418
  }
160
419
 
161
420
  /// Retrieves the decimal conversion rate used for cross-chain normalization.
162
421
  fn decimal_conversion_rate(env: &soroban_sdk::Env) -> i128 {
163
- decimal_conversion_rate(env)
422
+ Self::__decimal_conversion_rate(env)
164
423
  }
165
424
 
166
425
  /// Whether a separate token approval is required before sending.
@@ -174,6 +433,24 @@ pub trait OFTCore: OFTInternal + OAppReceiver + OAppSenderInternal + OAppOptions
174
433
  false
175
434
  }
176
435
 
436
+ /// Returns the message inspector address if set.
437
+ fn msg_inspector(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
438
+ Self::__msg_inspector(env)
439
+ }
440
+
441
+ /// Sets or removes the message inspector address.
442
+ ///
443
+ /// The message inspector is an optional contract that validates outgoing messages
444
+ /// and options before they are sent cross-chain. If set, the inspector's `inspect`
445
+ /// method will be called during `send` and `quote_send` operations.
446
+ ///
447
+ /// # Arguments
448
+ /// * `inspector` - The address of the inspector contract, or None to remove
449
+ #[only_auth]
450
+ fn set_msg_inspector(env: &soroban_sdk::Env, inspector: &Option<soroban_sdk::Address>) {
451
+ Self::__set_msg_inspector(env, inspector);
452
+ }
453
+
177
454
  /// Quotes an OFT transfer without executing.
178
455
  ///
179
456
  /// # Returns
@@ -182,258 +459,76 @@ pub trait OFTCore: OFTInternal + OAppReceiver + OAppSenderInternal + OAppOptions
182
459
  env: &soroban_sdk::Env,
183
460
  send_param: &oft_core::types::SendParam,
184
461
  ) -> (oft_core::types::OFTLimit, soroban_sdk::Vec<oft_core::types::OFTFeeDetail>, oft_core::types::OFTReceipt) {
185
- quote_oft::<Self>(env, send_param)
462
+ Self::__quote_oft(env, send_param)
186
463
  }
187
464
 
188
465
  /// Quotes a send operation including LayerZero messaging fees.
189
466
  fn quote_send(
190
467
  env: &soroban_sdk::Env,
191
- sender: &soroban_sdk::Address,
468
+ from: &soroban_sdk::Address,
192
469
  send_param: &oft_core::types::SendParam,
193
470
  pay_in_zro: bool,
194
471
  ) -> endpoint_v2::MessagingFee {
195
- quote_send::<Self>(env, sender, send_param, pay_in_zro)
472
+ Self::__quote_send(env, from, send_param, pay_in_zro)
196
473
  }
197
474
 
198
475
  /// Sends tokens cross-chain to another endpoint.
199
476
  ///
200
- /// Sender must be authenticated.
477
+ /// From must be authenticated.
201
478
  ///
202
479
  /// # Returns
203
480
  /// (MessagingReceipt, OFTReceipt)
204
481
  fn send(
205
482
  env: &soroban_sdk::Env,
206
- sender: &soroban_sdk::Address,
483
+ from: &soroban_sdk::Address,
207
484
  send_param: &oft_core::types::SendParam,
208
485
  fee: &endpoint_v2::MessagingFee,
209
486
  refund_address: &soroban_sdk::Address,
210
487
  ) -> (endpoint_v2::MessagingReceipt, oft_core::types::OFTReceipt) {
211
- send::<Self>(env, sender, send_param, fee, refund_address)
488
+ Self::__send(env, from, send_param, fee, refund_address)
212
489
  }
213
490
  }
214
491
 
215
492
  // ===========================================================================
216
- // Initialization
493
+ // LzReceive Handler (called by OAppReceiver)
217
494
  // ===========================================================================
218
495
 
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)
496
+ /// Implements `LzReceiveInternal` for an OFT contract using the default OFT receive logic.
231
497
  ///
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.
498
+ /// This macro generates the boilerplate `LzReceiveInternal` implementation that delegates
499
+ /// to `OFTInternal::__receive`, which handles decoding the OFT message, crediting
500
+ /// tokens to the recipient, and optionally queuing compose messages.
258
501
  ///
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)
502
+ /// # Usage
263
503
  ///
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
504
+ /// ```ignore
505
+ /// use oft_core::impl_oft_lz_receive;
346
506
  ///
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.
507
+ /// #[oapp]
508
+ /// pub struct MyOFT;
398
509
  ///
399
- /// Credits tokens to the recipient and optionally queues a compose message.
510
+ /// impl OFTInternal for MyOFT {
511
+ /// // ... implement __debit and __credit ...
512
+ /// }
400
513
  ///
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(),
514
+ /// // Instead of manually implementing LzReceiveInternal:
515
+ /// impl_oft_lz_receive!(MyOFT);
516
+ /// ```
517
+ #[macro_export]
518
+ macro_rules! impl_oft_lz_receive {
519
+ ($contract:ty) => {
520
+ impl oapp::oapp_receiver::LzReceiveInternal for $contract {
521
+ fn __lz_receive(
522
+ env: &soroban_sdk::Env,
523
+ origin: &endpoint_v2::Origin,
524
+ guid: &soroban_sdk::BytesN<32>,
525
+ message: &soroban_sdk::Bytes,
526
+ extra_data: &soroban_sdk::Bytes,
527
+ executor: &soroban_sdk::Address,
528
+ value: i128,
529
+ ) {
530
+ <Self as oft_core::OFTInternal>::__receive(env, origin, guid, message, extra_data, executor, value)
531
+ }
431
532
  }
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);
533
+ };
439
534
  }