@layerzerolabs/protocol-stellar-v2 0.2.10 → 0.2.11
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 -199
- package/.turbo/turbo-lint.log +79 -107
- package/.turbo/turbo-test.log +1017 -841
- package/Cargo.lock +13 -5
- package/contracts/common-macros/src/contract_impl.rs +6 -3
- package/contracts/common-macros/src/error.rs +9 -17
- package/contracts/common-macros/src/event.rs +4 -4
- package/contracts/common-macros/src/lib.rs +2 -2
- package/contracts/common-macros/src/ownable.rs +9 -5
- package/contracts/common-macros/src/tests/contract_impl.rs +178 -86
- package/contracts/common-macros/src/tests/error.rs +168 -0
- package/contracts/common-macros/src/tests/mod.rs +2 -4
- package/contracts/common-macros/src/tests/ownable.rs +37 -60
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap +16 -6
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__error__snapshot_generated_contract_error_code.snap +20 -0
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +3 -1
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap +12 -2
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +5 -1
- package/contracts/common-macros/src/tests/utils.rs +267 -0
- package/contracts/common-macros/src/ttl_configurable.rs +15 -12
- package/contracts/common-macros/src/utils.rs +35 -6
- package/contracts/message-libs/uln-302/src/receive_uln.rs +1 -1
- package/contracts/message-libs/uln-302/src/send_uln.rs +2 -2
- package/contracts/message-libs/uln-302/src/uln302.rs +2 -2
- package/contracts/oapp-macros/src/oapp_core.rs +1 -1
- package/contracts/oapps/oft/integration-tests/setup.rs +4 -3
- package/contracts/oapps/oft/src/default_oft_impl.rs +146 -0
- package/contracts/oapps/oft/src/extensions/mod.rs +3 -0
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +164 -0
- package/contracts/oapps/oft/src/extensions/pausable.rs +50 -0
- package/contracts/oapps/oft/src/extensions/rate_limiter.rs +198 -0
- package/contracts/oapps/oft/src/lib.rs +2 -3
- package/contracts/oapps/oft/src/oft.rs +16 -85
- package/contracts/oapps/oft/src/oft_types/mint_burn.rs +1 -1
- package/contracts/oapps/oft/src/tests/extensions/mod.rs +11 -0
- package/contracts/oapps/oft/src/tests/extensions/setup.rs +888 -0
- package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +749 -0
- package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +432 -0
- package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +1078 -0
- package/contracts/oapps/oft/src/tests/mod.rs +2 -0
- package/contracts/oapps/oft/src/tests/test_utils.rs +24 -6
- package/contracts/oapps/{oft-mint-burn → oft-std}/Cargo.toml +1 -8
- package/contracts/oapps/oft-std/src/lib.rs +5 -0
- package/contracts/oapps/oft-std/src/oft.rs +59 -0
- package/contracts/utils/src/ownable.rs +2 -2
- package/contracts/utils/src/tests/ownable.rs +0 -63
- package/contracts/utils/src/ttl.rs +19 -1
- package/contracts/workers/dvn/src/auth.rs +91 -27
- package/contracts/workers/dvn/src/dvn.rs +22 -20
- package/contracts/workers/dvn/src/interfaces/dvn.rs +48 -3
- package/contracts/workers/dvn/src/interfaces/multisig.rs +41 -0
- package/contracts/workers/dvn/src/lib.rs +6 -8
- package/contracts/workers/dvn/src/multisig.rs +6 -3
- package/contracts/workers/dvn/src/tests/auth.rs +1 -1
- package/contracts/workers/dvn/src/tests/dvn.rs +3 -4
- package/contracts/workers/dvn-fee-lib/Cargo.toml +2 -1
- package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +4 -3
- package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +8 -6
- package/contracts/workers/executor/src/interfaces/executor.rs +5 -2
- package/contracts/workers/executor/src/lz_executor.rs +6 -6
- package/contracts/workers/price-feed/Cargo.toml +21 -0
- package/contracts/workers/price-feed/src/errors.rs +9 -0
- package/contracts/workers/price-feed/src/events.rs +30 -0
- package/contracts/workers/price-feed/src/lib.rs +11 -0
- package/contracts/workers/price-feed/src/price_feed.rs +265 -0
- package/contracts/workers/price-feed/src/storage.rs +42 -0
- package/contracts/workers/price-feed/src/types.rs +59 -0
- package/contracts/workers/worker/src/interfaces/dvn_fee_lib.rs +2 -1
- package/package.json +3 -3
- package/sdk/dist/generated/bml.js +3 -1
- package/sdk/dist/generated/counter.d.ts +102 -0
- package/sdk/dist/generated/counter.js +13 -1
- package/sdk/dist/generated/endpoint.js +3 -1
- package/sdk/dist/generated/sml.js +3 -1
- package/sdk/dist/generated/uln302.js +3 -1
- package/sdk/package.json +1 -1
- package/contracts/oapps/oft/src/macro_tests/mod.rs +0 -2
- package/contracts/oapps/oft/src/macro_tests/test_all_default.rs +0 -41
- package/contracts/oapps/oft/src/macro_tests/test_override.rs +0 -83
- package/contracts/oapps/oft-mint-burn/src/lib.rs +0 -3
- package/contracts/oapps/oft-mint-burn/src/oft.rs +0 -28
- package/contracts/oapps/oft-mint-burn/src/tests/mod.rs +0 -1
- package/contracts/workers/dvn/src/types.rs +0 -26
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
#![no_std]
|
|
2
|
-
#[cfg(test)]
|
|
3
|
-
extern crate std;
|
|
4
2
|
|
|
5
|
-
mod errors;
|
|
3
|
+
pub mod errors;
|
|
6
4
|
pub mod events;
|
|
7
|
-
mod interfaces;
|
|
8
|
-
mod types;
|
|
5
|
+
pub mod interfaces;
|
|
9
6
|
|
|
10
|
-
pub use errors::*;
|
|
11
7
|
pub use interfaces::*;
|
|
12
|
-
pub use types::*;
|
|
13
8
|
|
|
14
9
|
cfg_if::cfg_if! {
|
|
15
10
|
if #[cfg(any(not(feature = "library"), feature = "testutils"))] {
|
|
16
11
|
mod storage;
|
|
17
12
|
mod dvn;
|
|
18
13
|
|
|
19
|
-
pub use dvn
|
|
14
|
+
pub use dvn::*;
|
|
20
15
|
}
|
|
21
16
|
}
|
|
22
17
|
|
|
18
|
+
#[cfg(test)]
|
|
19
|
+
extern crate std;
|
|
20
|
+
|
|
23
21
|
#[cfg(test)]
|
|
24
22
|
mod tests;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
|
|
1
3
|
use crate::{
|
|
4
|
+
errors::MultisigError,
|
|
2
5
|
events::{SignerSet, ThresholdSet},
|
|
3
6
|
storage::MultisigStorage,
|
|
4
|
-
IMultisig,
|
|
5
7
|
};
|
|
8
|
+
use soroban_sdk::assert_with_error;
|
|
6
9
|
|
|
7
|
-
#[
|
|
10
|
+
#[contract_impl]
|
|
8
11
|
impl IMultisig for Dvn {
|
|
9
12
|
fn set_signer(env: &Env, signer: &BytesN<20>, active: bool) {
|
|
10
13
|
env.current_contract_address().require_auth();
|
|
@@ -109,7 +112,7 @@ fn set_threshold(env: &Env, threshold: u32) {
|
|
|
109
112
|
ThresholdSet { threshold }.publish(env);
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
fn init_multisig(env: &Env, signers: &Vec<BytesN<20>>, threshold: u32) {
|
|
115
|
+
pub fn init_multisig(env: &Env, signers: &Vec<BytesN<20>>, threshold: u32) {
|
|
113
116
|
signers.iter().for_each(|signer| add_signer(env, &signer));
|
|
114
117
|
|
|
115
118
|
set_threshold(env, threshold);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use crate::tests::setup::{TestSetup, VID};
|
|
2
|
-
use crate::{errors::DvnError, errors::MultisigError
|
|
2
|
+
use crate::{dvn::auth::TransactionAuthData, errors::DvnError, errors::MultisigError};
|
|
3
3
|
use ed25519_dalek::{Signer, SigningKey};
|
|
4
4
|
use rand::thread_rng;
|
|
5
5
|
use soroban_sdk::{auth::Context, vec, Bytes, BytesN, Env, IntoVal, Vec};
|
|
@@ -52,11 +52,10 @@ fn test_vid_returns_configured_value() {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
#[test]
|
|
55
|
-
|
|
56
|
-
fn test_dst_config_not_set_panics() {
|
|
55
|
+
fn test_dst_config_not_set_returns_none() {
|
|
57
56
|
let setup = TestSetup::new(1);
|
|
58
57
|
let client = DVNClient::new(&setup.env, &setup.contract_id);
|
|
59
|
-
client.dst_config(&999);
|
|
58
|
+
assert_eq!(client.dst_config(&999), None);
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
#[test]
|
|
@@ -339,7 +338,7 @@ struct MockFeeLib;
|
|
|
339
338
|
|
|
340
339
|
#[contractimpl]
|
|
341
340
|
impl IDvnFeeLib for MockFeeLib {
|
|
342
|
-
fn get_fee(_env: &Env, params: &DvnFeeParams) -> i128 {
|
|
341
|
+
fn get_fee(_env: &Env, _dvn: &Address, params: &DvnFeeParams) -> i128 {
|
|
343
342
|
if params.multiplier_bps == 0 {
|
|
344
343
|
params.default_multiplier_bps as i128
|
|
345
344
|
} else {
|
|
@@ -16,9 +16,10 @@ doctest = false
|
|
|
16
16
|
[dependencies]
|
|
17
17
|
cfg-if = "1.0"
|
|
18
18
|
soroban-sdk = { workspace = true }
|
|
19
|
+
common-macros = { workspace = true }
|
|
19
20
|
message-lib-common = { workspace = true }
|
|
21
|
+
utils = { workspace = true }
|
|
20
22
|
worker = { workspace = true }
|
|
21
|
-
common-macros = { workspace = true }
|
|
22
23
|
|
|
23
24
|
[dev-dependencies]
|
|
24
25
|
soroban-sdk = { workspace = true, features = ["testutils"] }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
use crate::DvnFeeLibError;
|
|
2
|
-
use
|
|
2
|
+
use common_macros::contract_impl;
|
|
3
|
+
use soroban_sdk::{assert_with_error, contract, Address, Env};
|
|
3
4
|
use worker::{DvnFeeParams, IDvnFeeLib, LayerZeroPriceFeedClient};
|
|
4
5
|
|
|
5
6
|
// ============================================================================
|
|
@@ -29,9 +30,9 @@ pub(crate) const NATIVE_DECIMALS_RATE: u128 = 10_000_000;
|
|
|
29
30
|
#[contract]
|
|
30
31
|
pub struct DvnFeeLib;
|
|
31
32
|
|
|
32
|
-
#[
|
|
33
|
+
#[contract_impl]
|
|
33
34
|
impl IDvnFeeLib for DvnFeeLib {
|
|
34
|
-
fn get_fee(env: &Env, params: &DvnFeeParams) -> i128 {
|
|
35
|
+
fn get_fee(env: &Env, _dvn: &Address, params: &DvnFeeParams) -> i128 {
|
|
35
36
|
assert_with_error!(env, params.gas > 0, DvnFeeLibError::InvalidGas);
|
|
36
37
|
assert_with_error!(env, params.options.is_empty(), DvnFeeLibError::InvalidDVNOptions);
|
|
37
38
|
|
|
@@ -77,6 +77,7 @@ fn apply_premium_multiplier_wins_over_small_floor() {
|
|
|
77
77
|
fn get_fee_panics_when_gas_zero() {
|
|
78
78
|
let env = Env::default();
|
|
79
79
|
env.mock_all_auths();
|
|
80
|
+
let fee_lib_addr = env.register(DvnFeeLib, ());
|
|
80
81
|
let params = DvnFeeParams {
|
|
81
82
|
sender: Address::generate(&env),
|
|
82
83
|
dst_eid: 1,
|
|
@@ -90,7 +91,7 @@ fn get_fee_panics_when_gas_zero() {
|
|
|
90
91
|
floor_margin_usd: 0,
|
|
91
92
|
};
|
|
92
93
|
|
|
93
|
-
DvnFeeLib::get_fee(&env, ¶ms);
|
|
94
|
+
env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &fee_lib_addr, ¶ms));
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
#[test]
|
|
@@ -98,6 +99,7 @@ fn get_fee_panics_when_gas_zero() {
|
|
|
98
99
|
fn get_fee_panics_when_options_not_empty() {
|
|
99
100
|
let env = Env::default();
|
|
100
101
|
env.mock_all_auths();
|
|
102
|
+
let fee_lib_addr = env.register(DvnFeeLib, ());
|
|
101
103
|
let mut options = Bytes::new(&env);
|
|
102
104
|
options.push_back(1u8);
|
|
103
105
|
|
|
@@ -114,7 +116,7 @@ fn get_fee_panics_when_options_not_empty() {
|
|
|
114
116
|
floor_margin_usd: 0,
|
|
115
117
|
};
|
|
116
118
|
|
|
117
|
-
DvnFeeLib::get_fee(&env, ¶ms);
|
|
119
|
+
env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &fee_lib_addr, ¶ms));
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
#[contract]
|
|
@@ -183,7 +185,7 @@ fn get_fee_success_path() {
|
|
|
183
185
|
floor_margin_usd: 0,
|
|
184
186
|
};
|
|
185
187
|
|
|
186
|
-
let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, ¶ms));
|
|
188
|
+
let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &fee_lib_addr, ¶ms));
|
|
187
189
|
|
|
188
190
|
assert_eq!(fee, 100);
|
|
189
191
|
}
|
|
@@ -209,7 +211,7 @@ fn get_fee_uses_default_multiplier_when_zero() {
|
|
|
209
211
|
floor_margin_usd: 0,
|
|
210
212
|
};
|
|
211
213
|
|
|
212
|
-
let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, ¶ms));
|
|
214
|
+
let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &fee_lib_addr, ¶ms));
|
|
213
215
|
|
|
214
216
|
assert_eq!(fee, 120);
|
|
215
217
|
}
|
|
@@ -235,7 +237,7 @@ fn get_fee_prefers_dst_multiplier_over_default() {
|
|
|
235
237
|
floor_margin_usd: 0,
|
|
236
238
|
};
|
|
237
239
|
|
|
238
|
-
let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, ¶ms));
|
|
240
|
+
let fee = env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &fee_lib_addr, ¶ms));
|
|
239
241
|
|
|
240
242
|
assert_eq!(fee, 150);
|
|
241
243
|
}
|
|
@@ -259,7 +261,7 @@ fn get_fee_panics_when_price_feed_returns_negative_fee() {
|
|
|
259
261
|
floor_margin_usd: 0,
|
|
260
262
|
};
|
|
261
263
|
|
|
262
|
-
env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, ¶ms));
|
|
264
|
+
env.as_contract(&fee_lib_addr, || DvnFeeLib::get_fee(&env, &fee_lib_addr, ¶ms));
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
#[test]
|
|
@@ -59,11 +59,14 @@ pub trait IExecutor: Worker + ILayerZeroExecutor {
|
|
|
59
59
|
/// * `params` - Vector of (dst_eid, DstConfig) pairs to set
|
|
60
60
|
fn set_dst_config(env: &Env, admin: &Address, params: &Vec<SetDstConfigParam>);
|
|
61
61
|
|
|
62
|
-
///
|
|
62
|
+
/// Gets the destination configuration for a specific endpoint.
|
|
63
63
|
///
|
|
64
64
|
/// # Arguments
|
|
65
65
|
/// * `dst_eid` - Destination endpoint ID (chain identifier)
|
|
66
|
-
|
|
66
|
+
///
|
|
67
|
+
/// # Returns
|
|
68
|
+
/// The destination configuration, or `None` if not set
|
|
69
|
+
fn dst_config(env: &Env, dst_eid: u32) -> Option<DstConfig>;
|
|
67
70
|
|
|
68
71
|
/// Native token drops.
|
|
69
72
|
///
|
|
@@ -19,8 +19,8 @@ use soroban_sdk::{
|
|
|
19
19
|
use utils::option_ext::OptionExt;
|
|
20
20
|
use utils::ownable::Ownable;
|
|
21
21
|
use worker::{
|
|
22
|
-
assert_acl, assert_not_paused, assert_supported_message_lib, init_worker, require_admin_auth,
|
|
23
|
-
FeeParams, Worker,
|
|
22
|
+
assert_acl, assert_not_paused, assert_supported_message_lib, init_worker, require_admin_auth, set_admin_by_owner,
|
|
23
|
+
ExecutorFeeLibClient, FeeParams, Worker,
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
/// Signature data for Custom Account authorization.
|
|
@@ -140,8 +140,8 @@ impl IExecutor for LzExecutor {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
/// Returns the destination configuration for a specific endpoint.
|
|
143
|
-
fn dst_config(env: &Env, dst_eid: u32) -> DstConfig {
|
|
144
|
-
ExecutorStorage::dst_config(env, dst_eid)
|
|
143
|
+
fn dst_config(env: &Env, dst_eid: u32) -> Option<DstConfig> {
|
|
144
|
+
ExecutorStorage::dst_config(env, dst_eid)
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/// Native token drops.
|
|
@@ -219,7 +219,7 @@ impl ILayerZeroExecutor for LzExecutor {
|
|
|
219
219
|
assert_not_paused::<Self>(env);
|
|
220
220
|
assert_acl::<Self>(env, sender);
|
|
221
221
|
|
|
222
|
-
let dst_config = Self::dst_config(env, dst_eid);
|
|
222
|
+
let dst_config = Self::dst_config(env, dst_eid).unwrap_or_panic(env, ExecutorError::EidNotSupported);
|
|
223
223
|
let fee_params = FeeParams {
|
|
224
224
|
sender: sender.clone(),
|
|
225
225
|
dst_eid,
|
|
@@ -304,5 +304,5 @@ impl LzExecutor {
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
#[
|
|
307
|
+
#[contract_impl(contracttrait)]
|
|
308
308
|
impl Worker for LzExecutor {}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "price-feed"
|
|
3
|
+
version.workspace = true
|
|
4
|
+
edition.workspace = true
|
|
5
|
+
license.workspace = true
|
|
6
|
+
publish = false
|
|
7
|
+
|
|
8
|
+
[lib]
|
|
9
|
+
crate-type = ["cdylib"]
|
|
10
|
+
doctest = false
|
|
11
|
+
|
|
12
|
+
[dependencies]
|
|
13
|
+
soroban-sdk = { workspace = true }
|
|
14
|
+
endpoint-v2 = { workspace = true, features = ["library"] }
|
|
15
|
+
utils = { workspace = true }
|
|
16
|
+
worker = { workspace = true }
|
|
17
|
+
common-macros = { workspace = true }
|
|
18
|
+
|
|
19
|
+
[dev-dependencies]
|
|
20
|
+
soroban-sdk = { workspace = true, features = ["testutils"] }
|
|
21
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
use soroban_sdk::{contractevent, Address};
|
|
2
|
+
|
|
3
|
+
use worker::Price;
|
|
4
|
+
|
|
5
|
+
use crate::types::ArbitrumPriceExt;
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Price Feed Events
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
#[contractevent]
|
|
12
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
13
|
+
pub struct PriceUpdaterSet {
|
|
14
|
+
pub updater: Address,
|
|
15
|
+
pub active: bool,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#[contractevent]
|
|
19
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
20
|
+
pub struct PriceUpdated {
|
|
21
|
+
pub dst_eid: u32,
|
|
22
|
+
pub price: Price,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[contractevent]
|
|
26
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
27
|
+
pub struct ArbitrumPriceExtUpdated {
|
|
28
|
+
pub dst_eid: u32,
|
|
29
|
+
pub arbitrum_price_ext: ArbitrumPriceExt,
|
|
30
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
use common_macros::{only_owner, ttl_configurable};
|
|
2
|
+
use soroban_sdk::{assert_with_error, contract, contractimpl, panic_with_error, Address, Env, Vec};
|
|
3
|
+
use utils::ownable::Ownable;
|
|
4
|
+
use worker::{FeeEstimate, ILayerZeroPriceFeed, Price};
|
|
5
|
+
|
|
6
|
+
use crate::{
|
|
7
|
+
errors::PriceFeedError,
|
|
8
|
+
events::{ArbitrumPriceExtUpdated, PriceUpdated, PriceUpdaterSet},
|
|
9
|
+
storage::PriceFeedStorage,
|
|
10
|
+
types::{ArbitrumPriceExt, ModelType, SetEidToModelTypeParam, UpdatePrice, UpdatePriceExt},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
#[contract]
|
|
14
|
+
#[ttl_configurable]
|
|
15
|
+
pub struct LzPriceFeed;
|
|
16
|
+
|
|
17
|
+
#[contractimpl]
|
|
18
|
+
impl LzPriceFeed {
|
|
19
|
+
pub fn __constructor(env: &Env, owner: &Address, price_updater: &Address) {
|
|
20
|
+
Self::init_owner(env, owner);
|
|
21
|
+
|
|
22
|
+
PriceFeedStorage::set_price_updater(env, price_updater, &true);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#[contractimpl]
|
|
27
|
+
impl ILayerZeroPriceFeed for LzPriceFeed {
|
|
28
|
+
/// Estimate fee with detailed breakdown by endpoint ID
|
|
29
|
+
/// Corresponds to estimateFeeByEid in PriceFeed.sol
|
|
30
|
+
fn estimate_fee_by_eid(env: &Env, fee_lib: &Address, dst_eid: u32, calldata_size: u32, gas: u128) -> FeeEstimate {
|
|
31
|
+
fee_lib.require_auth();
|
|
32
|
+
|
|
33
|
+
let eid = dst_eid % 30_000;
|
|
34
|
+
|
|
35
|
+
let (fee, price_ratio) = if eid == 110 || eid == 10143 || eid == 20143 {
|
|
36
|
+
Self::estimate_fee_with_arbitrum_model(env, eid, calldata_size, gas)
|
|
37
|
+
} else if eid == 111 || eid == 10132 || eid == 20132 {
|
|
38
|
+
Self::estimate_fee_with_optimism_model(env, eid, calldata_size, gas)
|
|
39
|
+
} else {
|
|
40
|
+
// Check configured model type
|
|
41
|
+
let model_type = Self::eid_to_model_type(env, eid);
|
|
42
|
+
match model_type {
|
|
43
|
+
ModelType::OpStack => Self::estimate_fee_with_optimism_model(env, eid, calldata_size, gas),
|
|
44
|
+
ModelType::ArbStack => Self::estimate_fee_with_arbitrum_model(env, eid, calldata_size, gas),
|
|
45
|
+
ModelType::Default => Self::estimate_fee_with_default_model(env, eid, calldata_size, gas),
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let price_ratio_denominator = Self::get_price_ratio_denominator(env);
|
|
50
|
+
let native_price_usd = Self::native_token_price_usd(env);
|
|
51
|
+
|
|
52
|
+
FeeEstimate { total_gas_fee: fee as i128, price_ratio, price_ratio_denominator, native_price_usd }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Get the native token price in USD
|
|
56
|
+
fn native_token_price_usd(env: &Env) -> u128 {
|
|
57
|
+
PriceFeedStorage::native_price_usd(env)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Get the price for a destination EID.
|
|
61
|
+
fn get_price(env: &Env, dst_eid: u32) -> Price {
|
|
62
|
+
PriceFeedStorage::default_model_price(env, dst_eid)
|
|
63
|
+
.unwrap_or_else(|| panic_with_error!(env, PriceFeedError::NoPrice))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Get the price ratio denominator.
|
|
67
|
+
fn get_price_ratio_denominator(env: &Env) -> u128 {
|
|
68
|
+
PriceFeedStorage::price_ratio_denominator(env)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#[contractimpl]
|
|
73
|
+
impl LzPriceFeed {
|
|
74
|
+
// ========================================================================
|
|
75
|
+
// Owner Functions
|
|
76
|
+
// ========================================================================
|
|
77
|
+
|
|
78
|
+
/// Set price updater status (owner only)
|
|
79
|
+
#[only_owner]
|
|
80
|
+
pub fn set_price_updater(env: &Env, updater: &Address, active: bool) {
|
|
81
|
+
PriceFeedStorage::set_price_updater(env, updater, &active);
|
|
82
|
+
PriceUpdaterSet { updater: updater.clone(), active }.publish(env);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// Set the price ratio denominator (owner only)
|
|
86
|
+
#[only_owner]
|
|
87
|
+
pub fn set_price_ratio_denominator(env: &Env, denominator: u128) {
|
|
88
|
+
assert_with_error!(env, denominator > 0, PriceFeedError::InvalidDenominator);
|
|
89
|
+
PriceFeedStorage::set_price_ratio_denominator(env, &denominator);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Set the Arbitrum compression percentage (owner only)
|
|
93
|
+
#[only_owner]
|
|
94
|
+
pub fn set_arbitrum_compression_percent(env: &Env, compression_percent: u128) {
|
|
95
|
+
PriceFeedStorage::set_arbitrum_compression_percent(env, &compression_percent);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Set the fee model type for destination EIDs (owner only)
|
|
99
|
+
#[only_owner]
|
|
100
|
+
pub fn set_eid_to_model_type(env: &Env, params: &Vec<SetEidToModelTypeParam>) {
|
|
101
|
+
for param in params.iter() {
|
|
102
|
+
PriceFeedStorage::set_eid_to_model_type(env, param.dst_eid, ¶m.model_type);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ========================================================================
|
|
107
|
+
// Price Updater Functions
|
|
108
|
+
// ========================================================================
|
|
109
|
+
|
|
110
|
+
/// Set prices for multiple destinations (price updater or owner)
|
|
111
|
+
pub fn set_price(env: &Env, price_updater: &Address, prices: &Vec<UpdatePrice>) {
|
|
112
|
+
Self::require_price_updater(env, price_updater);
|
|
113
|
+
|
|
114
|
+
for update in prices.iter() {
|
|
115
|
+
Self::set_price_internal(env, update.eid, &update.price);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Set price for Arbitrum with extension (price updater or owner)
|
|
120
|
+
/// Corresponds to setPriceForArbitrum in PriceFeed.sol
|
|
121
|
+
pub fn set_price_for_arbitrum(env: &Env, price_updater: &Address, update: &UpdatePriceExt) {
|
|
122
|
+
Self::require_price_updater(env, price_updater);
|
|
123
|
+
|
|
124
|
+
Self::set_price_internal(env, update.eid, &update.price);
|
|
125
|
+
|
|
126
|
+
// Update Arbitrum-specific price extension
|
|
127
|
+
PriceFeedStorage::set_arbitrum_price_ext(env, &update.extend);
|
|
128
|
+
ArbitrumPriceExtUpdated { dst_eid: update.eid, arbitrum_price_ext: update.extend.clone() }.publish(env);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Set the native token price in USD (price updater or owner).
|
|
132
|
+
///
|
|
133
|
+
/// Kept as a standalone contract function (not part of the canonical `worker::ILayerZeroPriceFeed` interface).
|
|
134
|
+
pub fn set_native_token_price_usd(env: &Env, price_updater: &Address, native_token_price_usd: u128) {
|
|
135
|
+
Self::require_price_updater(env, price_updater);
|
|
136
|
+
PriceFeedStorage::set_native_price_usd(env, &native_token_price_usd);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ========================================================================
|
|
140
|
+
// View Functions
|
|
141
|
+
// ========================================================================
|
|
142
|
+
|
|
143
|
+
/// Check if an address is an active price updater
|
|
144
|
+
pub fn price_updater(env: &Env, updater: &Address) -> bool {
|
|
145
|
+
PriceFeedStorage::price_updater(env, updater)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Get the Arbitrum compression percent
|
|
149
|
+
pub fn arbitrum_compression_percent(env: &Env) -> u128 {
|
|
150
|
+
PriceFeedStorage::arbitrum_compression_percent(env)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Get the Arbitrum price extension
|
|
154
|
+
pub fn arbitrum_price_ext(env: &Env) -> ArbitrumPriceExt {
|
|
155
|
+
PriceFeedStorage::arbitrum_price_ext(env)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Get the model type for a destination EID
|
|
159
|
+
pub fn eid_to_model_type(env: &Env, dst_eid: u32) -> ModelType {
|
|
160
|
+
PriceFeedStorage::eid_to_model_type(env, dst_eid)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ========================================================================
|
|
164
|
+
// Internal Helper Functions
|
|
165
|
+
// ========================================================================
|
|
166
|
+
|
|
167
|
+
/// Set price for a destination EID
|
|
168
|
+
fn set_price_internal(env: &Env, dst_eid: u32, price: &Price) {
|
|
169
|
+
PriceFeedStorage::set_default_model_price(env, dst_eid, price);
|
|
170
|
+
PriceUpdated { dst_eid, price: price.clone() }.publish(env);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// Estimate fee with default model
|
|
174
|
+
fn estimate_fee_with_default_model(env: &Env, dst_eid: u32, calldata_size: u32, gas: u128) -> (u128, u128) {
|
|
175
|
+
let price = Self::get_price(env, dst_eid);
|
|
176
|
+
|
|
177
|
+
// assuming the _gas includes (1) the 21,000 overhead and (2) not the calldata gas
|
|
178
|
+
let gas_for_calldata = (calldata_size as u128) * (price.gas_per_byte as u128);
|
|
179
|
+
let remote_fee = (gas_for_calldata + gas) * (price.gas_price_in_unit as u128);
|
|
180
|
+
let fee = (remote_fee * price.price_ratio) / Self::get_price_ratio_denominator(env);
|
|
181
|
+
|
|
182
|
+
(fee, price.price_ratio)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Estimate fee with Optimism model
|
|
186
|
+
fn estimate_fee_with_optimism_model(env: &Env, dst_eid: u32, calldata_size: u32, gas: u128) -> (u128, u128) {
|
|
187
|
+
let ethereum_id = Self::get_l1_lookup_id_for_optimism_model(env, dst_eid);
|
|
188
|
+
|
|
189
|
+
// L1 fee (Ethereum)
|
|
190
|
+
let ethereum_price = Self::get_price(env, ethereum_id);
|
|
191
|
+
let gas_for_l1_calldata = ((calldata_size as u128) * (ethereum_price.gas_per_byte as u128)) + 3188; // 2100 + 68 * 16
|
|
192
|
+
let l1_fee = gas_for_l1_calldata * (ethereum_price.gas_price_in_unit as u128);
|
|
193
|
+
|
|
194
|
+
// L2 fee (Optimism)
|
|
195
|
+
let optimism_price = Self::get_price(env, dst_eid);
|
|
196
|
+
let gas_for_l2_calldata: u128 = (calldata_size as u128) * (optimism_price.gas_per_byte as u128);
|
|
197
|
+
let l2_fee = (gas_for_l2_calldata + gas) * (optimism_price.gas_price_in_unit as u128);
|
|
198
|
+
|
|
199
|
+
let price_ratio_denom = Self::get_price_ratio_denominator(env);
|
|
200
|
+
let l1_fee_in_src_price = (l1_fee * ethereum_price.price_ratio) / price_ratio_denom;
|
|
201
|
+
let l2_fee_in_src_price = (l2_fee * optimism_price.price_ratio) / price_ratio_denom;
|
|
202
|
+
let gas_fee = l1_fee_in_src_price + l2_fee_in_src_price;
|
|
203
|
+
|
|
204
|
+
(gas_fee, optimism_price.price_ratio)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Estimate fee with Arbitrum model
|
|
208
|
+
fn estimate_fee_with_arbitrum_model(env: &Env, dst_eid: u32, calldata_size: u32, gas: u128) -> (u128, u128) {
|
|
209
|
+
let arbitrum_price = Self::get_price(env, dst_eid);
|
|
210
|
+
|
|
211
|
+
let arbitrum_price_ext = Self::arbitrum_price_ext(env);
|
|
212
|
+
|
|
213
|
+
// L1 fee (compressed calldata)
|
|
214
|
+
let gas_for_l1_calldata = (((calldata_size as u128) * Self::arbitrum_compression_percent(env)) / 100)
|
|
215
|
+
* (arbitrum_price_ext.gas_per_l1_calldata_byte as u128);
|
|
216
|
+
|
|
217
|
+
// L2 fee
|
|
218
|
+
let gas_for_l2_calldata = (calldata_size as u128) * (arbitrum_price.gas_per_byte as u128);
|
|
219
|
+
let gas_fee = (gas + (arbitrum_price_ext.gas_per_l2_tx as u128) + gas_for_l1_calldata + gas_for_l2_calldata)
|
|
220
|
+
* (arbitrum_price.gas_price_in_unit as u128);
|
|
221
|
+
|
|
222
|
+
let fee = (gas_fee * arbitrum_price.price_ratio) / Self::get_price_ratio_denominator(env);
|
|
223
|
+
|
|
224
|
+
(fee, arbitrum_price.price_ratio)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// Get L1 lookup ID for Optimism model
|
|
228
|
+
fn get_l1_lookup_id_for_optimism_model(env: &Env, l2_eid: u32) -> u32 {
|
|
229
|
+
let eid = l2_eid % 30_000;
|
|
230
|
+
|
|
231
|
+
if eid == 111 {
|
|
232
|
+
return 101;
|
|
233
|
+
} else if eid == 10132 {
|
|
234
|
+
return 10121; // Ethereum Goerli
|
|
235
|
+
} else if eid == 20132 {
|
|
236
|
+
return 20121; // Ethereum Goerli
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Check if this EID is configured as OP_STACK model type
|
|
240
|
+
assert_with_error!(env, Self::eid_to_model_type(env, eid) == ModelType::OpStack, PriceFeedError::NotAnOpStack);
|
|
241
|
+
|
|
242
|
+
if eid < 10000 {
|
|
243
|
+
101
|
|
244
|
+
} else if eid < 20000 {
|
|
245
|
+
10161 // Ethereum Sepolia
|
|
246
|
+
} else {
|
|
247
|
+
20121 // Ethereum Goerli
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/// Check if caller is price updater or owner
|
|
252
|
+
fn require_price_updater(env: &Env, caller: &Address) {
|
|
253
|
+
caller.require_auth();
|
|
254
|
+
|
|
255
|
+
// Owner is always approved
|
|
256
|
+
if let Some(owner) = Self::owner(env) {
|
|
257
|
+
if &owner == caller {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check if caller is an active price updater
|
|
263
|
+
assert_with_error!(env, Self::price_updater(env, caller), PriceFeedError::OnlyPriceUpdater);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
use common_macros::storage;
|
|
2
|
+
use soroban_sdk::Address;
|
|
3
|
+
use worker::Price;
|
|
4
|
+
|
|
5
|
+
use crate::types::{ArbitrumPriceExt, ModelType};
|
|
6
|
+
|
|
7
|
+
#[storage()]
|
|
8
|
+
pub enum PriceFeedStorage {
|
|
9
|
+
/// Price ratio denominator used for price calculations (initialized to 1e20)
|
|
10
|
+
#[instance(u128)]
|
|
11
|
+
#[default(10u128.pow(20))]
|
|
12
|
+
PriceRatioDenominator,
|
|
13
|
+
|
|
14
|
+
/// Price updater status mapping (address => bool active)
|
|
15
|
+
#[persistent(bool)]
|
|
16
|
+
#[default(false)]
|
|
17
|
+
PriceUpdater { updater: Address },
|
|
18
|
+
|
|
19
|
+
/// Default price model for each destination EID
|
|
20
|
+
#[persistent(Price)]
|
|
21
|
+
DefaultModelPrice { dst_eid: u32 },
|
|
22
|
+
|
|
23
|
+
/// Arbitrum-specific price extension
|
|
24
|
+
#[instance(ArbitrumPriceExt)]
|
|
25
|
+
#[default(ArbitrumPriceExt { gas_per_l2_tx: 0, gas_per_l1_calldata_byte: 0 })]
|
|
26
|
+
ArbitrumPriceExt,
|
|
27
|
+
|
|
28
|
+
/// Native token price in USD (uses PRICE_RATIO_DENOMINATOR)
|
|
29
|
+
#[instance(u128)]
|
|
30
|
+
#[default(0)]
|
|
31
|
+
NativePriceUSD,
|
|
32
|
+
|
|
33
|
+
/// Arbitrum compression percentage (initialized to 47)
|
|
34
|
+
#[instance(u128)]
|
|
35
|
+
#[default(47)]
|
|
36
|
+
ArbitrumCompressionPercent,
|
|
37
|
+
|
|
38
|
+
/// Fee model type for each destination EID
|
|
39
|
+
#[persistent(ModelType)]
|
|
40
|
+
#[default(ModelType::Default)]
|
|
41
|
+
EidToModelType { dst_eid: u32 },
|
|
42
|
+
}
|