@layerzerolabs/protocol-stellar-v2 0.2.8 → 0.2.10

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 (239) hide show
  1. package/.turbo/turbo-build.log +443 -302
  2. package/.turbo/turbo-lint.log +118 -96
  3. package/.turbo/turbo-test.log +853 -731
  4. package/Cargo.lock +120 -37
  5. package/Cargo.toml +8 -5
  6. package/contracts/common-macros/src/contract_impl.rs +44 -0
  7. package/contracts/common-macros/src/lib.rs +86 -40
  8. package/contracts/common-macros/src/ownable.rs +24 -32
  9. package/contracts/common-macros/src/storage.rs +95 -120
  10. package/contracts/common-macros/src/tests/contract_impl.rs +289 -0
  11. package/contracts/common-macros/src/tests/mod.rs +9 -0
  12. package/contracts/common-macros/src/tests/ownable.rs +151 -0
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap +85 -0
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +30 -0
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap +9 -0
  16. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__storage__snapshot_generated_storage_code.snap +1072 -0
  17. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +45 -0
  18. package/contracts/common-macros/src/tests/storage.rs +485 -0
  19. package/contracts/common-macros/src/tests/test_helpers.rs +93 -0
  20. package/contracts/common-macros/src/tests/ttl_configurable.rs +34 -0
  21. package/contracts/common-macros/src/ttl_configurable.rs +31 -14
  22. package/contracts/common-macros/src/utils.rs +27 -0
  23. package/contracts/endpoint-v2/ARCHITECTURE.md +4 -4
  24. package/contracts/endpoint-v2/src/endpoint_v2.rs +18 -15
  25. package/contracts/endpoint-v2/src/interfaces/message_lib.rs +2 -3
  26. package/contracts/endpoint-v2/src/interfaces/message_lib_manager.rs +5 -3
  27. package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +2 -2
  28. package/contracts/endpoint-v2/src/interfaces/messaging_composer.rs +2 -2
  29. package/contracts/endpoint-v2/src/interfaces/send_lib.rs +4 -4
  30. package/contracts/endpoint-v2/src/lib.rs +6 -5
  31. package/contracts/endpoint-v2/src/message_lib_manager.rs +14 -6
  32. package/contracts/endpoint-v2/src/messaging_channel.rs +6 -2
  33. package/contracts/endpoint-v2/src/messaging_composer.rs +6 -2
  34. package/contracts/endpoint-v2/src/storage.rs +10 -7
  35. package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +16 -16
  36. package/contracts/endpoint-v2/src/tests/endpoint_v2/ttl_config.rs +46 -46
  37. package/contracts/endpoint-v2/src/tests/mock.rs +2 -2
  38. package/contracts/endpoint-v2/src/util.rs +8 -2
  39. package/contracts/message-libs/block-message-lib/Cargo.toml +1 -0
  40. package/contracts/message-libs/block-message-lib/src/lib.rs +5 -5
  41. package/contracts/message-libs/message-lib-common/src/errors.rs +8 -8
  42. package/contracts/message-libs/message-lib-common/src/interfaces/dvn.rs +0 -1
  43. package/contracts/message-libs/message-lib-common/src/interfaces/mod.rs +3 -3
  44. package/contracts/message-libs/message-lib-common/src/lib.rs +0 -2
  45. package/contracts/message-libs/message-lib-common/src/packet_codec_v1.rs +4 -6
  46. package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1.rs +2 -2
  47. package/contracts/message-libs/message-lib-common/src/tests/worker_options.rs +11 -11
  48. package/contracts/message-libs/message-lib-common/src/worker_options.rs +10 -16
  49. package/contracts/message-libs/simple-message-lib/src/errors.rs +0 -4
  50. package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +49 -34
  51. package/contracts/message-libs/simple-message-lib/src/storage.rs +3 -7
  52. package/contracts/message-libs/simple-message-lib/src/test.rs +3 -3
  53. package/contracts/message-libs/treasury/src/storage.rs +1 -2
  54. package/contracts/message-libs/treasury/src/tests/setup.rs +3 -2
  55. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +0 -13
  56. package/contracts/message-libs/treasury/src/treasury.rs +18 -21
  57. package/contracts/message-libs/uln-302/Cargo.toml +1 -0
  58. package/contracts/message-libs/uln-302/src/interfaces/mod.rs +4 -4
  59. package/contracts/message-libs/uln-302/src/interfaces/{receive.rs → receive_uln.rs} +3 -3
  60. package/contracts/message-libs/uln-302/src/interfaces/{send.rs → send_uln.rs} +8 -80
  61. package/contracts/message-libs/uln-302/src/lib.rs +5 -4
  62. package/contracts/message-libs/uln-302/src/{receive.rs → receive_uln.rs} +20 -12
  63. package/contracts/message-libs/uln-302/src/{send.rs → send_uln.rs} +19 -13
  64. package/contracts/message-libs/uln-302/src/storage.rs +1 -2
  65. package/contracts/message-libs/uln-302/src/tests/config/uln_config.rs +3 -2
  66. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +30 -30
  67. package/contracts/message-libs/uln-302/src/tests/setup.rs +12 -11
  68. package/contracts/message-libs/uln-302/src/tests/uln302/set_config.rs +1 -1
  69. package/contracts/message-libs/uln-302/src/{config_validation.rs → types.rs} +79 -11
  70. package/contracts/message-libs/uln-302/src/uln302.rs +15 -10
  71. package/contracts/oapp-macros/Cargo.toml +2 -8
  72. package/contracts/oapp-macros/src/lib.rs +57 -311
  73. package/contracts/oapp-macros/src/oapp_core.rs +23 -32
  74. package/contracts/oapp-macros/src/oapp_full.rs +8 -2
  75. package/contracts/oapp-macros/src/oapp_options_type3.rs +21 -36
  76. package/contracts/oapp-macros/src/oapp_receiver.rs +38 -57
  77. package/contracts/oapp-macros/src/oapp_sender.rs +12 -14
  78. package/contracts/oapp-macros/src/util.rs +14 -10
  79. package/contracts/oapps/counter/Cargo.toml +2 -1
  80. package/contracts/oapps/counter/integration_tests/utils.rs +4 -4
  81. package/contracts/oapps/counter/src/codec.rs +8 -9
  82. package/contracts/oapps/counter/src/counter.rs +156 -147
  83. package/contracts/oapps/counter/src/storage.rs +1 -2
  84. package/contracts/oapps/counter/src/tests/test_codec.rs +5 -5
  85. package/contracts/oapps/counter/src/tests/test_counter.rs +11 -13
  86. package/contracts/oapps/oapp/Cargo.toml +1 -0
  87. package/contracts/oapps/oapp/src/errors.rs +1 -1
  88. package/contracts/oapps/oapp/src/lib.rs +3 -0
  89. package/contracts/oapps/oapp/src/macro_tests/mod.rs +1 -0
  90. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +312 -0
  91. package/contracts/oapps/oapp/src/oapp_core.rs +52 -53
  92. package/contracts/oapps/oapp/src/oapp_options_type3.rs +18 -28
  93. package/contracts/oapps/oapp/src/oapp_receiver.rs +82 -31
  94. package/contracts/oapps/oapp/src/oapp_sender.rs +55 -13
  95. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +16 -3
  96. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +33 -8
  97. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +6 -9
  98. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +28 -15
  99. package/contracts/oapps/oft/Cargo.toml +27 -0
  100. package/contracts/oapps/oft/integration-tests/mod.rs +3 -0
  101. package/contracts/oapps/oft/integration-tests/setup.rs +320 -0
  102. package/contracts/oapps/oft/integration-tests/test_with_sml.rs +155 -0
  103. package/contracts/oapps/oft/integration-tests/utils.rs +201 -0
  104. package/contracts/oapps/oft/src/codec/mod.rs +2 -0
  105. package/contracts/oapps/oft/src/codec/oft_compose_msg_codec.rs +55 -0
  106. package/contracts/oapps/oft/src/codec/oft_msg_codec.rs +62 -0
  107. package/contracts/oapps/oft/src/constants.rs +5 -0
  108. package/contracts/oapps/oft/src/errors.rs +8 -0
  109. package/contracts/oapps/oft/src/events.rs +19 -0
  110. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +23 -0
  111. package/contracts/oapps/oft/src/interfaces/mod.rs +3 -0
  112. package/contracts/oapps/oft/src/lib.rs +22 -0
  113. package/contracts/oapps/oft/src/macro_tests/mod.rs +2 -0
  114. package/contracts/oapps/oft/src/macro_tests/test_all_default.rs +41 -0
  115. package/contracts/oapps/oft/src/macro_tests/test_override.rs +83 -0
  116. package/contracts/oapps/oft/src/oft.rs +320 -0
  117. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +50 -0
  118. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +50 -0
  119. package/contracts/oapps/oft/src/oft_types/mod.rs +10 -0
  120. package/contracts/oapps/oft/src/storage.rs +11 -0
  121. package/contracts/oapps/oft/src/tests/mod.rs +13 -0
  122. package/contracts/oapps/oft/src/tests/test_decimals.rs +89 -0
  123. package/contracts/oapps/oft/src/tests/test_lz_receive.rs +282 -0
  124. package/contracts/oapps/oft/src/tests/test_oft_compose_msg_codec.rs +68 -0
  125. package/contracts/oapps/oft/src/tests/test_oft_msg_codec.rs +136 -0
  126. package/contracts/oapps/oft/src/tests/test_oft_version.rs +13 -0
  127. package/contracts/oapps/oft/src/tests/test_quote_oft.rs +159 -0
  128. package/contracts/oapps/oft/src/tests/test_quote_send.rs +195 -0
  129. package/contracts/oapps/oft/src/tests/test_resolve_address.rs +37 -0
  130. package/contracts/oapps/oft/src/tests/test_send.rs +915 -0
  131. package/contracts/oapps/oft/src/tests/test_token.rs +47 -0
  132. package/contracts/oapps/oft/src/tests/test_utils.rs +789 -0
  133. package/contracts/oapps/oft/src/types.rs +38 -0
  134. package/contracts/oapps/oft/src/utils.rs +67 -0
  135. package/contracts/oapps/oft-mint-burn/Cargo.toml +26 -0
  136. package/contracts/oapps/oft-mint-burn/src/lib.rs +3 -0
  137. package/contracts/oapps/oft-mint-burn/src/oft.rs +28 -0
  138. package/contracts/oapps/oft-mint-burn/src/tests/mod.rs +1 -0
  139. package/contracts/utils/src/buffer_reader.rs +8 -9
  140. package/contracts/utils/src/buffer_writer.rs +11 -5
  141. package/contracts/utils/src/errors.rs +5 -5
  142. package/contracts/utils/src/ownable.rs +14 -6
  143. package/contracts/utils/src/testing_utils.rs +11 -1
  144. package/contracts/utils/src/tests/buffer_reader.rs +491 -730
  145. package/contracts/utils/src/tests/buffer_writer.rs +336 -148
  146. package/contracts/utils/src/tests/bytes_ext.rs +125 -40
  147. package/contracts/utils/src/tests/mod.rs +3 -0
  148. package/contracts/utils/src/tests/ownable.rs +379 -27
  149. package/contracts/utils/src/tests/test_helper.rs +47 -0
  150. package/contracts/utils/src/tests/testing_utils.rs +555 -0
  151. package/contracts/utils/src/tests/ttl.rs +421 -0
  152. package/contracts/utils/src/ttl.rs +29 -89
  153. package/contracts/workers/dvn/Cargo.toml +31 -0
  154. package/contracts/workers/dvn/src/auth.rs +66 -0
  155. package/contracts/workers/dvn/src/dvn.rs +143 -0
  156. package/contracts/workers/dvn/src/errors.rs +21 -0
  157. package/contracts/workers/dvn/src/events.rs +19 -0
  158. package/contracts/workers/dvn/src/interfaces/dvn.rs +12 -0
  159. package/contracts/workers/dvn/src/interfaces/mod.rs +5 -0
  160. package/contracts/workers/dvn/src/interfaces/multisig.rs +15 -0
  161. package/contracts/workers/dvn/src/lib.rs +24 -0
  162. package/contracts/workers/dvn/src/multisig.rs +127 -0
  163. package/contracts/workers/dvn/src/storage.rs +35 -0
  164. package/contracts/workers/dvn/src/tests/auth.rs +237 -0
  165. package/contracts/workers/dvn/src/tests/dvn.rs +349 -0
  166. package/contracts/workers/dvn/src/tests/key_pair.rs +66 -0
  167. package/contracts/workers/dvn/src/tests/mod.rs +5 -0
  168. package/contracts/workers/dvn/src/tests/multisig/mod.rs +3 -0
  169. package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +133 -0
  170. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +108 -0
  171. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +109 -0
  172. package/contracts/workers/dvn/src/tests/setup.rs +109 -0
  173. package/contracts/workers/dvn/src/types.rs +26 -0
  174. package/contracts/workers/dvn-fee-lib/Cargo.toml +24 -0
  175. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +113 -0
  176. package/contracts/workers/dvn-fee-lib/src/errors.rs +8 -0
  177. package/contracts/workers/dvn-fee-lib/src/lib.rs +17 -0
  178. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +282 -0
  179. package/contracts/workers/dvn-fee-lib/src/tests/mod.rs +1 -0
  180. package/contracts/workers/executor/Cargo.toml +10 -7
  181. package/contracts/workers/executor/src/errors.rs +8 -0
  182. package/contracts/workers/executor/src/events.rs +4 -7
  183. package/contracts/workers/executor/src/interfaces/executor.rs +72 -22
  184. package/contracts/workers/executor/src/interfaces/mod.rs +0 -2
  185. package/contracts/workers/executor/src/lib.rs +16 -7
  186. package/contracts/workers/executor/src/lz_executor.rs +308 -0
  187. package/contracts/workers/executor/src/storage.rs +24 -16
  188. package/contracts/workers/executor-fee-lib/Cargo.toml +22 -0
  189. package/contracts/workers/executor-fee-lib/src/errors.rs +15 -0
  190. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +215 -0
  191. package/contracts/workers/executor-fee-lib/src/executor_option.rs +203 -0
  192. package/contracts/workers/executor-fee-lib/src/lib.rs +7 -0
  193. package/contracts/workers/executor-helper/Cargo.toml +29 -0
  194. package/contracts/workers/executor-helper/src/executor_helper.rs +161 -0
  195. package/contracts/workers/executor-helper/src/lib.rs +11 -0
  196. package/contracts/workers/{worker-common → worker}/Cargo.toml +1 -4
  197. package/contracts/workers/worker/src/errors.rs +24 -0
  198. package/contracts/workers/worker/src/events.rs +62 -0
  199. package/contracts/workers/worker/src/interfaces/dvn_fee_lib.rs +75 -0
  200. package/contracts/workers/worker/src/interfaces/executor_fee_lib.rs +84 -0
  201. package/contracts/workers/{worker-common → worker}/src/interfaces/mod.rs +2 -2
  202. package/contracts/workers/worker/src/interfaces/price_feed.rs +85 -0
  203. package/contracts/workers/worker/src/lib.rs +14 -0
  204. package/contracts/workers/worker/src/storage.rs +63 -0
  205. package/contracts/workers/worker/src/worker.rs +459 -0
  206. package/package.json +3 -3
  207. package/sdk/dist/generated/bml.d.ts +88 -17
  208. package/sdk/dist/generated/bml.js +62 -16
  209. package/sdk/dist/generated/counter.d.ts +281 -102
  210. package/sdk/dist/generated/counter.js +93 -41
  211. package/sdk/dist/generated/endpoint.d.ts +128 -105
  212. package/sdk/dist/generated/endpoint.js +47 -45
  213. package/sdk/dist/generated/sml.d.ts +212 -69
  214. package/sdk/dist/generated/sml.js +103 -53
  215. package/sdk/dist/generated/uln302.d.ts +270 -173
  216. package/sdk/dist/generated/uln302.js +112 -64
  217. package/sdk/package.json +11 -11
  218. package/sdk/test/index.test.ts +147 -42
  219. package/sdk/test/suites/constants.ts +7 -3
  220. package/sdk/test/suites/deploy.ts +65 -42
  221. package/sdk/test/suites/localnet.ts +2 -2
  222. package/sdk/test/suites/scan.ts +28 -25
  223. package/sdk/test/utils.ts +199 -0
  224. package/sdk/tsconfig.json +93 -95
  225. package/tools/ts-bindings-gen/src/main.rs +2 -0
  226. package/contracts/common-macros/src/snapshots/common_macros__tests__tests__snapshot_generated_storage_code.snap +0 -310
  227. package/contracts/common-macros/src/tests.rs +0 -287
  228. package/contracts/oapp-macros/tests/test_macros.rs +0 -522
  229. package/contracts/workers/executor/src/executor.rs +0 -347
  230. package/contracts/workers/executor/src/interfaces/types.rs +0 -51
  231. package/contracts/workers/worker-common/src/constants.rs +0 -17
  232. package/contracts/workers/worker-common/src/errors.rs +0 -6
  233. package/contracts/workers/worker-common/src/events.rs +0 -34
  234. package/contracts/workers/worker-common/src/interfaces/executor_fee_lib.rs +0 -35
  235. package/contracts/workers/worker-common/src/interfaces/price_feed.rs +0 -40
  236. package/contracts/workers/worker-common/src/interfaces/worker.rs +0 -60
  237. package/contracts/workers/worker-common/src/lib.rs +0 -19
  238. package/contracts/workers/worker-common/src/storage.rs +0 -32
  239. package/contracts/workers/worker-common/src/worker_common.rs +0 -166
