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