@layerzerolabs/protocol-stellar-v2 0.2.10 → 0.2.12
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 +273 -219
- package/.turbo/turbo-lint.log +79 -107
- package/.turbo/turbo-test.log +1016 -840
- package/Cargo.lock +14 -6
- package/contracts/common-macros/src/contract_impl.rs +6 -3
- package/contracts/common-macros/src/error.rs +9 -17
- package/contracts/common-macros/src/lib.rs +4 -37
- 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/endpoint-v2/src/endpoint_v2.rs +4 -4
- package/contracts/endpoint-v2/src/events.rs +40 -22
- package/contracts/endpoint-v2/src/interfaces/message_lib.rs +2 -2
- package/contracts/endpoint-v2/src/interfaces/message_lib_manager.rs +2 -2
- package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +2 -2
- package/contracts/endpoint-v2/src/interfaces/messaging_composer.rs +2 -2
- package/contracts/endpoint-v2/src/interfaces/send_lib.rs +2 -2
- package/contracts/endpoint-v2/src/message_lib_manager.rs +3 -3
- package/contracts/endpoint-v2/src/messaging_channel.rs +1 -1
- package/contracts/endpoint-v2/src/messaging_composer.rs +1 -1
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +4 -8
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +3 -7
- package/contracts/message-libs/{block-message-lib → blocked-message-lib}/Cargo.toml +1 -1
- package/contracts/message-libs/treasury/src/events.rs +9 -6
- package/contracts/message-libs/uln-302/src/events.rs +19 -11
- package/contracts/message-libs/uln-302/src/interfaces/receive_uln.rs +2 -2
- package/contracts/message-libs/uln-302/src/interfaces/send_uln.rs +2 -2
- package/contracts/message-libs/uln-302/src/receive_uln.rs +2 -2
- package/contracts/message-libs/uln-302/src/send_uln.rs +3 -3
- package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +5 -5
- package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +5 -5
- package/contracts/message-libs/uln-302/src/tests/setup.rs +3 -3
- package/contracts/message-libs/uln-302/src/types.rs +24 -24
- package/contracts/message-libs/uln-302/src/uln302.rs +2 -2
- package/contracts/oapp-macros/src/oapp_core.rs +1 -1
- package/contracts/oapps/counter/integration_tests/utils.rs +1 -1
- package/contracts/oapps/oapp/src/oapp_core.rs +4 -3
- package/contracts/oapps/oapp/src/oapp_options_type3.rs +4 -3
- package/contracts/oapps/oft/integration-tests/setup.rs +4 -3
- package/contracts/oapps/oft/integration-tests/utils.rs +1 -1
- package/contracts/oapps/oft/src/default_oft_impl.rs +146 -0
- package/contracts/oapps/oft/src/events.rs +5 -4
- package/contracts/oapps/oft/src/extensions/mod.rs +3 -0
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +168 -0
- package/contracts/oapps/oft/src/extensions/pausable.rs +50 -0
- package/contracts/oapps/oft/src/extensions/rate_limiter.rs +200 -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 +8 -6
- package/contracts/utils/src/tests/ownable.rs +0 -63
- package/contracts/utils/src/tests/testing_utils.rs +7 -5
- package/contracts/utils/src/ttl.rs +21 -2
- package/contracts/workers/dvn/src/auth.rs +108 -30
- package/contracts/workers/dvn/src/dvn.rs +103 -33
- package/contracts/workers/dvn/src/errors.rs +10 -13
- package/contracts/workers/dvn/src/events.rs +7 -5
- package/contracts/workers/dvn/src/interfaces/dvn.rs +76 -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 +98 -72
- package/contracts/workers/dvn/src/storage.rs +9 -12
- package/contracts/workers/dvn/src/tests/auth.rs +56 -26
- package/contracts/workers/dvn/src/tests/dvn.rs +40 -41
- package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +8 -8
- package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +9 -9
- package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +6 -6
- package/contracts/workers/dvn/src/tests/setup.rs +5 -5
- 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/auth.rs +93 -0
- package/contracts/workers/executor/src/events.rs +5 -4
- package/contracts/workers/executor/src/{lz_executor.rs → executor.rs} +30 -103
- package/contracts/workers/executor/src/interfaces/executor.rs +5 -2
- package/contracts/workers/executor/src/interfaces/mod.rs +1 -1
- package/contracts/workers/executor/src/lib.rs +6 -5
- 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/events.rs +23 -13
- package/contracts/workers/worker/src/interfaces/dvn_fee_lib.rs +2 -1
- package/contracts/workers/worker/src/worker.rs +32 -21
- package/package.json +3 -3
- package/sdk/dist/generated/bml.js +24 -22
- package/sdk/dist/generated/counter.d.ts +102 -0
- package/sdk/dist/generated/counter.js +36 -24
- package/sdk/dist/generated/endpoint.js +24 -22
- package/sdk/dist/generated/sml.js +24 -22
- package/sdk/dist/generated/uln302.d.ts +1 -1
- package/sdk/dist/generated/uln302.js +34 -32
- package/sdk/package.json +1 -1
- package/sdk/test/index.test.ts +1 -1
- package/sdk/test/oft.test.ts +847 -0
- package/sdk/test/suites/scan.ts +20 -4
- package/tools/ts-bindings-gen/src/main.rs +2 -1
- package/contracts/common-macros/src/event.rs +0 -16
- 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
- /package/contracts/message-libs/{block-message-lib → blocked-message-lib}/src/lib.rs +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
use common_macros::event;
|
|
2
1
|
use endpoint_v2::Origin;
|
|
3
|
-
use soroban_sdk::{Address, Vec};
|
|
2
|
+
use soroban_sdk::{contractevent, Address, Vec};
|
|
4
3
|
|
|
5
4
|
use crate::interfaces::{NativeDropParams, SetDstConfigParam};
|
|
6
5
|
|
|
7
|
-
#[
|
|
6
|
+
#[contractevent]
|
|
7
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
8
8
|
pub struct DstConfigSet {
|
|
9
9
|
pub params: Vec<SetDstConfigParam>,
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
#[
|
|
12
|
+
#[contractevent]
|
|
13
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
13
14
|
pub struct NativeDropApplied {
|
|
14
15
|
pub origin: Origin,
|
|
15
16
|
pub dst_eid: u32,
|
|
@@ -8,44 +8,14 @@ use crate::{
|
|
|
8
8
|
use common_macros::{contract_impl, only_owner, ttl_configurable};
|
|
9
9
|
use endpoint_v2::{FeeRecipient, LayerZeroEndpointV2Client, Origin};
|
|
10
10
|
use message_lib_common::interfaces::ILayerZeroExecutor;
|
|
11
|
-
use soroban_sdk::{
|
|
12
|
-
address_payload::AddressPayload,
|
|
13
|
-
auth::{Context, CustomAccountInterface},
|
|
14
|
-
contract, contractimpl, contracttype,
|
|
15
|
-
crypto::Hash,
|
|
16
|
-
token::TokenClient,
|
|
17
|
-
vec, Address, Bytes, BytesN, Env, Symbol, Vec,
|
|
18
|
-
};
|
|
11
|
+
use soroban_sdk::{contract, token::TokenClient, vec, Address, Bytes, BytesN, Env, Symbol, Vec};
|
|
19
12
|
use utils::option_ext::OptionExt;
|
|
20
13
|
use utils::ownable::Ownable;
|
|
21
14
|
use worker::{
|
|
22
|
-
assert_acl, assert_not_paused, assert_supported_message_lib, init_worker, require_admin_auth,
|
|
23
|
-
FeeParams, Worker,
|
|
15
|
+
assert_acl, assert_not_paused, assert_supported_message_lib, init_worker, require_admin_auth, set_admin_by_owner,
|
|
16
|
+
ExecutorFeeLibClient, FeeParams, Worker,
|
|
24
17
|
};
|
|
25
18
|
|
|
26
|
-
/// Signature data for Custom Account authorization.
|
|
27
|
-
/// Contains the admin's public key and their Ed25519 signature over the authorization payload.
|
|
28
|
-
#[contracttype]
|
|
29
|
-
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
30
|
-
pub struct ExecutorSignature {
|
|
31
|
-
/// Admin's Ed25519 public key (32 bytes) - must correspond to a registered admin
|
|
32
|
-
pub public_key: BytesN<32>,
|
|
33
|
-
/// Ed25519 signature (64 bytes) over the signature_payload
|
|
34
|
-
pub signature: BytesN<64>,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/// Whitelist entry for authorized (contract, function) pairs.
|
|
38
|
-
///
|
|
39
|
-
/// Used to configure which contracts and functions can trigger Executor authorization.
|
|
40
|
-
#[contracttype]
|
|
41
|
-
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
42
|
-
pub struct WhitelistEntry {
|
|
43
|
-
/// Contract address that is allowed to call the function.
|
|
44
|
-
pub contract: Address,
|
|
45
|
-
/// Function name that is whitelisted for this contract.
|
|
46
|
-
pub fn_name: Symbol,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
19
|
/// LayerZero Executor contract for cross-chain message execution.
|
|
50
20
|
#[contract]
|
|
51
21
|
#[ttl_configurable]
|
|
@@ -65,6 +35,8 @@ impl LzExecutor {
|
|
|
65
35
|
/// * `price_feed` - Price feed contract address for fee calculations
|
|
66
36
|
/// * `default_multiplier_bps` - Default fee multiplier in basis points (10000 = 1x)
|
|
67
37
|
/// * `whitelist` - Initial whitelisted (contract, function) pairs for authorization
|
|
38
|
+
/// * `worker_fee_lib` - Worker fee library contract address
|
|
39
|
+
/// * `deposit_address` - Address to receive fee payments
|
|
68
40
|
pub fn __constructor(
|
|
69
41
|
env: &Env,
|
|
70
42
|
endpoint: &Address,
|
|
@@ -73,15 +45,25 @@ impl LzExecutor {
|
|
|
73
45
|
message_libs: &Vec<Address>,
|
|
74
46
|
price_feed: &Address,
|
|
75
47
|
default_multiplier_bps: u32,
|
|
76
|
-
whitelist: &Vec<
|
|
48
|
+
whitelist: &Vec<(Address, Symbol)>,
|
|
49
|
+
worker_fee_lib: &Address,
|
|
50
|
+
deposit_address: &Address,
|
|
77
51
|
) {
|
|
78
52
|
Self::init_owner(env, owner);
|
|
79
|
-
init_worker::<Self>(
|
|
53
|
+
init_worker::<Self>(
|
|
54
|
+
env,
|
|
55
|
+
admins,
|
|
56
|
+
message_libs,
|
|
57
|
+
price_feed,
|
|
58
|
+
default_multiplier_bps,
|
|
59
|
+
worker_fee_lib,
|
|
60
|
+
deposit_address,
|
|
61
|
+
);
|
|
80
62
|
ExecutorStorage::set_endpoint(env, endpoint);
|
|
81
63
|
|
|
82
64
|
// Set initial whitelisted functions
|
|
83
|
-
for
|
|
84
|
-
ExecutorStorage::set_whitelisted_fn(env, &
|
|
65
|
+
for (contract, fn_name) in whitelist.iter() {
|
|
66
|
+
ExecutorStorage::set_whitelisted_fn(env, &contract, &fn_name, &true);
|
|
85
67
|
}
|
|
86
68
|
}
|
|
87
69
|
|
|
@@ -140,8 +122,8 @@ impl IExecutor for LzExecutor {
|
|
|
140
122
|
}
|
|
141
123
|
|
|
142
124
|
/// 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)
|
|
125
|
+
fn dst_config(env: &Env, dst_eid: u32) -> Option<DstConfig> {
|
|
126
|
+
ExecutorStorage::dst_config(env, dst_eid)
|
|
145
127
|
}
|
|
146
128
|
|
|
147
129
|
/// Native token drops.
|
|
@@ -219,7 +201,7 @@ impl ILayerZeroExecutor for LzExecutor {
|
|
|
219
201
|
assert_not_paused::<Self>(env);
|
|
220
202
|
assert_acl::<Self>(env, sender);
|
|
221
203
|
|
|
222
|
-
let dst_config = Self::dst_config(env, dst_eid);
|
|
204
|
+
let dst_config = Self::dst_config(env, dst_eid).unwrap_or_panic(env, ExecutorError::EidNotSupported);
|
|
223
205
|
let fee_params = FeeParams {
|
|
224
206
|
sender: sender.clone(),
|
|
225
207
|
dst_eid,
|
|
@@ -239,70 +221,15 @@ impl ILayerZeroExecutor for LzExecutor {
|
|
|
239
221
|
}
|
|
240
222
|
|
|
241
223
|
// ============================================================================
|
|
242
|
-
//
|
|
224
|
+
// Worker Implementation
|
|
243
225
|
// ============================================================================
|
|
244
226
|
|
|
245
|
-
#[
|
|
246
|
-
impl
|
|
247
|
-
type Signature = ExecutorSignature;
|
|
248
|
-
type Error = ExecutorError;
|
|
249
|
-
|
|
250
|
-
/// Verifies authorization for the executor contract.
|
|
251
|
-
///
|
|
252
|
-
/// The public key must correspond to a registered admin and must have signed the signature_payload.
|
|
253
|
-
/// Uses Ed25519 signature verification.
|
|
254
|
-
/// Only whitelisted function calls are authorized.
|
|
255
|
-
fn __check_auth(
|
|
256
|
-
env: Env,
|
|
257
|
-
signature_payload: Hash<32>,
|
|
258
|
-
auth_data: Self::Signature,
|
|
259
|
-
auth_contexts: Vec<Context>,
|
|
260
|
-
) -> Result<(), Self::Error> {
|
|
261
|
-
Self::verify_admin_signature(&env, &signature_payload, &auth_data)?;
|
|
262
|
-
Self::validate_auth_contexts(&env, &auth_contexts)?;
|
|
263
|
-
Ok(())
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
impl LzExecutor {
|
|
268
|
-
/// Verifies that the signature is from a registered admin.
|
|
269
|
-
///
|
|
270
|
-
/// Converts the public key to an address, checks admin registration,
|
|
271
|
-
/// and verifies the Ed25519 signature over the payload.
|
|
272
|
-
fn verify_admin_signature(
|
|
273
|
-
env: &Env,
|
|
274
|
-
signature_payload: &Hash<32>,
|
|
275
|
-
auth_data: &ExecutorSignature,
|
|
276
|
-
) -> Result<(), ExecutorError> {
|
|
277
|
-
let admin = Address::from_payload(env, AddressPayload::AccountIdPublicKeyEd25519(auth_data.public_key.clone()));
|
|
278
|
-
if !Self::is_admin(env, &admin) {
|
|
279
|
-
return Err(ExecutorError::Unauthorized);
|
|
280
|
-
}
|
|
281
|
-
env.crypto().ed25519_verify(&auth_data.public_key, &signature_payload.clone().into(), &auth_data.signature);
|
|
282
|
-
Ok(())
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/// Validates that the first auth context is a whitelisted (contract, function) pair.
|
|
286
|
-
/// Sub-invocations are trusted since they're controlled by the whitelisted contract.
|
|
287
|
-
fn validate_auth_contexts(env: &Env, contexts: &Vec<Context>) -> Result<(), ExecutorError> {
|
|
288
|
-
let first_context = contexts.first().ok_or(ExecutorError::UnauthorizedContext)?;
|
|
289
|
-
|
|
290
|
-
match first_context {
|
|
291
|
-
Context::Contract(contract_context) => {
|
|
292
|
-
// Check if (contract, fn_name) pair is whitelisted
|
|
293
|
-
if !Self::is_whitelisted_fn(env, &contract_context.contract, &contract_context.fn_name) {
|
|
294
|
-
return Err(ExecutorError::UnauthorizedContext);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
// Contract creation is not allowed
|
|
298
|
-
Context::CreateContractHostFn(_) | Context::CreateContractWithCtorHostFn(_) => {
|
|
299
|
-
return Err(ExecutorError::UnauthorizedContext);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
227
|
+
#[contract_impl(contracttrait)]
|
|
228
|
+
impl Worker for LzExecutor {}
|
|
302
229
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// Include SubModules
|
|
232
|
+
// ============================================================================
|
|
306
233
|
|
|
307
|
-
#[
|
|
308
|
-
|
|
234
|
+
#[path = "auth.rs"]
|
|
235
|
+
pub mod auth;
|
|
@@ -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
|
///
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
#![no_std]
|
|
2
2
|
|
|
3
|
-
mod
|
|
3
|
+
pub mod errors;
|
|
4
|
+
pub mod events;
|
|
5
|
+
pub mod interfaces;
|
|
6
|
+
|
|
4
7
|
pub use interfaces::*;
|
|
5
8
|
|
|
6
9
|
cfg_if::cfg_if! {
|
|
7
10
|
// Include implementation when NOT in library mode, OR when testutils is enabled (for tests)
|
|
8
11
|
if #[cfg(any(not(feature = "library"), feature = "testutils"))] {
|
|
9
|
-
mod errors;
|
|
10
|
-
mod events;
|
|
11
12
|
mod storage;
|
|
12
|
-
mod
|
|
13
|
+
mod executor;
|
|
13
14
|
|
|
14
|
-
pub use
|
|
15
|
+
pub use executor::*;
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -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
|
+
}
|