@@ -0,0 +1,789 @@
1
+ //! Shared test utilities for OFT unit tests.
2
+ //!
3
+ //! This module provides common test contracts and helpers used across multiple test files.
4
+
5
+ extern crate self as oft;
6
+
7
+ use crate::codec::oft_msg_codec::OFTMessage;
8
+ use crate::oft::{oft_initialize, OFTClient, OFTInner, OFT};
9
+ use crate::types::{OFTReceipt, SendParam};
10
+ use endpoint_v2::{LayerZeroReceiverClient, MessagingFee, MessagingParams, MessagingReceipt, Origin};
11
+ use oapp::oapp_core::OAppCoreClient;
12
+ use oapp::oapp_receiver::OAppReceiver;
13
+ use oapp_macros::oapp_manual_impl;
14
+ use soroban_sdk::{address_payload::AddressPayload, log, String};
15
+ use soroban_sdk::{
16
+ bytes, contract, contractimpl, symbol_short,
17
+ testutils::{Address as _, MockAuth, MockAuthInvoke},
18
+ token::{StellarAssetClient, TokenClient},
19
+ Address, Bytes, BytesN, Env, IntoVal, Symbol,
20
+ };
21
+ use stellar_macros::default_impl;
22
+ use stellar_tokens::fungible::{Base, FungibleToken};
23
+
24
+ // ==================== Constants ====================
25
+
26
+ /// Default shared decimals used for cross-chain normalization in tests
27
+ pub const DEFAULT_SHARED_DECIMALS: u32 = 6;
28
+
29
+ // ==================== Helper Functions ====================
30
+
31
+ /// Create a SendParam for testing with default options.
32
+ pub fn create_send_param(env: &Env, dst_eid: u32, amount_ld: i128, min_amount_ld: i128) -> SendParam {
33
+ SendParam {
34
+ dst_eid,
35
+ to: BytesN::from_array(env, &[1u8; 32]),
36
+ amount_ld,
37
+ min_amount_ld,
38
+ extra_options: bytes!(env),
39
+ compose_msg: bytes!(env),
40
+ oft_cmd: bytes!(env),
41
+ }
42
+ }
43
+
44
+ /// Creates a valid recipient address by deploying a dummy contract.
45
+ /// Use this in tests when the address needs to pass the `.exists()` check.
46
+ pub fn create_recipient_address(env: &Env) -> Address {
47
+ env.register(DummyRecipient, ())
48
+ }
49
+
50
+ /// Creates a G-address (account address) from a 32-byte Ed25519 public key.
51
+ /// This is useful for testing with account addresses instead of contract addresses.
52
+ pub fn create_g_address(env: &Env, public_key: &BytesN<32>) -> Address {
53
+ Address::from_payload(env, AddressPayload::AccountIdPublicKeyEd25519(public_key.clone()))
54
+ }
55
+
56
+ /// Generates a unique G-address (account address) for testing.
57
+ /// Each call generates a different address by using a counter-based approach.
58
+ pub fn generate_g_address(env: &Env) -> Address {
59
+ // Use Address::generate which creates a unique address each time
60
+ // Then convert it to ensure it's a G-address (account address)
61
+ let addr = Address::generate(env);
62
+ // Extract the payload - if it's already a G-address, use it; otherwise convert
63
+ match addr.to_payload() {
64
+ Some(AddressPayload::AccountIdPublicKeyEd25519(_pk)) => {
65
+ // Already a G-address, return as-is
66
+ addr
67
+ }
68
+ Some(AddressPayload::ContractIdHash(hash)) => {
69
+ // It's a contract address, convert hash to G-address
70
+ // Use the hash bytes as the Ed25519 public key
71
+ create_g_address(env, &hash)
72
+ }
73
+ None => {
74
+ // Fallback: create from hash bytes
75
+ let hash = BytesN::from_array(env, &[0u8; 32]);
76
+ create_g_address(env, &hash)
77
+ }
78
+ }
79
+ }
80
+
81
+ pub fn encode_oft_message(env: &Env, send_to: &BytesN<32>, amount_sd: u64) -> Bytes {
82
+ let msg = OFTMessage { send_to: send_to.clone(), amount_sd, compose_from: None, compose_msg: None };
83
+ msg.encode(env).0
84
+ }
85
+
86
+ pub fn encode_oft_message_with_compose(
87
+ env: &Env,
88
+ send_to: &BytesN<32>,
89
+ amount_sd: u64,
90
+ compose_from: &BytesN<32>,
91
+ compose_msg: &Bytes,
92
+ ) -> Bytes {
93
+ let msg = OFTMessage {
94
+ send_to: send_to.clone(),
95
+ amount_sd,
96
+ compose_from: Some(compose_from.clone()),
97
+ compose_msg: Some(compose_msg.clone()),
98
+ };
99
+ msg.encode(env).0
100
+ }
101
+
102
+ pub fn create_origin(src_eid: u32, sender: &BytesN<32>, nonce: u64) -> Origin {
103
+ Origin { src_eid, sender: sender.clone(), nonce }
104
+ }
105
+
106
+ // ==================== Test OFT Contract ====================
107
+
108
+ #[oapp_macros::oapp]
109
+ pub struct TestMintBurnOFT;
110
+
111
+ #[contractimpl]
112
+ impl TestMintBurnOFT {
113
+ pub fn __constructor(env: &Env, token: &Address, owner: &Address, endpoint: &Address, delegate: &Option<Address>) {
114
+ oft_initialize::<Self>(env, owner, token, endpoint, delegate)
115
+ }
116
+ }
117
+
118
+ #[contractimpl(contracttrait)]
119
+ impl OFT for TestMintBurnOFT {}
120
+
121
+ impl OFTInner for TestMintBurnOFT {
122
+ fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
123
+ crate::oft_types::mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
124
+ }
125
+
126
+ fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
127
+ crate::oft_types::mint_burn::credit::<Self>(env, to, amount_ld, src_eid)
128
+ }
129
+ }
130
+
131
+ #[oapp_macros::oapp]
132
+ #[oapp_manual_impl(receiver)]
133
+ pub struct TestLockUnlockOFT;
134
+
135
+ #[contractimpl]
136
+ impl TestLockUnlockOFT {
137
+ pub fn __constructor(env: &Env, token: &Address, owner: &Address, endpoint: &Address, delegate: &Option<Address>) {
138
+ oft_initialize::<Self>(env, owner, token, endpoint, delegate)
139
+ }
140
+ }
141
+
142
+ #[contractimpl(contracttrait)]
143
+ impl OFT for TestLockUnlockOFT {}
144
+
145
+ #[contractimpl(contracttrait)]
146
+ impl OAppReceiver for TestLockUnlockOFT {
147
+ fn lz_receive(
148
+ env: &Env,
149
+ executor: &Address,
150
+ origin: &Origin,
151
+ guid: &BytesN<32>,
152
+ message: &Bytes,
153
+ extra_data: &Bytes,
154
+ value: i128,
155
+ ) {
156
+ oapp::oapp_receiver::verify_and_clear_payload::<Self>(env, executor, origin, guid, message, value);
157
+ Self::__lz_receive(env, executor, origin, guid, message, extra_data, value);
158
+ }
159
+ }
160
+
161
+ impl OFTInner for TestLockUnlockOFT {
162
+ fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
163
+ crate::oft_types::lock_unlock::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
164
+ }
165
+
166
+ fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
167
+ crate::oft_types::lock_unlock::credit::<Self>(env, to, amount_ld, src_eid)
168
+ }
169
+ }
170
+
171
+ // ==================== Dummy Contracts ====================
172
+
173
+ /// Dummy recipient contract for testing - used to create valid contract addresses
174
+ #[contract]
175
+ pub struct DummyRecipient;
176
+
177
+ #[contractimpl]
178
+ impl DummyRecipient {
179
+ pub fn __constructor(_env: &Env) {}
180
+ }
181
+
182
+ #[contract]
183
+ pub struct DummyToken;
184
+
185
+ #[contractimpl]
186
+ impl DummyToken {
187
+ fn admin(env: &Env) -> Address {
188
+ env.storage().instance().get(&symbol_short!("admin")).unwrap()
189
+ }
190
+
191
+ pub fn __constructor(env: &Env, owner: Address, decimals: u32) {
192
+ Base::set_metadata(env, decimals, String::from_str(env, "DummyToken"), String::from_str(env, "DUMMY"));
193
+ env.storage().instance().set(&symbol_short!("admin"), &owner);
194
+ }
195
+
196
+ // keep the same behavior as SAC that requires admin's authorization
197
+ pub fn set_admin(env: &Env, admin: &Address) {
198
+ Self::admin(env).require_auth();
199
+ env.storage().instance().set(&symbol_short!("admin"), &admin);
200
+ }
201
+
202
+ pub fn mint(env: &Env, to: &Address, amount: i128) {
203
+ Self::admin(env).require_auth();
204
+ Base::mint(env, &to, amount);
205
+ log!(&env, "minted {} to {}", amount, to);
206
+ }
207
+
208
+ // keep the same behavior as SAC that requires from's authorization
209
+ pub fn burn(env: &Env, from: &Address, amount: i128) {
210
+ Base::burn(env, &from, amount);
211
+ log!(&env, "burned {} from {}", amount, from);
212
+ }
213
+ }
214
+
215
+ #[default_impl]
216
+ #[contractimpl]
217
+ impl FungibleToken for DummyToken {
218
+ type ContractType = Base;
219
+ }
220
+
221
+ // ==================== Mock Endpoint ====================
222
+
223
+ /// A comprehensive mock endpoint contract for testing OFT functionality.
224
+ /// Supports: quote, set_delegate, clear, send_compose, and compose verification.
225
+ #[contract]
226
+ pub struct MockEndpointWithCompose;
227
+
228
+ #[contractimpl]
229
+ impl MockEndpointWithCompose {
230
+ pub fn __constructor(env: Env, native_fee: i128, zro_fee: i128, native_token: Address, zro_token: Address) {
231
+ env.storage().instance().set(&symbol_short!("ntv_fee"), &native_fee);
232
+ env.storage().instance().set(&symbol_short!("zro_fee"), &zro_fee);
233
+ env.storage().instance().set(&symbol_short!("ntk"), &native_token);
234
+ env.storage().instance().set(&symbol_short!("zro"), &zro_token);
235
+ }
236
+
237
+ /// Returns the native token address (required by OAppSender)
238
+ pub fn native_token(env: Env) -> Address {
239
+ env.storage().instance().get(&symbol_short!("ntk")).unwrap()
240
+ }
241
+
242
+ /// Returns the ZRO token address (required by OAppSender)
243
+ pub fn zro(env: Env) -> Option<Address> {
244
+ env.storage().instance().get(&symbol_short!("zro"))
245
+ }
246
+
247
+ /// Required by OApp initialization to set delegate
248
+ pub fn set_delegate(_env: Env, _oapp: Address, _delegate: Option<Address>) {
249
+ // No-op for testing
250
+ }
251
+
252
+ /// Required by OAppReceiver.lz_receive to clear the payload
253
+ pub fn clear(_env: Env, _oapp: Address, _origin: Origin, _receiver: Address, _guid: BytesN<32>, _message: Bytes) {
254
+ // No-op for testing
255
+ }
256
+
257
+ /// Required by quote_send to get messaging fees
258
+ pub fn quote(env: Env, _sender: Address, params: MessagingParams) -> MessagingFee {
259
+ let native_fee: i128 = env.storage().instance().get(&symbol_short!("ntv_fee")).unwrap_or(1000);
260
+ let zro_fee: i128 =
261
+ if params.pay_in_zro { env.storage().instance().get(&symbol_short!("zro_fee")).unwrap_or(500) } else { 0 };
262
+ MessagingFee { native_fee, zro_fee }
263
+ }
264
+
265
+ /// Required by send to send cross-chain messages
266
+ pub fn send(env: Env, _sender: Address, params: MessagingParams, _refund_address: Address) -> MessagingReceipt {
267
+ // Increment nonce for each send
268
+ let nonce: u64 = env.storage().instance().get(&symbol_short!("nonce")).unwrap_or(0) + 1;
269
+ env.storage().instance().set(&symbol_short!("nonce"), &nonce);
270
+
271
+ // Store send details for verification
272
+ env.storage().instance().set(&symbol_short!("sent"), &true);
273
+ env.storage().instance().set(&Symbol::new(&env, "last_dst_eid"), &params.dst_eid);
274
+ env.storage().instance().set(&Symbol::new(&env, "last_msg"), &params.message);
275
+
276
+ let native_fee: i128 = env.storage().instance().get(&symbol_short!("ntv_fee")).unwrap_or(1000);
277
+ let zro_fee: i128 =
278
+ if params.pay_in_zro { env.storage().instance().get(&symbol_short!("zro_fee")).unwrap_or(500) } else { 0 };
279
+
280
+ MessagingReceipt {
281
+ guid: BytesN::from_array(&env, &[nonce as u8; 32]),
282
+ nonce,
283
+ fee: MessagingFee { native_fee, zro_fee },
284
+ }
285
+ }
286
+
287
+ /// Helper to check if send was called
288
+ pub fn was_sent(env: Env) -> bool {
289
+ env.storage().instance().get(&symbol_short!("sent")).unwrap_or(false)
290
+ }
291
+
292
+ /// Get the last destination EID that was sent to
293
+ pub fn get_last_dst_eid(env: Env) -> Option<u32> {
294
+ env.storage().instance().get(&Symbol::new(&env, "last_dst_eid"))
295
+ }
296
+
297
+ /// Get the current nonce
298
+ pub fn get_nonce(env: Env) -> u64 {
299
+ env.storage().instance().get(&symbol_short!("nonce")).unwrap_or(0)
300
+ }
301
+
302
+ /// Implements the send_compose method from MessagingComposer
303
+ pub fn send_compose(env: Env, from: Address, to: Address, guid: BytesN<32>, index: u32, message: Bytes) {
304
+ env.storage().instance().set(&symbol_short!("composed"), &true);
305
+ env.storage().instance().set(&Symbol::new(&env, "compose_from"), &from);
306
+ env.storage().instance().set(&Symbol::new(&env, "compose_to"), &to);
307
+ env.storage().instance().set(&Symbol::new(&env, "compose_guid"), &guid);
308
+ env.storage().instance().set(&Symbol::new(&env, "compose_idx"), &index);
309
+ env.storage().instance().set(&Symbol::new(&env, "compose_msg"), &message);
310
+ }
311
+
312
+ /// Helper to check if compose was called
313
+ pub fn was_composed(env: Env) -> bool {
314
+ env.storage().instance().get(&symbol_short!("composed")).unwrap_or(false)
315
+ }
316
+
317
+ pub fn get_compose_to(env: Env) -> Option<Address> {
318
+ env.storage().instance().get(&Symbol::new(&env, "compose_to"))
319
+ }
320
+
321
+ #[allow(dead_code)]
322
+ pub fn get_compose_msg(env: Env) -> Option<Bytes> {
323
+ env.storage().instance().get(&Symbol::new(&env, "compose_msg"))
324
+ }
325
+ }
326
+
327
+ // ==================== Test Setup ====================
328
+
329
+ /// Default fees for mock endpoint
330
+ pub const DEFAULT_NATIVE_FEE: i128 = 1000;
331
+ pub const DEFAULT_ZRO_FEE: i128 = 500;
332
+ /// Large amount for pre-minting tokens during setup
333
+ pub const INITIAL_MINT_AMOUNT: i128 = 1_000_000_000_000_000_000;
334
+
335
+ /// OFT strategy type for test setup
336
+ #[derive(Clone, Copy, PartialEq, Eq, Default)]
337
+ pub enum OFTType {
338
+ #[default]
339
+ MintBurn,
340
+ LockUnlock,
341
+ }
342
+
343
+ /// Token type for test setup
344
+ #[derive(Clone, Copy, PartialEq, Eq, Default)]
345
+ pub enum TokenType {
346
+ SAC, // Stellar Asset Contract (native, 7 decimals)
347
+ #[default]
348
+ ContractToken, // Custom contract token (configurable decimals)
349
+ }
350
+
351
+ pub struct OFTTestSetup<'a> {
352
+ pub env: &'a Env,
353
+ pub oft: OFTClient<'a>,
354
+ pub endpoint_client: MockEndpointWithComposeClient<'a>,
355
+ pub token: Address,
356
+ pub token_client: TokenClient<'a>,
357
+ pub native_token: Address,
358
+ pub zro_token: Address,
359
+ pub owner: Address,
360
+ pub native_fee: i128,
361
+ pub zro_fee: i128,
362
+ pub oft_type: OFTType,
363
+ pub token_decimals: u32,
364
+ pub shared_decimals: u32,
365
+ pub issuer: Address,
366
+ }
367
+
368
+ /// Builder for OFTTestSetup
369
+ pub struct OFTTestSetupBuilder<'a> {
370
+ env: &'a Env,
371
+ native_fee: i128,
372
+ zro_fee: i128,
373
+ oft_type: OFTType,
374
+ token_type: TokenType,
375
+ token_decimals: u32,
376
+ shared_decimals: u32,
377
+ }
378
+
379
+ impl<'a> OFTTestSetupBuilder<'a> {
380
+ pub fn new(env: &'a Env) -> Self {
381
+ Self {
382
+ env,
383
+ native_fee: DEFAULT_NATIVE_FEE,
384
+ zro_fee: DEFAULT_ZRO_FEE,
385
+ oft_type: OFTType::default(),
386
+ token_type: TokenType::default(),
387
+ token_decimals: 7,
388
+ shared_decimals: DEFAULT_SHARED_DECIMALS,
389
+ }
390
+ }
391
+
392
+ pub fn with_token_decimals(mut self, decimals: u32) -> Self {
393
+ self.token_decimals = decimals;
394
+ // Automatically use ContractToken if custom decimals are requested (SAC is fixed at 7)
395
+ if decimals != 7 {
396
+ self.token_type = TokenType::ContractToken;
397
+ }
398
+ self
399
+ }
400
+
401
+ pub fn with_shared_decimals(mut self, decimals: u32) -> Self {
402
+ self.shared_decimals = decimals;
403
+ self
404
+ }
405
+
406
+ pub fn with_fees(mut self, native_fee: i128, zro_fee: i128) -> Self {
407
+ self.native_fee = native_fee;
408
+ self.zro_fee = zro_fee;
409
+ self
410
+ }
411
+
412
+ pub fn with_native_fee(mut self, native_fee: i128) -> Self {
413
+ self.native_fee = native_fee;
414
+ self
415
+ }
416
+
417
+ pub fn with_zro_fee(mut self, zro_fee: i128) -> Self {
418
+ self.zro_fee = zro_fee;
419
+ self
420
+ }
421
+
422
+ pub fn mint_burn(mut self) -> Self {
423
+ self.oft_type = OFTType::MintBurn;
424
+ self
425
+ }
426
+
427
+ pub fn lock_unlock(mut self) -> Self {
428
+ self.oft_type = OFTType::LockUnlock;
429
+ self
430
+ }
431
+
432
+ pub fn with_sac(mut self) -> Self {
433
+ self.token_type = TokenType::SAC;
434
+ self.token_decimals = 7; // SAC has fixed 7 decimals
435
+ self
436
+ }
437
+
438
+ pub fn with_contract_token(mut self) -> Self {
439
+ self.token_type = TokenType::ContractToken;
440
+ self
441
+ }
442
+
443
+ pub fn build(self) -> OFTTestSetup<'a> {
444
+ let env = self.env;
445
+ let native_fee = self.native_fee;
446
+ let zro_fee = self.zro_fee;
447
+ let oft_type = self.oft_type;
448
+
449
+ let owner = create_recipient_address(env);
450
+
451
+ // Create native token for fees
452
+ let native_sac = env.register_stellar_asset_contract_v2(owner.clone());
453
+ let native_token = native_sac.address();
454
+
455
+ // Create ZRO token
456
+ let zro_sac = env.register_stellar_asset_contract_v2(owner.clone());
457
+ let zro_token = zro_sac.address();
458
+
459
+ // Create OFT token based on token_type
460
+ let (token, actual_token_decimals, issuer) = match self.token_type {
461
+ TokenType::SAC => {
462
+ let sac = env.register_stellar_asset_contract_v2(owner.clone());
463
+ (sac.address(), 7u32, sac.issuer().address()) // SAC has fixed 7 decimals
464
+ }
465
+ TokenType::ContractToken => {
466
+ let token = env.register(DummyToken, (&owner, self.token_decimals));
467
+ (token, self.token_decimals, owner.clone())
468
+ }
469
+ };
470
+ let token_client = TokenClient::new(env, &token);
471
+
472
+ // Register mock endpoint
473
+ let endpoint_address =
474
+ env.register(MockEndpointWithCompose, (&native_fee, &zro_fee, &native_token, &zro_token));
475
+ let endpoint_client = MockEndpointWithComposeClient::new(env, &endpoint_address);
476
+
477
+ // Register OFT based on type
478
+ let delegate: Option<Address> = Some(owner.clone());
479
+ let oft_address = match oft_type {
480
+ OFTType::MintBurn => env.register(TestMintBurnOFT, (&token, &owner, &endpoint_address, &delegate)),
481
+ OFTType::LockUnlock => env.register(TestLockUnlockOFT, (&token, &owner, &endpoint_address, &delegate)),
482
+ };
483
+ let oft = OFTClient::new(env, &oft_address);
484
+
485
+ // Pre-mint large amounts to owner
486
+ OFTTestSetup::mint_to(env, &owner, &token, &owner, INITIAL_MINT_AMOUNT);
487
+ OFTTestSetup::mint_to(env, &owner, &native_token, &owner, INITIAL_MINT_AMOUNT);
488
+ OFTTestSetup::mint_to(env, &owner, &zro_token, &owner, INITIAL_MINT_AMOUNT);
489
+
490
+ // Setup based on OFT type
491
+ match oft_type {
492
+ OFTType::MintBurn => {
493
+ // Transfer token ownership to OFT so it can burn tokens
494
+ OFTTestSetup::transfer_token_ownership(env, &owner, &token, &oft_address);
495
+ }
496
+ OFTType::LockUnlock => {
497
+ // Fund the OFT with tokens so it can unlock/release them on receive
498
+ OFTTestSetup::mint_to(env, &owner, &token, &oft_address, INITIAL_MINT_AMOUNT);
499
+ }
500
+ }
501
+
502
+ log!(&env, "token decimals: {}", self.token_decimals);
503
+ log!(&env, "token address: {}", token);
504
+ log!(&env, "token client: {}", token_client.address);
505
+ log!(&env, "native token address: {}", native_token);
506
+ log!(&env, "zro token address: {}", zro_token);
507
+ log!(&env, "owner: {}", owner);
508
+ log!(&env, "native fee: {}", native_fee);
509
+ log!(&env, "zro fee: {}", zro_fee);
510
+ log!(&env, "oft address: {}", oft_address);
511
+
512
+ OFTTestSetup {
513
+ env,
514
+ oft,
515
+ endpoint_client,
516
+ token,
517
+ token_client,
518
+ native_token,
519
+ zro_token,
520
+ owner,
521
+ native_fee,
522
+ zro_fee,
523
+ oft_type,
524
+ token_decimals: actual_token_decimals,
525
+ shared_decimals: self.shared_decimals,
526
+ issuer,
527
+ }
528
+ }
529
+ }
530
+
531
+ impl<'a> OFTTestSetup<'a> {
532
+ /// Create a new test setup with default configuration (MintBurn OFT)
533
+ pub fn new(env: &'a Env) -> Self {
534
+ OFTTestSetupBuilder::new(env).build()
535
+ }
536
+
537
+ /// Create a builder for customized test setup
538
+ pub fn builder(env: &'a Env) -> OFTTestSetupBuilder<'a> {
539
+ OFTTestSetupBuilder::new(env)
540
+ }
541
+
542
+ /// Returns true if this setup uses a MintBurn OFT
543
+ pub fn is_mint_burn(&self) -> bool {
544
+ self.oft_type == OFTType::MintBurn
545
+ }
546
+
547
+ /// Returns true if this setup uses a LockUnlock OFT
548
+ pub fn is_lock_unlock(&self) -> bool {
549
+ self.oft_type == OFTType::LockUnlock
550
+ }
551
+
552
+ pub fn set_peer(&self, eid: u32, peer: &BytesN<32>) {
553
+ self.env.mock_auths(&[MockAuth {
554
+ address: &self.owner,
555
+ invoke: &MockAuthInvoke {
556
+ contract: &self.oft.address,
557
+ fn_name: "set_peer",
558
+ args: (&eid, peer).into_val(self.env),
559
+ sub_invokes: &[],
560
+ },
561
+ }]);
562
+ OAppCoreClient::new(self.env, &self.oft.address).set_peer(&eid, &Some(peer.clone()));
563
+ }
564
+
565
+ pub fn mint_to(env: &Env, owner: &Address, token: &Address, to: &Address, amount: i128) {
566
+ env.mock_auths(&[MockAuth {
567
+ address: owner,
568
+ invoke: &MockAuthInvoke {
569
+ contract: token,
570
+ fn_name: "mint",
571
+ args: (to, amount).into_val(env),
572
+ sub_invokes: &[],
573
+ },
574
+ }]);
575
+ StellarAssetClient::new(env, token).mint(to, &amount);
576
+ }
577
+
578
+ pub fn transfer_token_ownership(env: &Env, owner: &Address, token: &Address, new_admin: &Address) {
579
+ env.mock_auths(&[MockAuth {
580
+ address: owner,
581
+ invoke: &MockAuthInvoke {
582
+ contract: token,
583
+ fn_name: "set_admin",
584
+ args: (new_admin,).into_val(env),
585
+ sub_invokes: &[],
586
+ },
587
+ }]);
588
+ StellarAssetClient::new(env, token).set_admin(new_admin);
589
+ }
590
+
591
+ /// Fund an account with native fees only (transfers from owner)
592
+ pub fn fund_native_fees(&self, to: &Address, amount: i128) {
593
+ self.env.mock_auths(&[MockAuth {
594
+ address: &self.owner,
595
+ invoke: &MockAuthInvoke {
596
+ contract: &self.native_token,
597
+ fn_name: "transfer",
598
+ args: (&self.owner, to, amount).into_val(self.env),
599
+ sub_invokes: &[],
600
+ },
601
+ }]);
602
+ TokenClient::new(self.env, &self.native_token).transfer(&self.owner, to, &amount);
603
+ }
604
+
605
+ /// Fund an account with ZRO fees (transfers from owner)
606
+ pub fn fund_zro_fees(&self, to: &Address, amount: i128) {
607
+ self.env.mock_auths(&[MockAuth {
608
+ address: &self.owner,
609
+ invoke: &MockAuthInvoke {
610
+ contract: &self.zro_token,
611
+ fn_name: "transfer",
612
+ args: (&self.owner, to, amount).into_val(self.env),
613
+ sub_invokes: &[],
614
+ },
615
+ }]);
616
+ TokenClient::new(self.env, &self.zro_token).transfer(&self.owner, to, &amount);
617
+ }
618
+
619
+ /// Fund an account with OFT tokens only (transfers from owner)
620
+ pub fn fund_tokens(&self, to: &Address, amount: i128) {
621
+ self.env.mock_auths(&[MockAuth {
622
+ address: &self.owner,
623
+ invoke: &MockAuthInvoke {
624
+ contract: &self.token,
625
+ fn_name: "transfer",
626
+ args: (&self.owner, to, amount).into_val(self.env),
627
+ sub_invokes: &[],
628
+ },
629
+ }]);
630
+ self.token_client.transfer(&self.owner, to, &amount);
631
+ }
632
+
633
+ /// Quote OFT to get the receipt for authorization
634
+ pub fn quote_oft(&self, send_param: &SendParam) -> OFTReceipt {
635
+ let (_, _, receipt) = self.oft.quote_oft(send_param);
636
+ receipt
637
+ }
638
+
639
+ /// Send tokens cross-chain with proper sender authentication
640
+ pub fn send(
641
+ &self,
642
+ sender: &Address,
643
+ send_param: &SendParam,
644
+ fee: &MessagingFee,
645
+ refund_address: &Address,
646
+ oft_receipt: &OFTReceipt,
647
+ ) -> (MessagingReceipt, OFTReceipt) {
648
+ // Token operation sub-invoke differs based on OFT type
649
+ let token_sub_invoke = match self.oft_type {
650
+ OFTType::MintBurn => MockAuthInvoke {
651
+ contract: &self.token,
652
+ fn_name: "burn",
653
+ args: (sender, &oft_receipt.amount_sent_ld).into_val(self.env),
654
+ sub_invokes: &[],
655
+ },
656
+ OFTType::LockUnlock => MockAuthInvoke {
657
+ contract: &self.token,
658
+ fn_name: "transfer",
659
+ args: (sender, &self.oft.address, &oft_receipt.amount_sent_ld).into_val(self.env),
660
+ sub_invokes: &[],
661
+ },
662
+ };
663
+
664
+ self.env.mock_auths(&[MockAuth {
665
+ address: sender,
666
+ invoke: &MockAuthInvoke {
667
+ contract: &self.oft.address,
668
+ fn_name: "send",
669
+ args: (sender, send_param, fee, refund_address).into_val(self.env),
670
+ sub_invokes: &[
671
+ MockAuthInvoke {
672
+ contract: &self.native_token,
673
+ fn_name: "transfer",
674
+ args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
675
+ sub_invokes: &[],
676
+ },
677
+ MockAuthInvoke {
678
+ contract: &self.zro_token,
679
+ fn_name: "transfer",
680
+ args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
681
+ sub_invokes: &[],
682
+ },
683
+ token_sub_invoke,
684
+ ],
685
+ },
686
+ }]);
687
+ self.oft.send(sender, send_param, fee, refund_address)
688
+ }
689
+
690
+ /// Try send tokens cross-chain with proper sender authentication (returns Result)
691
+ pub fn try_send(
692
+ &self,
693
+ sender: &Address,
694
+ send_param: &SendParam,
695
+ fee: &MessagingFee,
696
+ refund_address: &Address,
697
+ oft_receipt: &OFTReceipt,
698
+ ) -> Result<
699
+ Result<(MessagingReceipt, OFTReceipt), soroban_sdk::Error>,
700
+ Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
701
+ > {
702
+ // Token operation sub-invoke differs based on OFT type
703
+ let token_sub_invoke = match self.oft_type {
704
+ OFTType::MintBurn => MockAuthInvoke {
705
+ contract: &self.token,
706
+ fn_name: "burn",
707
+ args: (sender, &oft_receipt.amount_sent_ld).into_val(self.env),
708
+ sub_invokes: &[],
709
+ },
710
+ OFTType::LockUnlock => MockAuthInvoke {
711
+ contract: &self.token,
712
+ fn_name: "transfer",
713
+ args: (sender, &self.oft.address, &oft_receipt.amount_sent_ld).into_val(self.env),
714
+ sub_invokes: &[],
715
+ },
716
+ };
717
+
718
+ self.env.mock_auths(&[MockAuth {
719
+ address: sender,
720
+ invoke: &MockAuthInvoke {
721
+ contract: &self.oft.address,
722
+ fn_name: "send",
723
+ args: (sender, send_param, fee, refund_address).into_val(self.env),
724
+ sub_invokes: &[
725
+ MockAuthInvoke {
726
+ contract: &self.native_token,
727
+ fn_name: "transfer",
728
+ args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
729
+ sub_invokes: &[],
730
+ },
731
+ MockAuthInvoke {
732
+ contract: &self.zro_token,
733
+ fn_name: "transfer",
734
+ args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
735
+ sub_invokes: &[],
736
+ },
737
+ token_sub_invoke,
738
+ ],
739
+ },
740
+ }]);
741
+ self.oft.try_send(sender, send_param, fee, refund_address)
742
+ }
743
+
744
+ /// Execute lz_receive with proper executor authentication
745
+ pub fn lz_receive(
746
+ &self,
747
+ executor: &Address,
748
+ origin: &Origin,
749
+ guid: &BytesN<32>,
750
+ message: &Bytes,
751
+ extra_data: &Bytes,
752
+ value: i128,
753
+ ) {
754
+ self.env.mock_auths(&[MockAuth {
755
+ address: executor,
756
+ invoke: &MockAuthInvoke {
757
+ contract: &self.oft.address,
758
+ fn_name: "lz_receive",
759
+ args: (executor, origin, guid, message, extra_data, value).into_val(self.env),
760
+ sub_invokes: &[],
761
+ },
762
+ }]);
763
+ LayerZeroReceiverClient::new(self.env, &self.oft.address)
764
+ .lz_receive(executor, origin, guid, message, extra_data, &value);
765
+ }
766
+
767
+ /// Try lz_receive with proper executor authentication (returns Result)
768
+ pub fn try_lz_receive(
769
+ &self,
770
+ executor: &Address,
771
+ origin: &Origin,
772
+ guid: &BytesN<32>,
773
+ message: &Bytes,
774
+ extra_data: &Bytes,
775
+ value: i128,
776
+ ) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
777
+ self.env.mock_auths(&[MockAuth {
778
+ address: executor,
779
+ invoke: &MockAuthInvoke {
780
+ contract: &self.oft.address,
781
+ fn_name: "lz_receive",
782
+ args: (executor, origin, guid, message, extra_data, value).into_val(self.env),
783
+ sub_invokes: &[],
784
+ },
785
+ }]);
786
+ LayerZeroReceiverClient::new(self.env, &self.oft.address)
787
+ .try_lz_receive(executor, origin, guid, message, extra_data, &value)
788
+ }
789
+ }