@layerzerolabs/protocol-stellar-v2 0.2.20 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +783 -802
- package/.turbo/turbo-lint.log +320 -157
- package/.turbo/turbo-test.log +1414 -1457
- package/Cargo.lock +109 -108
- package/Cargo.toml +32 -18
- package/contracts/common-macros/Cargo.toml +7 -7
- package/contracts/common-macros/src/auth.rs +18 -37
- package/contracts/common-macros/src/contract_ttl.rs +2 -2
- package/contracts/common-macros/src/lib.rs +27 -10
- package/contracts/common-macros/src/lz_contract.rs +38 -7
- package/contracts/common-macros/src/storage.rs +251 -292
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +6 -12
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +12 -17
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +2 -7
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +20 -9
- package/contracts/common-macros/src/tests/upgradeable.rs +26 -4
- package/contracts/common-macros/src/ttl_configurable.rs +2 -10
- package/contracts/common-macros/src/ttl_extendable.rs +2 -10
- package/contracts/common-macros/src/upgradeable.rs +56 -15
- package/contracts/common-macros/src/utils.rs +0 -9
- package/contracts/endpoint-v2/src/lib.rs +3 -2
- package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +2 -2
- package/contracts/endpoint-v2/src/tests/endpoint_v2/lz_receive_alert.rs +3 -3
- package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +4 -4
- package/contracts/endpoint-v2/src/tests/endpoint_v2/set_delegate.rs +17 -5
- package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
- package/contracts/endpoint-v2/src/tests/endpoint_v2/verify.rs +2 -2
- package/contracts/endpoint-v2/src/tests/message_lib_manager/register_library.rs +2 -2
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_lib_timeout.rs +6 -6
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_receive_library.rs +67 -37
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_default_send_library.rs +5 -5
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library.rs +44 -54
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_receive_library_timeout.rs +7 -7
- package/contracts/endpoint-v2/src/tests/message_lib_manager/set_send_library.rs +8 -8
- package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +3 -3
- package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +4 -4
- package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +3 -3
- package/contracts/endpoint-v2/src/tests/messaging_composer/clear_compose.rs +2 -2
- package/contracts/endpoint-v2/src/tests/messaging_composer/lz_compose_alert.rs +3 -3
- package/contracts/endpoint-v2/src/tests/messaging_composer/send_compose.rs +2 -2
- package/contracts/layerzero-views/Cargo.toml +0 -1
- package/contracts/layerzero-views/src/layerzero_view.rs +1 -13
- package/contracts/macro-integration-tests/Cargo.toml +5 -15
- package/contracts/macro-integration-tests/tests/runtime/oapp/mod.rs +48 -0
- package/contracts/macro-integration-tests/tests/runtime/oapp/oapp_core.rs +170 -0
- package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +154 -0
- package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +338 -0
- package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +435 -0
- package/contracts/macro-integration-tests/tests/runtime.rs +1 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.rs +8 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.rs +8 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.stderr +71 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.rs +10 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +5 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.rs +8 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.rs +8 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_all.rs +38 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_single_trait.rs +96 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/minimal_contract.rs +64 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/struct_with_fields.rs +46 -0
- package/contracts/macro-integration-tests/tests/ui/ownable/fail/only_auth_missing_env.stderr +8 -0
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +1 -1
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/only_auth_env_param_variants.rs +1 -1
- package/contracts/macro-integration-tests/tests/ui_oapp.rs +11 -0
- package/contracts/message-libs/message-lib-common/Cargo.toml +0 -1
- package/contracts/message-libs/message-lib-common/src/errors.rs +1 -1
- package/contracts/message-libs/treasury/Cargo.toml +0 -2
- package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/receive_uln302/set_default_receive_uln_configs.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/receive_uln302/verify.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +7 -27
- package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_executor_configs.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/send_uln302/set_default_send_uln_configs.rs +2 -2
- package/contracts/oapps/counter/Cargo.toml +4 -6
- package/contracts/oapps/counter/integration_tests/utils.rs +19 -12
- package/contracts/oapps/oapp/src/errors.rs +1 -1
- package/contracts/oapps/oapp/src/interfaces/mod.rs +3 -0
- package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +47 -0
- package/contracts/oapps/oapp/src/lib.rs +1 -0
- package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +4 -4
- package/contracts/oapps/oapp/src/oapp_core.rs +5 -5
- package/contracts/oapps/oapp/src/oapp_options_type3.rs +12 -4
- package/contracts/oapps/oapp/src/oapp_receiver.rs +14 -9
- package/contracts/oapps/oapp/src/tests/mod.rs +4 -4
- package/contracts/oapps/oapp/src/tests/{test_oapp_core.rs → oapp_core.rs} +4 -4
- package/contracts/oapps/oapp/src/tests/{test_oapp_options_type3.rs → oapp_options_type3.rs} +3 -4
- package/contracts/oapps/oapp-macros/Cargo.toml +8 -4
- package/contracts/oapps/oapp-macros/src/generators.rs +9 -34
- package/contracts/oapps/oapp-macros/src/lib.rs +3 -0
- package/contracts/oapps/oapp-macros/src/tests/mod.rs +2 -0
- package/contracts/oapps/oapp-macros/src/tests/oapp.rs +88 -0
- package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +86 -0
- package/contracts/oapps/oapp-macros/src/tests/snapshots/oapp_macros__tests__oapp__snapshot_generate_oapp.snap +103 -0
- package/contracts/oapps/oft/integration-tests/utils.rs +28 -8
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +136 -74
- package/contracts/oapps/oft/src/extensions/pausable.rs +44 -10
- package/contracts/oapps/oft/src/extensions/rate_limiter.rs +170 -130
- package/contracts/oapps/oft/src/oft.rs +19 -12
- package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +1 -1
- package/contracts/oapps/oft/src/oft_types/mint_burn.rs +1 -1
- package/contracts/oapps/oft-core/Cargo.toml +1 -4
- package/contracts/oapps/oft-core/integration-tests/setup.rs +2 -2
- package/contracts/oapps/oft-core/integration-tests/utils.rs +21 -3
- package/contracts/oapps/oft-core/src/errors.rs +3 -2
- package/contracts/oapps/oft-core/src/events.rs +6 -0
- package/contracts/oapps/oft-core/src/lib.rs +1 -1
- package/contracts/oapps/oft-core/src/oft_core.rs +115 -60
- package/contracts/oapps/oft-core/src/storage.rs +7 -3
- package/contracts/oapps/oft-core/src/tests/mod.rs +1 -0
- package/contracts/oapps/oft-core/src/tests/test_decimals.rs +37 -2
- package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +2 -2
- package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +323 -0
- package/contracts/oapps/oft-core/src/tests/test_send.rs +2 -2
- package/contracts/oapps/oft-core/src/tests/test_utils.rs +59 -14
- package/contracts/utils/Cargo.toml +0 -1
- package/contracts/utils/src/errors.rs +1 -1
- package/contracts/utils/src/multisig.rs +17 -8
- package/contracts/utils/src/ownable.rs +6 -6
- package/contracts/utils/src/testing_utils.rs +124 -54
- package/contracts/utils/src/tests/multisig.rs +12 -12
- package/contracts/utils/src/tests/ownable.rs +6 -6
- package/contracts/utils/src/tests/testing_utils.rs +50 -167
- package/contracts/utils/src/tests/ttl_configurable.rs +5 -5
- package/contracts/utils/src/tests/upgradeable.rs +1 -1
- package/contracts/utils/src/ttl_configurable.rs +10 -4
- package/contracts/utils/src/upgradeable.rs +5 -5
- package/contracts/workers/dvn/Cargo.toml +5 -6
- package/contracts/workers/dvn/src/dvn.rs +2 -12
- package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
- package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +37 -19
- package/contracts/workers/dvn-fee-lib/src/lib.rs +12 -2
- package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +15 -13
- package/contracts/workers/executor/Cargo.toml +3 -0
- package/contracts/workers/executor/src/executor.rs +2 -12
- package/contracts/workers/executor/src/lib.rs +2 -2
- package/contracts/workers/executor/src/tests/auth.rs +394 -0
- package/contracts/workers/executor/src/tests/executor.rs +410 -0
- package/contracts/workers/executor/src/tests/mod.rs +3 -0
- package/contracts/workers/executor/src/tests/setup.rs +250 -0
- package/contracts/workers/executor-fee-lib/Cargo.toml +5 -0
- package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +1 -12
- package/contracts/workers/executor-fee-lib/src/lib.rs +8 -2
- package/contracts/workers/executor-helper/Cargo.toml +0 -1
- package/contracts/workers/price-feed/Cargo.toml +5 -0
- package/contracts/workers/price-feed/src/lib.rs +9 -4
- package/contracts/workers/price-feed/src/price_feed.rs +1 -11
- package/contracts/workers/worker/src/errors.rs +1 -1
- package/contracts/workers/worker/src/tests/setup.rs +1 -1
- package/contracts/workers/worker/src/tests/worker.rs +55 -41
- package/contracts/workers/worker/src/worker.rs +34 -25
- package/docs/error-spec.md +55 -0
- package/docs/layerzero-v2-on-stellar.md +447 -0
- package/docs/oapp-guide.md +212 -0
- package/docs/oft-guide.md +314 -0
- package/package.json +3 -3
- package/sdk/.turbo/turbo-test.log +260 -257
- package/sdk/dist/generated/bml.d.ts +3 -3
- package/sdk/dist/generated/bml.js +4 -4
- package/sdk/dist/generated/counter.d.ts +295 -295
- package/sdk/dist/generated/counter.js +43 -43
- package/sdk/dist/generated/dvn.d.ts +91 -91
- package/sdk/dist/generated/dvn.js +24 -24
- package/sdk/dist/generated/dvn_fee_lib.d.ts +92 -92
- package/sdk/dist/generated/dvn_fee_lib.js +25 -25
- package/sdk/dist/generated/endpoint.d.ts +99 -99
- package/sdk/dist/generated/endpoint.js +16 -16
- package/sdk/dist/generated/executor.d.ts +91 -91
- package/sdk/dist/generated/executor.js +24 -24
- package/sdk/dist/generated/executor_fee_lib.d.ts +92 -92
- package/sdk/dist/generated/executor_fee_lib.js +25 -25
- package/sdk/dist/generated/executor_helper.d.ts +3 -3
- package/sdk/dist/generated/executor_helper.js +4 -4
- package/sdk/dist/generated/layerzero_view.d.ts +186 -186
- package/sdk/dist/generated/layerzero_view.js +35 -35
- package/sdk/dist/generated/oft.d.ts +366 -352
- package/sdk/dist/generated/oft.js +74 -79
- package/sdk/dist/generated/price_feed.d.ts +198 -198
- package/sdk/dist/generated/price_feed.js +39 -39
- package/sdk/dist/generated/sml.d.ts +99 -99
- package/sdk/dist/generated/sml.js +16 -16
- package/sdk/dist/generated/treasury.d.ts +99 -99
- package/sdk/dist/generated/treasury.js +16 -16
- package/sdk/dist/generated/uln302.d.ts +99 -99
- package/sdk/dist/generated/uln302.js +16 -16
- package/sdk/dist/generated/upgrader.d.ts +3 -3
- package/sdk/dist/generated/upgrader.js +3 -3
- package/sdk/package.json +1 -1
- package/sdk/test/suites/localnet.ts +84 -20
- package/contracts/ERROR_SPEC.md +0 -51
- package/contracts/endpoint-v2/ARCHITECTURE.md +0 -233
- /package/contracts/oapps/oapp/src/tests/{test_oapp_receiver.rs → oapp_receiver.rs} +0 -0
- /package/contracts/oapps/oapp/src/tests/{test_oapp_sender.rs → oapp_sender.rs} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
use crate as oft;
|
|
1
2
|
use common_macros::{contract_error, contract_trait, only_auth, storage};
|
|
2
|
-
use soroban_sdk::{assert_with_error, contractevent, contracttype,
|
|
3
|
-
use utils::
|
|
3
|
+
use soroban_sdk::{assert_with_error, contractevent, contracttype, Env};
|
|
4
|
+
use utils::auth::Auth;
|
|
4
5
|
|
|
5
6
|
// =========================================================================
|
|
6
7
|
// Types
|
|
@@ -14,12 +15,29 @@ pub enum Direction {
|
|
|
14
15
|
Outbound,
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
/// Configuration for rate limiting, used as input parameter.
|
|
17
19
|
#[contracttype]
|
|
18
20
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
19
|
-
pub struct
|
|
20
|
-
limit: i128,
|
|
21
|
-
window_seconds: u64,
|
|
21
|
+
pub struct RateLimitConfig {
|
|
22
|
+
pub limit: i128,
|
|
23
|
+
pub window_seconds: u64,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Internal storage struct for rate limit state.
|
|
27
|
+
///
|
|
28
|
+
/// The rate limiter uses a "leaky bucket" algorithm where:
|
|
29
|
+
/// - `config.limit` defines the maximum tokens that can be "in flight" at any time
|
|
30
|
+
/// - `config.window_seconds` defines how long it takes for the bucket to fully drain
|
|
31
|
+
/// - Tokens decay linearly over time: `decay = elapsed_time * limit / window_seconds`
|
|
32
|
+
/// - Current in-flight = `in_flight_on_last_update - decay` (clamped to 0)
|
|
33
|
+
#[contracttype]
|
|
34
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
35
|
+
struct RateLimitState {
|
|
36
|
+
/// The rate limit configuration (limit and window_seconds)
|
|
37
|
+
config: RateLimitConfig,
|
|
38
|
+
/// The in-flight amount at the time of `last_update` (before decay is applied)
|
|
22
39
|
in_flight_on_last_update: i128,
|
|
40
|
+
/// Timestamp of the last update (used to calculate decay)
|
|
23
41
|
last_update: u64,
|
|
24
42
|
}
|
|
25
43
|
|
|
@@ -28,8 +46,8 @@ pub struct RateLimit {
|
|
|
28
46
|
// =========================================================================
|
|
29
47
|
|
|
30
48
|
#[storage]
|
|
31
|
-
|
|
32
|
-
#[persistent(
|
|
49
|
+
enum RateLimitStorage {
|
|
50
|
+
#[persistent(RateLimitState)]
|
|
33
51
|
RateLimit { direction: Direction, eid: u32 },
|
|
34
52
|
}
|
|
35
53
|
|
|
@@ -40,9 +58,9 @@ pub enum RateLimitStorage {
|
|
|
40
58
|
#[contract_error]
|
|
41
59
|
pub enum RateLimitError {
|
|
42
60
|
ExceededRateLimit = 3120,
|
|
61
|
+
InvalidAmount,
|
|
43
62
|
InvalidTimestamp,
|
|
44
|
-
|
|
45
|
-
InvalidLimit,
|
|
63
|
+
InvalidConfig,
|
|
46
64
|
SameValue,
|
|
47
65
|
}
|
|
48
66
|
|
|
@@ -55,24 +73,8 @@ pub enum RateLimitError {
|
|
|
55
73
|
pub struct RateLimitSet {
|
|
56
74
|
pub direction: Direction,
|
|
57
75
|
pub eid: u32,
|
|
58
|
-
|
|
59
|
-
pub
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
#[contractevent]
|
|
63
|
-
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
64
|
-
pub struct RateLimitUpdated {
|
|
65
|
-
pub direction: Direction,
|
|
66
|
-
pub eid: u32,
|
|
67
|
-
pub limit: i128,
|
|
68
|
-
pub window_seconds: u64,
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
#[contractevent]
|
|
72
|
-
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
73
|
-
pub struct RateLimitUnset {
|
|
74
|
-
pub direction: Direction,
|
|
75
|
-
pub eid: u32,
|
|
76
|
+
/// The rate limit configuration, or None if the rate limit is removed
|
|
77
|
+
pub config: Option<RateLimitConfig>,
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
// =========================================================================
|
|
@@ -80,114 +82,167 @@ pub struct RateLimitUnset {
|
|
|
80
82
|
// =========================================================================
|
|
81
83
|
|
|
82
84
|
#[contract_trait]
|
|
83
|
-
pub trait RateLimiter: RateLimiterInternal +
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
assert_with_error!(env, window_seconds > 0, RateLimitError::InvalidWindowSeconds);
|
|
88
|
-
if RateLimitStorage::has_rate_limit(env, direction, eid) {
|
|
89
|
-
let rate_limit_data = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
|
|
90
|
-
assert_with_error!(
|
|
91
|
-
env,
|
|
92
|
-
limit != rate_limit_data.limit || window_seconds != rate_limit_data.window_seconds,
|
|
93
|
-
RateLimitError::SameValue
|
|
94
|
-
);
|
|
95
|
-
checkpoint_rate_limit_in_flight(env, direction, eid);
|
|
96
|
-
let mut rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
|
|
97
|
-
rate_limit.limit = limit;
|
|
98
|
-
rate_limit.window_seconds = window_seconds;
|
|
99
|
-
RateLimitStorage::set_rate_limit(env, direction, eid, &rate_limit);
|
|
100
|
-
RateLimitUpdated { direction: direction.clone(), eid, limit, window_seconds }.publish(env);
|
|
101
|
-
} else {
|
|
102
|
-
RateLimitStorage::set_rate_limit(
|
|
103
|
-
env,
|
|
104
|
-
direction,
|
|
105
|
-
eid,
|
|
106
|
-
&RateLimit {
|
|
107
|
-
limit,
|
|
108
|
-
window_seconds,
|
|
109
|
-
in_flight_on_last_update: 0,
|
|
110
|
-
last_update: env.ledger().timestamp(),
|
|
111
|
-
},
|
|
112
|
-
);
|
|
113
|
-
RateLimitSet { direction: direction.clone(), eid, limit, window_seconds }.publish(env);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
85
|
+
pub trait RateLimiter: RateLimiterInternal + Auth {
|
|
86
|
+
// =========================================================================
|
|
87
|
+
// Management Functions
|
|
88
|
+
// =========================================================================
|
|
116
89
|
|
|
90
|
+
/// Sets or removes a rate limit for a specific direction and endpoint.
|
|
91
|
+
///
|
|
92
|
+
/// # Arguments
|
|
93
|
+
/// * `direction` - The direction (Inbound or Outbound)
|
|
94
|
+
/// * `eid` - The endpoint ID
|
|
95
|
+
/// * `config` - The rate limit configuration, or None to remove the rate limit
|
|
117
96
|
#[only_auth]
|
|
118
|
-
fn
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
97
|
+
fn set_rate_limit(
|
|
98
|
+
env: &soroban_sdk::Env,
|
|
99
|
+
direction: &oft::rate_limiter::Direction,
|
|
100
|
+
eid: u32,
|
|
101
|
+
config: Option<oft::rate_limiter::RateLimitConfig>,
|
|
102
|
+
) {
|
|
103
|
+
Self::__set_rate_limit(env, direction, eid, config);
|
|
122
104
|
}
|
|
123
105
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
106
|
+
// =========================================================================
|
|
107
|
+
// View Functions
|
|
108
|
+
// =========================================================================
|
|
109
|
+
|
|
110
|
+
/// Returns the rate limit configuration for a direction and endpoint.
|
|
111
|
+
/// Returns None if no rate limit is configured.
|
|
112
|
+
fn rate_limit_config(
|
|
113
|
+
env: &soroban_sdk::Env,
|
|
114
|
+
direction: &oft::rate_limiter::Direction,
|
|
115
|
+
eid: u32,
|
|
116
|
+
) -> Option<oft::rate_limiter::RateLimitConfig> {
|
|
117
|
+
Self::__rate_limit_config(env, direction, eid)
|
|
130
118
|
}
|
|
131
119
|
|
|
132
|
-
|
|
133
|
-
|
|
120
|
+
/// Returns the current in-flight amount for a direction and endpoint.
|
|
121
|
+
fn rate_limit_in_flight(env: &soroban_sdk::Env, direction: &oft::rate_limiter::Direction, eid: u32) -> i128 {
|
|
122
|
+
Self::__rate_limit_in_flight(env, direction, eid)
|
|
134
123
|
}
|
|
135
124
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
let rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
|
|
141
|
-
let in_flight = calculate_in_flight(env, direction, eid);
|
|
142
|
-
if rate_limit.limit > in_flight {
|
|
143
|
-
rate_limit.limit - in_flight
|
|
144
|
-
} else {
|
|
145
|
-
0
|
|
146
|
-
}
|
|
125
|
+
/// Returns the available capacity for a direction and endpoint.
|
|
126
|
+
/// Returns i128::MAX if no rate limit is configured.
|
|
127
|
+
fn rate_limit_capacity(env: &soroban_sdk::Env, direction: &oft::rate_limiter::Direction, eid: u32) -> i128 {
|
|
128
|
+
Self::__rate_limit_capacity(env, direction, eid)
|
|
147
129
|
}
|
|
148
130
|
}
|
|
149
131
|
|
|
150
132
|
/// Internal trait for rate limiter operations used by OFT hooks.
|
|
151
133
|
/// Contains only truly internal methods that are called from OFTInternal implementations.
|
|
152
134
|
pub trait RateLimiterInternal {
|
|
135
|
+
// =========================================================================
|
|
136
|
+
// OFT Hooks
|
|
137
|
+
// =========================================================================
|
|
138
|
+
|
|
153
139
|
/// Consumes the specified amount from the rate limit capacity.
|
|
154
140
|
/// Used internally by `__debit` and `__credit` to enforce rate limits.
|
|
155
141
|
///
|
|
156
142
|
/// # Errors
|
|
157
143
|
/// * `ExceededRateLimit` - If the amount exceeds the available capacity.
|
|
158
144
|
fn __consume_rate_limit_capacity(env: &Env, direction: &Direction, eid: u32, amount: i128) {
|
|
159
|
-
|
|
145
|
+
assert_with_error!(env, amount >= 0, RateLimitError::InvalidAmount);
|
|
146
|
+
|
|
147
|
+
let Some(mut state) = RateLimitStorage::rate_limit(env, direction, eid) else {
|
|
160
148
|
return;
|
|
161
|
-
}
|
|
162
|
-
checkpoint_rate_limit_in_flight(env, direction, eid);
|
|
163
|
-
let mut rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
|
|
164
|
-
// Check against remaining capacity (limit - current in_flight), not total limit
|
|
165
|
-
let capacity = if rate_limit.limit > rate_limit.in_flight_on_last_update {
|
|
166
|
-
rate_limit.limit - rate_limit.in_flight_on_last_update
|
|
167
|
-
} else {
|
|
168
|
-
0
|
|
169
149
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
150
|
+
|
|
151
|
+
// Apply decay and update timestamp
|
|
152
|
+
let in_flight = calculate_decayed_in_flight(env, &state);
|
|
153
|
+
state.in_flight_on_last_update = in_flight;
|
|
154
|
+
state.last_update = env.ledger().timestamp();
|
|
155
|
+
|
|
156
|
+
// Check capacity and consume
|
|
157
|
+
let capacity = (state.config.limit - in_flight).max(0);
|
|
158
|
+
assert_with_error!(env, amount <= capacity, RateLimitError::ExceededRateLimit);
|
|
159
|
+
state.in_flight_on_last_update += amount;
|
|
160
|
+
|
|
161
|
+
RateLimitStorage::set_rate_limit(env, direction, eid, &state);
|
|
175
162
|
}
|
|
176
163
|
|
|
177
164
|
/// Releases the specified amount back to the rate limit capacity.
|
|
178
165
|
/// Used internally by `__credit` to release outbound capacity on inbound messages.
|
|
179
166
|
fn __release_rate_limit_capacity(env: &Env, direction: &Direction, eid: u32, amount: i128) {
|
|
180
|
-
|
|
167
|
+
assert_with_error!(env, amount >= 0, RateLimitError::InvalidAmount);
|
|
168
|
+
|
|
169
|
+
let Some(mut state) = RateLimitStorage::rate_limit(env, direction, eid) else {
|
|
181
170
|
return;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Apply decay and update timestamp
|
|
174
|
+
let in_flight = calculate_decayed_in_flight(env, &state);
|
|
175
|
+
state.in_flight_on_last_update = (in_flight - amount).max(0);
|
|
176
|
+
state.last_update = env.ledger().timestamp();
|
|
177
|
+
|
|
178
|
+
RateLimitStorage::set_rate_limit(env, direction, eid, &state);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// =========================================================================
|
|
182
|
+
// Management Functions
|
|
183
|
+
// =========================================================================
|
|
184
|
+
|
|
185
|
+
/// Sets or removes a rate limit for a specific direction and endpoint.
|
|
186
|
+
///
|
|
187
|
+
/// # Arguments
|
|
188
|
+
/// * `direction` - The direction (Inbound or Outbound)
|
|
189
|
+
/// * `eid` - The endpoint ID
|
|
190
|
+
/// * `config` - The rate limit configuration, or None to remove the rate limit
|
|
191
|
+
fn __set_rate_limit(env: &Env, direction: &Direction, eid: u32, config: Option<RateLimitConfig>) {
|
|
192
|
+
let current_state = RateLimitStorage::rate_limit(env, direction, eid);
|
|
193
|
+
let current_config = current_state.as_ref().map(|s| s.config.clone());
|
|
194
|
+
assert_with_error!(env, current_config != config, RateLimitError::SameValue);
|
|
195
|
+
|
|
196
|
+
match config {
|
|
197
|
+
Some(cfg) => {
|
|
198
|
+
assert_with_error!(env, cfg.limit >= 0 && cfg.window_seconds > 0, RateLimitError::InvalidConfig);
|
|
199
|
+
|
|
200
|
+
let state = if let Some(mut existing) = current_state {
|
|
201
|
+
// Update existing: checkpoint in-flight before changing config
|
|
202
|
+
existing.in_flight_on_last_update = calculate_decayed_in_flight(env, &existing);
|
|
203
|
+
existing.last_update = env.ledger().timestamp();
|
|
204
|
+
existing.config = cfg.clone();
|
|
205
|
+
existing
|
|
206
|
+
} else {
|
|
207
|
+
// Create new
|
|
208
|
+
RateLimitState {
|
|
209
|
+
config: cfg.clone(),
|
|
210
|
+
in_flight_on_last_update: 0,
|
|
211
|
+
last_update: env.ledger().timestamp(),
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
RateLimitStorage::set_rate_limit(env, direction, eid, &state);
|
|
216
|
+
RateLimitSet { direction: direction.clone(), eid, config: Some(cfg) }.publish(env);
|
|
217
|
+
}
|
|
218
|
+
None => {
|
|
219
|
+
RateLimitStorage::remove_rate_limit(env, direction, eid);
|
|
220
|
+
RateLimitSet { direction: direction.clone(), eid, config: None }.publish(env);
|
|
221
|
+
}
|
|
182
222
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// =========================================================================
|
|
226
|
+
// View Functions
|
|
227
|
+
// =========================================================================
|
|
228
|
+
|
|
229
|
+
/// Returns the rate limit configuration for a direction and endpoint.
|
|
230
|
+
/// Returns None if no rate limit is configured.
|
|
231
|
+
fn __rate_limit_config(env: &Env, direction: &Direction, eid: u32) -> Option<RateLimitConfig> {
|
|
232
|
+
RateLimitStorage::rate_limit(env, direction, eid).map(|s| s.config)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// Returns the current in-flight amount for a direction and endpoint.
|
|
236
|
+
fn __rate_limit_in_flight(env: &Env, direction: &Direction, eid: u32) -> i128 {
|
|
237
|
+
RateLimitStorage::rate_limit(env, direction, eid).map(|s| calculate_decayed_in_flight(env, &s)).unwrap_or(0)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/// Returns the available capacity for a direction and endpoint.
|
|
241
|
+
/// Returns i128::MAX if no rate limit is configured.
|
|
242
|
+
fn __rate_limit_capacity(env: &Env, direction: &Direction, eid: u32) -> i128 {
|
|
243
|
+
RateLimitStorage::rate_limit(env, direction, eid)
|
|
244
|
+
.map(|s| (s.config.limit - calculate_decayed_in_flight(env, &s)).max(0))
|
|
245
|
+
.unwrap_or(i128::MAX)
|
|
191
246
|
}
|
|
192
247
|
}
|
|
193
248
|
|
|
@@ -195,30 +250,15 @@ pub trait RateLimiterInternal {
|
|
|
195
250
|
// Helper Functions
|
|
196
251
|
// =========================================================================
|
|
197
252
|
|
|
198
|
-
///
|
|
199
|
-
///
|
|
200
|
-
fn
|
|
201
|
-
if !RateLimitStorage::has_rate_limit(env, direction, eid) {
|
|
202
|
-
return 0;
|
|
203
|
-
}
|
|
204
|
-
let rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
|
|
253
|
+
/// Calculates the current in-flight amount with decay applied.
|
|
254
|
+
/// This is a pure function that doesn't access storage.
|
|
255
|
+
fn calculate_decayed_in_flight(env: &Env, state: &RateLimitState) -> i128 {
|
|
205
256
|
let timestamp = env.ledger().timestamp();
|
|
206
|
-
assert_with_error!(env, timestamp >=
|
|
207
|
-
|
|
208
|
-
let
|
|
209
|
-
|
|
210
|
-
rate_limit.in_flight_on_last_update - decay
|
|
211
|
-
} else {
|
|
212
|
-
0
|
|
213
|
-
}
|
|
214
|
-
}
|
|
257
|
+
assert_with_error!(env, timestamp >= state.last_update, RateLimitError::InvalidTimestamp);
|
|
258
|
+
|
|
259
|
+
let elapsed = timestamp - state.last_update;
|
|
260
|
+
let decay = (elapsed as i128) * state.config.limit / (state.config.window_seconds as i128);
|
|
215
261
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
fn checkpoint_rate_limit_in_flight(env: &Env, direction: &Direction, eid: u32) {
|
|
219
|
-
let in_flight = calculate_in_flight(env, direction, eid);
|
|
220
|
-
let mut rate_limit = RateLimitStorage::rate_limit(env, direction, eid).unwrap();
|
|
221
|
-
rate_limit.in_flight_on_last_update = in_flight;
|
|
222
|
-
rate_limit.last_update = env.ledger().timestamp();
|
|
223
|
-
RateLimitStorage::set_rate_limit(env, direction, eid, &rate_limit);
|
|
262
|
+
// Ensure the decayed in-flight amount is not negative
|
|
263
|
+
(state.in_flight_on_last_update - decay).max(0)
|
|
224
264
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
+
self as oft,
|
|
2
3
|
extensions::{
|
|
3
4
|
oft_fee::{OFTFee, OFTFeeInternal},
|
|
4
5
|
pausable::{OFTPausable, OFTPausableInternal},
|
|
@@ -7,7 +8,6 @@ use crate::{
|
|
|
7
8
|
oft_types::{lock_unlock, mint_burn, OftType},
|
|
8
9
|
};
|
|
9
10
|
use common_macros::{contract_impl, storage};
|
|
10
|
-
use endpoint_v2::Origin;
|
|
11
11
|
use oapp_macros::oapp;
|
|
12
12
|
use oft_core::{
|
|
13
13
|
errors::OFTError,
|
|
@@ -15,7 +15,7 @@ use oft_core::{
|
|
|
15
15
|
types::{OFTFeeDetail, OFTLimit, OFTReceipt, SendParam},
|
|
16
16
|
utils as oft_utils, OFTCore, OFTInternal,
|
|
17
17
|
};
|
|
18
|
-
use soroban_sdk::{assert_with_error, vec, Address, Bytes,
|
|
18
|
+
use soroban_sdk::{assert_with_error, vec, Address, Bytes, Env, Vec};
|
|
19
19
|
|
|
20
20
|
// =========================================================================
|
|
21
21
|
// Storage
|
|
@@ -83,19 +83,26 @@ impl OFTCore for OFT {
|
|
|
83
83
|
/// OFT behavior for standard OFT with extension hooks
|
|
84
84
|
impl OFTInternal for OFT {
|
|
85
85
|
/// Overrides default to add pausable check and fee calculation.
|
|
86
|
+
///
|
|
87
|
+
/// Dust handling (consistent with EVM):
|
|
88
|
+
/// - fee = 0: dust stays with sender
|
|
89
|
+
/// - fee > 0: dust goes to fee recipient (included in charged fee)
|
|
86
90
|
fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
|
|
87
91
|
Self::__assert_not_paused(env);
|
|
88
92
|
|
|
89
93
|
let conversion_rate = Self::__decimal_conversion_rate(env);
|
|
90
|
-
|
|
91
|
-
// Apply the fee before dust removal to ensure the fee is calculated on the full amount and dust is not transferred
|
|
92
94
|
let fee = Self::__fee_view(env, dst_eid, amount_ld);
|
|
93
|
-
let amount_after_fee = amount_ld - fee;
|
|
94
|
-
let amount_received_ld = oft_utils::remove_dust(amount_after_fee, conversion_rate);
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
let (amount_sent_ld, amount_received_ld) = if fee == 0 {
|
|
97
|
+
// No fee: dust stays with sender (default OFT behavior)
|
|
98
|
+
let amount_sent_ld = oft_utils::remove_dust(amount_ld, conversion_rate);
|
|
99
|
+
(amount_sent_ld, amount_sent_ld)
|
|
100
|
+
} else {
|
|
101
|
+
// With fee: match EVM OFTFee behavior
|
|
102
|
+
// - dust is included in charged fee (goes to fee recipient)
|
|
103
|
+
let amount_received_ld = oft_utils::remove_dust(amount_ld - fee, conversion_rate);
|
|
104
|
+
(amount_ld, amount_received_ld)
|
|
105
|
+
};
|
|
99
106
|
|
|
100
107
|
assert_with_error!(env, amount_received_ld >= min_amount_ld, OFTError::SlippageExceeded);
|
|
101
108
|
|
|
@@ -130,9 +137,9 @@ impl OFTInternal for OFT {
|
|
|
130
137
|
OftType::MintBurn => mint_burn::credit::<Self>(env, to, amount_ld, src_eid),
|
|
131
138
|
};
|
|
132
139
|
|
|
133
|
-
// Rate limit checks
|
|
134
|
-
Self::__consume_rate_limit_capacity(env, &Direction::Inbound, src_eid,
|
|
135
|
-
Self::__release_rate_limit_capacity(env, &Direction::Outbound, src_eid,
|
|
140
|
+
// Rate limit checks
|
|
141
|
+
Self::__consume_rate_limit_capacity(env, &Direction::Inbound, src_eid, amount_ld);
|
|
142
|
+
Self::__release_rate_limit_capacity(env, &Direction::Outbound, src_eid, amount_ld);
|
|
136
143
|
|
|
137
144
|
amount_credited
|
|
138
145
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//! tokens from the contract on credit (receive).
|
|
5
5
|
//! Used for OFT adapters that wrap existing tokens.
|
|
6
6
|
|
|
7
|
-
use oft_core::{
|
|
7
|
+
use oft_core::{types::OFTReceipt, OFTCore};
|
|
8
8
|
use soroban_sdk::{token::TokenClient, Address, Env};
|
|
9
9
|
|
|
10
10
|
/// Debit tokens using LockUnlock OFT type (locks tokens in contract).
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//! This OFT type burns tokens on debit (send) and mints tokens on credit (receive).
|
|
4
4
|
//! Used when the OFT contract has mint/burn authority over the token.
|
|
5
5
|
|
|
6
|
-
use oft_core::{
|
|
6
|
+
use oft_core::{types::OFTReceipt, OFTCore};
|
|
7
7
|
use soroban_sdk::{token::StellarAssetClient, Address, Env};
|
|
8
8
|
|
|
9
9
|
/// Debit tokens using MintBurn OFT type (burns tokens from sender).
|
|
@@ -21,7 +21,4 @@ simple-message-lib = { workspace = true }
|
|
|
21
21
|
message-lib-common = { workspace = true, features = ["testutils"] }
|
|
22
22
|
endpoint-v2 = { workspace = true, features = ["testutils"] }
|
|
23
23
|
utils = { workspace = true, features = ["testutils"] }
|
|
24
|
-
|
|
25
|
-
oapp-macros = { workspace = true }
|
|
26
|
-
stellar-macros = { workspace = true }
|
|
27
|
-
stellar-tokens = { workspace = true }
|
|
24
|
+
oapp-macros = { workspace = true }
|
|
@@ -7,7 +7,7 @@ extern crate std;
|
|
|
7
7
|
use crate::{
|
|
8
8
|
self as oft_core,
|
|
9
9
|
integration_tests::utils::{address_to_peer_bytes32, peer_bytes32_to_address},
|
|
10
|
-
oft_core::{
|
|
10
|
+
oft_core::{OFTClient, OFTCore, OFTInternal},
|
|
11
11
|
storage::OFTStorage,
|
|
12
12
|
types::OFTReceipt,
|
|
13
13
|
};
|
|
@@ -39,7 +39,7 @@ impl LzReceiveInternal for TestOFT {
|
|
|
39
39
|
executor: &Address,
|
|
40
40
|
value: i128,
|
|
41
41
|
) {
|
|
42
|
-
|
|
42
|
+
<Self as OFTInternal>::__receive(env, origin, guid, message, extra_data, executor, value)
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -155,10 +155,28 @@ pub fn lz_compose(
|
|
|
155
155
|
|
|
156
156
|
// returns (encoded_payload, options, send_library)
|
|
157
157
|
pub fn scan_packet_sent_event(env: &Env, endpoint: &Address) -> Option<(Bytes, Bytes, Address)> {
|
|
158
|
+
use soroban_sdk::TryFromVal;
|
|
159
|
+
|
|
158
160
|
let mut packet = None;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
let events = env.events().all().filter_by_contract(endpoint);
|
|
162
|
+
for event in events.events().iter() {
|
|
163
|
+
let v0 = match &event.body {
|
|
164
|
+
soroban_sdk::xdr::ContractEventBody::V0(v0) => v0,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Check if this is a packet_sent event by looking at topics
|
|
168
|
+
let mut is_packet_sent = false;
|
|
169
|
+
for topic in v0.topics.iter() {
|
|
170
|
+
if let Ok(sym) = Symbol::try_from_val(env, topic) {
|
|
171
|
+
if sym == Symbol::new(env, "packet_sent") {
|
|
172
|
+
is_packet_sent = true;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if is_packet_sent {
|
|
179
|
+
let data: Val = Val::try_from_val(env, &v0.data).unwrap();
|
|
162
180
|
let map: Map<Symbol, Val> = data.into_val(env);
|
|
163
181
|
|
|
164
182
|
let encoded_payload: Bytes = map.get(Symbol::new(env, "encoded_packet")).unwrap().into_val(env);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
use common_macros::contract_error;
|
|
2
2
|
|
|
3
3
|
// OFT library error codes: 3000-3099
|
|
4
|
-
// See
|
|
4
|
+
// See docs/error-spec.md for allocation rules
|
|
5
5
|
|
|
6
6
|
/// OFTError: 3000-3099
|
|
7
7
|
#[contract_error]
|
|
8
8
|
pub enum OFTError {
|
|
9
|
-
|
|
9
|
+
InvalidAmount = 3000,
|
|
10
|
+
InvalidLocalDecimals,
|
|
10
11
|
NotInitialized,
|
|
11
12
|
Overflow,
|
|
12
13
|
SlippageExceeded,
|