@layerzerolabs/protocol-stellar-v2 0.2.20 → 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 (198) hide show
  1. package/.turbo/turbo-build.log +783 -802
  2. package/.turbo/turbo-lint.log +320 -157
  3. package/.turbo/turbo-test.log +1414 -1457
  4. package/Cargo.lock +109 -108
  5. package/Cargo.toml +32 -18
  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 +2 -2
  9. package/contracts/common-macros/src/lib.rs +27 -10
  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/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +6 -12
  13. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +12 -17
  14. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +2 -7
  15. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +20 -9
  16. package/contracts/common-macros/src/tests/upgradeable.rs +26 -4
  17. package/contracts/common-macros/src/ttl_configurable.rs +2 -10
  18. package/contracts/common-macros/src/ttl_extendable.rs +2 -10
  19. package/contracts/common-macros/src/upgradeable.rs +56 -15
  20. package/contracts/common-macros/src/utils.rs +0 -9
  21. package/contracts/endpoint-v2/src/lib.rs +3 -2
  22. package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +2 -2
  23. package/contracts/endpoint-v2/src/tests/endpoint_v2/lz_receive_alert.rs +3 -3
  24. package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +4 -4
  25. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_delegate.rs +17 -5
  26. package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
  27. package/contracts/endpoint-v2/src/tests/endpoint_v2/verify.rs +2 -2
  28. package/contracts/endpoint-v2/src/tests/message_lib_manager/register_library.rs +2 -2
  29. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +6 -6
  30. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +67 -37
  31. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_send_library.rs +5 -5
  32. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library.rs +44 -54
  33. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library_timeout.rs +7 -7
  34. package/contracts/endpoint-v2/src/tests/message_lib_manager/set_send_library.rs +8 -8
  35. package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +3 -3
  36. package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +4 -4
  37. package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +3 -3
  38. package/contracts/endpoint-v2/src/tests/messaging_composer/clear_compose.rs +2 -2
  39. package/contracts/endpoint-v2/src/tests/messaging_composer/lz_compose_alert.rs +3 -3
  40. package/contracts/endpoint-v2/src/tests/messaging_composer/send_compose.rs +2 -2
  41. package/contracts/layerzero-views/Cargo.toml +0 -1
  42. package/contracts/layerzero-views/src/layerzero_view.rs +1 -13
  43. package/contracts/macro-integration-tests/Cargo.toml +5 -15
  44. package/contracts/macro-integration-tests/tests/runtime/oapp/mod.rs +48 -0
  45. package/contracts/macro-integration-tests/tests/runtime/oapp/oapp_core.rs +170 -0
  46. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +154 -0
  47. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +338 -0
  48. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +435 -0
  49. package/contracts/macro-integration-tests/tests/runtime.rs +1 -0
  50. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.rs +8 -0
  51. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -0
  52. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.rs +8 -0
  53. package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.stderr +71 -0
  54. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.rs +10 -0
  55. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +5 -0
  56. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.rs +8 -0
  57. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -0
  58. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.rs +8 -0
  59. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -0
  60. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_all.rs +38 -0
  61. package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_single_trait.rs +96 -0
  62. package/contracts/macro-integration-tests/tests/ui/oapp/pass/minimal_contract.rs +64 -0
  63. package/contracts/macro-integration-tests/tests/ui/oapp/pass/struct_with_fields.rs +46 -0
  64. package/contracts/macro-integration-tests/tests/ui/ownable/fail/only_auth_missing_env.stderr +8 -0
  65. package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +1 -1
  66. package/contracts/macro-integration-tests/tests/ui/ownable/pass/only_auth_env_param_variants.rs +1 -1
  67. package/contracts/macro-integration-tests/tests/ui_oapp.rs +11 -0
  68. package/contracts/message-libs/message-lib-common/Cargo.toml +0 -1
  69. package/contracts/message-libs/message-lib-common/src/errors.rs +1 -1
  70. package/contracts/message-libs/treasury/Cargo.toml +0 -2
  71. package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +2 -2
  72. package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +2 -2
  73. package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +2 -2
  74. package/contracts/message-libs/uln-302/src/tests/receive_uln302/verify.rs +2 -2
  75. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +2 -2
  76. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +2 -2
  77. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +7 -27
  78. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_executor_configs.rs +2 -2
  79. package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +2 -2
  80. package/contracts/oapps/counter/Cargo.toml +4 -6
  81. package/contracts/oapps/counter/integration_tests/utils.rs +19 -12
  82. package/contracts/oapps/oapp/src/errors.rs +1 -1
  83. package/contracts/oapps/oapp/src/interfaces/mod.rs +3 -0
  84. package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +47 -0
  85. package/contracts/oapps/oapp/src/lib.rs +1 -0
  86. package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +4 -4
  87. package/contracts/oapps/oapp/src/oapp_core.rs +5 -5
  88. package/contracts/oapps/oapp/src/oapp_options_type3.rs +12 -4
  89. package/contracts/oapps/oapp/src/oapp_receiver.rs +14 -9
  90. package/contracts/oapps/oapp/src/tests/mod.rs +4 -4
  91. package/contracts/oapps/oapp/src/tests/{test_oapp_core.rs → oapp_core.rs} +4 -4
  92. package/contracts/oapps/oapp/src/tests/{test_oapp_options_type3.rs → oapp_options_type3.rs} +3 -4
  93. package/contracts/oapps/oapp-macros/Cargo.toml +8 -4
  94. package/contracts/oapps/oapp-macros/src/generators.rs +9 -34
  95. package/contracts/oapps/oapp-macros/src/lib.rs +3 -0
  96. package/contracts/oapps/oapp-macros/src/tests/mod.rs +2 -0
  97. package/contracts/oapps/oapp-macros/src/tests/oapp.rs +88 -0
  98. package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +86 -0
  99. package/contracts/oapps/oapp-macros/src/tests/snapshots/oapp_macros__tests__oapp__snapshot_generate_oapp.snap +103 -0
  100. package/contracts/oapps/oft/integration-tests/utils.rs +28 -8
  101. package/contracts/oapps/oft/src/extensions/oft_fee.rs +136 -74
  102. package/contracts/oapps/oft/src/extensions/pausable.rs +44 -10
  103. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +170 -130
  104. package/contracts/oapps/oft/src/oft.rs +19 -12
  105. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +1 -1
  106. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +1 -1
  107. package/contracts/oapps/oft-core/Cargo.toml +1 -4
  108. package/contracts/oapps/oft-core/integration-tests/setup.rs +2 -2
  109. package/contracts/oapps/oft-core/integration-tests/utils.rs +21 -3
  110. package/contracts/oapps/oft-core/src/errors.rs +3 -2
  111. package/contracts/oapps/oft-core/src/events.rs +6 -0
  112. package/contracts/oapps/oft-core/src/lib.rs +1 -1
  113. package/contracts/oapps/oft-core/src/oft_core.rs +115 -60
  114. package/contracts/oapps/oft-core/src/storage.rs +7 -3
  115. package/contracts/oapps/oft-core/src/tests/mod.rs +1 -0
  116. package/contracts/oapps/oft-core/src/tests/test_decimals.rs +37 -2
  117. package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +2 -2
  118. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +323 -0
  119. package/contracts/oapps/oft-core/src/tests/test_send.rs +2 -2
  120. package/contracts/oapps/oft-core/src/tests/test_utils.rs +59 -14
  121. package/contracts/utils/Cargo.toml +0 -1
  122. package/contracts/utils/src/errors.rs +1 -1
  123. package/contracts/utils/src/multisig.rs +17 -8
  124. package/contracts/utils/src/ownable.rs +6 -6
  125. package/contracts/utils/src/testing_utils.rs +124 -54
  126. package/contracts/utils/src/tests/multisig.rs +12 -12
  127. package/contracts/utils/src/tests/ownable.rs +6 -6
  128. package/contracts/utils/src/tests/testing_utils.rs +50 -167
  129. package/contracts/utils/src/tests/ttl_configurable.rs +5 -5
  130. package/contracts/utils/src/tests/upgradeable.rs +1 -1
  131. package/contracts/utils/src/ttl_configurable.rs +10 -4
  132. package/contracts/utils/src/upgradeable.rs +5 -5
  133. package/contracts/workers/dvn/Cargo.toml +5 -6
  134. package/contracts/workers/dvn/src/dvn.rs +2 -12
  135. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  136. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +37 -19
  137. package/contracts/workers/dvn-fee-lib/src/lib.rs +12 -2
  138. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +15 -13
  139. package/contracts/workers/executor/Cargo.toml +3 -0
  140. package/contracts/workers/executor/src/executor.rs +2 -12
  141. package/contracts/workers/executor/src/lib.rs +2 -2
  142. package/contracts/workers/executor/src/tests/auth.rs +394 -0
  143. package/contracts/workers/executor/src/tests/executor.rs +410 -0
  144. package/contracts/workers/executor/src/tests/mod.rs +3 -0
  145. package/contracts/workers/executor/src/tests/setup.rs +250 -0
  146. package/contracts/workers/executor-fee-lib/Cargo.toml +5 -0
  147. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +1 -12
  148. package/contracts/workers/executor-fee-lib/src/lib.rs +8 -2
  149. package/contracts/workers/executor-helper/Cargo.toml +0 -1
  150. package/contracts/workers/price-feed/Cargo.toml +5 -0
  151. package/contracts/workers/price-feed/src/lib.rs +9 -4
  152. package/contracts/workers/price-feed/src/price_feed.rs +1 -11
  153. package/contracts/workers/worker/src/errors.rs +1 -1
  154. package/contracts/workers/worker/src/tests/setup.rs +1 -1
  155. package/contracts/workers/worker/src/tests/worker.rs +55 -41
  156. package/contracts/workers/worker/src/worker.rs +34 -25
  157. package/docs/error-spec.md +55 -0
  158. package/docs/layerzero-v2-on-stellar.md +447 -0
  159. package/docs/oapp-guide.md +212 -0
  160. package/docs/oft-guide.md +314 -0
  161. package/package.json +3 -3
  162. package/sdk/.turbo/turbo-test.log +260 -257
  163. package/sdk/dist/generated/bml.d.ts +3 -3
  164. package/sdk/dist/generated/bml.js +4 -4
  165. package/sdk/dist/generated/counter.d.ts +295 -295
  166. package/sdk/dist/generated/counter.js +43 -43
  167. package/sdk/dist/generated/dvn.d.ts +91 -91
  168. package/sdk/dist/generated/dvn.js +24 -24
  169. package/sdk/dist/generated/dvn_fee_lib.d.ts +92 -92
  170. package/sdk/dist/generated/dvn_fee_lib.js +25 -25
  171. package/sdk/dist/generated/endpoint.d.ts +99 -99
  172. package/sdk/dist/generated/endpoint.js +16 -16
  173. package/sdk/dist/generated/executor.d.ts +91 -91
  174. package/sdk/dist/generated/executor.js +24 -24
  175. package/sdk/dist/generated/executor_fee_lib.d.ts +92 -92
  176. package/sdk/dist/generated/executor_fee_lib.js +25 -25
  177. package/sdk/dist/generated/executor_helper.d.ts +3 -3
  178. package/sdk/dist/generated/executor_helper.js +4 -4
  179. package/sdk/dist/generated/layerzero_view.d.ts +186 -186
  180. package/sdk/dist/generated/layerzero_view.js +35 -35
  181. package/sdk/dist/generated/oft.d.ts +366 -352
  182. package/sdk/dist/generated/oft.js +74 -79
  183. package/sdk/dist/generated/price_feed.d.ts +198 -198
  184. package/sdk/dist/generated/price_feed.js +39 -39
  185. package/sdk/dist/generated/sml.d.ts +99 -99
  186. package/sdk/dist/generated/sml.js +16 -16
  187. package/sdk/dist/generated/treasury.d.ts +99 -99
  188. package/sdk/dist/generated/treasury.js +16 -16
  189. package/sdk/dist/generated/uln302.d.ts +99 -99
  190. package/sdk/dist/generated/uln302.js +16 -16
  191. package/sdk/dist/generated/upgrader.d.ts +3 -3
  192. package/sdk/dist/generated/upgrader.js +3 -3
  193. package/sdk/package.json +1 -1
  194. package/sdk/test/suites/localnet.ts +84 -20
  195. package/contracts/ERROR_SPEC.md +0 -51
  196. package/contracts/endpoint-v2/ARCHITECTURE.md +0 -233
  197. /package/contracts/oapps/oapp/src/tests/{test_oapp_receiver.rs → oapp_receiver.rs} +0 -0
  198. /package/contracts/oapps/oapp/src/tests/{test_oapp_sender.rs → oapp_sender.rs} +0 -0
