@layerzerolabs/protocol-stellar-v2 0.2.15 → 0.2.19
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 +365 -297
- package/.turbo/turbo-lint.log +142 -110
- package/.turbo/turbo-test.log +1273 -1222
- package/Cargo.lock +20 -5
- package/Cargo.toml +4 -1
- package/contracts/ERROR_SPEC.md +44 -0
- package/contracts/common-macros/src/auth.rs +113 -0
- package/contracts/common-macros/src/contract_ttl.rs +84 -0
- package/contracts/common-macros/src/lib.rs +181 -30
- package/contracts/common-macros/src/lz_contract.rs +83 -0
- package/contracts/common-macros/src/tests/{ownable.rs → auth.rs} +48 -15
- package/contracts/common-macros/src/tests/contract_ttl.rs +662 -0
- package/contracts/common-macros/src/tests/mod.rs +2 -2
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +20 -0
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +24 -0
- package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__ownable__snapshot_only_owner_preserves_function_signature.snap → common_macros__tests__auth__snapshot_only_auth_preserves_function_signature.snap} +4 -4
- package/contracts/common-macros/src/tests/snapshots/{common_macros__tests__contract_impl__snapshot_generated_contract_impl_code.snap → common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap} +3 -3
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contracttrait_code.snap +69 -0
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ttl_configurable__snapshot_generated_ttl_configurable_code.snap +7 -21
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +2 -2
- package/contracts/common-macros/src/ttl_configurable.rs +19 -34
- package/contracts/common-macros/src/ttl_extendable.rs +36 -0
- package/contracts/common-macros/src/upgradeable.rs +5 -5
- package/contracts/common-macros/src/utils.rs +9 -0
- package/contracts/endpoint-v2/src/constants.rs +4 -4
- package/contracts/endpoint-v2/src/endpoint_v2.rs +38 -40
- package/contracts/endpoint-v2/src/errors.rs +4 -3
- package/contracts/endpoint-v2/src/events.rs +1 -1
- package/contracts/endpoint-v2/src/message_lib_manager.rs +18 -5
- package/contracts/endpoint-v2/src/messaging_channel.rs +11 -1
- package/contracts/endpoint-v2/src/messaging_composer.rs +11 -1
- package/contracts/endpoint-v2/src/storage.rs +1 -1
- package/contracts/endpoint-v2/src/tests/endpoint_v2/pay_messaging_fees.rs +3 -3
- package/contracts/endpoint-v2/src/tests/endpoint_v2/quote.rs +1 -1
- package/contracts/endpoint-v2/src/tests/endpoint_v2/require_oapp_auth.rs +2 -2
- package/contracts/endpoint-v2/src/tests/endpoint_v2/send.rs +3 -3
- package/contracts/endpoint-v2/src/tests/endpoint_v2/set_zro.rs +4 -4
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_receive_lib_for_eid.rs +3 -3
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_registered.rs +1 -1
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_send_lib_for_eid.rs +3 -3
- package/contracts/endpoint-v2/src/tests/message_lib_manager/require_supported_eid.rs +1 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +4 -4
- package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +1 -1
- package/contracts/layerzero-views/src/layerzero_view.rs +3 -6
- package/contracts/macro-integration-tests/tests/runtime/ownable/mod.rs +2 -2
- package/contracts/macro-integration-tests/tests/runtime/ownable/{only_owner_guard.rs → only_auth_guard.rs} +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/configuration.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/freeze.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/mod.rs +0 -1
- package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.rs → only_auth_missing_env.rs} +3 -3
- package/contracts/macro-integration-tests/tests/ui/ownable/fail/{only_owner_missing_env.stderr → only_auth_missing_env.stderr} +4 -4
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/namespacing_and_imports.rs +2 -3
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/{only_owner_env_param_variants.rs → only_auth_env_param_variants.rs} +9 -9
- package/contracts/macro-integration-tests/tests/ui/ttl_configurable/pass/minimal_contract.rs +6 -6
- package/contracts/message-libs/message-lib-common/src/errors.rs +7 -2
- package/contracts/message-libs/message-lib-common/src/tests/packet_codec_v1/decode_packet_header.rs +3 -3
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_lz_receive_option.rs +1 -2
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/append_native_drop_option.rs +1 -2
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/convert_legacy_options.rs +9 -9
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +1 -1
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/left_pad_to_bytes32.rs +1 -1
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/split_worker_options.rs +2 -2
- package/contracts/message-libs/simple-message-lib/src/simple_message_lib.rs +7 -9
- package/contracts/message-libs/treasury/src/errors.rs +2 -2
- package/contracts/message-libs/treasury/src/events.rs +1 -1
- package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +2 -2
- package/contracts/message-libs/treasury/src/storage.rs +1 -1
- package/contracts/message-libs/treasury/src/tests/treasury_tests.rs +1 -1
- package/contracts/message-libs/treasury/src/treasury.rs +14 -16
- package/contracts/message-libs/uln-302/src/receive_uln.rs +13 -2
- package/contracts/message-libs/uln-302/src/send_uln.rs +24 -4
- package/contracts/message-libs/uln-302/src/uln302.rs +6 -24
- package/contracts/oapps/counter/Cargo.toml +14 -1
- package/contracts/oapps/counter/integration_tests/mod.rs +4 -1
- package/contracts/oapps/counter/integration_tests/{setup.rs → setup_sml.rs} +48 -80
- package/contracts/oapps/counter/integration_tests/setup_uln.rs +997 -0
- package/contracts/oapps/counter/integration_tests/signing.rs +62 -0
- package/contracts/oapps/counter/integration_tests/test_with_sml.rs +24 -55
- package/contracts/oapps/counter/integration_tests/test_with_uln.rs +314 -0
- package/contracts/oapps/counter/integration_tests/utils.rs +196 -53
- package/contracts/oapps/counter/src/counter.rs +67 -43
- package/contracts/oapps/counter/src/tests/mod.rs +0 -13
- package/contracts/oapps/counter/src/tests/test_counter.rs +5 -7
- package/contracts/oapps/oapp/src/errors.rs +5 -1
- package/contracts/oapps/oapp/src/macro_tests/test_macros.rs +93 -78
- package/contracts/oapps/oapp/src/oapp_core.rs +36 -21
- package/contracts/oapps/oapp/src/oapp_options_type3.rs +48 -12
- package/contracts/oapps/oapp/src/oapp_receiver.rs +106 -41
- package/contracts/oapps/oapp/src/oapp_sender.rs +26 -34
- package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +9 -8
- package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +25 -17
- package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +7 -7
- package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +14 -15
- package/contracts/oapps/oapp-macros/src/generators.rs +128 -0
- package/contracts/oapps/oapp-macros/src/lib.rs +113 -56
- package/contracts/oapps/oft/Cargo.toml +10 -7
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
- package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
- package/contracts/oapps/oft/integration-tests/setup.rs +29 -110
- package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +13 -14
- package/contracts/oapps/oft/src/extensions/pausable.rs +4 -4
- package/contracts/oapps/oft/src/extensions/rate_limiter.rs +5 -5
- package/contracts/oapps/oft/src/lib.rs +11 -13
- package/contracts/oapps/oft/src/oft.rs +147 -225
- package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -13
- package/contracts/oapps/oft/src/oft_types/mint_burn.rs +31 -14
- package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
- package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
- package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
- package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +129 -30
- package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
- package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
- package/contracts/oapps/oft-core/src/errors.rs +13 -0
- package/contracts/oapps/oft-core/src/lib.rs +18 -0
- package/contracts/oapps/oft-core/src/oft_core.rs +439 -0
- package/contracts/oapps/{oft → oft-core}/src/storage.rs +2 -0
- package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
- package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +2 -2
- package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
- package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -5
- package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
- package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +78 -37
- package/contracts/oapps/oft-core/src/types.rs +58 -0
- package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
- package/contracts/upgrader/src/lib.rs +4 -4
- package/contracts/utils/src/auth.rs +44 -0
- package/contracts/utils/src/errors.rs +31 -5
- package/contracts/utils/src/lib.rs +3 -0
- package/contracts/utils/src/multisig.rs +211 -0
- package/contracts/utils/src/ownable.rs +137 -13
- package/contracts/utils/src/tests/buffer_reader.rs +6 -6
- package/contracts/utils/src/tests/buffer_writer.rs +6 -6
- package/contracts/utils/src/tests/bytes_ext.rs +2 -4
- package/contracts/utils/src/tests/mod.rs +1 -0
- package/contracts/utils/src/tests/multisig.rs +731 -0
- package/contracts/utils/src/tests/option_ext.rs +2 -5
- package/contracts/utils/src/tests/ownable.rs +456 -7
- package/contracts/utils/src/tests/ttl_configurable.rs +27 -16
- package/contracts/utils/src/tests/upgradeable.rs +4 -2
- package/contracts/utils/src/ttl_configurable.rs +23 -8
- package/contracts/utils/src/ttl_extendable.rs +27 -0
- package/contracts/utils/src/upgradeable.rs +2 -0
- package/contracts/workers/dvn/Cargo.toml +1 -1
- package/contracts/workers/dvn/src/auth.rs +7 -7
- package/contracts/workers/dvn/src/dvn.rs +10 -38
- package/contracts/workers/dvn/src/errors.rs +0 -7
- package/contracts/workers/dvn/src/events.rs +1 -14
- package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
- package/contracts/workers/dvn/src/interfaces/mod.rs +0 -2
- package/contracts/workers/dvn/src/storage.rs +3 -13
- package/contracts/workers/dvn/src/tests/auth.rs +4 -4
- package/contracts/workers/dvn/src/tests/dvn.rs +1 -2
- package/contracts/workers/dvn/src/tests/multisig/set_signer.rs +7 -8
- package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +11 -8
- package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +11 -12
- package/contracts/workers/dvn/src/tests/setup.rs +5 -5
- package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
- package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -6
- package/contracts/workers/executor/src/auth.rs +80 -16
- package/contracts/workers/executor/src/executor.rs +5 -31
- package/contracts/workers/executor/src/storage.rs +2 -9
- package/contracts/workers/executor-fee-lib/Cargo.toml +1 -1
- package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +3 -6
- package/contracts/workers/executor-helper/Cargo.toml +1 -1
- package/contracts/workers/executor-helper/src/executor_helper.rs +53 -73
- package/contracts/workers/price-feed/Cargo.toml +1 -1
- package/contracts/workers/price-feed/src/price_feed.rs +7 -10
- package/contracts/workers/worker/src/errors.rs +4 -0
- package/contracts/workers/worker/src/tests/worker.rs +7 -6
- package/contracts/workers/worker/src/worker.rs +20 -16
- package/package.json +7 -5
- package/sdk/.turbo/turbo-build.log +1 -0
- package/sdk/.turbo/turbo-test.log +1019 -0
- package/sdk/dist/generated/bml.d.ts +95 -8
- package/sdk/dist/generated/bml.js +95 -36
- package/sdk/dist/generated/counter.d.ts +289 -44
- package/sdk/dist/generated/counter.js +119 -49
- package/sdk/dist/generated/dvn.d.ts +312 -229
- package/sdk/dist/generated/dvn.js +144 -83
- package/sdk/dist/generated/dvn_fee_lib.d.ts +258 -63
- package/sdk/dist/generated/dvn_fee_lib.js +95 -26
- package/sdk/dist/generated/endpoint.d.ts +219 -24
- package/sdk/dist/generated/endpoint.js +108 -41
- package/sdk/dist/generated/executor.d.ts +239 -87
- package/sdk/dist/generated/executor.js +135 -63
- package/sdk/dist/generated/executor_fee_lib.d.ts +278 -74
- package/sdk/dist/generated/executor_fee_lib.js +135 -59
- package/sdk/dist/generated/executor_helper.d.ts +163 -21
- package/sdk/dist/generated/executor_helper.js +124 -52
- package/sdk/dist/generated/oft.d.ts +1842 -0
- package/sdk/dist/generated/oft.js +345 -0
- package/sdk/dist/generated/price_feed.d.ts +258 -63
- package/sdk/dist/generated/price_feed.js +95 -26
- package/sdk/dist/generated/sml.d.ts +235 -34
- package/sdk/dist/generated/sml.js +126 -53
- package/sdk/dist/generated/treasury.d.ts +1016 -0
- package/sdk/dist/generated/treasury.js +248 -0
- package/sdk/dist/generated/uln302.d.ts +235 -34
- package/sdk/dist/generated/uln302.js +126 -53
- package/sdk/dist/generated/upgrader.d.ts +17 -2
- package/sdk/dist/generated/upgrader.js +19 -1
- package/sdk/dist/index.d.ts +2 -1
- package/sdk/dist/index.js +2 -1
- package/sdk/package.json +6 -3
- package/sdk/src/index.ts +2 -1
- package/sdk/test/counter-sml.test.ts +376 -0
- package/sdk/test/counter-uln.test.ts +493 -0
- package/sdk/test/{oft.test.ts → oft-sml.test.ts} +196 -321
- package/sdk/test/suites/constants.ts +22 -2
- package/sdk/test/suites/globalSetup.ts +450 -0
- package/sdk/test/suites/localnet.ts +23 -6
- package/sdk/test/upgrader.test.ts +7 -16
- package/sdk/test/utils.ts +558 -85
- package/sdk/turbo.json +8 -0
- package/sdk/vitest.config.ts +21 -0
- package/tools/ts-bindings-gen/Cargo.toml +2 -0
- package/tools/ts-bindings-gen/src/main.rs +52 -4
- package/contracts/common-macros/src/contract_impl.rs +0 -52
- package/contracts/common-macros/src/ownable.rs +0 -41
- package/contracts/common-macros/src/tests/contract_impl.rs +0 -386
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__ownable__snapshot_generated_ownable_code.snap +0 -12
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/extend_instance_ttl.rs +0 -50
- package/contracts/oapps/oapp-macros/src/oapp_core.rs +0 -41
- package/contracts/oapps/oapp-macros/src/oapp_full.rs +0 -21
- package/contracts/oapps/oapp-macros/src/oapp_options_type3.rs +0 -31
- package/contracts/oapps/oapp-macros/src/oapp_receiver.rs +0 -48
- package/contracts/oapps/oapp-macros/src/oapp_sender.rs +0 -21
- package/contracts/oapps/oapp-macros/src/util.rs +0 -107
- package/contracts/oapps/oft/src/constants.rs +0 -5
- package/contracts/oapps/oft/src/default_oft_impl.rs +0 -152
- package/contracts/oapps/oft/src/errors.rs +0 -8
- package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
- package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
- package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
- package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -903
- package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -749
- package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -432
- package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1078
- package/contracts/oapps/oft/src/types.rs +0 -38
- package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
- package/contracts/oapps/oft-std/src/lib.rs +0 -16
- package/contracts/oapps/oft-std/src/oft.rs +0 -156
- package/contracts/workers/dvn/src/interfaces/multisig.rs +0 -56
- package/contracts/workers/dvn/src/multisig.rs +0 -157
- package/sdk/dist/generated/oft_std.d.ts +0 -1544
- package/sdk/dist/generated/oft_std.js +0 -271
- package/sdk/test/index.test.ts +0 -375
- /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
- /package/sdk/test/suites/{testUpgradeable.ts → dummyContractClient.ts} +0 -0
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
//! Test setup for ExtensiveOFT with all extensions (pausable, oft_fee, rate_limiter).
|
|
2
|
-
//!
|
|
3
|
-
//! This module provides a comprehensive test setup similar to `test_utils.rs` but
|
|
4
|
-
//! specifically for testing ExtensiveOFT with extension functionality.
|
|
5
|
-
|
|
6
|
-
extern crate self as oft;
|
|
7
|
-
|
|
8
|
-
use crate::default_oft_impl::{default_quote_oft, default_quote_send};
|
|
9
|
-
use crate::errors::OFTError;
|
|
10
|
-
use crate::extensions::oft_fee::{OFTFee, OFTFeeInternal};
|
|
11
|
-
use crate::extensions::pausable::{OFTPausable, OFTPausableInternal};
|
|
12
|
-
use crate::extensions::rate_limiter::{Direction, RateLimiter, RateLimiterInternal};
|
|
13
|
-
use crate::oft::{oft_initialize, OFTInternal, OFT};
|
|
14
|
-
use crate::storage::OFTStorage;
|
|
15
|
-
use crate::tests::test_utils::{
|
|
16
|
-
create_recipient_address, DummyToken, MockEndpointWithCompose, MockEndpointWithComposeClient, DEFAULT_NATIVE_FEE,
|
|
17
|
-
DEFAULT_SHARED_DECIMALS, DEFAULT_ZRO_FEE, INITIAL_MINT_AMOUNT,
|
|
18
|
-
};
|
|
19
|
-
use crate::types::{OFTFeeDetail, OFTLimit, OFTReceipt, SendParam};
|
|
20
|
-
use crate::utils::remove_dust;
|
|
21
|
-
use endpoint_v2::{LayerZeroReceiverClient, MessagingFee, MessagingReceipt, Origin};
|
|
22
|
-
use oapp::oapp_core::OAppCoreClient;
|
|
23
|
-
use soroban_sdk::{
|
|
24
|
-
assert_with_error, bytes, contractimpl, log,
|
|
25
|
-
testutils::{Ledger, MockAuth, MockAuthInvoke},
|
|
26
|
-
token::{StellarAssetClient, TokenClient},
|
|
27
|
-
Address, Bytes, BytesN, Env, IntoVal,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// ==================== ExtensiveOFT Contract ====================
|
|
31
|
-
|
|
32
|
-
/// OFT contract with all three extensions enabled: pausable, oft_fee, rate_limiter.
|
|
33
|
-
#[oapp_macros::oapp]
|
|
34
|
-
pub struct ExtensiveOFT;
|
|
35
|
-
|
|
36
|
-
#[contractimpl]
|
|
37
|
-
impl ExtensiveOFT {
|
|
38
|
-
pub fn __constructor(
|
|
39
|
-
env: &Env,
|
|
40
|
-
token: &Address,
|
|
41
|
-
owner: &Address,
|
|
42
|
-
endpoint: &Address,
|
|
43
|
-
delegate: &Option<Address>,
|
|
44
|
-
shared_decimals: u32,
|
|
45
|
-
) {
|
|
46
|
-
oft_initialize::<Self>(env, owner, token, endpoint, delegate, shared_decimals);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Implement OFT trait with custom overrides for extensions
|
|
51
|
-
#[contractimpl(contracttrait)]
|
|
52
|
-
impl OFT for ExtensiveOFT {
|
|
53
|
-
fn quote_oft(
|
|
54
|
-
env: &Env,
|
|
55
|
-
send_param: &crate::types::SendParam,
|
|
56
|
-
) -> (OFTLimit, soroban_sdk::Vec<OFTFeeDetail>, OFTReceipt) {
|
|
57
|
-
Self::__assert_not_paused(env);
|
|
58
|
-
let (_, fee_details, oft_receipt) = default_quote_oft::<Self>(env, send_param);
|
|
59
|
-
let capacity = Self::rate_limit_capacity(env, &Direction::Outbound, send_param.dst_eid);
|
|
60
|
-
let oft_limit = OFTLimit { min_amount_ld: 0, max_amount_ld: capacity };
|
|
61
|
-
(oft_limit, fee_details, oft_receipt)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
fn quote_send(
|
|
65
|
-
env: &Env,
|
|
66
|
-
sender: &Address,
|
|
67
|
-
send_param: &crate::types::SendParam,
|
|
68
|
-
pay_in_zro: bool,
|
|
69
|
-
) -> endpoint_v2::MessagingFee {
|
|
70
|
-
Self::__assert_not_paused(env);
|
|
71
|
-
default_quote_send::<Self>(env, sender, send_param, pay_in_zro)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Internal OFT implementation - NOT exposed as contract endpoints
|
|
76
|
-
impl OFTInternal for ExtensiveOFT {
|
|
77
|
-
fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
|
|
78
|
-
// 1. Pausable check
|
|
79
|
-
Self::__assert_not_paused(env);
|
|
80
|
-
|
|
81
|
-
// 2. Core debit logic
|
|
82
|
-
let oft_receipt = crate::oft_types::mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid);
|
|
83
|
-
|
|
84
|
-
// 3. Rate limit checks
|
|
85
|
-
Self::__consume_rate_limit_capacity(env, &Direction::Outbound, dst_eid, oft_receipt.amount_received_ld);
|
|
86
|
-
Self::__release_rate_limit_capacity(env, &Direction::Inbound, dst_eid, oft_receipt.amount_received_ld);
|
|
87
|
-
|
|
88
|
-
// 4. Charge fee
|
|
89
|
-
Self::__charge_fee(
|
|
90
|
-
env,
|
|
91
|
-
&Self::token(env),
|
|
92
|
-
sender,
|
|
93
|
-
oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld,
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
oft_receipt
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
|
|
100
|
-
// 1. Pausable check
|
|
101
|
-
Self::__assert_not_paused(env);
|
|
102
|
-
|
|
103
|
-
// 2. Core credit logic
|
|
104
|
-
let amount_credited = crate::oft_types::mint_burn::credit::<Self>(env, to, amount_ld, src_eid);
|
|
105
|
-
|
|
106
|
-
// 3. Rate limit checks
|
|
107
|
-
Self::__consume_rate_limit_capacity(env, &Direction::Inbound, src_eid, amount_credited);
|
|
108
|
-
Self::__release_rate_limit_capacity(env, &Direction::Outbound, src_eid, amount_credited);
|
|
109
|
-
|
|
110
|
-
amount_credited
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
|
|
114
|
-
let conversion_rate = OFTStorage::decimal_conversion_rate(env).unwrap();
|
|
115
|
-
let amount_after_fee = Self::__fee_view(env, dst_eid, amount_ld);
|
|
116
|
-
let amount_received_ld = remove_dust(amount_after_fee, conversion_rate);
|
|
117
|
-
// Note: when no fee is applied, amount_send_ld is equal to amount_received_ld
|
|
118
|
-
// this is to align the behavior with the fee extensions on other VMs
|
|
119
|
-
let amount_sent_ld = if amount_after_fee == amount_ld { amount_received_ld } else { amount_ld };
|
|
120
|
-
assert_with_error!(env, amount_received_ld >= min_amount_ld, OFTError::SlippageExceeded);
|
|
121
|
-
|
|
122
|
-
OFTReceipt { amount_sent_ld, amount_received_ld }
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Extension traits - exposed as contract endpoints
|
|
127
|
-
#[contractimpl(contracttrait)]
|
|
128
|
-
impl OFTFee for ExtensiveOFT {}
|
|
129
|
-
|
|
130
|
-
impl OFTFeeInternal for ExtensiveOFT {}
|
|
131
|
-
|
|
132
|
-
#[contractimpl(contracttrait)]
|
|
133
|
-
impl OFTPausable for ExtensiveOFT {}
|
|
134
|
-
|
|
135
|
-
impl OFTPausableInternal for ExtensiveOFT {}
|
|
136
|
-
|
|
137
|
-
#[contractimpl(contracttrait)]
|
|
138
|
-
impl RateLimiter for ExtensiveOFT {}
|
|
139
|
-
|
|
140
|
-
impl RateLimiterInternal for ExtensiveOFT {}
|
|
141
|
-
|
|
142
|
-
// ==================== ExtensiveOFT Test Setup ====================
|
|
143
|
-
|
|
144
|
-
/// Test setup for ExtensiveOFT with all three extensions enabled.
|
|
145
|
-
pub struct ExtensiveOFTTestSetup<'a> {
|
|
146
|
-
pub env: &'a Env,
|
|
147
|
-
pub oft: ExtensiveOFTClient<'a>,
|
|
148
|
-
pub endpoint_client: MockEndpointWithComposeClient<'a>,
|
|
149
|
-
pub token: Address,
|
|
150
|
-
pub token_client: TokenClient<'a>,
|
|
151
|
-
pub native_token: Address,
|
|
152
|
-
pub zro_token: Address,
|
|
153
|
-
pub owner: Address,
|
|
154
|
-
pub fee_collector: Address,
|
|
155
|
-
pub native_fee: i128,
|
|
156
|
-
pub zro_fee: i128,
|
|
157
|
-
pub token_decimals: u32,
|
|
158
|
-
pub shared_decimals: u32,
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/// Builder for ExtensiveOFTTestSetup
|
|
162
|
-
pub struct ExtensiveOFTTestSetupBuilder<'a> {
|
|
163
|
-
env: &'a Env,
|
|
164
|
-
native_fee: i128,
|
|
165
|
-
zro_fee: i128,
|
|
166
|
-
token_decimals: u32,
|
|
167
|
-
shared_decimals: u32,
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
impl<'a> ExtensiveOFTTestSetupBuilder<'a> {
|
|
171
|
-
pub fn new(env: &'a Env) -> Self {
|
|
172
|
-
Self {
|
|
173
|
-
env,
|
|
174
|
-
native_fee: DEFAULT_NATIVE_FEE,
|
|
175
|
-
zro_fee: DEFAULT_ZRO_FEE,
|
|
176
|
-
token_decimals: 7,
|
|
177
|
-
shared_decimals: DEFAULT_SHARED_DECIMALS,
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
pub fn with_token_decimals(mut self, decimals: u32) -> Self {
|
|
182
|
-
self.token_decimals = decimals;
|
|
183
|
-
self
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
pub fn with_shared_decimals(mut self, decimals: u32) -> Self {
|
|
187
|
-
self.shared_decimals = decimals;
|
|
188
|
-
self
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
pub fn with_fees(mut self, native_fee: i128, zro_fee: i128) -> Self {
|
|
192
|
-
self.native_fee = native_fee;
|
|
193
|
-
self.zro_fee = zro_fee;
|
|
194
|
-
self
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
pub fn build(self) -> ExtensiveOFTTestSetup<'a> {
|
|
198
|
-
let env = self.env;
|
|
199
|
-
let native_fee = self.native_fee;
|
|
200
|
-
let zro_fee = self.zro_fee;
|
|
201
|
-
|
|
202
|
-
let owner = create_recipient_address(env);
|
|
203
|
-
let fee_collector = create_recipient_address(env);
|
|
204
|
-
|
|
205
|
-
// Create native token for fees
|
|
206
|
-
let native_sac = env.register_stellar_asset_contract_v2(owner.clone());
|
|
207
|
-
let native_token = native_sac.address();
|
|
208
|
-
|
|
209
|
-
// Create ZRO token
|
|
210
|
-
let zro_sac = env.register_stellar_asset_contract_v2(owner.clone());
|
|
211
|
-
let zro_token = zro_sac.address();
|
|
212
|
-
|
|
213
|
-
// Create OFT token (DummyToken with mint/burn capabilities)
|
|
214
|
-
let token = env.register(DummyToken, (&owner, self.token_decimals));
|
|
215
|
-
let token_client = TokenClient::new(env, &token);
|
|
216
|
-
|
|
217
|
-
// Register mock endpoint
|
|
218
|
-
let endpoint_address =
|
|
219
|
-
env.register(MockEndpointWithCompose, (&native_fee, &zro_fee, &native_token, &zro_token));
|
|
220
|
-
let endpoint_client = MockEndpointWithComposeClient::new(env, &endpoint_address);
|
|
221
|
-
|
|
222
|
-
// Register ExtensiveOFT
|
|
223
|
-
let delegate: Option<Address> = Some(owner.clone());
|
|
224
|
-
let shared_decimals = self.shared_decimals;
|
|
225
|
-
let oft_address = env.register(ExtensiveOFT, (&token, &owner, &endpoint_address, &delegate, &shared_decimals));
|
|
226
|
-
let oft = ExtensiveOFTClient::new(env, &oft_address);
|
|
227
|
-
|
|
228
|
-
// Pre-mint large amounts to owner
|
|
229
|
-
ExtensiveOFTTestSetup::mint_to(env, &owner, &token, &owner, INITIAL_MINT_AMOUNT);
|
|
230
|
-
ExtensiveOFTTestSetup::mint_to(env, &owner, &native_token, &owner, INITIAL_MINT_AMOUNT);
|
|
231
|
-
ExtensiveOFTTestSetup::mint_to(env, &owner, &zro_token, &owner, INITIAL_MINT_AMOUNT);
|
|
232
|
-
|
|
233
|
-
// Transfer token ownership to OFT so it can mint/burn tokens
|
|
234
|
-
ExtensiveOFTTestSetup::transfer_token_ownership(env, &owner, &token, &oft_address);
|
|
235
|
-
|
|
236
|
-
log!(&env, "token decimals: {}", self.token_decimals);
|
|
237
|
-
log!(&env, "token address: {}", token);
|
|
238
|
-
log!(&env, "native token address: {}", native_token);
|
|
239
|
-
log!(&env, "zro token address: {}", zro_token);
|
|
240
|
-
log!(&env, "owner: {}", owner);
|
|
241
|
-
log!(&env, "fee_collector: {}", fee_collector);
|
|
242
|
-
log!(&env, "native fee: {}", native_fee);
|
|
243
|
-
log!(&env, "zro fee: {}", zro_fee);
|
|
244
|
-
log!(&env, "oft address: {}", oft_address);
|
|
245
|
-
|
|
246
|
-
ExtensiveOFTTestSetup {
|
|
247
|
-
env,
|
|
248
|
-
oft,
|
|
249
|
-
endpoint_client,
|
|
250
|
-
token,
|
|
251
|
-
token_client,
|
|
252
|
-
native_token,
|
|
253
|
-
zro_token,
|
|
254
|
-
owner,
|
|
255
|
-
fee_collector,
|
|
256
|
-
native_fee,
|
|
257
|
-
zro_fee,
|
|
258
|
-
token_decimals: self.token_decimals,
|
|
259
|
-
shared_decimals: self.shared_decimals,
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
impl<'a> ExtensiveOFTTestSetup<'a> {
|
|
265
|
-
/// Create a new test setup with default configuration
|
|
266
|
-
pub fn new(env: &'a Env) -> Self {
|
|
267
|
-
ExtensiveOFTTestSetupBuilder::new(env).build()
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/// Create a builder for customized test setup
|
|
271
|
-
pub fn builder(env: &'a Env) -> ExtensiveOFTTestSetupBuilder<'a> {
|
|
272
|
-
ExtensiveOFTTestSetupBuilder::new(env)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ==================== Token Helpers ====================
|
|
276
|
-
|
|
277
|
-
pub fn mint_to(env: &Env, owner: &Address, token: &Address, to: &Address, amount: i128) {
|
|
278
|
-
env.mock_auths(&[MockAuth {
|
|
279
|
-
address: owner,
|
|
280
|
-
invoke: &MockAuthInvoke {
|
|
281
|
-
contract: token,
|
|
282
|
-
fn_name: "mint",
|
|
283
|
-
args: (to, amount).into_val(env),
|
|
284
|
-
sub_invokes: &[],
|
|
285
|
-
},
|
|
286
|
-
}]);
|
|
287
|
-
StellarAssetClient::new(env, token).mint(to, &amount);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
pub fn transfer_token_ownership(env: &Env, owner: &Address, token: &Address, new_admin: &Address) {
|
|
291
|
-
env.mock_auths(&[MockAuth {
|
|
292
|
-
address: owner,
|
|
293
|
-
invoke: &MockAuthInvoke {
|
|
294
|
-
contract: token,
|
|
295
|
-
fn_name: "set_admin",
|
|
296
|
-
args: (new_admin,).into_val(env),
|
|
297
|
-
sub_invokes: &[],
|
|
298
|
-
},
|
|
299
|
-
}]);
|
|
300
|
-
StellarAssetClient::new(env, token).set_admin(new_admin);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/// Fund an account with native fees (transfers from owner)
|
|
304
|
-
pub fn fund_native_fees(&self, to: &Address, amount: i128) {
|
|
305
|
-
self.env.mock_auths(&[MockAuth {
|
|
306
|
-
address: &self.owner,
|
|
307
|
-
invoke: &MockAuthInvoke {
|
|
308
|
-
contract: &self.native_token,
|
|
309
|
-
fn_name: "transfer",
|
|
310
|
-
args: (&self.owner, to, amount).into_val(self.env),
|
|
311
|
-
sub_invokes: &[],
|
|
312
|
-
},
|
|
313
|
-
}]);
|
|
314
|
-
TokenClient::new(self.env, &self.native_token).transfer(&self.owner, to, &amount);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/// Fund an account with ZRO fees (transfers from owner)
|
|
318
|
-
pub fn fund_zro_fees(&self, to: &Address, amount: i128) {
|
|
319
|
-
self.env.mock_auths(&[MockAuth {
|
|
320
|
-
address: &self.owner,
|
|
321
|
-
invoke: &MockAuthInvoke {
|
|
322
|
-
contract: &self.zro_token,
|
|
323
|
-
fn_name: "transfer",
|
|
324
|
-
args: (&self.owner, to, amount).into_val(self.env),
|
|
325
|
-
sub_invokes: &[],
|
|
326
|
-
},
|
|
327
|
-
}]);
|
|
328
|
-
TokenClient::new(self.env, &self.zro_token).transfer(&self.owner, to, &amount);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/// Fund an account with OFT tokens (transfers from owner)
|
|
332
|
-
pub fn fund_tokens(&self, to: &Address, amount: i128) {
|
|
333
|
-
self.env.mock_auths(&[MockAuth {
|
|
334
|
-
address: &self.owner,
|
|
335
|
-
invoke: &MockAuthInvoke {
|
|
336
|
-
contract: &self.token,
|
|
337
|
-
fn_name: "transfer",
|
|
338
|
-
args: (&self.owner, to, amount).into_val(self.env),
|
|
339
|
-
sub_invokes: &[],
|
|
340
|
-
},
|
|
341
|
-
}]);
|
|
342
|
-
self.token_client.transfer(&self.owner, to, &amount);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// ==================== OApp Helpers ====================
|
|
346
|
-
|
|
347
|
-
pub fn set_peer(&self, eid: u32, peer: &BytesN<32>) {
|
|
348
|
-
self.env.mock_auths(&[MockAuth {
|
|
349
|
-
address: &self.owner,
|
|
350
|
-
invoke: &MockAuthInvoke {
|
|
351
|
-
contract: &self.oft.address,
|
|
352
|
-
fn_name: "set_peer",
|
|
353
|
-
args: (&eid, peer).into_val(self.env),
|
|
354
|
-
sub_invokes: &[],
|
|
355
|
-
},
|
|
356
|
-
}]);
|
|
357
|
-
OAppCoreClient::new(self.env, &self.oft.address).set_peer(&eid, &Some(peer.clone()));
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// ==================== Pausable Extension Helpers ====================
|
|
361
|
-
|
|
362
|
-
/// Set the paused state (owner only)
|
|
363
|
-
pub fn set_paused(&self, paused: bool) {
|
|
364
|
-
self.env.mock_auths(&[MockAuth {
|
|
365
|
-
address: &self.owner,
|
|
366
|
-
invoke: &MockAuthInvoke {
|
|
367
|
-
contract: &self.oft.address,
|
|
368
|
-
fn_name: "set_paused",
|
|
369
|
-
args: (paused,).into_val(self.env),
|
|
370
|
-
sub_invokes: &[],
|
|
371
|
-
},
|
|
372
|
-
}]);
|
|
373
|
-
self.oft.set_paused(&paused);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/// Try to set paused state (returns Result)
|
|
377
|
-
pub fn try_set_paused(
|
|
378
|
-
&self,
|
|
379
|
-
paused: bool,
|
|
380
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
381
|
-
self.env.mock_auths(&[MockAuth {
|
|
382
|
-
address: &self.owner,
|
|
383
|
-
invoke: &MockAuthInvoke {
|
|
384
|
-
contract: &self.oft.address,
|
|
385
|
-
fn_name: "set_paused",
|
|
386
|
-
args: (paused,).into_val(self.env),
|
|
387
|
-
sub_invokes: &[],
|
|
388
|
-
},
|
|
389
|
-
}]);
|
|
390
|
-
self.oft.try_set_paused(&paused)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/// Check if paused
|
|
394
|
-
pub fn is_paused(&self) -> bool {
|
|
395
|
-
self.oft.is_paused()
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// ==================== OFT Fee Extension Helpers ====================
|
|
399
|
-
|
|
400
|
-
/// Set the default fee in basis points (owner only)
|
|
401
|
-
pub fn set_default_fee_bps(&self, fee_bps: u64) {
|
|
402
|
-
self.env.mock_auths(&[MockAuth {
|
|
403
|
-
address: &self.owner,
|
|
404
|
-
invoke: &MockAuthInvoke {
|
|
405
|
-
contract: &self.oft.address,
|
|
406
|
-
fn_name: "set_default_fee_bps",
|
|
407
|
-
args: (fee_bps,).into_val(self.env),
|
|
408
|
-
sub_invokes: &[],
|
|
409
|
-
},
|
|
410
|
-
}]);
|
|
411
|
-
self.oft.set_default_fee_bps(&fee_bps);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/// Try to set default fee bps (returns Result)
|
|
415
|
-
pub fn try_set_default_fee_bps(
|
|
416
|
-
&self,
|
|
417
|
-
fee_bps: u64,
|
|
418
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
419
|
-
self.env.mock_auths(&[MockAuth {
|
|
420
|
-
address: &self.owner,
|
|
421
|
-
invoke: &MockAuthInvoke {
|
|
422
|
-
contract: &self.oft.address,
|
|
423
|
-
fn_name: "set_default_fee_bps",
|
|
424
|
-
args: (fee_bps,).into_val(self.env),
|
|
425
|
-
sub_invokes: &[],
|
|
426
|
-
},
|
|
427
|
-
}]);
|
|
428
|
-
self.oft.try_set_default_fee_bps(&fee_bps)
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/// Set fee bps for a specific destination (owner only)
|
|
432
|
-
pub fn set_fee_bps(&self, dst_eid: u32, fee_bps: u64) {
|
|
433
|
-
self.env.mock_auths(&[MockAuth {
|
|
434
|
-
address: &self.owner,
|
|
435
|
-
invoke: &MockAuthInvoke {
|
|
436
|
-
contract: &self.oft.address,
|
|
437
|
-
fn_name: "set_fee_bps",
|
|
438
|
-
args: (dst_eid, fee_bps).into_val(self.env),
|
|
439
|
-
sub_invokes: &[],
|
|
440
|
-
},
|
|
441
|
-
}]);
|
|
442
|
-
self.oft.set_fee_bps(&dst_eid, &fee_bps);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/// Try to set fee bps (returns Result)
|
|
446
|
-
pub fn try_set_fee_bps(
|
|
447
|
-
&self,
|
|
448
|
-
dst_eid: u32,
|
|
449
|
-
fee_bps: u64,
|
|
450
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
451
|
-
self.env.mock_auths(&[MockAuth {
|
|
452
|
-
address: &self.owner,
|
|
453
|
-
invoke: &MockAuthInvoke {
|
|
454
|
-
contract: &self.oft.address,
|
|
455
|
-
fn_name: "set_fee_bps",
|
|
456
|
-
args: (dst_eid, fee_bps).into_val(self.env),
|
|
457
|
-
sub_invokes: &[],
|
|
458
|
-
},
|
|
459
|
-
}]);
|
|
460
|
-
self.oft.try_set_fee_bps(&dst_eid, &fee_bps)
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/// Unset fee bps for a specific destination (owner only)
|
|
464
|
-
pub fn unset_fee_bps(&self, dst_eid: u32) {
|
|
465
|
-
self.env.mock_auths(&[MockAuth {
|
|
466
|
-
address: &self.owner,
|
|
467
|
-
invoke: &MockAuthInvoke {
|
|
468
|
-
contract: &self.oft.address,
|
|
469
|
-
fn_name: "unset_fee_bps",
|
|
470
|
-
args: (dst_eid,).into_val(self.env),
|
|
471
|
-
sub_invokes: &[],
|
|
472
|
-
},
|
|
473
|
-
}]);
|
|
474
|
-
self.oft.unset_fee_bps(&dst_eid);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/// Try to unset fee bps (returns Result)
|
|
478
|
-
pub fn try_unset_fee_bps(
|
|
479
|
-
&self,
|
|
480
|
-
dst_eid: u32,
|
|
481
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
482
|
-
self.env.mock_auths(&[MockAuth {
|
|
483
|
-
address: &self.owner,
|
|
484
|
-
invoke: &MockAuthInvoke {
|
|
485
|
-
contract: &self.oft.address,
|
|
486
|
-
fn_name: "unset_fee_bps",
|
|
487
|
-
args: (dst_eid,).into_val(self.env),
|
|
488
|
-
sub_invokes: &[],
|
|
489
|
-
},
|
|
490
|
-
}]);
|
|
491
|
-
self.oft.try_unset_fee_bps(&dst_eid)
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/// Set the fee deposit address (owner only)
|
|
495
|
-
pub fn set_fee_deposit_address(&self, address: &Address) {
|
|
496
|
-
self.env.mock_auths(&[MockAuth {
|
|
497
|
-
address: &self.owner,
|
|
498
|
-
invoke: &MockAuthInvoke {
|
|
499
|
-
contract: &self.oft.address,
|
|
500
|
-
fn_name: "set_fee_deposit_address",
|
|
501
|
-
args: (address,).into_val(self.env),
|
|
502
|
-
sub_invokes: &[],
|
|
503
|
-
},
|
|
504
|
-
}]);
|
|
505
|
-
self.oft.set_fee_deposit_address(address);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/// Try to set fee deposit address (returns Result)
|
|
509
|
-
pub fn try_set_fee_deposit_address(
|
|
510
|
-
&self,
|
|
511
|
-
address: &Address,
|
|
512
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
513
|
-
self.env.mock_auths(&[MockAuth {
|
|
514
|
-
address: &self.owner,
|
|
515
|
-
invoke: &MockAuthInvoke {
|
|
516
|
-
contract: &self.oft.address,
|
|
517
|
-
fn_name: "set_fee_deposit_address",
|
|
518
|
-
args: (address,).into_val(self.env),
|
|
519
|
-
sub_invokes: &[],
|
|
520
|
-
},
|
|
521
|
-
}]);
|
|
522
|
-
self.oft.try_set_fee_deposit_address(address)
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/// Get the default fee bps
|
|
526
|
-
pub fn default_fee_bps(&self) -> u64 {
|
|
527
|
-
self.oft.default_fee_bps()
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
/// Get the fee bps for a specific destination
|
|
531
|
-
pub fn fee_bps(&self, dst_eid: u32) -> u64 {
|
|
532
|
-
self.oft.fee_bps(&dst_eid).unwrap_or(0)
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/// Get the effective fee bps for a specific destination
|
|
536
|
-
pub fn effective_fee_bps(&self, dst_eid: u32) -> u64 {
|
|
537
|
-
self.oft.effective_fee_bps(&dst_eid)
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/// Check if fee bps is set for a specific destination
|
|
541
|
-
pub fn has_fee_bps(&self, dst_eid: u32) -> bool {
|
|
542
|
-
self.oft.has_fee_bps(&dst_eid)
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/// Get the fee deposit address
|
|
546
|
-
pub fn fee_deposit_address(&self) -> Address {
|
|
547
|
-
self.oft.fee_deposit_address()
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// ==================== Rate Limiter Extension Helpers ====================
|
|
551
|
-
|
|
552
|
-
/// Set rate limit for a direction and eid (owner only)
|
|
553
|
-
pub fn set_rate_limit(&self, direction: &Direction, eid: u32, limit: i128, window_seconds: u64) {
|
|
554
|
-
self.env.mock_auths(&[MockAuth {
|
|
555
|
-
address: &self.owner,
|
|
556
|
-
invoke: &MockAuthInvoke {
|
|
557
|
-
contract: &self.oft.address,
|
|
558
|
-
fn_name: "set_rate_limit",
|
|
559
|
-
args: (direction, eid, limit, window_seconds).into_val(self.env),
|
|
560
|
-
sub_invokes: &[],
|
|
561
|
-
},
|
|
562
|
-
}]);
|
|
563
|
-
self.oft.set_rate_limit(direction, &eid, &limit, &window_seconds);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/// Try to set rate limit (returns Result)
|
|
567
|
-
pub fn try_set_rate_limit(
|
|
568
|
-
&self,
|
|
569
|
-
direction: &Direction,
|
|
570
|
-
eid: u32,
|
|
571
|
-
limit: i128,
|
|
572
|
-
window_seconds: u64,
|
|
573
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
574
|
-
self.env.mock_auths(&[MockAuth {
|
|
575
|
-
address: &self.owner,
|
|
576
|
-
invoke: &MockAuthInvoke {
|
|
577
|
-
contract: &self.oft.address,
|
|
578
|
-
fn_name: "set_rate_limit",
|
|
579
|
-
args: (direction, eid, limit, window_seconds).into_val(self.env),
|
|
580
|
-
sub_invokes: &[],
|
|
581
|
-
},
|
|
582
|
-
}]);
|
|
583
|
-
self.oft.try_set_rate_limit(direction, &eid, &limit, &window_seconds)
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/// Unset rate limit for a direction and eid (owner only)
|
|
587
|
-
pub fn unset_rate_limit(&self, direction: &Direction, eid: u32) {
|
|
588
|
-
self.env.mock_auths(&[MockAuth {
|
|
589
|
-
address: &self.owner,
|
|
590
|
-
invoke: &MockAuthInvoke {
|
|
591
|
-
contract: &self.oft.address,
|
|
592
|
-
fn_name: "unset_rate_limit",
|
|
593
|
-
args: (direction, eid).into_val(self.env),
|
|
594
|
-
sub_invokes: &[],
|
|
595
|
-
},
|
|
596
|
-
}]);
|
|
597
|
-
self.oft.unset_rate_limit(direction, &eid);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/// Try to unset rate limit (returns Result)
|
|
601
|
-
pub fn try_unset_rate_limit(
|
|
602
|
-
&self,
|
|
603
|
-
direction: &Direction,
|
|
604
|
-
eid: u32,
|
|
605
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
606
|
-
self.env.mock_auths(&[MockAuth {
|
|
607
|
-
address: &self.owner,
|
|
608
|
-
invoke: &MockAuthInvoke {
|
|
609
|
-
contract: &self.oft.address,
|
|
610
|
-
fn_name: "unset_rate_limit",
|
|
611
|
-
args: (direction, eid).into_val(self.env),
|
|
612
|
-
sub_invokes: &[],
|
|
613
|
-
},
|
|
614
|
-
}]);
|
|
615
|
-
self.oft.try_unset_rate_limit(direction, &eid)
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/// Get rate limit config (limit, window_seconds)
|
|
619
|
-
pub fn rate_limit_config(&self, direction: &Direction, eid: u32) -> (i128, u64) {
|
|
620
|
-
self.oft.rate_limit_config(direction, &eid)
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/// Get rate limit in flight
|
|
624
|
-
pub fn rate_limit_in_flight(&self, direction: &Direction, eid: u32) -> i128 {
|
|
625
|
-
self.oft.rate_limit_in_flight(direction, &eid)
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/// Try to get rate limit in-flight (returns Result for testing error cases)
|
|
629
|
-
pub fn try_rate_limit_in_flight(
|
|
630
|
-
&self,
|
|
631
|
-
direction: &Direction,
|
|
632
|
-
eid: u32,
|
|
633
|
-
) -> Result<Result<i128, soroban_sdk::Error>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
634
|
-
self.oft.try_rate_limit_in_flight(direction, &eid)
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/// Get rate limit capacity
|
|
638
|
-
pub fn rate_limit_capacity(&self, direction: &Direction, eid: u32) -> i128 {
|
|
639
|
-
self.oft.rate_limit_capacity(direction, &eid)
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// ==================== Time Helpers ====================
|
|
643
|
-
|
|
644
|
-
/// Advance the ledger timestamp by the given number of seconds
|
|
645
|
-
pub fn advance_time(&self, seconds: u64) {
|
|
646
|
-
let current = self.env.ledger().timestamp();
|
|
647
|
-
self.env.ledger().set_timestamp(current + seconds);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/// Set the ledger timestamp to a specific value
|
|
651
|
-
pub fn set_timestamp(&self, timestamp: u64) {
|
|
652
|
-
self.env.ledger().set_timestamp(timestamp);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// ==================== OFT Core Operations ====================
|
|
656
|
-
|
|
657
|
-
/// Create a SendParam for testing
|
|
658
|
-
pub fn create_send_param(&self, dst_eid: u32, amount_ld: i128, min_amount_ld: i128) -> SendParam {
|
|
659
|
-
SendParam {
|
|
660
|
-
dst_eid,
|
|
661
|
-
to: BytesN::from_array(self.env, &[1u8; 32]),
|
|
662
|
-
amount_ld,
|
|
663
|
-
min_amount_ld,
|
|
664
|
-
extra_options: bytes!(self.env),
|
|
665
|
-
compose_msg: bytes!(self.env),
|
|
666
|
-
oft_cmd: bytes!(self.env),
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/// Quote OFT to get the receipt for authorization
|
|
671
|
-
pub fn quote_oft(&self, send_param: &SendParam) -> OFTReceipt {
|
|
672
|
-
let (_, _, receipt) = self.oft.quote_oft(send_param);
|
|
673
|
-
receipt
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/// Try quote OFT (returns Result)
|
|
677
|
-
pub fn try_quote_oft(
|
|
678
|
-
&self,
|
|
679
|
-
send_param: &SendParam,
|
|
680
|
-
) -> Result<
|
|
681
|
-
Result<(crate::types::OFTLimit, soroban_sdk::Vec<crate::types::OFTFeeDetail>, OFTReceipt), soroban_sdk::Error>,
|
|
682
|
-
Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
|
|
683
|
-
> {
|
|
684
|
-
self.oft.try_quote_oft(send_param)
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/// Quote send to get messaging fees
|
|
688
|
-
pub fn quote_send(&self, sender: &Address, send_param: &SendParam, pay_in_zro: bool) -> MessagingFee {
|
|
689
|
-
self.oft.quote_send(sender, send_param, &pay_in_zro)
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/// Try quote send (returns Result)
|
|
693
|
-
pub fn try_quote_send(
|
|
694
|
-
&self,
|
|
695
|
-
sender: &Address,
|
|
696
|
-
send_param: &SendParam,
|
|
697
|
-
pay_in_zro: bool,
|
|
698
|
-
) -> Result<Result<MessagingFee, soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>>
|
|
699
|
-
{
|
|
700
|
-
self.oft.try_quote_send(sender, send_param, &pay_in_zro)
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/// Send tokens cross-chain with proper sender authentication and fee transfer
|
|
704
|
-
/// Use this when OFT fee extension is configured and fee > 0
|
|
705
|
-
pub fn send_with_fee(
|
|
706
|
-
&self,
|
|
707
|
-
sender: &Address,
|
|
708
|
-
send_param: &SendParam,
|
|
709
|
-
fee: &MessagingFee,
|
|
710
|
-
refund_address: &Address,
|
|
711
|
-
oft_receipt: &OFTReceipt,
|
|
712
|
-
fee_deposit_address: &Address,
|
|
713
|
-
) -> (MessagingReceipt, OFTReceipt) {
|
|
714
|
-
let fee_amount = oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld;
|
|
715
|
-
|
|
716
|
-
// Mock auth order must match contract execution order:
|
|
717
|
-
// 1. Transfer fee (__charge_fee) - happens before burn
|
|
718
|
-
// 2. Burn tokens (amount_received_ld, not amount_sent_ld)
|
|
719
|
-
// 3. Transfer native/zro fees (__lz_send)
|
|
720
|
-
self.env.mock_auths(&[MockAuth {
|
|
721
|
-
address: sender,
|
|
722
|
-
invoke: &MockAuthInvoke {
|
|
723
|
-
contract: &self.oft.address,
|
|
724
|
-
fn_name: "send",
|
|
725
|
-
args: (sender, send_param, fee, refund_address).into_val(self.env),
|
|
726
|
-
sub_invokes: &[
|
|
727
|
-
// 1. Transfer fee to fee deposit address - happens in __charge_fee (BEFORE burn)
|
|
728
|
-
MockAuthInvoke {
|
|
729
|
-
contract: &self.token,
|
|
730
|
-
fn_name: "transfer",
|
|
731
|
-
args: (sender, fee_deposit_address, &fee_amount).into_val(self.env),
|
|
732
|
-
sub_invokes: &[],
|
|
733
|
-
},
|
|
734
|
-
// 2. Burn tokens (amount_received_ld, not amount_sent_ld)
|
|
735
|
-
MockAuthInvoke {
|
|
736
|
-
contract: &self.token,
|
|
737
|
-
fn_name: "burn",
|
|
738
|
-
args: (sender, &oft_receipt.amount_received_ld).into_val(self.env),
|
|
739
|
-
sub_invokes: &[],
|
|
740
|
-
},
|
|
741
|
-
// 3. Transfer native fee - happens in __lz_send
|
|
742
|
-
MockAuthInvoke {
|
|
743
|
-
contract: &self.native_token,
|
|
744
|
-
fn_name: "transfer",
|
|
745
|
-
args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
|
|
746
|
-
sub_invokes: &[],
|
|
747
|
-
},
|
|
748
|
-
// 4. Transfer zro fee - happens in __lz_send
|
|
749
|
-
MockAuthInvoke {
|
|
750
|
-
contract: &self.zro_token,
|
|
751
|
-
fn_name: "transfer",
|
|
752
|
-
args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
|
|
753
|
-
sub_invokes: &[],
|
|
754
|
-
},
|
|
755
|
-
],
|
|
756
|
-
},
|
|
757
|
-
}]);
|
|
758
|
-
self.oft.send(sender, send_param, fee, refund_address)
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
/// Send tokens cross-chain without fee transfer (for cases where no fee is configured)
|
|
762
|
-
pub fn send(
|
|
763
|
-
&self,
|
|
764
|
-
sender: &Address,
|
|
765
|
-
send_param: &SendParam,
|
|
766
|
-
fee: &MessagingFee,
|
|
767
|
-
refund_address: &Address,
|
|
768
|
-
oft_receipt: &OFTReceipt,
|
|
769
|
-
) -> (MessagingReceipt, OFTReceipt) {
|
|
770
|
-
// When no fee is configured, amount_sent_ld == amount_received_ld
|
|
771
|
-
// Mock auth order must match contract execution order:
|
|
772
|
-
// 1. Burn tokens (amount_received_ld)
|
|
773
|
-
// 2. Transfer native/zro fees (__lz_send)
|
|
774
|
-
self.env.mock_auths(&[MockAuth {
|
|
775
|
-
address: sender,
|
|
776
|
-
invoke: &MockAuthInvoke {
|
|
777
|
-
contract: &self.oft.address,
|
|
778
|
-
fn_name: "send",
|
|
779
|
-
args: (sender, send_param, fee, refund_address).into_val(self.env),
|
|
780
|
-
sub_invokes: &[
|
|
781
|
-
// 1. Burn tokens (mint-burn strategy) - amount_received_ld == amount_sent_ld when no fee
|
|
782
|
-
MockAuthInvoke {
|
|
783
|
-
contract: &self.token,
|
|
784
|
-
fn_name: "burn",
|
|
785
|
-
args: (sender, &oft_receipt.amount_received_ld).into_val(self.env),
|
|
786
|
-
sub_invokes: &[],
|
|
787
|
-
},
|
|
788
|
-
// 2. Transfer native fee - happens in __lz_send
|
|
789
|
-
MockAuthInvoke {
|
|
790
|
-
contract: &self.native_token,
|
|
791
|
-
fn_name: "transfer",
|
|
792
|
-
args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
|
|
793
|
-
sub_invokes: &[],
|
|
794
|
-
},
|
|
795
|
-
// 3. Transfer zro fee - happens in __lz_send
|
|
796
|
-
MockAuthInvoke {
|
|
797
|
-
contract: &self.zro_token,
|
|
798
|
-
fn_name: "transfer",
|
|
799
|
-
args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
|
|
800
|
-
sub_invokes: &[],
|
|
801
|
-
},
|
|
802
|
-
],
|
|
803
|
-
},
|
|
804
|
-
}]);
|
|
805
|
-
self.oft.send(sender, send_param, fee, refund_address)
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
/// Try send tokens cross-chain without fee (returns Result)
|
|
809
|
-
pub fn try_send(
|
|
810
|
-
&self,
|
|
811
|
-
sender: &Address,
|
|
812
|
-
send_param: &SendParam,
|
|
813
|
-
fee: &MessagingFee,
|
|
814
|
-
refund_address: &Address,
|
|
815
|
-
oft_receipt: &OFTReceipt,
|
|
816
|
-
) -> Result<
|
|
817
|
-
Result<(MessagingReceipt, OFTReceipt), soroban_sdk::Error>,
|
|
818
|
-
Result<soroban_sdk::Error, soroban_sdk::InvokeError>,
|
|
819
|
-
> {
|
|
820
|
-
// When no fee is configured, amount_sent_ld == amount_received_ld
|
|
821
|
-
// Mock auth order must match contract execution order:
|
|
822
|
-
// 1. Burn tokens (amount_received_ld)
|
|
823
|
-
// 2. Transfer native/zro fees (__lz_send)
|
|
824
|
-
self.env.mock_auths(&[MockAuth {
|
|
825
|
-
address: sender,
|
|
826
|
-
invoke: &MockAuthInvoke {
|
|
827
|
-
contract: &self.oft.address,
|
|
828
|
-
fn_name: "send",
|
|
829
|
-
args: (sender, send_param, fee, refund_address).into_val(self.env),
|
|
830
|
-
sub_invokes: &[
|
|
831
|
-
// 1. Burn tokens (mint-burn strategy) - amount_received_ld == amount_sent_ld when no fee
|
|
832
|
-
MockAuthInvoke {
|
|
833
|
-
contract: &self.token,
|
|
834
|
-
fn_name: "burn",
|
|
835
|
-
args: (sender, &oft_receipt.amount_received_ld).into_val(self.env),
|
|
836
|
-
sub_invokes: &[],
|
|
837
|
-
},
|
|
838
|
-
// 2. Transfer native fee - happens in __lz_send
|
|
839
|
-
MockAuthInvoke {
|
|
840
|
-
contract: &self.native_token,
|
|
841
|
-
fn_name: "transfer",
|
|
842
|
-
args: (sender, &self.endpoint_client.address, &fee.native_fee).into_val(self.env),
|
|
843
|
-
sub_invokes: &[],
|
|
844
|
-
},
|
|
845
|
-
// 3. Transfer zro fee - happens in __lz_send
|
|
846
|
-
MockAuthInvoke {
|
|
847
|
-
contract: &self.zro_token,
|
|
848
|
-
fn_name: "transfer",
|
|
849
|
-
args: (sender, &self.endpoint_client.address, &fee.zro_fee).into_val(self.env),
|
|
850
|
-
sub_invokes: &[],
|
|
851
|
-
},
|
|
852
|
-
],
|
|
853
|
-
},
|
|
854
|
-
}]);
|
|
855
|
-
self.oft.try_send(sender, send_param, fee, refund_address)
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
/// Execute lz_receive with proper executor authentication
|
|
859
|
-
pub fn lz_receive(
|
|
860
|
-
&self,
|
|
861
|
-
executor: &Address,
|
|
862
|
-
origin: &Origin,
|
|
863
|
-
guid: &BytesN<32>,
|
|
864
|
-
message: &Bytes,
|
|
865
|
-
extra_data: &Bytes,
|
|
866
|
-
value: i128,
|
|
867
|
-
) {
|
|
868
|
-
self.env.mock_auths(&[MockAuth {
|
|
869
|
-
address: executor,
|
|
870
|
-
invoke: &MockAuthInvoke {
|
|
871
|
-
contract: &self.oft.address,
|
|
872
|
-
fn_name: "lz_receive",
|
|
873
|
-
args: (executor, origin, guid, message, extra_data, value).into_val(self.env),
|
|
874
|
-
sub_invokes: &[],
|
|
875
|
-
},
|
|
876
|
-
}]);
|
|
877
|
-
LayerZeroReceiverClient::new(self.env, &self.oft.address)
|
|
878
|
-
.lz_receive(executor, origin, guid, message, extra_data, &value);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
/// Try lz_receive (returns Result)
|
|
882
|
-
pub fn try_lz_receive(
|
|
883
|
-
&self,
|
|
884
|
-
executor: &Address,
|
|
885
|
-
origin: &Origin,
|
|
886
|
-
guid: &BytesN<32>,
|
|
887
|
-
message: &Bytes,
|
|
888
|
-
extra_data: &Bytes,
|
|
889
|
-
value: i128,
|
|
890
|
-
) -> Result<Result<(), soroban_sdk::ConversionError>, Result<soroban_sdk::Error, soroban_sdk::InvokeError>> {
|
|
891
|
-
self.env.mock_auths(&[MockAuth {
|
|
892
|
-
address: executor,
|
|
893
|
-
invoke: &MockAuthInvoke {
|
|
894
|
-
contract: &self.oft.address,
|
|
895
|
-
fn_name: "lz_receive",
|
|
896
|
-
args: (executor, origin, guid, message, extra_data, value).into_val(self.env),
|
|
897
|
-
sub_invokes: &[],
|
|
898
|
-
},
|
|
899
|
-
}]);
|
|
900
|
-
LayerZeroReceiverClient::new(self.env, &self.oft.address)
|
|
901
|
-
.try_lz_receive(executor, origin, guid, message, extra_data, &value)
|
|
902
|
-
}
|
|
903
|
-
}
|