@layerzerolabs/protocol-stellar-v2 0.2.51 → 0.2.53
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 +281 -185
- package/.turbo/turbo-lint.log +69 -70
- package/.turbo/turbo-test.log +2010 -1754
- package/Cargo.lock +16 -0
- package/Cargo.toml +1 -0
- package/contracts/oapps/console-oft/Cargo.toml +30 -0
- package/contracts/oapps/console-oft/integration-tests/extensions/mod.rs +5 -0
- package/contracts/oapps/console-oft/integration-tests/extensions/test_combined.rs +90 -0
- package/contracts/oapps/console-oft/integration-tests/extensions/test_oft_fee.rs +186 -0
- package/contracts/oapps/console-oft/integration-tests/extensions/test_ownership.rs +161 -0
- package/contracts/oapps/console-oft/integration-tests/extensions/test_pausable.rs +154 -0
- package/contracts/oapps/console-oft/integration-tests/extensions/test_rate_limiter.rs +479 -0
- package/contracts/oapps/console-oft/integration-tests/mod.rs +3 -0
- package/contracts/oapps/console-oft/integration-tests/setup.rs +303 -0
- package/contracts/oapps/console-oft/integration-tests/utils.rs +685 -0
- package/contracts/oapps/console-oft/src/errors.rs +7 -0
- package/contracts/oapps/console-oft/src/extensions/mod.rs +3 -0
- package/contracts/oapps/console-oft/src/extensions/oft_fee.rs +239 -0
- package/contracts/oapps/console-oft/src/extensions/pausable.rs +185 -0
- package/contracts/oapps/console-oft/src/extensions/rate_limiter.rs +478 -0
- package/contracts/oapps/console-oft/src/interfaces/mintable.rs +14 -0
- package/contracts/oapps/console-oft/src/interfaces/mod.rs +3 -0
- package/contracts/oapps/console-oft/src/lib.rs +26 -0
- package/contracts/oapps/console-oft/src/oft.rs +208 -0
- package/contracts/oapps/console-oft/src/oft_access_control.rs +93 -0
- package/contracts/oapps/console-oft/src/oft_types/lock_unlock.rs +50 -0
- package/contracts/oapps/console-oft/src/oft_types/mint_burn.rs +50 -0
- package/contracts/oapps/console-oft/src/oft_types/mod.rs +24 -0
- package/contracts/oapps/console-oft/src/tests/extensions/mod.rs +3 -0
- package/contracts/oapps/console-oft/src/tests/extensions/oft_fee.rs +255 -0
- package/contracts/oapps/console-oft/src/tests/extensions/pausable.rs +212 -0
- package/contracts/oapps/console-oft/src/tests/extensions/rate_limiter.rs +992 -0
- package/contracts/oapps/console-oft/src/tests/mod.rs +2 -0
- package/contracts/oapps/console-oft/src/tests/oft_types/lock_unlock.rs +185 -0
- package/contracts/oapps/console-oft/src/tests/oft_types/mod.rs +1 -0
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +5 -2
- package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +1 -1
- package/contracts/oapps/sac-manager/src/sac_manager.rs +8 -0
- package/contracts/workers/worker/src/worker.rs +8 -1
- package/docs/oft-guide.md +2 -2
- package/package.json +4 -4
- package/sdk/.turbo/turbo-test.log +359 -423
- package/sdk/dist/generated/dvn.d.ts +9 -3
- package/sdk/dist/generated/dvn.js +4 -4
- package/sdk/dist/generated/executor.d.ts +9 -3
- package/sdk/dist/generated/executor.js +4 -4
- package/sdk/package.json +1 -1
- package/sdk/test/oft-sml.test.ts +22 -41
- package/sdk/test/sac-manager.test.ts +23 -22
- package/sdk/test/secp256k1.ts +59 -0
- package/sdk/test/suites/constants.ts +5 -1
- package/sdk/test/suites/deploy.ts +14 -8
- package/sdk/test/suites/globalSetup.ts +144 -60
- package/sdk/test/suites/localnet.ts +20 -25
- package/sdk/test/utils.ts +1 -61
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
//! Rate Limiter extension e2e tests for OFT-STD.
|
|
2
|
+
//!
|
|
3
|
+
//! Tests verify that the RateLimiter extension properly enforces rate limits
|
|
4
|
+
//! on cross-chain transfers. Uses real EndpointV2 and SimpleMessageLib.
|
|
5
|
+
|
|
6
|
+
use crate::integration_tests::{
|
|
7
|
+
setup::{create_recipient_address, decode_packet, setup, wire_endpoint, wire_oft, TestSetup},
|
|
8
|
+
utils::{
|
|
9
|
+
address_to_peer_bytes32, advance_time, create_send_param, globally_disable_rate_limiter,
|
|
10
|
+
inbound_rate_limit_capacity, lz_receive, mint_oft_token_to, mint_to, outbound_rate_limit_capacity,
|
|
11
|
+
outbound_rate_limit_usage, quote_oft, quote_send, scan_packet_sent_event, send,
|
|
12
|
+
set_bidirectional_net_rate_limit, set_inbound_rate_limit, set_outbound_rate_limit,
|
|
13
|
+
set_rate_limit_config, set_rate_limit_exemption, try_lz_receive, try_send, validate_packet,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
use crate::extensions::rate_limiter::RateLimitConfig;
|
|
17
|
+
use soroban_sdk::{testutils::Address as _, token::TokenClient, Address};
|
|
18
|
+
|
|
19
|
+
/// Test e2e send with rate limiter globally disabled (unlimited)
|
|
20
|
+
#[test]
|
|
21
|
+
fn test_send_without_rate_limit() {
|
|
22
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
23
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
24
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
25
|
+
globally_disable_rate_limiter(&env, &chain_a);
|
|
26
|
+
globally_disable_rate_limiter(&env, &chain_b);
|
|
27
|
+
|
|
28
|
+
let sender = Address::generate(&env);
|
|
29
|
+
let receiver = create_recipient_address(&env);
|
|
30
|
+
let executor = Address::generate(&env);
|
|
31
|
+
|
|
32
|
+
mint_oft_token_to(&env, &chain_a, &sender, 100_000_000);
|
|
33
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
|
|
34
|
+
|
|
35
|
+
let capacity = outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid);
|
|
36
|
+
assert_eq!(capacity, i128::MAX);
|
|
37
|
+
|
|
38
|
+
let to = address_to_peer_bytes32(&receiver);
|
|
39
|
+
let send_param = create_send_param(&env, chain_b.eid, 50_000_000, 0, &to);
|
|
40
|
+
let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
|
|
41
|
+
let fee = quote_send(&env, &chain_a, &sender, &send_param, false);
|
|
42
|
+
|
|
43
|
+
send(&env, &chain_a, &sender, &send_param, &fee, &sender, &oft_receipt);
|
|
44
|
+
|
|
45
|
+
let packet_event = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
46
|
+
validate_packet(&env, &chain_b, &packet_event);
|
|
47
|
+
let packet = decode_packet(&env, &packet_event.0);
|
|
48
|
+
|
|
49
|
+
lz_receive(&env, &chain_b, &executor, &packet, &receiver, 0);
|
|
50
|
+
|
|
51
|
+
let receiver_balance = TokenClient::new(&env, &chain_b.oft_token).balance(&receiver);
|
|
52
|
+
assert_eq!(receiver_balance, oft_receipt.amount_received_ld);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Test e2e send within rate limit succeeds
|
|
56
|
+
#[test]
|
|
57
|
+
fn test_send_within_rate_limit() {
|
|
58
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
59
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
60
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
61
|
+
globally_disable_rate_limiter(&env, &chain_b);
|
|
62
|
+
|
|
63
|
+
let sender = Address::generate(&env);
|
|
64
|
+
let receiver = create_recipient_address(&env);
|
|
65
|
+
let executor = Address::generate(&env);
|
|
66
|
+
|
|
67
|
+
mint_oft_token_to(&env, &chain_a, &sender, 100_000_000);
|
|
68
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
|
|
69
|
+
|
|
70
|
+
set_outbound_rate_limit(&env, &chain_a, chain_b.eid, 10_000_000, 3600);
|
|
71
|
+
|
|
72
|
+
let capacity = outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid);
|
|
73
|
+
assert_eq!(capacity, 10_000_000);
|
|
74
|
+
|
|
75
|
+
let to = address_to_peer_bytes32(&receiver);
|
|
76
|
+
let send_param = create_send_param(&env, chain_b.eid, 5_000_000, 0, &to);
|
|
77
|
+
let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
|
|
78
|
+
let fee = quote_send(&env, &chain_a, &sender, &send_param, false);
|
|
79
|
+
|
|
80
|
+
let in_flight_before = outbound_rate_limit_usage(&env, &chain_a, chain_b.eid);
|
|
81
|
+
assert_eq!(in_flight_before, 0);
|
|
82
|
+
|
|
83
|
+
send(&env, &chain_a, &sender, &send_param, &fee, &sender, &oft_receipt);
|
|
84
|
+
|
|
85
|
+
let packet_event =
|
|
86
|
+
scan_packet_sent_event(&env, &chain_a.endpoint.address).expect("packet_sent event should be emitted");
|
|
87
|
+
|
|
88
|
+
let in_flight = outbound_rate_limit_usage(&env, &chain_a, chain_b.eid);
|
|
89
|
+
assert_eq!(in_flight, oft_receipt.amount_received_ld);
|
|
90
|
+
validate_packet(&env, &chain_b, &packet_event);
|
|
91
|
+
let packet = decode_packet(&env, &packet_event.0);
|
|
92
|
+
|
|
93
|
+
lz_receive(&env, &chain_b, &executor, &packet, &receiver, 0);
|
|
94
|
+
|
|
95
|
+
let receiver_balance = TokenClient::new(&env, &chain_b.oft_token).balance(&receiver);
|
|
96
|
+
assert_eq!(receiver_balance, oft_receipt.amount_received_ld);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Test e2e send exceeding rate limit fails
|
|
100
|
+
#[test]
|
|
101
|
+
fn test_send_exceeds_rate_limit() {
|
|
102
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
103
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
104
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
105
|
+
|
|
106
|
+
let sender = Address::generate(&env);
|
|
107
|
+
let receiver = create_recipient_address(&env);
|
|
108
|
+
|
|
109
|
+
mint_oft_token_to(&env, &chain_a, &sender, 100_000_000);
|
|
110
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
|
|
111
|
+
|
|
112
|
+
set_outbound_rate_limit(&env, &chain_a, chain_b.eid, 1_000_000, 3600);
|
|
113
|
+
|
|
114
|
+
let to = address_to_peer_bytes32(&receiver);
|
|
115
|
+
let send_param = create_send_param(&env, chain_b.eid, 5_000_000, 0, &to);
|
|
116
|
+
let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
|
|
117
|
+
let fee = quote_send(&env, &chain_a, &sender, &send_param, false);
|
|
118
|
+
|
|
119
|
+
let success = try_send(&env, &chain_a, &sender, &send_param, &fee, &sender, &oft_receipt);
|
|
120
|
+
assert!(!success);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Test rate limit decay over time
|
|
124
|
+
#[test]
|
|
125
|
+
fn test_rate_limit_decay() {
|
|
126
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
127
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
128
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
129
|
+
globally_disable_rate_limiter(&env, &chain_b);
|
|
130
|
+
|
|
131
|
+
let sender = Address::generate(&env);
|
|
132
|
+
let receiver = create_recipient_address(&env);
|
|
133
|
+
let executor = Address::generate(&env);
|
|
134
|
+
|
|
135
|
+
mint_oft_token_to(&env, &chain_a, &sender, 100_000_000);
|
|
136
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
|
|
137
|
+
|
|
138
|
+
set_outbound_rate_limit(&env, &chain_a, chain_b.eid, 10_000_000, 1000);
|
|
139
|
+
|
|
140
|
+
let to = address_to_peer_bytes32(&receiver);
|
|
141
|
+
|
|
142
|
+
let send_param1 = create_send_param(&env, chain_b.eid, 8_000_000, 0, &to);
|
|
143
|
+
let (_, _, oft_receipt1) = quote_oft(&chain_a, &sender, &send_param1);
|
|
144
|
+
let fee1 = quote_send(&env, &chain_a, &sender, &send_param1, false);
|
|
145
|
+
send(&env, &chain_a, &sender, &send_param1, &fee1, &sender, &oft_receipt1);
|
|
146
|
+
|
|
147
|
+
let packet_event1 = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
148
|
+
validate_packet(&env, &chain_b, &packet_event1);
|
|
149
|
+
let packet1 = decode_packet(&env, &packet_event1.0);
|
|
150
|
+
lz_receive(&env, &chain_b, &executor, &packet1, &receiver, 0);
|
|
151
|
+
|
|
152
|
+
let capacity_before = outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid);
|
|
153
|
+
assert!(capacity_before < 3_000_000);
|
|
154
|
+
|
|
155
|
+
advance_time(&env, 500);
|
|
156
|
+
|
|
157
|
+
let capacity_after = outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid);
|
|
158
|
+
assert!(capacity_after > capacity_before);
|
|
159
|
+
|
|
160
|
+
let send_param2 = create_send_param(&env, chain_b.eid, 4_000_000, 0, &to);
|
|
161
|
+
let (_, _, oft_receipt2) = quote_oft(&chain_a, &sender, &send_param2);
|
|
162
|
+
let fee2 = quote_send(&env, &chain_a, &sender, &send_param2, false);
|
|
163
|
+
send(&env, &chain_a, &sender, &send_param2, &fee2, &sender, &oft_receipt2);
|
|
164
|
+
|
|
165
|
+
let packet_event2 = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
166
|
+
validate_packet(&env, &chain_b, &packet_event2);
|
|
167
|
+
let packet2 = decode_packet(&env, &packet_event2.0);
|
|
168
|
+
lz_receive(&env, &chain_b, &executor, &packet2, &receiver, 0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/// Test outbound-only mode: round-trip shows outbound capacity is NOT released on receive
|
|
172
|
+
#[test]
|
|
173
|
+
fn test_gross_mode_does_not_release() {
|
|
174
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
175
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
176
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
177
|
+
globally_disable_rate_limiter(&env, &chain_b);
|
|
178
|
+
|
|
179
|
+
let sender_a = Address::generate(&env);
|
|
180
|
+
let sender_b = Address::generate(&env);
|
|
181
|
+
let receiver_b = create_recipient_address(&env);
|
|
182
|
+
let receiver_a = create_recipient_address(&env);
|
|
183
|
+
let executor = Address::generate(&env);
|
|
184
|
+
|
|
185
|
+
mint_oft_token_to(&env, &chain_a, &sender_a, 100_000_000);
|
|
186
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender_a, 10_000_000_000);
|
|
187
|
+
mint_oft_token_to(&env, &chain_b, &sender_b, 100_000_000);
|
|
188
|
+
mint_to(&env, &chain_b.owner, &chain_b.native_token, &sender_b, 10_000_000_000);
|
|
189
|
+
|
|
190
|
+
set_outbound_rate_limit(&env, &chain_a, chain_b.eid, 10_000_000, 3600);
|
|
191
|
+
|
|
192
|
+
let to_b = address_to_peer_bytes32(&receiver_b);
|
|
193
|
+
let send_param_1 = create_send_param(&env, chain_b.eid, 8_000_000, 0, &to_b);
|
|
194
|
+
let (_, _, oft_receipt_1) = quote_oft(&chain_a, &sender_a, &send_param_1);
|
|
195
|
+
let fee_1 = quote_send(&env, &chain_a, &sender_a, &send_param_1, false);
|
|
196
|
+
send(&env, &chain_a, &sender_a, &send_param_1, &fee_1, &sender_a, &oft_receipt_1);
|
|
197
|
+
|
|
198
|
+
let packet_event_1 = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
199
|
+
validate_packet(&env, &chain_b, &packet_event_1);
|
|
200
|
+
let packet_1 = decode_packet(&env, &packet_event_1.0);
|
|
201
|
+
|
|
202
|
+
let in_flight_after_send = outbound_rate_limit_usage(&env, &chain_a, chain_b.eid);
|
|
203
|
+
assert_eq!(in_flight_after_send, oft_receipt_1.amount_received_ld);
|
|
204
|
+
assert_eq!(outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid), 10_000_000 - in_flight_after_send);
|
|
205
|
+
|
|
206
|
+
lz_receive(&env, &chain_b, &executor, &packet_1, &receiver_b, 0);
|
|
207
|
+
|
|
208
|
+
let to_a = address_to_peer_bytes32(&receiver_a);
|
|
209
|
+
let send_param_2 = create_send_param(&env, chain_a.eid, 6_000_000, 0, &to_a);
|
|
210
|
+
let (_, _, oft_receipt_2) = quote_oft(&chain_b, &sender_b, &send_param_2);
|
|
211
|
+
let fee_2 = quote_send(&env, &chain_b, &sender_b, &send_param_2, false);
|
|
212
|
+
send(&env, &chain_b, &sender_b, &send_param_2, &fee_2, &sender_b, &oft_receipt_2);
|
|
213
|
+
|
|
214
|
+
let packet_event_2 = scan_packet_sent_event(&env, &chain_b.endpoint.address).unwrap();
|
|
215
|
+
validate_packet(&env, &chain_a, &packet_event_2);
|
|
216
|
+
let packet_2 = decode_packet(&env, &packet_event_2.0);
|
|
217
|
+
|
|
218
|
+
lz_receive(&env, &chain_a, &executor, &packet_2, &receiver_a, 0);
|
|
219
|
+
|
|
220
|
+
let in_flight_after_return = outbound_rate_limit_usage(&env, &chain_a, chain_b.eid);
|
|
221
|
+
assert_eq!(
|
|
222
|
+
in_flight_after_return, in_flight_after_send,
|
|
223
|
+
"outbound-only: in-flight should not decrease when receiving"
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
let capacity_after_return = outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid);
|
|
227
|
+
assert!(capacity_after_return < 3_000_000, "outbound-only: capacity should remain low");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/// Test net mode: round-trip shows net mode releases outbound capacity on receive
|
|
231
|
+
#[test]
|
|
232
|
+
fn test_net_mode_does_release() {
|
|
233
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
234
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
235
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
236
|
+
globally_disable_rate_limiter(&env, &chain_b);
|
|
237
|
+
|
|
238
|
+
let sender_a = Address::generate(&env);
|
|
239
|
+
let sender_b = Address::generate(&env);
|
|
240
|
+
let receiver_b = create_recipient_address(&env);
|
|
241
|
+
let receiver_a = create_recipient_address(&env);
|
|
242
|
+
let executor = Address::generate(&env);
|
|
243
|
+
|
|
244
|
+
mint_oft_token_to(&env, &chain_a, &sender_a, 100_000_000);
|
|
245
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender_a, 10_000_000_000);
|
|
246
|
+
mint_oft_token_to(&env, &chain_b, &sender_b, 100_000_000);
|
|
247
|
+
mint_to(&env, &chain_b.owner, &chain_b.native_token, &sender_b, 10_000_000_000);
|
|
248
|
+
|
|
249
|
+
set_bidirectional_net_rate_limit(&env, &chain_a, chain_b.eid, 10_000_000, 3600);
|
|
250
|
+
|
|
251
|
+
let to_b = address_to_peer_bytes32(&receiver_b);
|
|
252
|
+
let send_param_1 = create_send_param(&env, chain_b.eid, 8_000_000, 0, &to_b);
|
|
253
|
+
let (_, _, oft_receipt_1) = quote_oft(&chain_a, &sender_a, &send_param_1);
|
|
254
|
+
let fee_1 = quote_send(&env, &chain_a, &sender_a, &send_param_1, false);
|
|
255
|
+
send(&env, &chain_a, &sender_a, &send_param_1, &fee_1, &sender_a, &oft_receipt_1);
|
|
256
|
+
|
|
257
|
+
let packet_event_1 = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
258
|
+
validate_packet(&env, &chain_b, &packet_event_1);
|
|
259
|
+
let packet_1 = decode_packet(&env, &packet_event_1.0);
|
|
260
|
+
|
|
261
|
+
let in_flight_after_send = outbound_rate_limit_usage(&env, &chain_a, chain_b.eid);
|
|
262
|
+
assert_eq!(in_flight_after_send, oft_receipt_1.amount_received_ld);
|
|
263
|
+
assert_eq!(outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid), 10_000_000 - in_flight_after_send);
|
|
264
|
+
|
|
265
|
+
lz_receive(&env, &chain_b, &executor, &packet_1, &receiver_b, 0);
|
|
266
|
+
|
|
267
|
+
let to_a = address_to_peer_bytes32(&receiver_a);
|
|
268
|
+
let send_param_2 = create_send_param(&env, chain_a.eid, 6_000_000, 0, &to_a);
|
|
269
|
+
let (_, _, oft_receipt_2) = quote_oft(&chain_b, &sender_b, &send_param_2);
|
|
270
|
+
let fee_2 = quote_send(&env, &chain_b, &sender_b, &send_param_2, false);
|
|
271
|
+
send(&env, &chain_b, &sender_b, &send_param_2, &fee_2, &sender_b, &oft_receipt_2);
|
|
272
|
+
|
|
273
|
+
let packet_event_2 = scan_packet_sent_event(&env, &chain_b.endpoint.address).unwrap();
|
|
274
|
+
validate_packet(&env, &chain_a, &packet_event_2);
|
|
275
|
+
let packet_2 = decode_packet(&env, &packet_event_2.0);
|
|
276
|
+
|
|
277
|
+
lz_receive(&env, &chain_a, &executor, &packet_2, &receiver_a, 0);
|
|
278
|
+
|
|
279
|
+
let in_flight_after_return = outbound_rate_limit_usage(&env, &chain_a, chain_b.eid);
|
|
280
|
+
assert!(
|
|
281
|
+
in_flight_after_return < in_flight_after_send,
|
|
282
|
+
"net mode: outbound in-flight should decrease when receiving"
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
let capacity_after_return = outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid);
|
|
286
|
+
assert!(capacity_after_return > 7_000_000, "net mode: capacity should increase after receiving");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/// Test inbound rate limit blocks lz_receive when exceeded
|
|
290
|
+
#[test]
|
|
291
|
+
fn test_inbound_rate_limit_blocks_receive() {
|
|
292
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
293
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
294
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
295
|
+
globally_disable_rate_limiter(&env, &chain_a);
|
|
296
|
+
|
|
297
|
+
let sender = Address::generate(&env);
|
|
298
|
+
let receiver = create_recipient_address(&env);
|
|
299
|
+
let executor = Address::generate(&env);
|
|
300
|
+
|
|
301
|
+
mint_oft_token_to(&env, &chain_a, &sender, 100_000_000);
|
|
302
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
|
|
303
|
+
|
|
304
|
+
// Set inbound rate limit on chain_b: only 1M allowed from chain_a
|
|
305
|
+
set_inbound_rate_limit(&env, &chain_b, chain_a.eid, 1_000_000, 3600);
|
|
306
|
+
assert_eq!(inbound_rate_limit_capacity(&env, &chain_b, chain_a.eid), 1_000_000);
|
|
307
|
+
|
|
308
|
+
// Send 5M from chain_a (outbound unlimited)
|
|
309
|
+
let to = address_to_peer_bytes32(&receiver);
|
|
310
|
+
let send_param = create_send_param(&env, chain_b.eid, 5_000_000, 0, &to);
|
|
311
|
+
let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
|
|
312
|
+
let fee = quote_send(&env, &chain_a, &sender, &send_param, false);
|
|
313
|
+
send(&env, &chain_a, &sender, &send_param, &fee, &sender, &oft_receipt);
|
|
314
|
+
|
|
315
|
+
let packet_event = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
316
|
+
validate_packet(&env, &chain_b, &packet_event);
|
|
317
|
+
let packet = decode_packet(&env, &packet_event.0);
|
|
318
|
+
|
|
319
|
+
// Receive on chain_b should fail — inbound capacity (1M) < transfer amount (5M)
|
|
320
|
+
let success = try_lz_receive(&env, &chain_b, &executor, &packet, &receiver, 0);
|
|
321
|
+
assert!(!success, "receive should be blocked by inbound rate limit");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/// Test that an exempt sender bypasses outbound rate limit on send.
|
|
325
|
+
///
|
|
326
|
+
/// Verifies the full e2e path: quote_oft reports limited capacity (matching EVM behavior
|
|
327
|
+
/// where quoteOFT does not account for per-address exemptions), but send succeeds for an
|
|
328
|
+
/// amount exceeding the rate limit, and the exempt sender's transfer does NOT consume
|
|
329
|
+
/// outbound usage. Non-exempt senders remain limited.
|
|
330
|
+
#[test]
|
|
331
|
+
fn test_exempt_sender_bypasses_outbound_rate_limit() {
|
|
332
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
333
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
334
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
335
|
+
globally_disable_rate_limiter(&env, &chain_b);
|
|
336
|
+
|
|
337
|
+
let exempt_sender = Address::generate(&env);
|
|
338
|
+
let normal_sender = Address::generate(&env);
|
|
339
|
+
let receiver = create_recipient_address(&env);
|
|
340
|
+
let executor = Address::generate(&env);
|
|
341
|
+
|
|
342
|
+
mint_oft_token_to(&env, &chain_a, &exempt_sender, 100_000_000);
|
|
343
|
+
mint_oft_token_to(&env, &chain_a, &normal_sender, 100_000_000);
|
|
344
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &exempt_sender, 10_000_000_000);
|
|
345
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &normal_sender, 10_000_000_000);
|
|
346
|
+
|
|
347
|
+
// Set outbound rate limit with address exemption enabled
|
|
348
|
+
set_rate_limit_config(
|
|
349
|
+
&env,
|
|
350
|
+
&chain_a,
|
|
351
|
+
chain_b.eid,
|
|
352
|
+
RateLimitConfig {
|
|
353
|
+
outbound_enabled: true,
|
|
354
|
+
inbound_enabled: false,
|
|
355
|
+
net_accounting_enabled: false,
|
|
356
|
+
address_exemption_enabled: true,
|
|
357
|
+
outbound_limit: 1_000_000,
|
|
358
|
+
inbound_limit: 0,
|
|
359
|
+
outbound_window: 3600,
|
|
360
|
+
inbound_window: 0,
|
|
361
|
+
},
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// Mark exempt_sender as exempt
|
|
365
|
+
set_rate_limit_exemption(&env, &chain_a, &exempt_sender, true);
|
|
366
|
+
|
|
367
|
+
let to = address_to_peer_bytes32(&receiver);
|
|
368
|
+
|
|
369
|
+
let send_param = create_send_param(&env, chain_b.eid, 5_000_000, 0, &to);
|
|
370
|
+
let (oft_limit, _, oft_receipt) = quote_oft(&chain_a, &exempt_sender, &send_param);
|
|
371
|
+
assert_eq!(oft_limit.max_amount_ld, 1_000_000, "quote_oft should report rate-limited capacity");
|
|
372
|
+
|
|
373
|
+
// Exempt sender can send 5M despite the 1M rate limit
|
|
374
|
+
let fee = quote_send(&env, &chain_a, &exempt_sender, &send_param, false);
|
|
375
|
+
send(&env, &chain_a, &exempt_sender, &send_param, &fee, &exempt_sender, &oft_receipt);
|
|
376
|
+
|
|
377
|
+
// Scan packet event immediately after send (events are cleared by subsequent contract calls)
|
|
378
|
+
let packet_event = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
379
|
+
|
|
380
|
+
// Outbound usage should remain 0 — exempt sender does not consume capacity
|
|
381
|
+
let usage_after = outbound_rate_limit_usage(&env, &chain_a, chain_b.eid);
|
|
382
|
+
assert_eq!(usage_after, 0, "exempt sender should not consume outbound usage");
|
|
383
|
+
|
|
384
|
+
// Full capacity is still available for non-exempt users
|
|
385
|
+
let capacity_after = outbound_rate_limit_capacity(&env, &chain_a, chain_b.eid);
|
|
386
|
+
assert_eq!(capacity_after, 1_000_000, "capacity should be unchanged after exempt send");
|
|
387
|
+
|
|
388
|
+
// Verify the transfer went through end-to-end
|
|
389
|
+
validate_packet(&env, &chain_b, &packet_event);
|
|
390
|
+
let packet = decode_packet(&env, &packet_event.0);
|
|
391
|
+
lz_receive(&env, &chain_b, &executor, &packet, &receiver, 0);
|
|
392
|
+
|
|
393
|
+
let receiver_balance = TokenClient::new(&env, &chain_b.oft_token).balance(&receiver);
|
|
394
|
+
assert_eq!(receiver_balance, oft_receipt.amount_received_ld);
|
|
395
|
+
|
|
396
|
+
// Non-exempt sender is still blocked when exceeding the rate limit
|
|
397
|
+
let send_param_normal = create_send_param(&env, chain_b.eid, 5_000_000, 0, &to);
|
|
398
|
+
let (_, _, oft_receipt_normal) = quote_oft(&chain_a, &normal_sender, &send_param_normal);
|
|
399
|
+
let fee_normal = quote_send(&env, &chain_a, &normal_sender, &send_param_normal, false);
|
|
400
|
+
let success = try_send(&env, &chain_a, &normal_sender, &send_param_normal, &fee_normal, &normal_sender, &oft_receipt_normal);
|
|
401
|
+
assert!(!success, "non-exempt sender should still be blocked by rate limit");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/// Test that an exempt receiver bypasses inbound rate limit on lz_receive.
|
|
405
|
+
///
|
|
406
|
+
/// Verifies that when `address_exemption_enabled` is true and the receiver is exempt,
|
|
407
|
+
/// `lz_receive` succeeds for an amount exceeding the inbound rate limit, and the
|
|
408
|
+
/// exempt receiver's transfer does NOT consume inbound usage. A non-exempt receiver
|
|
409
|
+
/// is still blocked.
|
|
410
|
+
#[test]
|
|
411
|
+
fn test_exempt_receiver_bypasses_inbound_rate_limit() {
|
|
412
|
+
let TestSetup { env, chain_a, chain_b } = setup();
|
|
413
|
+
wire_endpoint(&env, &[&chain_a, &chain_b]);
|
|
414
|
+
wire_oft(&env, &[&chain_a, &chain_b]);
|
|
415
|
+
globally_disable_rate_limiter(&env, &chain_a);
|
|
416
|
+
|
|
417
|
+
let sender = Address::generate(&env);
|
|
418
|
+
let exempt_receiver = create_recipient_address(&env);
|
|
419
|
+
let normal_receiver = create_recipient_address(&env);
|
|
420
|
+
let executor = Address::generate(&env);
|
|
421
|
+
|
|
422
|
+
mint_oft_token_to(&env, &chain_a, &sender, 100_000_000);
|
|
423
|
+
mint_to(&env, &chain_a.owner, &chain_a.native_token, &sender, 10_000_000_000);
|
|
424
|
+
|
|
425
|
+
// Set inbound rate limit on chain_b with address exemption enabled
|
|
426
|
+
set_rate_limit_config(
|
|
427
|
+
&env,
|
|
428
|
+
&chain_b,
|
|
429
|
+
chain_a.eid,
|
|
430
|
+
RateLimitConfig {
|
|
431
|
+
outbound_enabled: false,
|
|
432
|
+
inbound_enabled: true,
|
|
433
|
+
net_accounting_enabled: false,
|
|
434
|
+
address_exemption_enabled: true,
|
|
435
|
+
outbound_limit: 0,
|
|
436
|
+
inbound_limit: 1_000_000,
|
|
437
|
+
outbound_window: 0,
|
|
438
|
+
inbound_window: 3600,
|
|
439
|
+
},
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
// Mark exempt_receiver as exempt on chain_b
|
|
443
|
+
set_rate_limit_exemption(&env, &chain_b, &exempt_receiver, true);
|
|
444
|
+
|
|
445
|
+
// Send 5M from chain_a (outbound unlimited on chain_a)
|
|
446
|
+
let to_exempt = address_to_peer_bytes32(&exempt_receiver);
|
|
447
|
+
let send_param = create_send_param(&env, chain_b.eid, 5_000_000, 0, &to_exempt);
|
|
448
|
+
let (_, _, oft_receipt) = quote_oft(&chain_a, &sender, &send_param);
|
|
449
|
+
let fee = quote_send(&env, &chain_a, &sender, &send_param, false);
|
|
450
|
+
send(&env, &chain_a, &sender, &send_param, &fee, &sender, &oft_receipt);
|
|
451
|
+
|
|
452
|
+
let packet_event = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
453
|
+
validate_packet(&env, &chain_b, &packet_event);
|
|
454
|
+
let packet = decode_packet(&env, &packet_event.0);
|
|
455
|
+
|
|
456
|
+
// Exempt receiver can receive 5M despite the 1M inbound rate limit
|
|
457
|
+
lz_receive(&env, &chain_b, &executor, &packet, &exempt_receiver, 0);
|
|
458
|
+
|
|
459
|
+
let exempt_balance = TokenClient::new(&env, &chain_b.oft_token).balance(&exempt_receiver);
|
|
460
|
+
assert_eq!(exempt_balance, oft_receipt.amount_received_ld);
|
|
461
|
+
|
|
462
|
+
// Inbound usage should remain 0 — exempt receiver does not consume capacity
|
|
463
|
+
let inbound_usage = inbound_rate_limit_capacity(&env, &chain_b, chain_a.eid);
|
|
464
|
+
assert_eq!(inbound_usage, 1_000_000, "inbound capacity should be unchanged after exempt receive");
|
|
465
|
+
|
|
466
|
+
// Non-exempt receiver is still blocked when exceeding the inbound rate limit
|
|
467
|
+
let to_normal = address_to_peer_bytes32(&normal_receiver);
|
|
468
|
+
let send_param2 = create_send_param(&env, chain_b.eid, 5_000_000, 0, &to_normal);
|
|
469
|
+
let (_, _, oft_receipt2) = quote_oft(&chain_a, &sender, &send_param2);
|
|
470
|
+
let fee2 = quote_send(&env, &chain_a, &sender, &send_param2, false);
|
|
471
|
+
send(&env, &chain_a, &sender, &send_param2, &fee2, &sender, &oft_receipt2);
|
|
472
|
+
|
|
473
|
+
let packet_event2 = scan_packet_sent_event(&env, &chain_a.endpoint.address).unwrap();
|
|
474
|
+
validate_packet(&env, &chain_b, &packet_event2);
|
|
475
|
+
let packet2 = decode_packet(&env, &packet_event2.0);
|
|
476
|
+
|
|
477
|
+
let success = try_lz_receive(&env, &chain_b, &executor, &packet2, &normal_receiver, 0);
|
|
478
|
+
assert!(!success, "non-exempt receiver should still be blocked by inbound rate limit");
|
|
479
|
+
}
|