@@ -0,0 +1,447 @@
1
+ # LayerZero V2 on Stellar
2
+
3
+ This document provides a comprehensive guide to LayerZero V2's implementation on
4
+ the Stellar blockchain using Soroban smart contracts.
5
+
6
+ ## Overview
7
+
8
+ LayerZero V2 on Stellar enables cross-chain messaging between Stellar and other
9
+ blockchains. Built on Soroban (Stellar's smart contract platform) using Rust, it
10
+ follows a modular, plugin-based architecture that maintains compatibility with
11
+ LayerZero's V2 protocol design while adapting to Stellar's unique
12
+ characteristics.
13
+
14
+ ### Key components
15
+
16
+ - **EndpointV2**: Central hub for all cross-chain messaging operations
17
+ - **Message libraries**: Pluggable verification logic (ULN302, SimpleMessageLib)
18
+ - **Workers**: DVNs (Decentralized Verifier Networks) and executors
19
+ - **OApps**: Omnichain applications that leverage the protocol
20
+ - **OFT**: Omnichain Fungible Token standard for cross-chain token transfers
21
+
22
+ ## Architecture
23
+
24
+ ```
25
+ ┌─────────────────────────────────────────────────────────────────────────────┐
26
+ │ OApp / OFT │
27
+ │ (Omnichain Application Layer) │
28
+ └─────────────────────────────────────────────────────────────────────────────┘
29
+ ┌─────────────────────────────────────────────────────────────────────────────┐
30
+ │ EndpointV2 │
31
+ │ (Central Messaging Hub) │
32
+ │ ┌─────────────────┐ ┌──────────────────┐ ┌────────────────────────────┐ │
33
+ │ │ MessageLib │ │ MessagingChannel │ │ MessagingComposer │ │
34
+ │ │ Manager │ │ (Nonce/Payload) │ │ (Multi-stage Execution) │ │
35
+ │ └─────────────────┘ └──────────────────┘ └────────────────────────────┘ │
36
+ └─────────────────────────────────────────────────────────────────────────────┘
37
+ ┌─────────────────────────────────────────────────────────────────────────────┐
38
+ │ Message Libraries │
39
+ │ ┌──────────────────────────────────┐ ┌─────────────────────────────────┐ │
40
+ │ │ ULN302 │ │ SimpleMessageLib │ │
41
+ │ │ (Ultra Light Node - Production) │ │ (Testing/Basic) │ │
42
+ │ └──────────────────────────────────┘ └─────────────────────────────────┘ │
43
+ └─────────────────────────────────────────────────────────────────────────────┘
44
+ ┌─────────────────────────────────────────────────────────────────────────────┐
45
+ │ Workers │
46
+ │ ┌────────────────────────────┐ ┌────────────────────────────────────┐ │
47
+ │ │ DVN │ │ Executor │ │
48
+ │ │ (Message Verification) │ │ (Message Execution) │ │
49
+ │ └────────────────────────────┘ └────────────────────────────────────┘ │
50
+ └─────────────────────────────────────────────────────────────────────────────┘
51
+ ```
52
+
53
+ ## Core contracts
54
+
55
+ ### EndpointV2
56
+
57
+ The central contract managing all cross-chain messaging operations.
58
+
59
+ **Location**: `contracts/endpoint-v2/`
60
+
61
+ **Key responsibilities**:
62
+
63
+ - Quote messaging fees
64
+ - Send cross-chain messages
65
+ - Verify inbound messages
66
+ - Clear and deliver verified messages
67
+ - Manage message libraries
68
+ - Track nonce ordering for message sequencing
69
+
70
+ **Internal modules**:
71
+
72
+ | Module | Purpose |
73
+ | ------------------------ | -------------------------------------------------------------------------------------------------------- |
74
+ | `message_lib_manager.rs` | Library registration, selection, and configuration. Tracks default/per-OApp libraries per EID. |
75
+ | `messaging_channel.rs` | Nonce tracking, payload hash storage/verification, message state transitions (verify → receive → clear). |
76
+ | `messaging_composer.rs` | Manages compose message queues for multi-stage execution between OApp and Composers. |
77
+ | `util.rs` | Public utilities: `compute_guid()`, `build_payload()`, `keccak256()` for message processing. |
78
+
79
+ **Design patterns**:
80
+
81
+ - **Interfaces as library exports**: External contracts depend on trait
82
+ definitions only, controlled by the `library` feature flag
83
+ - **Interface composition**: `ILayerZeroEndpointV2` composes
84
+ `IMessageLibManager`, `IMessagingChannel`, and `IMessagingComposer`
85
+ - **Client generation**: `#[contractclient]` macro generates type-safe client
86
+ wrappers for cross-contract calls
87
+
88
+ ### ULN302 (Ultra Light Node)
89
+
90
+ The primary message library implementing verification through DVNs and execution
91
+ through executors.
92
+
93
+ **Location**: `contracts/message-libs/uln-302/`
94
+
95
+ ### DVN (Decentralized Verifier Network)
96
+
97
+ Provides cryptographic attestations that messages were properly sent on the
98
+ source chain.
99
+
100
+ **Location**: `contracts/workers/dvn/`
101
+
102
+ **Features**:
103
+
104
+ - Multisig-based verification using secp256k1 signatures
105
+ - Custom Soroban account interface for transaction signing
106
+ - Destination-specific configuration
107
+ - Fee calculation based on quorum size and gas costs
108
+
109
+ ### Executor
110
+
111
+ Executes verified messages on the destination chain.
112
+
113
+ **Location**: `contracts/workers/executor/`
114
+
115
+ **Features**:
116
+
117
+ - Message delivery to receiving OApps
118
+ - Native token drops to addresses
119
+ - Destination-specific configuration
120
+ - Fee calculation based on message size and execution parameters
121
+
122
+ ### OApp (Omnichain Application)
123
+
124
+ Base framework for building cross-chain applications.
125
+
126
+ **Location**: `contracts/oapps/oapp/`
127
+
128
+ **Key traits**:
129
+
130
+ | Trait | Purpose |
131
+ | -------------------- | ------------------------------------------------- |
132
+ | `OAppCore` | Foundation: peer management, endpoint reference |
133
+ | `OAppSenderInternal` | Internal helper for sending cross-chain messages |
134
+ | `OAppReceiver` | Public receiver interface, payload clearing |
135
+ | `LzReceiveInternal` | Application message handling (**must implement**) |
136
+ | `OAppOptionsType3` | Enforced execution options management |
137
+
138
+ ### OFT (Omnichain Fungible Token)
139
+
140
+ Standardized cross-chain token bridge implementation.
141
+
142
+ **Location**: `contracts/oapps/oft/`
143
+
144
+ **Key traits**:
145
+
146
+ | Trait | Purpose |
147
+ | ------------- | ----------------------------------------------------- |
148
+ | `OFTCore` | Public interface: `send()`, `quote_send()`, `token()` |
149
+ | `OFTInternal` | Internal implementation: `__debit()`, `__credit()` |
150
+
151
+ **Features**:
152
+
153
+ - Token type flexibility (LockUnlock or MintBurn)
154
+ - Fee extension for outbound transfers
155
+ - Pausable extension
156
+ - Rate limiter extension
157
+ - Dust removal and slippage protection
158
+
159
+ ## Messaging flow
160
+
161
+ ### Sending a message
162
+
163
+ ```mermaid
164
+ sequenceDiagram
165
+ participant OApp
166
+ participant Endpoint as EndpointV2
167
+ participant SendLib as SendLib (ULN302)
168
+ participant DVN
169
+ participant Executor
170
+
171
+ OApp->>Endpoint: quote(params)
172
+ Endpoint-->>OApp: MessagingFee
173
+
174
+ OApp->>Endpoint: send(params, refund_address)
175
+ Note over Endpoint: Increment outbound nonce
176
+ Note over Endpoint: Create GUID = keccak256(nonce, src_eid, sender, dst_eid, receiver)
177
+
178
+ Endpoint->>SendLib: send(packet, options)
179
+ SendLib->>DVN: assignJob(packet_header, payload_hash, confirmations)
180
+ SendLib->>Executor: assignJob(packet_header, payload_hash, options)
181
+ Note over SendLib: Calculate Treasury fee
182
+ SendLib-->>Endpoint: (fee recipients, fees)
183
+
184
+ Note over Endpoint: Distribute fees to workers
185
+ Endpoint-->>OApp: MessagingReceipt
186
+ Note over Endpoint: Emit PacketSent event
187
+ ```
188
+
189
+ ### Receiving a message
190
+
191
+ ```mermaid
192
+ sequenceDiagram
193
+ participant DVN
194
+ participant ReceiveLib as ReceiveLib (ULN302)
195
+ participant Endpoint as EndpointV2
196
+ participant Executor
197
+ participant OApp
198
+
199
+ rect rgb(240, 248, 255)
200
+ Note over DVN, Endpoint: Step 1: Verification
201
+ Note over DVN: Monitor source chain for PacketSent
202
+ Note over DVN: Wait for block confirmations
203
+ DVN->>ReceiveLib: verify(packet_header, payload_hash)
204
+ Note over ReceiveLib: Record DVN attestation
205
+ Note over ReceiveLib: Check if quorum reached
206
+ ReceiveLib->>Endpoint: verify(origin, receiver, payload_hash)
207
+ Note over Endpoint: Store payload_hash
208
+ Note over Endpoint: Emit PacketVerified event
209
+ end
210
+
211
+ rect rgb(240, 255, 240)
212
+ Note over Executor, OApp: Step 2: Execution
213
+ Executor->>OApp: lz_receive(origin, guid, message, extra_data)
214
+ OApp->>Endpoint: clear(origin, guid, message)
215
+ Note over Endpoint: Validate payload against stored hash
216
+ Note over Endpoint: Emit PacketDelivered event
217
+ Endpoint-->>OApp: (success)
218
+ Note over OApp: Execute __lz_receive() logic
219
+ end
220
+ ```
221
+
222
+ ## Stellar-specific considerations
223
+
224
+ Two core differences between Soroban and EVM require protocol-level adaptations:
225
+
226
+ 1. Stellar's variable-length address model differs from LayerZero's fixed
227
+ bytes32 address abstraction
228
+ 2. Soroban's Time-To-Live (TTL)-based storage model requires active state
229
+ maintenance, unlike EVM's persistent storage
230
+
231
+ ### Constraint 1: bytes32 address format mismatch
232
+
233
+ LayerZero V2 standardizes all cross-chain addresses using bytes32. Stellar uses
234
+ StrKey-encoded addresses of different types:
235
+
236
+ | Address Type | Structure | Usage |
237
+ | ------------ | ------------------------------------------------------ | ------------------------ |
238
+ | G-Address | 1-byte version + 32-byte public key + 2-byte checksum | EOAs (Ed25519 key pairs) |
239
+ | C-Address | 1-byte version + 32-byte contract ID + 2-byte checksum | Soroban smart contracts |
240
+ | M-Address | G-address payload + 64-bit ID (exceeds 32 bytes) | Exchange sub-accounts |
241
+
242
+ **Key observation**: G-Address and C-Address share the same 32-byte payload
243
+ structure, and the payload alone is guaranteed to be unique across both types.
244
+
245
+ **Solution: Deterministic address resolution**
246
+
247
+ All bytes32-encoded addresses are resolved at execution time:
248
+
249
+ 1. **OApp & Composer addresses**: The bytes32 value is interpreted directly as a
250
+ Soroban contract ID and converted to a C-address.
251
+
252
+ 2. **Native drop & OFT receiver addresses**: Contract-first detection is
253
+ applied:
254
+ - Convert the bytes32 payload to a C-address
255
+ - If a contract exists at that address, treat as C-address
256
+ - Otherwise, treat as G-address
257
+
258
+ This resolution is deterministic and safe. Soroban guarantees that a deployed
259
+ contract address cannot correspond to a valid G-address private key, eliminating
260
+ ambiguity.
261
+
262
+ ### Constraint 2: TTL-based storage model
263
+
264
+ All contract data entries on Soroban have a TTL. When TTL expires, data becomes
265
+ archived and inaccessible (but always restorable).
266
+
267
+ **Challenges**:
268
+
269
+ - **Cost impact**: The Endpoint contract (~60 KB) costs ~3.0 USD/month to keep
270
+ alive. Restoration after archival (~4 months bump) costs ~12 USD.
271
+ - **Execution variability**: TTL extension during `lzReceive` introduces
272
+ additional fees and risk of out-of-gas failures.
273
+ - **OApp-induced operations**: OApps (e.g., SAC tokens) may perform their own
274
+ TTL bumps, adding unpredictable overhead.
275
+
276
+ **Solution: Hybrid TTL extension strategy**
277
+
278
+ LayerZero adopts a hybrid approach combining Self-Extension with Client
279
+ Extension:
280
+
281
+ 1. **Self-Extension (default)**: Contracts automatically extend TTL for storage
282
+ entries when accessed if remaining TTL falls below a configured threshold.
283
+ This is autonomous and requires no external maintenance.
284
+
285
+ 2. **Client Extension (selective)**: For low-activity periods, off-chain
286
+ services or sealer-assisted extension can keep critical components (Endpoint,
287
+ ULN) alive without imposing costs on users.
288
+
289
+ **Configurable TTL parameters**: Unlike typical Soroban apps that hardcode TTL
290
+ values, LayerZero exposes TTL thresholds and extension targets as configurable
291
+ values. This follows Stellar team recommendations, as storage fees and network
292
+ constraints may evolve.
293
+
294
+ **Governance safeguards**:
295
+
296
+ - Hard upper cap on extension targets (e.g., 1 year) to prevent excessive fees
297
+ - TTL parameters can be permanently frozen once the ecosystem stabilizes
298
+
299
+ ### Constraint 3: Reentrancy prohibition
300
+
301
+ Soroban prohibits reentrancy—a contract cannot call itself, directly or
302
+ indirectly, within the same transaction. This fundamental difference from EVM
303
+ requires rethinking several LayerZero patterns.
304
+
305
+ #### Impact on DVN design
306
+
307
+ On EVM, the DVN contract uses a **self-call pattern** for multisig-authorized
308
+ self-configuration (e.g., setting destination configs, updating signers). An
309
+ admin submits M-of-N signatures over a set of calls to the DVN. The DVN verifies
310
+ the signatures, then calls itself to execute the configuration changes. For
311
+ external calls like `ULN302.verify()`, the DVN simply calls the target contract
312
+ directly after signature verification.
313
+
314
+ ```
315
+ EVM DVN Flow (self-configuration):
316
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
317
+ │ Admin │───►│ DVN │───►│ DVN │ (self-call after
318
+ │ │ │ (verify) │ │ (execute) │ sig verification)
319
+ └─────────────┘ └─────────────┘ └─────────────┘
320
+
321
+ EVM DVN Flow (external calls):
322
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
323
+ │ Admin │───►│ DVN │───►│ ULN302 │ (direct call after
324
+ │ │ │ (verify) │ │ .verify() │ sig verification)
325
+ └─────────────┘ └─────────────┘ └─────────────┘
326
+ ```
327
+
328
+ The self-call pattern for configuration fails on Soroban because the DVN cannot
329
+ call itself. The solution is to implement DVN as an **Abstract Account** (custom
330
+ account). Soroban's custom account interface allows contracts to define
331
+ authorization logic via `__check_auth`. Instead of self-calling, the DVN
332
+ authorizes calls made on its behalf—both self-configuration and external calls:
333
+
334
+ ```
335
+ Stellar DVN Flow (Abstract Account):
336
+ ┌─────────────┐ ┌─────────────┐
337
+ │ Admin │───►│ ULN302 │ DVN authorizes via __check_auth
338
+ │ (submits │ │ .verify() │ (signature verification happens
339
+ │ tx with │ │ │ in the auth context, not as
340
+ │ DVN auth) │ │ │ a separate call)
341
+ └─────────────┘ └─────────────┘
342
+ ```
343
+
344
+ The DVN's `__check_auth` validates:
345
+
346
+ - Verifier ID (VID) matches
347
+ - Authorization hasn't expired
348
+ - Admin signature is valid (Ed25519) — except for `set_admin` calls
349
+ - Call data hash hasn't been replayed
350
+ - M-of-N secp256k1 signatures from the quorum
351
+
352
+ #### Impact on executor design
353
+
354
+ LayerZero supports a common cross-chain pattern called **ABA messaging**—an
355
+ OApp on chain B receives a message from chain A and atomically sends a response
356
+ back to chain A within the same transaction:
357
+
358
+ ```
359
+ ABA Pattern:
360
+ Chain A Chain B
361
+ ┌───────┐ ┌───────┐
362
+ │ OApp │ ──── msg1 ─────► │ OApp │
363
+ │ │ ◄─── msg2 ───── │ │ (msg2 sent in lz_receive handler)
364
+ └───────┘ └───────┘
365
+ ```
366
+
367
+ If the executor were a regular contract that calls the OApp, it would be locked
368
+ during the receive flow. When the OApp attempts to send a message back (which
369
+ requires interacting with the executor for fee quoting), the transaction would
370
+ fail due to reentrancy:
371
+
372
+ ```
373
+ ❌ Regular Contract Executor (fails ABA):
374
+ ┌──────────┐ ┌───────┐ ┌──────────┐ ┌─────────────┐
375
+ │ Executor │───►│ OApp │───►│ Endpoint │───►│ Executor │ BLOCKED!
376
+ │ .call() │ │ │ │ .send() │ │.assign_job()| (reentrancy)
377
+ └──────────┘ └───────┘ └──────────┘ └─────────────┘
378
+ ```
379
+
380
+ By implementing executor as an **Abstract Account**, the executor doesn't
381
+ "call" the OApp—it authorizes the OApp's `lz_receive` invocation. The executor
382
+ contract remains accessible for the send flow:
383
+
384
+ ```
385
+ ✓ Abstract Account Executor (supports ABA):
386
+ ┌───────┐ ┌──────────┐
387
+ │ OApp │ ◄───────── executor auth ──────────── │ Executor │ (not in call stack,
388
+ │ │ via __check_auth │ AA │ remains accessible)
389
+ │ │ ─► Endpoint.send() ─► .assign_job() ─► │ │ ✓ Works!
390
+ └───────┘ └──────────┘
391
+ ```
392
+
393
+ #### Impact on message delivery design
394
+
395
+ The same reentrancy constraint affects how messages are delivered to OApps.
396
+
397
+ **EVM approach (push mode)**: The endpoint is the entry point. After verifying
398
+ and clearing the payload, the endpoint calls the OApp's `lzReceive`:
399
+
400
+ ```
401
+ EVM Push Mode:
402
+ Executor ──► Endpoint.lzReceive() ──► OApp.lzReceive()
403
+
404
+ └── (clear payload, then call OApp)
405
+ ```
406
+
407
+ This fails on Soroban for ABA messaging. If the endpoint calls the OApp, the
408
+ endpoint is locked. When the OApp tries to send a response message, it cannot
409
+ call back into the endpoint:
410
+
411
+ ```
412
+ ❌ Push Mode on Stellar (fails ABA):
413
+ Executor ──► Endpoint.lz_receive() ──► OApp ──► Endpoint.send() BLOCKED!
414
+ (reentrancy)
415
+ ```
416
+
417
+ **Stellar approach (pull mode)**: The OApp is the entry point. The executor
418
+ authorizes the OApp's `lz_receive` call, and the OApp pulls verification from
419
+ the endpoint by calling `clear`:
420
+
421
+ ```
422
+ ✓ Pull Mode on Stellar (supports ABA):
423
+ Executor auth ──► OApp.lz_receive() ──► Endpoint.clear()
424
+
425
+ └──► Endpoint.send() ✓ Works!
426
+ (endpoint not in call stack)
427
+ ```
428
+
429
+ **Summary of reentrancy adaptations**:
430
+
431
+ | Component | EVM Pattern | Stellar Pattern | Reason |
432
+ | --------- | -------------------------------- | ------------------------------------ | ------------------- |
433
+ | DVN | Self-call after sig verification | Abstract Account with `__check_auth` | Cannot self-call |
434
+ | Executor | Regular contract calls OApp | Abstract Account authorizes OApp | ABA pattern support |
435
+ | Delivery | Push (Endpoint → OApp) | Pull (OApp → Endpoint.clear) | ABA pattern support |
436
+
437
+ ## Building OApps and OFTs
438
+
439
+ - [oapp-guide.md](oapp-guide.md) - Building Omnichain Applications using the
440
+ OApp framework, macros, and traits
441
+ - [oft-guide.md](oft-guide.md) - Building Omnichain Fungible Tokens with
442
+ MintBurn/LockUnlock strategies and extensions
443
+
444
+ ## Error codes
445
+
446
+ See [error-spec.md](error-spec.md) for the error code allocation strategy and
447
+ detailed specifications.
@@ -0,0 +1,212 @@
1
+ # Building OApps on Stellar
2
+
3
+ This guide explains how to build Omnichain Applications (OApps) using the LayerZero V2 framework on Stellar.
4
+
5
+ ## Overview
6
+
7
+ An OApp is a cross-chain application that can send and receive messages through LayerZero. The framework provides:
8
+
9
+ - **OAppCore**: Foundation for all OApp functionality (peer management, endpoint access)
10
+ - **OAppSenderInternal**: Enables sending cross-chain messages
11
+ - **OAppReceiver**: Handles incoming cross-chain messages
12
+ - **LzReceiveInternal**: Application-specific message handling logic
13
+ - **OAppOptionsType3**: Manages enforced options for message execution
14
+
15
+ ## Quick start
16
+
17
+ The `#[oapp]` macro provides the simplest way to create an OApp:
18
+
19
+ ```rust
20
+ use oapp::oapp_receiver::LzReceiveInternal;
21
+ use oapp_macros::oapp;
22
+
23
+ #[oapp]
24
+ pub struct MyOApp;
25
+
26
+ impl LzReceiveInternal for MyOApp {
27
+ fn __lz_receive(
28
+ env: &Env,
29
+ origin: &Origin,
30
+ guid: &BytesN<32>,
31
+ message: &Bytes,
32
+ extra_data: &Bytes,
33
+ executor: &Address,
34
+ value: i128,
35
+ ) {
36
+ // Your message handling logic here
37
+ }
38
+ }
39
+ ```
40
+
41
+ The macro automatically generates:
42
+
43
+ - `#[contract]` attribute
44
+ - `#[ownable]` trait implementation
45
+ - `OAppCore` implementation
46
+ - `OAppSenderInternal` implementation
47
+ - `OAppReceiver` implementation
48
+ - `OAppOptionsType3` implementation
49
+
50
+ ## Initialization
51
+
52
+ Initialize your OApp in the constructor:
53
+
54
+ ```rust
55
+ use oapp::oapp_core::initialize_oapp;
56
+
57
+ #[contract_impl]
58
+ impl MyOApp {
59
+ pub fn __constructor(env: &Env, owner: &Address, endpoint: &Address, delegate: &Address) {
60
+ initialize_oapp::<Self>(env, owner, endpoint, &Some(delegate.clone()));
61
+ }
62
+ }
63
+ ```
64
+
65
+ The `initialize_oapp` function:
66
+
67
+ 1. Sets the contract owner
68
+ 2. Stores the LayerZero endpoint address
69
+ 3. Optionally sets a delegate on the endpoint
70
+
71
+ ## Peer management
72
+
73
+ Before sending or receiving messages, configure peers for each destination chain:
74
+
75
+ ```rust
76
+ // Set a peer (owner only)
77
+ oapp.set_peer(dst_eid, peer_address_bytes32);
78
+
79
+ // Remove a peer
80
+ oapp.set_peer(dst_eid, None);
81
+
82
+ // Query a peer
83
+ let peer = oapp.peer(dst_eid);
84
+ ```
85
+
86
+ Peers are stored as `BytesN<32>` to maintain cross-chain address compatibility.
87
+
88
+ ## Sending messages
89
+
90
+ Use the internal sender methods to send cross-chain messages:
91
+
92
+ ```rust
93
+ use oapp::oapp_sender::OAppSenderInternal;
94
+
95
+ impl MyOApp {
96
+ pub fn send_message(env: &Env, caller: &Address, dst_eid: u32, message: &Bytes, options: &Bytes, fee: &MessagingFee) {
97
+ caller.require_auth();
98
+
99
+ // Send the message (handles fee payment automatically)
100
+ Self::__lz_send(env, dst_eid, message, options, caller, fee, caller);
101
+ }
102
+
103
+ pub fn quote(env: &Env, dst_eid: u32, message: &Bytes, options: &Bytes, pay_in_zro: bool) -> MessagingFee {
104
+ Self::__quote(env, dst_eid, message, options, pay_in_zro)
105
+ }
106
+ }
107
+ ```
108
+
109
+ The `__lz_send` method:
110
+
111
+ 1. Transfers the native fee from the payer to the endpoint
112
+ 2. Transfers the ZRO fee if applicable
113
+ 3. Looks up the peer for the destination
114
+ 4. Calls the endpoint's `send` function
115
+
116
+ ## Receiving messages
117
+
118
+ Implement `LzReceiveInternal` to handle incoming messages:
119
+
120
+ ```rust
121
+ impl LzReceiveInternal for MyOApp {
122
+ fn __lz_receive(
123
+ env: &Env,
124
+ origin: &Origin, // Contains src_eid, sender, nonce
125
+ guid: &BytesN<32>, // Unique message identifier
126
+ message: &Bytes, // The message payload
127
+ extra_data: &Bytes, // Additional data from executor
128
+ executor: &Address, // Executor who delivered the message
129
+ value: i128, // Native token value sent with message
130
+ ) {
131
+ // Your message handling logic
132
+ // Note: clear_payload_and_transfer is called automatically before this
133
+ }
134
+ }
135
+ ```
136
+
137
+ The default `lz_receive` flow:
138
+
139
+ 1. Requires executor authorization
140
+ 2. Transfers native value from executor to OApp (if any)
141
+ 3. Verifies the sender matches the configured peer
142
+ 4. Clears the payload from the endpoint
143
+ 5. Calls your `__lz_receive` implementation
144
+
145
+ ## Custom implementations
146
+
147
+ Use `#[oapp(custom = [...])]` to override default behavior:
148
+
149
+ ### Custom receiver (ordered delivery)
150
+
151
+ ```rust
152
+ #[oapp(custom = [receiver])]
153
+ pub struct MyOrderedOApp;
154
+
155
+ impl LzReceiveInternal for MyOrderedOApp {
156
+ fn __lz_receive(env: &Env, origin: &Origin, ...) {
157
+ // Your logic here
158
+ }
159
+ }
160
+
161
+ #[contract_impl(contracttrait)]
162
+ impl OAppReceiver for MyOrderedOApp {
163
+ fn next_nonce(env: &Env, src_eid: u32, sender: &BytesN<32>) -> u64 {
164
+ // Return expected nonce for ordered delivery
165
+ // Return 0 for unordered (default)
166
+ Storage::max_received_nonce(env, src_eid, sender) + 1
167
+ }
168
+ }
169
+ ```
170
+
171
+ ### Custom core (version override)
172
+
173
+ ```rust
174
+ #[oapp(custom = [core])]
175
+ pub struct MyOApp;
176
+
177
+ #[contract_impl(contracttrait)]
178
+ #[common_macros::ownable]
179
+ impl OAppCore for MyOApp {
180
+ fn oapp_version(_env: &Env) -> (u64, u64) {
181
+ (2, 1) // Custom version
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### Multiple custom implementations
187
+
188
+ ```rust
189
+ #[oapp(custom = [core, receiver, options_type3])]
190
+ pub struct MyCustomOApp;
191
+
192
+ // Implement each trait manually...
193
+ ```
194
+
195
+ ## Example: Counter OApp
196
+
197
+ See `contracts/oapps/counter/` for a complete example demonstrating:
198
+
199
+ - Basic send/receive flow
200
+ - Ordered nonce enforcement
201
+ - Compose message handling
202
+ - ABA (request-response) pattern
203
+
204
+ ## Key traits summary
205
+
206
+ | Trait | Purpose | Default behavior |
207
+ | -------------------- | -------------------------------- | ------------------------------------------- |
208
+ | `OAppCore` | Peer management, endpoint access | Stores endpoint, manages peers |
209
+ | `OAppSenderInternal` | Send cross-chain messages | Handles fee payment and message dispatch |
210
+ | `OAppReceiver` | Receive cross-chain messages | Clears payload, delegates to `__lz_receive` |
211
+ | `LzReceiveInternal` | Application message handling | **Must implement** |
212
+ | `OAppOptionsType3` | Enforced execution options | No enforced options |