@layerzerolabs/protocol-stellar-v2 0.2.29 → 0.2.30
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 +371 -321
- package/.turbo/turbo-lint.log +211 -202
- package/.turbo/turbo-test.log +1766 -1673
- package/Cargo.lock +11 -1
- package/contracts/common-macros/src/lib.rs +0 -2
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +1 -0
- package/contracts/endpoint-v2/src/messaging_channel.rs +32 -3
- package/contracts/endpoint-v2/src/tests/endpoint_setup.rs +1 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +1 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +6 -6
- package/contracts/endpoint-v2/src/tests/messaging_channel/inbound_payload_hash.rs +1 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/outbound.rs +16 -10
- package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +10 -10
- package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +3 -3
- package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +4 -3
- package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +1 -57
- package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_missing_internal.stderr +0 -30
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -3
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +6 -4
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -3
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -3
- package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_auth_trait.stderr +0 -30
- package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_upgradeable_internal.stderr +0 -30
- package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/basic.rs +0 -2
- package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/multisig_contract.rs +0 -2
- package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_migration.rs +0 -2
- package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_user_contractimpl.rs +1 -3
- package/contracts/message-libs/message-lib-common/src/packet_codec_v1.rs +3 -6
- package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +10 -0
- package/contracts/message-libs/message-lib-common/src/worker_options.rs +6 -2
- package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +3 -3
- package/contracts/message-libs/treasury/src/lib.rs +2 -1
- package/contracts/message-libs/treasury/src/tests/setup.rs +1 -1
- package/contracts/message-libs/treasury/src/treasury.rs +5 -2
- package/contracts/message-libs/uln-302/src/errors.rs +2 -0
- package/contracts/message-libs/uln-302/src/events.rs +3 -3
- package/contracts/message-libs/uln-302/src/interfaces/receive_uln.rs +8 -0
- package/contracts/message-libs/uln-302/src/lib.rs +2 -1
- package/contracts/message-libs/uln-302/src/receive_uln.rs +16 -13
- package/contracts/message-libs/uln-302/src/send_uln.rs +51 -24
- package/contracts/message-libs/uln-302/src/storage.rs +2 -2
- package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +45 -1
- package/contracts/message-libs/uln-302/src/tests/receive_uln302/verifiable.rs +63 -0
- package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +47 -2
- package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +50 -1
- package/contracts/message-libs/uln-302/src/uln302.rs +0 -8
- package/contracts/oapps/counter/Cargo.toml +4 -4
- package/contracts/oapps/counter/integration_tests/setup_uln.rs +22 -2
- package/contracts/oapps/counter/src/counter.rs +8 -8
- package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +33 -10
- package/contracts/oapps/oapp/src/lib.rs +6 -2
- package/contracts/oapps/oapp/src/oapp_core.rs +49 -24
- package/contracts/oapps/oapp/src/oapp_options_type3.rs +21 -14
- package/contracts/oapps/oapp/src/oapp_receiver.rs +17 -16
- package/contracts/oapps/oapp/src/oapp_sender.rs +66 -15
- package/contracts/oapps/oapp/src/tests/oapp_core.rs +5 -5
- package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +18 -18
- package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +4 -4
- package/contracts/oapps/oapp/src/tests/oapp_sender.rs +3 -3
- package/contracts/oapps/oapp-macros/Cargo.toml +0 -1
- package/contracts/oapps/oapp-macros/src/generators.rs +87 -46
- package/contracts/oapps/oapp-macros/src/lib.rs +3 -61
- package/contracts/oapps/oapp-macros/src/tests/oapp.rs +9 -23
- package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +15 -11
- package/contracts/oapps/oft/Cargo.toml +1 -1
- package/contracts/oapps/oft/integration-tests/extensions/test_oft_fee.rs +3 -3
- package/contracts/oapps/oft/integration-tests/extensions/test_pausable.rs +4 -4
- package/contracts/oapps/oft/integration-tests/extensions/test_rate_limiter.rs +144 -8
- package/contracts/oapps/oft/integration-tests/setup.rs +4 -2
- package/contracts/oapps/oft/integration-tests/utils.rs +25 -11
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +65 -63
- package/contracts/oapps/oft/src/extensions/pausable.rs +2 -3
- package/contracts/oapps/oft/src/extensions/rate_limiter.rs +22 -5
- package/contracts/oapps/oft/src/interfaces/mint_burnable.rs +18 -0
- package/contracts/oapps/oft/src/interfaces/mod.rs +3 -0
- package/contracts/oapps/oft/src/lib.rs +4 -2
- package/contracts/oapps/oft/src/oft.rs +35 -36
- package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +13 -9
- package/contracts/oapps/oft/src/oft_types/mint_burn.rs +14 -9
- package/contracts/oapps/oft/src/oft_types/mod.rs +14 -12
- package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +28 -20
- package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +136 -2
- package/contracts/oapps/oft/src/tests/oft_types/lock_unlock.rs +12 -8
- package/contracts/oapps/oft-core/integration-tests/setup.rs +8 -9
- package/contracts/oapps/oft-core/integration-tests/test_with_sml.rs +7 -6
- package/contracts/oapps/oft-core/integration-tests/utils.rs +5 -4
- package/contracts/oapps/oft-core/src/codec/oft_compose_msg_codec.rs +2 -2
- package/contracts/oapps/oft-core/src/codec/oft_msg_codec.rs +33 -37
- package/contracts/oapps/oft-core/src/errors.rs +2 -1
- package/contracts/oapps/oft-core/src/events.rs +6 -0
- package/contracts/oapps/oft-core/src/lib.rs +8 -4
- package/contracts/oapps/oft-core/src/oft_core.rs +205 -148
- package/contracts/oapps/oft-core/src/storage.rs +4 -2
- package/contracts/oapps/oft-core/src/tests/test_decimals.rs +2 -2
- package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +6 -6
- package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +7 -6
- package/contracts/oapps/oft-core/src/tests/test_oft_msg_codec.rs +11 -82
- package/contracts/oapps/oft-core/src/tests/test_quote_oft.rs +13 -13
- package/contracts/oapps/oft-core/src/tests/test_quote_send.rs +1 -1
- package/contracts/oapps/oft-core/src/tests/test_resolve_address.rs +2 -2
- package/contracts/oapps/oft-core/src/tests/test_send.rs +22 -22
- package/contracts/oapps/oft-core/src/tests/test_utils.rs +20 -22
- package/contracts/oapps/oft-core/src/utils.rs +12 -8
- package/contracts/sac-manager/Cargo.toml +25 -0
- package/contracts/sac-manager/src/errors.rs +18 -0
- package/contracts/sac-manager/src/extensions/mod.rs +6 -0
- package/contracts/sac-manager/src/extensions/redistribution.rs +109 -0
- package/contracts/sac-manager/src/extensions/supply_control/mod.rs +488 -0
- package/contracts/sac-manager/src/extensions/supply_control/rate_limit.rs +126 -0
- package/contracts/sac-manager/src/interfaces/mod.rs +3 -0
- package/contracts/sac-manager/src/interfaces/sac_manager.rs +52 -0
- package/contracts/sac-manager/src/lib.rs +23 -0
- package/contracts/sac-manager/src/sac_manager.rs +193 -0
- package/contracts/sac-manager/src/storage.rs +20 -0
- package/contracts/sac-manager/src/tests/mod.rs +14 -0
- package/contracts/sac-manager/src/tests/redistribution/mod.rs +1 -0
- package/contracts/sac-manager/src/tests/redistribution/redistribute_funds.rs +82 -0
- package/contracts/sac-manager/src/tests/sac_manager/admin_mint.rs +206 -0
- package/contracts/sac-manager/src/tests/sac_manager/burn.rs +215 -0
- package/contracts/sac-manager/src/tests/sac_manager/clawback.rs +209 -0
- package/contracts/sac-manager/src/tests/sac_manager/mint.rs +252 -0
- package/contracts/sac-manager/src/tests/sac_manager/mod.rs +9 -0
- package/contracts/sac-manager/src/tests/sac_manager/set_admin.rs +36 -0
- package/contracts/sac-manager/src/tests/sac_manager/set_authorized.rs +43 -0
- package/contracts/sac-manager/src/tests/sac_manager/set_oft_address.rs +47 -0
- package/contracts/sac-manager/src/tests/sac_manager/test_helper.rs +75 -0
- package/contracts/sac-manager/src/tests/sac_manager/view_functions.rs +60 -0
- package/contracts/sac-manager/src/tests/supply_control/enumerable_set.rs +256 -0
- package/contracts/sac-manager/src/tests/supply_control/mod.rs +8 -0
- package/contracts/sac-manager/src/tests/supply_control/refill.rs +90 -0
- package/contracts/sac-manager/src/tests/supply_control/set_mint_whitelist.rs +245 -0
- package/contracts/sac-manager/src/tests/supply_control/set_supply_controller.rs +267 -0
- package/contracts/sac-manager/src/tests/supply_control/set_supply_controller_manager.rs +122 -0
- package/contracts/sac-manager/src/tests/supply_control/test_helper.rs +38 -0
- package/contracts/sac-manager/src/tests/supply_control/update_allow_any_mint_burn.rs +114 -0
- package/contracts/sac-manager/src/tests/supply_control/update_limit_config.rs +257 -0
- package/contracts/sac-manager/src/tests/test_helper.rs +190 -0
- package/contracts/upgrader/src/lib.rs +2 -1
- package/contracts/utils/src/errors.rs +0 -1
- package/contracts/utils/src/tests/upgradeable.rs +0 -66
- package/contracts/utils/src/upgradeable.rs +0 -18
- package/contracts/workers/dvn/src/dvn.rs +2 -2
- package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
- package/contracts/workers/dvn/src/lib.rs +2 -1
- package/contracts/workers/dvn-fee-lib/src/lib.rs +3 -1
- package/contracts/workers/executor/src/auth.rs +42 -26
- package/contracts/workers/executor/src/executor.rs +28 -3
- package/contracts/workers/executor/src/lib.rs +4 -2
- package/contracts/workers/executor/src/storage.rs +21 -1
- package/contracts/workers/executor/src/tests/auth.rs +64 -20
- package/contracts/workers/executor/src/tests/executor.rs +1 -1
- package/contracts/workers/executor/src/tests/setup.rs +18 -0
- package/contracts/workers/executor-fee-lib/src/lib.rs +4 -1
- package/contracts/workers/executor-helper/src/executor_helper.rs +24 -10
- package/contracts/workers/executor-helper/src/tests/setup.rs +147 -34
- package/contracts/workers/price-feed/src/lib.rs +3 -1
- package/contracts/workers/worker/src/lib.rs +2 -1
- package/contracts/workers/worker/src/worker.rs +31 -17
- package/docs/oapp-guide.md +17 -8
- package/docs/oft-guide.md +3 -3
- package/package.json +3 -3
- package/sdk/.turbo/turbo-test.log +512 -351
- package/sdk/dist/generated/bml.d.ts +3 -9
- package/sdk/dist/generated/bml.js +6 -7
- package/sdk/dist/generated/counter.d.ts +22 -28
- package/sdk/dist/generated/counter.js +11 -12
- package/sdk/dist/generated/dvn.d.ts +36 -54
- package/sdk/dist/generated/dvn.js +10 -15
- package/sdk/dist/generated/dvn_fee_lib.d.ts +3 -21
- package/sdk/dist/generated/dvn_fee_lib.js +6 -11
- package/sdk/dist/generated/endpoint.d.ts +3 -9
- package/sdk/dist/generated/endpoint.js +6 -7
- package/sdk/dist/generated/executor.d.ts +80 -54
- package/sdk/dist/generated/executor.js +16 -16
- package/sdk/dist/generated/executor_fee_lib.d.ts +3 -21
- package/sdk/dist/generated/executor_fee_lib.js +6 -11
- package/sdk/dist/generated/executor_helper.d.ts +36 -42
- package/sdk/dist/generated/executor_helper.js +9 -10
- package/sdk/dist/generated/layerzero_view.d.ts +20 -32
- package/sdk/dist/generated/layerzero_view.js +25 -26
- package/sdk/dist/generated/oft.d.ts +147 -79
- package/sdk/dist/generated/oft.js +47 -54
- package/sdk/dist/generated/price_feed.d.ts +20 -38
- package/sdk/dist/generated/price_feed.js +15 -20
- package/sdk/dist/generated/sac_manager.d.ts +1309 -0
- package/sdk/dist/generated/sac_manager.js +484 -0
- package/sdk/dist/generated/sml.d.ts +3 -9
- package/sdk/dist/generated/sml.js +6 -7
- package/sdk/dist/generated/treasury.d.ts +3 -9
- package/sdk/dist/generated/treasury.js +8 -9
- package/sdk/dist/generated/uln302.d.ts +20 -20
- package/sdk/dist/generated/uln302.js +25 -22
- package/sdk/dist/generated/upgrader.d.ts +3 -9
- package/sdk/dist/generated/upgrader.js +6 -7
- package/sdk/dist/index.d.ts +1 -0
- package/sdk/dist/index.js +1 -0
- package/sdk/package.json +1 -1
- package/sdk/src/index.ts +1 -0
- package/sdk/test/oft-sml.test.ts +7 -5
- package/sdk/test/sac-manager-redistribution.test.ts +578 -0
- package/sdk/test/suites/globalSetup.ts +11 -6
- package/sdk/test/test_data/test_upgradeable_dvn.wasm +0 -0
- package/sdk/test/upgrader.test.ts +75 -202
- package/sdk/test/utils.ts +40 -0
- package/tools/ts-bindings-gen/src/main.rs +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
use soroban_sdk::{address_payload::AddressPayload, assert_with_error, Address, BytesN, Env};
|
|
2
|
-
|
|
3
1
|
use crate::errors::OFTError;
|
|
2
|
+
use soroban_sdk::{address_payload::AddressPayload, assert_with_error, Address, BytesN, Env};
|
|
3
|
+
use utils::option_ext::OptionExt;
|
|
4
4
|
|
|
5
5
|
// =====================================================
|
|
6
6
|
// OFT Helper Functions
|
|
@@ -38,18 +38,22 @@ pub fn remove_dust(amount_ld: i128, conversion_rate: i128) -> i128 {
|
|
|
38
38
|
///
|
|
39
39
|
/// # Returns
|
|
40
40
|
/// A 32-byte payload (contract ID hash or Ed25519 public key)
|
|
41
|
-
pub fn address_payload(address: &Address) -> BytesN<32> {
|
|
42
|
-
match address.to_payload().
|
|
41
|
+
pub fn address_payload(env: &Env, address: &Address) -> BytesN<32> {
|
|
42
|
+
match address.to_payload().unwrap_or_panic(env, OFTError::InvalidAddress) {
|
|
43
43
|
AddressPayload::ContractIdHash(payload) => payload,
|
|
44
44
|
AddressPayload::AccountIdPublicKeyEd25519(payload) => payload,
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/// Resolves a 32-byte payload to
|
|
48
|
+
/// Resolves a 32-byte payload back to a Stellar address.
|
|
49
|
+
///
|
|
50
|
+
/// Cross-chain messages only carry 32-byte addresses, but Stellar has two address types
|
|
51
|
+
/// (contract C-addresses and account G-addresses) that share the same 32-byte payload.
|
|
52
|
+
/// This function disambiguates by checking contract existence first, then falling back
|
|
53
|
+
/// to a G-address.
|
|
49
54
|
///
|
|
50
|
-
///
|
|
51
|
-
///
|
|
52
|
-
/// If the contract doesn't exist, it falls back to creating a G-address (account address).
|
|
55
|
+
/// Sending tokens to a non-existent contract address is unlikely in practice — the sender
|
|
56
|
+
/// on the source chain is expected to deploy the destination contract beforehand.
|
|
53
57
|
///
|
|
54
58
|
/// # Arguments
|
|
55
59
|
/// * `env` - The Soroban environment
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "sac-manager"
|
|
3
|
+
version.workspace = true
|
|
4
|
+
edition.workspace = true
|
|
5
|
+
license.workspace = true
|
|
6
|
+
|
|
7
|
+
[lib]
|
|
8
|
+
crate-type = ["cdylib", "rlib"]
|
|
9
|
+
doctest = false
|
|
10
|
+
|
|
11
|
+
[features]
|
|
12
|
+
library = []
|
|
13
|
+
testutils = []
|
|
14
|
+
|
|
15
|
+
[dependencies]
|
|
16
|
+
soroban-sdk = { workspace = true }
|
|
17
|
+
utils = { workspace = true }
|
|
18
|
+
common-macros = { workspace = true }
|
|
19
|
+
cfg-if = { workspace = true }
|
|
20
|
+
oft = { workspace = true, features = ["library"] }
|
|
21
|
+
|
|
22
|
+
[dev-dependencies]
|
|
23
|
+
soroban-sdk = { workspace = true, features = ["testutils"] }
|
|
24
|
+
utils = { workspace = true, features = ["testutils"] }
|
|
25
|
+
oft = { workspace = true, features = ["testutils"] }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//! General errors for the SAC Manager contract.
|
|
2
|
+
//!
|
|
3
|
+
//! Note: Extension-specific errors are co-located with their modules:
|
|
4
|
+
//! - Supply control errors: `extensions/supply_control.rs` (SupplyControlError)
|
|
5
|
+
//! - Redistribution errors: `extensions/redistribution.rs` (RedistributionError)
|
|
6
|
+
|
|
7
|
+
use common_macros::contract_error;
|
|
8
|
+
|
|
9
|
+
/// General SacManagerError: 3220-3249
|
|
10
|
+
#[contract_error]
|
|
11
|
+
pub enum SacManagerError {
|
|
12
|
+
/// OFT address has not been set yet
|
|
13
|
+
OftAddressNotSet,
|
|
14
|
+
/// The values are the same
|
|
15
|
+
SameValue,
|
|
16
|
+
/// Caller is not authorized for this operation
|
|
17
|
+
Unauthorized,
|
|
18
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
//! Redistribution extension for handling assets associated with blacklisted addresses.
|
|
2
|
+
|
|
3
|
+
use crate::{interfaces::ISacManager, sac_manager::sac_client};
|
|
4
|
+
use common_macros::{contract_error, contract_trait, only_auth, storage};
|
|
5
|
+
use soroban_sdk::{assert_with_error, contractevent, Address, Env};
|
|
6
|
+
use utils::ownable::Ownable;
|
|
7
|
+
|
|
8
|
+
// =========================================================================
|
|
9
|
+
// Errors
|
|
10
|
+
// =========================================================================
|
|
11
|
+
|
|
12
|
+
#[contract_error]
|
|
13
|
+
pub enum RedistributionError {
|
|
14
|
+
/// Account is not blacklisted (required for redistribution)
|
|
15
|
+
NotBlacklisted = 100,
|
|
16
|
+
/// Redistribution is not enabled
|
|
17
|
+
RedistributionNotEnabled,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// =========================================================================
|
|
21
|
+
// Events
|
|
22
|
+
// =========================================================================
|
|
23
|
+
|
|
24
|
+
/// Event emitted when funds are redistributed from a blacklisted account.
|
|
25
|
+
#[contractevent]
|
|
26
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
27
|
+
pub struct RedistributeFunds {
|
|
28
|
+
pub user: Address,
|
|
29
|
+
pub amount: i128,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// =========================================================================
|
|
33
|
+
// Storage
|
|
34
|
+
// =========================================================================
|
|
35
|
+
|
|
36
|
+
#[storage]
|
|
37
|
+
pub enum RedistributionStorage {
|
|
38
|
+
/// Whether redistribution is enabled.
|
|
39
|
+
/// Note: variant name must be globally unique across all storage enums to avoid
|
|
40
|
+
/// Soroban storage key collisions (contracttype serializes by variant name hash).
|
|
41
|
+
#[instance(bool)]
|
|
42
|
+
RedistributionEnabled,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// =========================================================================
|
|
46
|
+
// Redistribution Trait
|
|
47
|
+
// =========================================================================
|
|
48
|
+
|
|
49
|
+
/// Redistribution trait for handling transfers to blacklisted addresses.
|
|
50
|
+
/// Uses SAC's authorization system - blacklisted accounts have authorized=false.
|
|
51
|
+
///
|
|
52
|
+
/// Only the contract owner can redistribute funds.
|
|
53
|
+
/// The redistribution target is always the contract owner.
|
|
54
|
+
#[contract_trait(client_name = "RedistributionClient")]
|
|
55
|
+
pub trait Redistribution: ISacManager + RedistributionInternal {
|
|
56
|
+
// =========================================================================
|
|
57
|
+
// Redistribution Operations
|
|
58
|
+
// =========================================================================
|
|
59
|
+
|
|
60
|
+
/// Redistributes funds from a blacklisted account to the owner.
|
|
61
|
+
#[only_auth]
|
|
62
|
+
fn redistribute_blacklisted_funds(env: &Env, from: &Address, amount: i128) {
|
|
63
|
+
assert_with_error!(env, Self::__redistribution_enabled(env), RedistributionError::RedistributionNotEnabled);
|
|
64
|
+
|
|
65
|
+
let sac_client = sac_client::<Self>(env);
|
|
66
|
+
|
|
67
|
+
// Require `from` to be blacklisted (not authorized on SAC)
|
|
68
|
+
assert_with_error!(env, !sac_client.authorized(from), RedistributionError::NotBlacklisted);
|
|
69
|
+
|
|
70
|
+
// Transfer from blacklisted account to owner using clawback + mint
|
|
71
|
+
let owner = Self::owner(env).unwrap();
|
|
72
|
+
sac_client.clawback(from, &amount);
|
|
73
|
+
sac_client.mint(&owner, &amount);
|
|
74
|
+
|
|
75
|
+
RedistributeFunds { user: from.clone(), amount }.publish(env);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// =========================================================================
|
|
79
|
+
// View Functions
|
|
80
|
+
// =========================================================================
|
|
81
|
+
|
|
82
|
+
fn redistribution_enabled(env: &Env) -> bool {
|
|
83
|
+
Self::__redistribution_enabled(env)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Internal trait for redistribution operations.
|
|
88
|
+
pub trait RedistributionInternal: ISacManager + Ownable {
|
|
89
|
+
/// Redistributes funds from a blacklisted account to the owner.
|
|
90
|
+
fn __redistribute_funds(env: &Env, to: &Address, amount: i128) -> Address {
|
|
91
|
+
// If redistribution is enabled and the address is not authorized, redistribute the funds to the owner.
|
|
92
|
+
if Self::__redistribution_enabled(env) && !sac_client::<Self>(env).authorized(to) {
|
|
93
|
+
RedistributeFunds { user: to.clone(), amount }.publish(env);
|
|
94
|
+
Self::owner(env).unwrap()
|
|
95
|
+
} else {
|
|
96
|
+
to.clone()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Returns whether redistribution is enabled.
|
|
101
|
+
fn __redistribution_enabled(env: &Env) -> bool {
|
|
102
|
+
RedistributionStorage::has_redistribution_enabled(env)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Enables redistribution. only call this function at construction time.
|
|
106
|
+
fn __enable_redistribution(env: &Env) {
|
|
107
|
+
RedistributionStorage::set_redistribution_enabled(env, &true);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
//! Supply control extension for rate-limited minting.
|
|
2
|
+
//!
|
|
3
|
+
//! Provides rate-limited minting with per-controller capacity tracking
|
|
4
|
+
//! and optional mint whitelist enforcement.
|
|
5
|
+
//!
|
|
6
|
+
//! Config and rate limit state are nested in the same struct for atomic storage,
|
|
7
|
+
//! but logically separated so config updates don't reset inflight state.
|
|
8
|
+
|
|
9
|
+
pub(crate) mod rate_limit;
|
|
10
|
+
|
|
11
|
+
use common_macros::{contract_error, contract_trait, only_auth, storage};
|
|
12
|
+
use rate_limit::{get_remaining_amount, try_consume};
|
|
13
|
+
pub use rate_limit::{LimitConfig, RateLimitState};
|
|
14
|
+
use soroban_sdk::{assert_with_error, contractevent, contracttype, panic_with_error, Address, Env, Vec};
|
|
15
|
+
use utils::{option_ext::OptionExt, ownable::Ownable};
|
|
16
|
+
|
|
17
|
+
// =========================================================================
|
|
18
|
+
// Errors
|
|
19
|
+
// =========================================================================
|
|
20
|
+
|
|
21
|
+
/// Supply control errors: 3250-3299
|
|
22
|
+
#[contract_error]
|
|
23
|
+
pub enum SupplyControlError {
|
|
24
|
+
/// Entry already exists (supply controller, manager, or whitelist address)
|
|
25
|
+
AlreadyExists = 200,
|
|
26
|
+
/// Cannot burn from this address
|
|
27
|
+
CannotBurnFromAddress,
|
|
28
|
+
/// Cannot mint to this address (not in whitelist)
|
|
29
|
+
CannotMintToAddress,
|
|
30
|
+
/// Invalid amount
|
|
31
|
+
InvalidAmount,
|
|
32
|
+
/// Invalid configuration
|
|
33
|
+
InvalidConfig,
|
|
34
|
+
/// Entry not found (supply controller, manager, or whitelist address)
|
|
35
|
+
NotFound,
|
|
36
|
+
/// Timestamp is older than the last refill time
|
|
37
|
+
OldTimestamp,
|
|
38
|
+
/// Rate limit exceeded
|
|
39
|
+
RateLimitExceeded,
|
|
40
|
+
/// Value is already set to the same state
|
|
41
|
+
SameValue,
|
|
42
|
+
/// Caller is not authorized (not a supply controller or manager)
|
|
43
|
+
Unauthorized,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// =========================================================================
|
|
47
|
+
// Events
|
|
48
|
+
// =========================================================================
|
|
49
|
+
|
|
50
|
+
/// Event emitted when a supply controller is added.
|
|
51
|
+
#[contractevent]
|
|
52
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
53
|
+
pub struct SupplyControllerAdded {
|
|
54
|
+
pub supply_controller: Address,
|
|
55
|
+
pub config: SupplyControllerConfig,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Event emitted when a supply controller is removed.
|
|
59
|
+
#[contractevent]
|
|
60
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
61
|
+
pub struct SupplyControllerRemoved {
|
|
62
|
+
pub supply_controller: Address,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Event emitted when limit configuration is updated for a supply controller.
|
|
66
|
+
#[contractevent]
|
|
67
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
68
|
+
pub struct LimitConfigUpdated {
|
|
69
|
+
pub supply_controller: Address,
|
|
70
|
+
pub new_limit_config: LimitConfig,
|
|
71
|
+
pub old_limit_config: LimitConfig,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Event emitted when `allow_any_mint_burn` is updated for a supply controller.
|
|
75
|
+
#[contractevent]
|
|
76
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
77
|
+
pub struct AllowAnyMintBurnUpdated {
|
|
78
|
+
pub supply_controller: Address,
|
|
79
|
+
pub new_allow: bool,
|
|
80
|
+
pub old_allow: bool,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// Event emitted when a mint whitelist entry is added or removed.
|
|
84
|
+
#[contractevent]
|
|
85
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
86
|
+
pub struct MintWhitelistSet {
|
|
87
|
+
pub supply_controller: Address,
|
|
88
|
+
pub mint_address: Address,
|
|
89
|
+
pub active: bool,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Event emitted when a supply controller manager is added or removed.
|
|
93
|
+
#[contractevent]
|
|
94
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
95
|
+
pub struct SupplyCtrlManagerSet {
|
|
96
|
+
pub manager: Address,
|
|
97
|
+
pub active: bool,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Event emitted when tokens are minted.
|
|
101
|
+
#[contractevent]
|
|
102
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
103
|
+
pub struct SupplyIncreased {
|
|
104
|
+
pub controller: Address,
|
|
105
|
+
pub to: Address,
|
|
106
|
+
pub amount: i128,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Event emitted when tokens are burned.
|
|
110
|
+
#[contractevent]
|
|
111
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
112
|
+
pub struct SupplyDecreased {
|
|
113
|
+
pub controller: Address,
|
|
114
|
+
pub from: Address,
|
|
115
|
+
pub amount: i128,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// =========================================================================
|
|
119
|
+
// Types
|
|
120
|
+
// =========================================================================
|
|
121
|
+
|
|
122
|
+
/// Supply controller configuration (the configurable parameters).
|
|
123
|
+
#[contracttype]
|
|
124
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
125
|
+
pub struct SupplyControllerConfig {
|
|
126
|
+
/// Limit configuration
|
|
127
|
+
pub limit_config: LimitConfig,
|
|
128
|
+
/// If true, allows the supply controller to mint to and burn from any address.
|
|
129
|
+
/// When false, the controller can only mint to addresses in its whitelist.
|
|
130
|
+
pub allow_any_mint_burn: bool,
|
|
131
|
+
/// Addresses that this supply controller is allowed to mint to (when `allow_any_mint_burn` is false).
|
|
132
|
+
pub whitelist_addresses: Vec<Address>,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// Supply controller with both config and state nested together.
|
|
136
|
+
/// Stored per-controller for O(1) access on every mint/burn.
|
|
137
|
+
#[contracttype]
|
|
138
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
139
|
+
pub struct SupplyControllerEntry {
|
|
140
|
+
/// Configuration (can be updated independently)
|
|
141
|
+
pub config: SupplyControllerConfig,
|
|
142
|
+
/// Rate limit state (preserved across config updates)
|
|
143
|
+
pub state: RateLimitState,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// =========================================================================
|
|
147
|
+
// Storage
|
|
148
|
+
// =========================================================================
|
|
149
|
+
|
|
150
|
+
#[storage]
|
|
151
|
+
pub enum SupplyControlStorage {
|
|
152
|
+
/// Whether supply control is enabled.
|
|
153
|
+
/// Note: variant name must be globally unique across all storage enums to avoid
|
|
154
|
+
/// Soroban storage key collisions (contracttype serializes by variant name hash).
|
|
155
|
+
#[instance(bool)]
|
|
156
|
+
SupplyControlEnabled,
|
|
157
|
+
|
|
158
|
+
/// Per-controller entry: config + rate limit state.
|
|
159
|
+
/// Keyed by controller address for O(1) access on every mint/burn.
|
|
160
|
+
#[persistent(SupplyControllerEntry)]
|
|
161
|
+
SupplyControllerEntry { controller: Address },
|
|
162
|
+
|
|
163
|
+
/// All supply controller addresses.
|
|
164
|
+
/// Stored as a single Vec since the number of controllers is expected to be small.
|
|
165
|
+
#[persistent(Vec<Address>)]
|
|
166
|
+
#[default(Vec::new(env))]
|
|
167
|
+
SupplyControllers,
|
|
168
|
+
|
|
169
|
+
/// All supply controller manager addresses.
|
|
170
|
+
/// Stored as a single Vec since the number of managers is expected to be small.
|
|
171
|
+
#[persistent(Vec<Address>)]
|
|
172
|
+
#[default(Vec::new(env))]
|
|
173
|
+
SupplyControllerManagers,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// =========================================================================
|
|
177
|
+
// Supply Control Trait
|
|
178
|
+
// =========================================================================
|
|
179
|
+
|
|
180
|
+
/// Supply control trait for managing rate-limited minting.
|
|
181
|
+
///
|
|
182
|
+
/// ## Authorization Model
|
|
183
|
+
/// - **Owner**: Can add/remove supply controller managers.
|
|
184
|
+
/// - **Supply Controller Manager**: Can add/remove/update supply controllers.
|
|
185
|
+
/// - **Supply Controller**: Can mint and burn tokens.
|
|
186
|
+
///
|
|
187
|
+
/// Note: Global enable/disable is managed by the SacManager contract directly.
|
|
188
|
+
#[contract_trait(client_name = "SupplyControlClient")]
|
|
189
|
+
pub trait SupplyControl: Ownable + SupplyControlInternal {
|
|
190
|
+
// =========================================================================
|
|
191
|
+
// Supply Controller Manager Management (owner-only)
|
|
192
|
+
// =========================================================================
|
|
193
|
+
|
|
194
|
+
/// Sets whether an address is a supply controller manager. Owner-only.
|
|
195
|
+
#[only_auth]
|
|
196
|
+
fn set_supply_controller_manager(env: &Env, manager: &Address, active: bool) {
|
|
197
|
+
let mut managers = Self::get_supply_controller_managers(env);
|
|
198
|
+
|
|
199
|
+
let idx = managers.first_index_of(manager);
|
|
200
|
+
if active {
|
|
201
|
+
assert_with_error!(env, idx.is_none(), SupplyControlError::AlreadyExists);
|
|
202
|
+
managers.push_back(manager.clone());
|
|
203
|
+
} else {
|
|
204
|
+
assert_with_error!(env, idx.is_some(), SupplyControlError::NotFound);
|
|
205
|
+
managers.remove(idx.unwrap());
|
|
206
|
+
}
|
|
207
|
+
SupplyControlStorage::set_supply_controller_managers(env, &managers);
|
|
208
|
+
|
|
209
|
+
SupplyCtrlManagerSet { manager: manager.clone(), active }.publish(env);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// =========================================================================
|
|
213
|
+
// Supply Controller Management (supply controller manager-only)
|
|
214
|
+
// =========================================================================
|
|
215
|
+
|
|
216
|
+
/// Sets or removes a supply controller. Manager-only.
|
|
217
|
+
///
|
|
218
|
+
/// - `Some(config)`: Adds a new supply controller with the given configuration.
|
|
219
|
+
/// - `None`: Removes the supply controller.
|
|
220
|
+
fn set_supply_controller(
|
|
221
|
+
env: &Env,
|
|
222
|
+
sender: &Address,
|
|
223
|
+
supply_controller: &Address,
|
|
224
|
+
config: &Option<SupplyControllerConfig>,
|
|
225
|
+
) {
|
|
226
|
+
require_manager_auth::<Self>(env, sender);
|
|
227
|
+
|
|
228
|
+
if let Some(config) = config {
|
|
229
|
+
Self::__add_supply_controller(env, supply_controller, config);
|
|
230
|
+
} else {
|
|
231
|
+
Self::__remove_supply_controller(env, supply_controller);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// Updates the rate limit configuration for a supply controller. Manager-only.
|
|
236
|
+
/// Note: This does NOT reset the rate limit state (remaining_amount, last_refill_time).
|
|
237
|
+
fn update_limit_config(env: &Env, sender: &Address, supply_controller: &Address, limit_config: &LimitConfig) {
|
|
238
|
+
require_manager_auth::<Self>(env, sender);
|
|
239
|
+
|
|
240
|
+
validate_limit_config(env, limit_config);
|
|
241
|
+
|
|
242
|
+
let mut sc = Self::__get_entry_or_panic(env, supply_controller);
|
|
243
|
+
let old_limit_config = sc.config.limit_config.clone();
|
|
244
|
+
assert_with_error!(env, old_limit_config != *limit_config, SupplyControlError::SameValue);
|
|
245
|
+
sc.config.limit_config = limit_config.clone();
|
|
246
|
+
SupplyControlStorage::set_supply_controller_entry(env, supply_controller, &sc);
|
|
247
|
+
|
|
248
|
+
LimitConfigUpdated {
|
|
249
|
+
supply_controller: supply_controller.clone(),
|
|
250
|
+
new_limit_config: limit_config.clone(),
|
|
251
|
+
old_limit_config,
|
|
252
|
+
}
|
|
253
|
+
.publish(env);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// Updates whether the supply controller can mint to and burn from any address. Manager-only.
|
|
257
|
+
fn update_allow_any_mint_burn(env: &Env, sender: &Address, supply_controller: &Address, allow_any_mint_burn: bool) {
|
|
258
|
+
require_manager_auth::<Self>(env, sender);
|
|
259
|
+
|
|
260
|
+
let mut sc = Self::__get_entry_or_panic(env, supply_controller);
|
|
261
|
+
let old_allow = sc.config.allow_any_mint_burn;
|
|
262
|
+
assert_with_error!(env, old_allow != allow_any_mint_burn, SupplyControlError::SameValue);
|
|
263
|
+
sc.config.allow_any_mint_burn = allow_any_mint_burn;
|
|
264
|
+
SupplyControlStorage::set_supply_controller_entry(env, supply_controller, &sc);
|
|
265
|
+
|
|
266
|
+
AllowAnyMintBurnUpdated {
|
|
267
|
+
supply_controller: supply_controller.clone(),
|
|
268
|
+
new_allow: allow_any_mint_burn,
|
|
269
|
+
old_allow,
|
|
270
|
+
}
|
|
271
|
+
.publish(env);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// Sets whether an address is in the mint whitelist of a supply controller. Manager-only.
|
|
275
|
+
fn set_mint_whitelist(
|
|
276
|
+
env: &Env,
|
|
277
|
+
sender: &Address,
|
|
278
|
+
supply_controller: &Address,
|
|
279
|
+
mint_address: &Address,
|
|
280
|
+
active: bool,
|
|
281
|
+
) {
|
|
282
|
+
require_manager_auth::<Self>(env, sender);
|
|
283
|
+
|
|
284
|
+
let mut sc = Self::__get_entry_or_panic(env, supply_controller);
|
|
285
|
+
|
|
286
|
+
let idx = sc.config.whitelist_addresses.first_index_of(mint_address);
|
|
287
|
+
if active {
|
|
288
|
+
assert_with_error!(env, idx.is_none(), SupplyControlError::AlreadyExists);
|
|
289
|
+
sc.config.whitelist_addresses.push_back(mint_address.clone());
|
|
290
|
+
} else {
|
|
291
|
+
assert_with_error!(env, idx.is_some(), SupplyControlError::NotFound);
|
|
292
|
+
sc.config.whitelist_addresses.remove(idx.unwrap());
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
SupplyControlStorage::set_supply_controller_entry(env, supply_controller, &sc);
|
|
296
|
+
|
|
297
|
+
MintWhitelistSet { supply_controller: supply_controller.clone(), mint_address: mint_address.clone(), active }
|
|
298
|
+
.publish(env);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// =========================================================================
|
|
302
|
+
// View Functions
|
|
303
|
+
// =========================================================================
|
|
304
|
+
|
|
305
|
+
/// Returns whether the given address is a supply controller manager.
|
|
306
|
+
fn is_supply_controller_manager(env: &Env, address: &Address) -> bool {
|
|
307
|
+
Self::get_supply_controller_managers(env).first_index_of(address).is_some()
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/// Gets all supply controller manager addresses.
|
|
311
|
+
fn get_supply_controller_managers(env: &Env) -> Vec<Address> {
|
|
312
|
+
SupplyControlStorage::supply_controller_managers(env)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/// Gets supply controller configuration.
|
|
316
|
+
fn get_supply_controller_config(env: &Env, supply_controller: &Address) -> Option<SupplyControllerConfig> {
|
|
317
|
+
SupplyControlStorage::supply_controller_entry(env, supply_controller).map(|sc| sc.config)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
fn allow_any_mint_burn(env: &Env, supply_controller: &Address) -> bool {
|
|
321
|
+
SupplyControlStorage::supply_controller_entry(env, supply_controller)
|
|
322
|
+
.map(|sc| sc.config.allow_any_mint_burn)
|
|
323
|
+
.unwrap_or(false)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/// Gets all active supply controller addresses.
|
|
327
|
+
fn get_supply_controllers(env: &Env) -> Vec<Address> {
|
|
328
|
+
SupplyControlStorage::supply_controllers(env)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/// Returns whether the given address is in the whitelist of a supply controller.
|
|
332
|
+
fn is_address_whitelisted(env: &Env, supply_controller: &Address, mint_address: &Address) -> bool {
|
|
333
|
+
SupplyControlStorage::supply_controller_entry(env, supply_controller)
|
|
334
|
+
.map(|sc| sc.config.whitelist_addresses.contains(mint_address))
|
|
335
|
+
.unwrap_or(false)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/// Gets remaining amount which can be minted at current timestamp.
|
|
339
|
+
fn get_remaining_mint_amount(env: &Env, supply_controller: &Address) -> i128 {
|
|
340
|
+
let Some(sc) = SupplyControlStorage::supply_controller_entry(env, supply_controller) else {
|
|
341
|
+
return 0;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
get_remaining_amount(&sc.config.limit_config, &sc.state, env.ledger().timestamp())
|
|
345
|
+
.unwrap_or_else(|e| panic_with_error!(env, e))
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
fn supply_control_enabled(env: &Env) -> bool {
|
|
349
|
+
Self::__supply_control_enabled(env)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// =========================================================================
|
|
354
|
+
// Internal Trait
|
|
355
|
+
// =========================================================================
|
|
356
|
+
|
|
357
|
+
/// Internal methods for supply control operations.
|
|
358
|
+
/// Separated from the public `SupplyControl` trait for encapsulation.
|
|
359
|
+
pub trait SupplyControlInternal {
|
|
360
|
+
/// Enforces mint supply controls: authorization, whitelist, rate limit, and state update.
|
|
361
|
+
/// Panics on failure.
|
|
362
|
+
fn __enforce_mint(env: &Env, sender: &Address, mint_to: &Address, amount: i128, fallback_auth: impl Fn(&Address)) {
|
|
363
|
+
if !Self::__supply_control_enabled(env) {
|
|
364
|
+
fallback_auth(sender);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
let mut sc = Self::__get_entry_or_panic(env, sender);
|
|
369
|
+
|
|
370
|
+
assert_with_error!(
|
|
371
|
+
env,
|
|
372
|
+
sc.config.allow_any_mint_burn || sc.config.whitelist_addresses.contains(mint_to),
|
|
373
|
+
SupplyControlError::CannotMintToAddress
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
try_consume(&sc.config.limit_config, &mut sc.state, env.ledger().timestamp(), amount)
|
|
377
|
+
.unwrap_or_else(|e| panic_with_error!(env, e));
|
|
378
|
+
SupplyControlStorage::set_supply_controller_entry(env, sender, &sc);
|
|
379
|
+
|
|
380
|
+
SupplyIncreased { controller: sender.clone(), to: mint_to.clone(), amount }.publish(env);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/// Enforces burn supply controls: authorization and burn permissions.
|
|
384
|
+
/// Panics on failure.
|
|
385
|
+
fn __enforce_burn(
|
|
386
|
+
env: &Env,
|
|
387
|
+
sender: &Address,
|
|
388
|
+
burn_from: &Address,
|
|
389
|
+
amount: i128,
|
|
390
|
+
fallback_auth: impl Fn(&Address),
|
|
391
|
+
) {
|
|
392
|
+
if !Self::__supply_control_enabled(env) {
|
|
393
|
+
fallback_auth(sender);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
assert_with_error!(
|
|
398
|
+
env,
|
|
399
|
+
Self::__get_entry_or_panic(env, sender).config.allow_any_mint_burn,
|
|
400
|
+
SupplyControlError::CannotBurnFromAddress
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
SupplyDecreased { controller: sender.clone(), from: burn_from.clone(), amount }.publish(env);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/// Enables supply control. only call this function at construction time.
|
|
407
|
+
fn __enable_supply_control(env: &Env) {
|
|
408
|
+
SupplyControlStorage::set_supply_control_enabled(env, &true);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/// Adds a new supply controller with the given configuration.
|
|
412
|
+
fn __add_supply_controller(env: &Env, supply_controller: &Address, config: &SupplyControllerConfig) {
|
|
413
|
+
assert_with_error!(
|
|
414
|
+
env,
|
|
415
|
+
!SupplyControlStorage::has_supply_controller_entry(env, supply_controller),
|
|
416
|
+
SupplyControlError::AlreadyExists
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
validate_limit_config(env, &config.limit_config);
|
|
420
|
+
|
|
421
|
+
// set the initial state of the supply controller
|
|
422
|
+
let init_state = RateLimitState {
|
|
423
|
+
remaining_amount: config.limit_config.limit_capacity,
|
|
424
|
+
last_refill_time: env.ledger().timestamp(),
|
|
425
|
+
};
|
|
426
|
+
let entry = SupplyControllerEntry { config: config.clone(), state: init_state };
|
|
427
|
+
SupplyControlStorage::set_supply_controller_entry(env, supply_controller, &entry);
|
|
428
|
+
|
|
429
|
+
// add the supply controller to the list of supply controllers
|
|
430
|
+
let mut addresses = SupplyControlStorage::supply_controllers(env);
|
|
431
|
+
addresses.push_back(supply_controller.clone());
|
|
432
|
+
SupplyControlStorage::set_supply_controllers(env, &addresses);
|
|
433
|
+
|
|
434
|
+
SupplyControllerAdded { supply_controller: supply_controller.clone(), config: config.clone() }.publish(env);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/// Removes an existing supply controller.
|
|
438
|
+
fn __remove_supply_controller(env: &Env, supply_controller: &Address) {
|
|
439
|
+
assert_with_error!(
|
|
440
|
+
env,
|
|
441
|
+
SupplyControlStorage::has_supply_controller_entry(env, supply_controller),
|
|
442
|
+
SupplyControlError::NotFound
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// remove the supply controller from the list of supply controllers
|
|
446
|
+
SupplyControlStorage::remove_supply_controller_entry(env, supply_controller);
|
|
447
|
+
|
|
448
|
+
// remove the supply controller from the list of supply controllers
|
|
449
|
+
let mut addresses = SupplyControlStorage::supply_controllers(env);
|
|
450
|
+
let idx = addresses.first_index_of(supply_controller).unwrap();
|
|
451
|
+
addresses.remove(idx);
|
|
452
|
+
SupplyControlStorage::set_supply_controllers(env, &addresses);
|
|
453
|
+
|
|
454
|
+
SupplyControllerRemoved { supply_controller: supply_controller.clone() }.publish(env);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// =========================================================================
|
|
458
|
+
// View Functions
|
|
459
|
+
// =========================================================================
|
|
460
|
+
|
|
461
|
+
fn __supply_control_enabled(env: &Env) -> bool {
|
|
462
|
+
SupplyControlStorage::has_supply_control_enabled(env)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/// Loads a supply controller entry, panicking with `NotFound` if it doesn't exist.
|
|
466
|
+
fn __get_entry_or_panic(env: &Env, controller: &Address) -> SupplyControllerEntry {
|
|
467
|
+
SupplyControlStorage::supply_controller_entry(env, controller)
|
|
468
|
+
.unwrap_or_panic(env, SupplyControlError::NotFound)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// =========================================================================
|
|
473
|
+
// Helper Functions
|
|
474
|
+
// =========================================================================
|
|
475
|
+
|
|
476
|
+
fn require_manager_auth<T: SupplyControl>(env: &Env, sender: &Address) {
|
|
477
|
+
sender.require_auth();
|
|
478
|
+
assert_with_error!(env, T::is_supply_controller_manager(env, sender), SupplyControlError::Unauthorized);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/// Validates that limit_capacity and refill_per_second are non-negative.
|
|
482
|
+
fn validate_limit_config(env: &Env, limit_config: &LimitConfig) {
|
|
483
|
+
assert_with_error!(
|
|
484
|
+
env,
|
|
485
|
+
limit_config.limit_capacity >= 0 && limit_config.refill_per_second >= 0,
|
|
486
|
+
SupplyControlError::InvalidConfig
|
|
487
|
+
);
|
|
488
|
+
}
|