From 3acc2e76f3be651fbed22b3ba0ea2d68d5c86082 Mon Sep 17 00:00:00 2001 From: Andy Golay Date: Fri, 28 Nov 2025 14:34:00 -0500 Subject: [PATCH 1/2] implement fix for issue 1 --- .../sources/shared_oft/oft_impl_config.move | 5 ++++- .../tests/shared_oft/oft_impl_config_tests.move | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move b/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move index 6d0ba58d69..e6ff6f0e95 100644 --- a/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move +++ b/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move @@ -213,7 +213,7 @@ module oft::oft_impl_config { /// Checkpoint the in-flight amount for a given EID for the provided timestamp. /// This should whenever there is a change in rate limit or before consuming rate limit capacity - public(friend) fun checkpoint_rate_limit_in_flight(eid: u32, timestamp: u64) acquires Config { + public fun checkpoint_rate_limit_in_flight(eid: u32, timestamp: u64) acquires Config { let inflight = in_flight_at_time(eid, timestamp); let rate_limit = table::borrow_mut(&mut store_mut().rate_limit_by_eid, eid); rate_limit.in_flight_on_last_update = inflight; @@ -308,6 +308,9 @@ module oft::oft_impl_config { public(friend) fun release_rate_limit_capacity(eid: u32, amount: u64) acquires Config { if (!has_rate_limit(eid)) return; + // Checkpoint to account for decay before modifying in_flight_on_last_update + checkpoint_rate_limit_in_flight(eid, timestamp::now_seconds()); + let rate_limit = table::borrow_mut(&mut store_mut().rate_limit_by_eid, eid); if (amount >= rate_limit.in_flight_on_last_update) { rate_limit.in_flight_on_last_update = 0; diff --git a/examples/oft-evm-move-adapters/tests/shared_oft/oft_impl_config_tests.move b/examples/oft-evm-move-adapters/tests/shared_oft/oft_impl_config_tests.move index 888e67dca1..b5c5199881 100644 --- a/examples/oft-evm-move-adapters/tests/shared_oft/oft_impl_config_tests.move +++ b/examples/oft-evm-move-adapters/tests/shared_oft/oft_impl_config_tests.move @@ -1,8 +1,9 @@ #[test_only] module oft::oft_impl_config_tests { - use std::account::create_account_for_test; + use std::account::{create_account_for_test, create_signer_for_test}; use std::event::was_event_emitted; use std::string::utf8; + use std::timestamp; use std::vector; use oft::oapp_store; @@ -12,6 +13,7 @@ module oft::oft_impl_config_tests { assert_not_blocklisted, blocked_amount_redirected_event, blocklisting_disabled_event, + checkpoint_rate_limit_in_flight, debit_view_with_possible_fee, fee_bps, fee_details_with_possible_fee, @@ -279,6 +281,7 @@ module oft::oft_impl_config_tests { #[test] fun test_set_rate_limit_net() { setup(); + timestamp::set_time_has_started_for_testing(&create_signer_for_test(@std)); // No rate limit configured assert!(has_rate_limit(30100) == false, 2); @@ -300,7 +303,10 @@ module oft::oft_impl_config_tests { assert!(in_flight_at_time(30100, 500) == 10000, 1); assert!(rate_limit_capacity_at_time(30100, 500) == 10000, 2); - // release most of remaining capacity + // Release most of remaining capacity + // release_rate_limit_capacity will checkpoint at now_seconds() (which is 0 after initialization) + // At time 0: in_flight = 20000, so after release of 9000: in_flight_on_last_update = 11000, last_update = 0 + // At time 500: in_flight = 11000 - (500 * 20000 / 1000) = 11000 - 10000 = 1000 release_rate_limit_capacity(30100, 9000); assert!(in_flight_at_time(30100, 500) == 1000, 1); assert!(rate_limit_capacity_at_time(30100, 500) == 19000, 2); @@ -310,7 +316,9 @@ module oft::oft_impl_config_tests { assert!(in_flight_at_time(30100, 500) == 20000, 1); assert!(rate_limit_capacity_at_time(30100, 500) == 0, 2); - // release excess capacity (5x limit) - should not overshoot + // Release excess capacity (5x limit) - should not overshoot + // First checkpoint at 500 to get correct state, then release will checkpoint at now_seconds() + checkpoint_rate_limit_in_flight(30100, 500); release_rate_limit_capacity(30100, 100_000); assert!(in_flight_at_time(30100, 500) == 0, 1); assert!(rate_limit_capacity_at_time(30100, 500) == 20000, 2); From 321ef82a91b759a73caeade326a1e39c87183749 Mon Sep 17 00:00:00 2001 From: Andy Golay Date: Fri, 28 Nov 2025 14:40:42 -0500 Subject: [PATCH 2/2] make checkpoint_rate_limit_in_flight public(friend) --- .../sources/shared_oft/oft_impl_config.move | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move b/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move index e6ff6f0e95..e16406822a 100644 --- a/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move +++ b/examples/oft-evm-move-adapters/sources/shared_oft/oft_impl_config.move @@ -213,7 +213,7 @@ module oft::oft_impl_config { /// Checkpoint the in-flight amount for a given EID for the provided timestamp. /// This should whenever there is a change in rate limit or before consuming rate limit capacity - public fun checkpoint_rate_limit_in_flight(eid: u32, timestamp: u64) acquires Config { + public(friend) fun checkpoint_rate_limit_in_flight(eid: u32, timestamp: u64) acquires Config { let inflight = in_flight_at_time(eid, timestamp); let rate_limit = table::borrow_mut(&mut store_mut().rate_limit_by_eid, eid); rate_limit.in_flight_on_last_update = inflight;