@layerzerolabs/protocol-stellar-v2 0.2.33 → 0.2.34
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 +377 -392
- package/.turbo/turbo-lint.log +209 -207
- package/.turbo/turbo-test.log +1687 -1753
- package/contracts/oapps/oft/integration-tests/extensions/test_oft_fee.rs +5 -11
- package/contracts/oapps/oft/integration-tests/extensions/test_pausable.rs +7 -14
- package/contracts/oapps/oft/integration-tests/extensions/test_rate_limiter.rs +11 -22
- package/contracts/oapps/oft/integration-tests/setup.rs +59 -7
- package/contracts/oapps/oft/integration-tests/utils.rs +28 -2
- package/contracts/oapps/oft/src/interfaces/mintable.rs +14 -0
- package/contracts/oapps/oft/src/interfaces/mod.rs +2 -2
- package/contracts/oapps/oft/src/oft.rs +3 -3
- package/contracts/oapps/oft/src/oft_types/mint_burn.rs +8 -8
- package/contracts/oapps/oft/src/oft_types/mod.rs +3 -4
- package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +7 -5
- package/contracts/oapps/sac-manager/src/errors.rs +14 -0
- package/contracts/{sac-manager → oapps/sac-manager}/src/lib.rs +0 -4
- package/contracts/oapps/sac-manager/src/sac_manager.rs +115 -0
- package/contracts/oapps/sac-manager/src/storage.rs +20 -0
- package/contracts/{sac-manager → oapps/sac-manager}/src/tests/mod.rs +0 -4
- package/contracts/oapps/sac-manager/src/tests/sac_manager/clawback.rs +86 -0
- package/contracts/oapps/sac-manager/src/tests/sac_manager/mint.rs +58 -0
- package/contracts/{sac-manager → oapps/sac-manager}/src/tests/sac_manager/mod.rs +1 -3
- package/contracts/oapps/sac-manager/src/tests/sac_manager/set_minter.rs +69 -0
- package/contracts/oapps/sac-manager/src/tests/sac_manager/test_helper.rs +18 -0
- package/contracts/oapps/sac-manager/src/tests/sac_manager/view_functions.rs +28 -0
- package/contracts/{sac-manager → oapps/sac-manager}/src/tests/test_helper.rs +16 -59
- package/package.json +8 -3
- package/sdk/.turbo/turbo-test.log +373 -374
- package/sdk/dist/generated/oft.d.ts +3 -3
- package/sdk/dist/generated/oft.js +4 -4
- package/sdk/dist/generated/sac_manager.d.ts +26 -318
- package/sdk/dist/generated/sac_manager.js +23 -129
- package/sdk/package.json +6 -1
- package/sdk/test/oft-sml.test.ts +72 -36
- package/sdk/test/sac-manager-redistribution.test.ts +38 -182
- package/contracts/oapps/oft/src/interfaces/mint_burnable.rs +0 -18
- package/contracts/sac-manager/src/errors.rs +0 -18
- package/contracts/sac-manager/src/extensions/mod.rs +0 -6
- package/contracts/sac-manager/src/extensions/redistribution.rs +0 -109
- package/contracts/sac-manager/src/extensions/supply_control/mod.rs +0 -488
- package/contracts/sac-manager/src/extensions/supply_control/rate_limit.rs +0 -126
- package/contracts/sac-manager/src/interfaces/mod.rs +0 -3
- package/contracts/sac-manager/src/interfaces/sac_manager.rs +0 -52
- package/contracts/sac-manager/src/sac_manager.rs +0 -193
- package/contracts/sac-manager/src/storage.rs +0 -20
- package/contracts/sac-manager/src/tests/redistribution/mod.rs +0 -1
- package/contracts/sac-manager/src/tests/redistribution/redistribute_funds.rs +0 -82
- package/contracts/sac-manager/src/tests/sac_manager/admin_mint.rs +0 -206
- package/contracts/sac-manager/src/tests/sac_manager/burn.rs +0 -215
- package/contracts/sac-manager/src/tests/sac_manager/clawback.rs +0 -209
- package/contracts/sac-manager/src/tests/sac_manager/mint.rs +0 -252
- package/contracts/sac-manager/src/tests/sac_manager/set_oft_address.rs +0 -47
- package/contracts/sac-manager/src/tests/sac_manager/test_helper.rs +0 -75
- package/contracts/sac-manager/src/tests/sac_manager/view_functions.rs +0 -60
- package/contracts/sac-manager/src/tests/supply_control/enumerable_set.rs +0 -256
- package/contracts/sac-manager/src/tests/supply_control/mod.rs +0 -8
- package/contracts/sac-manager/src/tests/supply_control/refill.rs +0 -90
- package/contracts/sac-manager/src/tests/supply_control/set_mint_whitelist.rs +0 -245
- package/contracts/sac-manager/src/tests/supply_control/set_supply_controller.rs +0 -267
- package/contracts/sac-manager/src/tests/supply_control/set_supply_controller_manager.rs +0 -122
- package/contracts/sac-manager/src/tests/supply_control/test_helper.rs +0 -38
- package/contracts/sac-manager/src/tests/supply_control/update_allow_any_mint_burn.rs +0 -114
- package/contracts/sac-manager/src/tests/supply_control/update_limit_config.rs +0 -257
- /package/contracts/{sac-manager → oapps/sac-manager}/Cargo.toml +0 -0
- /package/contracts/{sac-manager → oapps/sac-manager}/src/tests/sac_manager/set_admin.rs +0 -0
- /package/contracts/{sac-manager → oapps/sac-manager}/src/tests/sac_manager/set_authorized.rs +0 -0
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
//! Rate limiting types and logic for supply control.
|
|
2
|
-
//!
|
|
3
|
-
//! Pure rate-limit primitives: configuration, runtime state, and the token-bucket
|
|
4
|
-
//! refill algorithm. No storage or authorization concerns — those live in the
|
|
5
|
-
//! parent `supply_control` module.
|
|
6
|
-
|
|
7
|
-
use super::SupplyControlError;
|
|
8
|
-
use soroban_sdk::contracttype;
|
|
9
|
-
|
|
10
|
-
/// A `refill_per_second` value of 0 means rate limit checking is skipped (unlimited minting).
|
|
11
|
-
pub(super) const SKIP_RATE_LIMIT_CHECK: i128 = 0;
|
|
12
|
-
|
|
13
|
-
// =========================================================================
|
|
14
|
-
// Types
|
|
15
|
-
// =========================================================================
|
|
16
|
-
|
|
17
|
-
/// Limit configuration for rate limiting.
|
|
18
|
-
#[contracttype]
|
|
19
|
-
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
20
|
-
pub struct LimitConfig {
|
|
21
|
-
/// Max amount for the rate limit
|
|
22
|
-
pub limit_capacity: i128,
|
|
23
|
-
/// Amount to add to limit each second up to the limitCapacity.
|
|
24
|
-
/// A value of 0 means rate limiting is disabled (unlimited minting allowed).
|
|
25
|
-
pub refill_per_second: i128,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/// Rate limit state (the runtime state that persists across config updates).
|
|
29
|
-
#[contracttype]
|
|
30
|
-
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
31
|
-
pub struct RateLimitState {
|
|
32
|
-
/// Remaining amount for the time period
|
|
33
|
-
pub remaining_amount: i128,
|
|
34
|
-
/// Timestamp of last refill
|
|
35
|
-
pub last_refill_time: u64,
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// =========================================================================
|
|
39
|
-
// Helper Functions
|
|
40
|
-
// =========================================================================
|
|
41
|
-
|
|
42
|
-
/// Refills and returns the remaining amount based on elapsed time.
|
|
43
|
-
/// Returns `Err(SupplyControlError::OldTimestamp)` if `timestamp` is older than `last_refill_time`.
|
|
44
|
-
/// On arithmetic overflow, returns `limit_capacity` (aligned with EVM RateLimiter).
|
|
45
|
-
pub(super) fn refill(
|
|
46
|
-
limit_config: &LimitConfig,
|
|
47
|
-
state: &RateLimitState,
|
|
48
|
-
timestamp: u64,
|
|
49
|
-
) -> Result<i128, SupplyControlError> {
|
|
50
|
-
if timestamp < state.last_refill_time {
|
|
51
|
-
return Err(SupplyControlError::OldTimestamp);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let seconds_elapsed = timestamp - state.last_refill_time;
|
|
55
|
-
let new_tokens = (seconds_elapsed as i128).checked_mul(limit_config.refill_per_second);
|
|
56
|
-
let amount = new_tokens.and_then(|t| state.remaining_amount.checked_add(t));
|
|
57
|
-
|
|
58
|
-
Ok(match amount {
|
|
59
|
-
Some(amount) => amount.min(limit_config.limit_capacity),
|
|
60
|
-
None => limit_config.limit_capacity,
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/// Gets remaining amount that can be sent in the window.
|
|
65
|
-
/// A `refill_per_second` of 0 is a special value indicating rate limit checking is skipped.
|
|
66
|
-
pub(super) fn get_remaining_amount(
|
|
67
|
-
limit_config: &LimitConfig,
|
|
68
|
-
state: &RateLimitState,
|
|
69
|
-
timestamp: u64,
|
|
70
|
-
) -> Result<i128, SupplyControlError> {
|
|
71
|
-
if limit_config.refill_per_second == SKIP_RATE_LIMIT_CHECK {
|
|
72
|
-
return Ok(i128::MAX);
|
|
73
|
-
}
|
|
74
|
-
refill(limit_config, state, timestamp)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/// Attempts to consume `amount` from the rate limit budget.
|
|
78
|
-
///
|
|
79
|
-
/// 1. Skips if rate limiting is disabled (`refill_per_second == 0`).
|
|
80
|
-
/// 2. Refills the bucket based on elapsed time.
|
|
81
|
-
/// 3. Checks if `amount` fits within the remaining capacity.
|
|
82
|
-
/// 4. Deducts `amount` and updates the state in place.
|
|
83
|
-
///
|
|
84
|
-
/// Returns `Err(RateLimitExceeded)` if the amount exceeds remaining capacity.
|
|
85
|
-
/// Returns `Err(OldTimestamp)` if `timestamp` is older than `last_refill_time`.
|
|
86
|
-
pub(super) fn try_consume(
|
|
87
|
-
limit_config: &LimitConfig,
|
|
88
|
-
state: &mut RateLimitState,
|
|
89
|
-
timestamp: u64,
|
|
90
|
-
amount: i128,
|
|
91
|
-
) -> Result<(), SupplyControlError> {
|
|
92
|
-
if limit_config.refill_per_second == SKIP_RATE_LIMIT_CHECK {
|
|
93
|
-
return Ok(());
|
|
94
|
-
}
|
|
95
|
-
if amount < 0 {
|
|
96
|
-
return Err(SupplyControlError::InvalidAmount);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
state.remaining_amount = refill(limit_config, state, timestamp)?;
|
|
100
|
-
state.last_refill_time = timestamp;
|
|
101
|
-
|
|
102
|
-
if amount > state.remaining_amount {
|
|
103
|
-
return Err(SupplyControlError::RateLimitExceeded);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
state.remaining_amount -= amount;
|
|
107
|
-
Ok(())
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ============================================================================
|
|
111
|
-
// Test-only Functions
|
|
112
|
-
// ============================================================================
|
|
113
|
-
|
|
114
|
-
#[cfg(test)]
|
|
115
|
-
pub(crate) mod test {
|
|
116
|
-
use super::*;
|
|
117
|
-
|
|
118
|
-
/// Test-only wrapper for refill to enable testing.
|
|
119
|
-
pub fn refill_for_test(
|
|
120
|
-
limit_config: &LimitConfig,
|
|
121
|
-
state: &RateLimitState,
|
|
122
|
-
timestamp: u64,
|
|
123
|
-
) -> Result<i128, SupplyControlError> {
|
|
124
|
-
refill(limit_config, state, timestamp)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
//! SAC Manager interface - ISacManager trait.
|
|
2
|
-
//!
|
|
3
|
-
//! Defines the admin-only methods where the SAC manager adds value beyond the raw SAC.
|
|
4
|
-
//! Mint/burn for the OFT are provided via the MintBurnable trait.
|
|
5
|
-
//! Pure pass-through queries (balance, decimals, name, symbol, etc.)
|
|
6
|
-
//! should be called directly on the underlying SAC.
|
|
7
|
-
|
|
8
|
-
use common_macros::contract_trait;
|
|
9
|
-
use soroban_sdk::{Address, Env};
|
|
10
|
-
|
|
11
|
-
// =========================================================================
|
|
12
|
-
// ISacManager Interface
|
|
13
|
-
// =========================================================================
|
|
14
|
-
|
|
15
|
-
/// SAC Manager Interface.
|
|
16
|
-
///
|
|
17
|
-
/// Admin-only operations are authorized via the contract owner (Ownable).
|
|
18
|
-
/// OFT mint/burn operations are handled by the MintBurnable trait separately.
|
|
19
|
-
#[contract_trait(client_name = "ISacManagerClient")]
|
|
20
|
-
pub trait ISacManager {
|
|
21
|
-
// =========================================================================
|
|
22
|
-
// Admin Operations
|
|
23
|
-
// =========================================================================
|
|
24
|
-
|
|
25
|
-
/// Mints `amount` to `to`. Subject to supply control when enabled.
|
|
26
|
-
/// When supply control is off, only the owner can call.
|
|
27
|
-
fn authorized_mint(env: &Env, sender: &Address, to: &Address, amount: i128);
|
|
28
|
-
|
|
29
|
-
/// Clawback `amount` from `from` account. Subject to supply control when enabled.
|
|
30
|
-
/// When supply control is off, only the owner or OFT can call.
|
|
31
|
-
fn clawback(env: &Env, sender: &Address, from: &Address, amount: i128);
|
|
32
|
-
|
|
33
|
-
/// Releases the SAC admin role. Owner-only.
|
|
34
|
-
fn release_sac_admin(env: &Env, to: &Address);
|
|
35
|
-
|
|
36
|
-
/// Sets whether the account is authorized to use its balance. Admin-only.
|
|
37
|
-
fn set_authorized(env: &Env, id: &Address, authorize: bool);
|
|
38
|
-
|
|
39
|
-
/// Sets the OFT contract address authorized to call mint/burn.
|
|
40
|
-
/// Must be called after construction since OFT and sac-manager are circular dependencies.
|
|
41
|
-
fn set_oft_address(env: &Env, new_oft: &Address);
|
|
42
|
-
|
|
43
|
-
// =========================================================================
|
|
44
|
-
// View Functions
|
|
45
|
-
// =========================================================================
|
|
46
|
-
|
|
47
|
-
/// Returns the underlying SAC (Stellar Asset Contract) address managed by this contract.
|
|
48
|
-
fn underlying_sac(env: &Env) -> Address;
|
|
49
|
-
|
|
50
|
-
/// Returns the OFT contract address authorized to call mint/burn.
|
|
51
|
-
fn oft_address(env: &Env) -> Address;
|
|
52
|
-
}
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
//! SAC Manager Contract - Manages a Stellar Asset Contract (SAC) as its admin.
|
|
2
|
-
//!
|
|
3
|
-
//! This contract becomes the admin of a SAC and provides:
|
|
4
|
-
//! - MintBurnable interface for OFT (mint/burn with supply control)
|
|
5
|
-
//! - Admin operations (admin_mint, clawback, set_admin, set_authorized)
|
|
6
|
-
//! - Redistribution feature (transfer to blacklisted → redirect to configurable target)
|
|
7
|
-
//! - Supply control feature (rate limiting on mint)
|
|
8
|
-
//! - Upgradeable (owner-authorized)
|
|
9
|
-
//!
|
|
10
|
-
//! ## Authorization Model
|
|
11
|
-
//!
|
|
12
|
-
//! - **Owner** (via Ownable): The admin address. Can do everything (upgrades, TTL, admin ops).
|
|
13
|
-
//! - **OFT address**: Stored separately. Can only call mint/burn (MintBurnable interface).
|
|
14
|
-
//! - **Redistribution target**: Always the current owner address.
|
|
15
|
-
|
|
16
|
-
use crate::{
|
|
17
|
-
errors::SacManagerError,
|
|
18
|
-
extensions::{
|
|
19
|
-
redistribution::{Redistribution, RedistributionInternal},
|
|
20
|
-
supply_control::{LimitConfig, SupplyControl, SupplyControlInternal, SupplyControllerConfig},
|
|
21
|
-
},
|
|
22
|
-
interfaces::ISacManager,
|
|
23
|
-
storage::SacManagerStorage,
|
|
24
|
-
};
|
|
25
|
-
use common_macros::{contract_impl, lz_contract, only_auth};
|
|
26
|
-
use oft::MintBurnable;
|
|
27
|
-
use soroban_sdk::{assert_with_error, token::StellarAssetClient, Address, Env, Vec};
|
|
28
|
-
use utils::{option_ext::OptionExt, ownable::OwnableInitializer};
|
|
29
|
-
|
|
30
|
-
// =========================================================================
|
|
31
|
-
// SAC Manager Contract
|
|
32
|
-
// =========================================================================
|
|
33
|
-
|
|
34
|
-
/// SAC Manager Contract
|
|
35
|
-
///
|
|
36
|
-
/// Manages a SAC as its admin, forwarding token actions to
|
|
37
|
-
/// the underlying SAC while enforcing access control and feature policies.
|
|
38
|
-
#[lz_contract(upgradeable(no_migration))]
|
|
39
|
-
pub struct SacManager;
|
|
40
|
-
|
|
41
|
-
#[contract_impl]
|
|
42
|
-
impl SacManager {
|
|
43
|
-
/// Constructs the SAC manager contract.
|
|
44
|
-
///
|
|
45
|
-
/// # Arguments
|
|
46
|
-
/// * `sac_token` - The underlying Stellar Asset Contract address
|
|
47
|
-
/// * `owner` - The initial owner address (for upgrades, owner ops, redistribution target)
|
|
48
|
-
/// * `redistribution_enabled` - Whether redistribution is enabled globally
|
|
49
|
-
/// * `supply_control_enabled` - Whether supply control is enabled globally
|
|
50
|
-
///
|
|
51
|
-
/// Note: The OFT address must be set separately via `set_oft_address` after construction,
|
|
52
|
-
/// because the OFT and sac-manager have a circular dependency on each other's addresses.
|
|
53
|
-
pub fn __constructor(
|
|
54
|
-
env: &Env,
|
|
55
|
-
sac_token: &Address,
|
|
56
|
-
owner: &Address,
|
|
57
|
-
redistribution_enabled: bool,
|
|
58
|
-
supply_control_enabled: bool,
|
|
59
|
-
) {
|
|
60
|
-
// Initialize admin for upgrade/TTL authorization and admin operations
|
|
61
|
-
Self::init_owner(env, owner);
|
|
62
|
-
|
|
63
|
-
// Store SAC token
|
|
64
|
-
SacManagerStorage::set_sac_token(env, sac_token);
|
|
65
|
-
|
|
66
|
-
// Set feature flags
|
|
67
|
-
if redistribution_enabled {
|
|
68
|
-
Self::__enable_redistribution(env);
|
|
69
|
-
}
|
|
70
|
-
if supply_control_enabled {
|
|
71
|
-
Self::__enable_supply_control(env);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// =========================================================================
|
|
77
|
-
// MintBurnable Implementation (OFT interface)
|
|
78
|
-
// =========================================================================
|
|
79
|
-
|
|
80
|
-
#[contract_impl]
|
|
81
|
-
impl MintBurnable for SacManager {
|
|
82
|
-
fn mint(env: &Env, to: &Address, amount: i128) {
|
|
83
|
-
let oft = Self::oft_address(env);
|
|
84
|
-
Self::authorized_mint(env, &oft, to, amount);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
fn burn(env: &Env, from: &Address, amount: i128) {
|
|
88
|
-
from.require_auth();
|
|
89
|
-
|
|
90
|
-
let oft = Self::oft_address(env);
|
|
91
|
-
oft.require_auth();
|
|
92
|
-
Self::__enforce_burn(env, &oft, from, amount, |_| {});
|
|
93
|
-
|
|
94
|
-
sac_client::<Self>(env).burn(from, &amount);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// =========================================================================
|
|
99
|
-
// ISacManager Implementation (Owner operations)
|
|
100
|
-
// =========================================================================
|
|
101
|
-
|
|
102
|
-
#[contract_impl(contracttrait)]
|
|
103
|
-
impl ISacManager for SacManager {
|
|
104
|
-
// =========================================================================
|
|
105
|
-
// Supply Control Operations
|
|
106
|
-
// =========================================================================
|
|
107
|
-
|
|
108
|
-
fn authorized_mint(env: &Env, sender: &Address, to: &Address, amount: i128) {
|
|
109
|
-
sender.require_auth();
|
|
110
|
-
|
|
111
|
-
// Validate the mint operation by supply control extension if needed.
|
|
112
|
-
Self::__enforce_mint(env, sender, to, amount, |sender| {
|
|
113
|
-
assert_with_error!(
|
|
114
|
-
env,
|
|
115
|
-
Some(sender) == Self::owner(env).as_ref() || sender == &Self::oft_address(env),
|
|
116
|
-
SacManagerError::Unauthorized
|
|
117
|
-
)
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Resolve the actual receiver address by redistribution extension if needed.
|
|
121
|
-
let actual_to = Self::__redistribute_funds(env, to, amount);
|
|
122
|
-
|
|
123
|
-
// Mint the tokens to the actual receiver address.
|
|
124
|
-
sac_client::<Self>(env).mint(&actual_to, &amount);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
fn clawback(env: &Env, sender: &Address, from: &Address, amount: i128) {
|
|
128
|
-
sender.require_auth();
|
|
129
|
-
Self::__enforce_burn(env, sender, from, amount, |sender| {
|
|
130
|
-
assert_with_error!(env, Some(sender) == Self::owner(env).as_ref(), SacManagerError::Unauthorized)
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
sac_client::<Self>(env).clawback(from, &amount);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// =========================================================================
|
|
137
|
-
// SAC Admin Operations
|
|
138
|
-
// =========================================================================
|
|
139
|
-
|
|
140
|
-
#[only_auth]
|
|
141
|
-
fn release_sac_admin(env: &Env, to: &Address) {
|
|
142
|
-
sac_client::<Self>(env).set_admin(to);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
#[only_auth]
|
|
146
|
-
fn set_authorized(env: &Env, id: &Address, authorize: bool) {
|
|
147
|
-
sac_client::<Self>(env).set_authorized(id, &authorize);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
#[only_auth]
|
|
151
|
-
fn set_oft_address(env: &Env, new_oft: &Address) {
|
|
152
|
-
assert_with_error!(
|
|
153
|
-
env,
|
|
154
|
-
Some(new_oft) != SacManagerStorage::oft_address(env).as_ref(),
|
|
155
|
-
SacManagerError::SameValue
|
|
156
|
-
);
|
|
157
|
-
SacManagerStorage::set_oft_address(env, new_oft);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// =========================================================================
|
|
161
|
-
// View Functions
|
|
162
|
-
// =========================================================================
|
|
163
|
-
|
|
164
|
-
fn underlying_sac(env: &Env) -> Address {
|
|
165
|
-
SacManagerStorage::sac_token(env).unwrap()
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
fn oft_address(env: &Env) -> Address {
|
|
169
|
-
SacManagerStorage::oft_address(env).unwrap_or_panic(env, SacManagerError::OftAddressNotSet)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// =========================================================================
|
|
174
|
-
// Extension Implementations
|
|
175
|
-
// =========================================================================
|
|
176
|
-
|
|
177
|
-
/// Supply control trait implementation
|
|
178
|
-
#[contract_impl(contracttrait)]
|
|
179
|
-
impl SupplyControl for SacManager {}
|
|
180
|
-
impl SupplyControlInternal for SacManager {}
|
|
181
|
-
|
|
182
|
-
/// Redistribution trait implementation
|
|
183
|
-
#[contract_impl(contracttrait)]
|
|
184
|
-
impl Redistribution for SacManager {}
|
|
185
|
-
impl RedistributionInternal for SacManager {}
|
|
186
|
-
|
|
187
|
-
// =========================================================================
|
|
188
|
-
// Helper Functions
|
|
189
|
-
// =========================================================================
|
|
190
|
-
|
|
191
|
-
pub(crate) fn sac_client<T: ISacManager>(env: &Env) -> StellarAssetClient<'_> {
|
|
192
|
-
StellarAssetClient::new(env, &T::underlying_sac(env))
|
|
193
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
//! Storage definitions for the SAC manager contract.
|
|
2
|
-
//!
|
|
3
|
-
//! Each feature module owns its own storage to maintain modularity.
|
|
4
|
-
//! This file contains the core storage and feature enable flags.
|
|
5
|
-
|
|
6
|
-
use common_macros::storage;
|
|
7
|
-
use soroban_sdk::Address;
|
|
8
|
-
|
|
9
|
-
/// Core storage for the SacManager contract.
|
|
10
|
-
/// Contains essential state and feature enable flags.
|
|
11
|
-
#[storage]
|
|
12
|
-
pub enum SacManagerStorage {
|
|
13
|
-
/// The underlying SAC (Stellar Asset Contract) address
|
|
14
|
-
#[instance(Address)]
|
|
15
|
-
SacToken,
|
|
16
|
-
|
|
17
|
-
/// The OFT contract address (authorized to call mint/burn)
|
|
18
|
-
#[instance(Address)]
|
|
19
|
-
OftAddress,
|
|
20
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
mod redistribute_funds;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
//! Redistribution Extension Integration Tests
|
|
2
|
-
//!
|
|
3
|
-
//! Tests for the redistribution feature including:
|
|
4
|
-
//! - redistribute_funds auth requirements
|
|
5
|
-
//! - redistribute_funds error cases (NotBlacklisted)
|
|
6
|
-
|
|
7
|
-
use crate::extensions::redistribution::RedistributionError;
|
|
8
|
-
use crate::tests::test_helper::{mock_auth, TestSetup};
|
|
9
|
-
use crate::extensions::redistribution::{RedistributeFunds, RedistributionClient};
|
|
10
|
-
use soroban_sdk::{testutils::IssuerFlags, Address};
|
|
11
|
-
use utils::testing_utils::assert_contains_event;
|
|
12
|
-
|
|
13
|
-
fn mock_redistribute_funds_auth(setup: &TestSetup, from: &Address, amount: i128) {
|
|
14
|
-
mock_auth(&setup.env, &setup.sac_manager, &setup.owner, "redistribute_blacklisted_funds", (from, amount));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// =========================================================================
|
|
18
|
-
// redistribute_funds Happy Path
|
|
19
|
-
// =========================================================================
|
|
20
|
-
|
|
21
|
-
#[test]
|
|
22
|
-
fn test_redistribute_funds_success() {
|
|
23
|
-
let setup = TestSetup::new().with_manager_as_sac_admin().with_redistribution_enabled().build();
|
|
24
|
-
let blacklisted_user = setup.generate_address();
|
|
25
|
-
let redistribution_client = RedistributionClient::new(&setup.env, &setup.sac_manager);
|
|
26
|
-
|
|
27
|
-
// Enable issuer flags required by set_authorized + clawback in SAC tests.
|
|
28
|
-
setup.sac_contract.issuer().set_flag(IssuerFlags::RevocableFlag);
|
|
29
|
-
setup.sac_contract.issuer().set_flag(IssuerFlags::ClawbackEnabledFlag);
|
|
30
|
-
|
|
31
|
-
// Prepare user funds, then blacklist the user.
|
|
32
|
-
mock_auth(&setup.env, &setup.sac_manager, &setup.oft, "mint", (&blacklisted_user, 1_000_i128));
|
|
33
|
-
setup.sac_manager_client.mint(&blacklisted_user, &1_000);
|
|
34
|
-
mock_auth(&setup.env, &setup.sac_manager, &setup.owner, "set_authorized", (&blacklisted_user, false));
|
|
35
|
-
setup.sac_manager_client.set_authorized(&blacklisted_user, &false);
|
|
36
|
-
|
|
37
|
-
let owner_balance_before = setup.sac_client.balance(&setup.owner);
|
|
38
|
-
let user_balance_before = setup.sac_client.balance(&blacklisted_user);
|
|
39
|
-
assert_eq!(user_balance_before, 1_000);
|
|
40
|
-
assert!(!setup.sac_client.authorized(&blacklisted_user));
|
|
41
|
-
|
|
42
|
-
mock_redistribute_funds_auth(&setup, &blacklisted_user, 400_i128);
|
|
43
|
-
redistribution_client.redistribute_blacklisted_funds(&blacklisted_user, &400);
|
|
44
|
-
|
|
45
|
-
let expected = RedistributeFunds { user: blacklisted_user.clone(), amount: 400 };
|
|
46
|
-
assert_contains_event(&setup.env, &setup.sac_manager, expected);
|
|
47
|
-
|
|
48
|
-
// Verify clawback from user + mint to owner.
|
|
49
|
-
assert_eq!(setup.sac_client.balance(&blacklisted_user), user_balance_before - 400);
|
|
50
|
-
assert_eq!(setup.sac_client.balance(&setup.owner), owner_balance_before + 400);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// =========================================================================
|
|
54
|
-
// redistribute_funds Error Cases
|
|
55
|
-
// =========================================================================
|
|
56
|
-
|
|
57
|
-
#[test]
|
|
58
|
-
fn test_redistribute_funds_fails_if_not_blacklisted() {
|
|
59
|
-
let setup = TestSetup::new().with_manager_as_sac_admin().with_redistribution_enabled().build();
|
|
60
|
-
let user = setup.generate_address();
|
|
61
|
-
let redistribution_client = RedistributionClient::new(&setup.env, &setup.sac_manager);
|
|
62
|
-
|
|
63
|
-
// User is authorized by default (not blacklisted) — should fail
|
|
64
|
-
mock_redistribute_funds_auth(&setup, &user, 100_i128);
|
|
65
|
-
let result = redistribution_client.try_redistribute_blacklisted_funds(&user, &100);
|
|
66
|
-
assert_eq!(result.err().unwrap().unwrap(), RedistributionError::NotBlacklisted.into());
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// =========================================================================
|
|
70
|
-
// redistribute_funds Auth Tests
|
|
71
|
-
// =========================================================================
|
|
72
|
-
|
|
73
|
-
#[test]
|
|
74
|
-
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
75
|
-
fn test_redistribute_funds_fails_without_owner_auth() {
|
|
76
|
-
let setup = TestSetup::new().with_manager_as_sac_admin().with_redistribution_enabled().build();
|
|
77
|
-
let user = setup.generate_address();
|
|
78
|
-
let redistribution_client = RedistributionClient::new(&setup.env, &setup.sac_manager);
|
|
79
|
-
|
|
80
|
-
// No auth mocked — owner auth fails
|
|
81
|
-
redistribution_client.redistribute_blacklisted_funds(&user, &100);
|
|
82
|
-
}
|