@layerzerolabs/ethena-sac-manager-stellar-contracts 1.2.28
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/Cargo.lock +1859 -0
- package/Cargo.toml +38 -0
- package/LICENSE +23 -0
- package/clippy.toml +6 -0
- package/package.json +43 -0
- package/rust-toolchain.toml +4 -0
- package/rustfmt.toml +12 -0
- package/src/errors.rs +19 -0
- package/src/lib.rs +19 -0
- package/src/sac_manager.rs +262 -0
- package/src/storage.rs +18 -0
- package/src/tests/mod.rs +8 -0
- package/src/tests/sac_manager/clawback.rs +85 -0
- package/src/tests/sac_manager/mint.rs +103 -0
- package/src/tests/sac_manager/mod.rs +8 -0
- package/src/tests/sac_manager/redistribute_funds.rs +103 -0
- package/src/tests/sac_manager/set_admin.rs +47 -0
- package/src/tests/sac_manager/set_authorized.rs +85 -0
- package/src/tests/sac_manager/set_escrow.rs +146 -0
- package/src/tests/sac_manager/test_helper.rs +29 -0
- package/src/tests/sac_manager/view_functions.rs +13 -0
- package/src/tests/test_helper.rs +160 -0
- package/turbo-snapshot.lock.json +142 -0
- package/turbo.json +12 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//! redistribute_blacklisted_funds Integration Tests
|
|
2
|
+
//!
|
|
3
|
+
//! Covers the `REDISTRIBUTOR_ROLE`-gated redistribution path:
|
|
4
|
+
//! - happy path (including escrow field on emitted event)
|
|
5
|
+
//! - guard: target must be blacklisted
|
|
6
|
+
//! - guard: escrow must be configured
|
|
7
|
+
//! - role/auth: caller must hold REDISTRIBUTOR_ROLE and authorize the call
|
|
8
|
+
|
|
9
|
+
use super::test_helper::{mock_redistribute_funds_auth, mock_set_authorized_auth};
|
|
10
|
+
use crate::errors::SACManagerError;
|
|
11
|
+
use crate::sac_manager::RedistributeFunds;
|
|
12
|
+
use crate::tests::test_helper::{mock_oft_mint_auth, TestSetup};
|
|
13
|
+
use soroban_sdk::testutils::IssuerFlags;
|
|
14
|
+
use utils::{errors::RbacError, testing_utils::assert_contains_event};
|
|
15
|
+
|
|
16
|
+
// =========================================================================
|
|
17
|
+
// Happy Path — clawback from blacklisted, mint to escrow, emit event
|
|
18
|
+
// =========================================================================
|
|
19
|
+
|
|
20
|
+
#[test]
|
|
21
|
+
fn test_redistribute_funds_success() {
|
|
22
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().with_escrow_enabled().build();
|
|
23
|
+
let escrow = setup.escrow.clone().unwrap();
|
|
24
|
+
let blacklisted_user = setup.generate_address();
|
|
25
|
+
|
|
26
|
+
setup.sac_contract.issuer().set_flag(IssuerFlags::RevocableFlag);
|
|
27
|
+
setup.sac_contract.issuer().set_flag(IssuerFlags::ClawbackEnabledFlag);
|
|
28
|
+
|
|
29
|
+
mock_oft_mint_auth(&setup, &blacklisted_user, 1_000_i128);
|
|
30
|
+
setup.sac_manager_client.mint(&blacklisted_user, &1_000, &setup.minter);
|
|
31
|
+
mock_set_authorized_auth(&setup, &setup.owner, &blacklisted_user, false);
|
|
32
|
+
setup.sac_manager_client.set_authorized(&blacklisted_user, &false, &setup.owner);
|
|
33
|
+
|
|
34
|
+
let escrow_balance_before = setup.sac_client.balance(&escrow);
|
|
35
|
+
let user_balance_before = setup.sac_client.balance(&blacklisted_user);
|
|
36
|
+
assert_eq!(user_balance_before, 1_000);
|
|
37
|
+
assert!(!setup.sac_client.authorized(&blacklisted_user));
|
|
38
|
+
|
|
39
|
+
mock_redistribute_funds_auth(&setup, &setup.owner, &blacklisted_user, 400_i128);
|
|
40
|
+
setup.sac_manager_client.redistribute_blacklisted_funds(&blacklisted_user, &400, &setup.owner);
|
|
41
|
+
|
|
42
|
+
let expected = RedistributeFunds { user: blacklisted_user.clone(), escrow: escrow.clone(), amount: 400 };
|
|
43
|
+
assert_contains_event(&setup.env, &setup.sac_manager, expected);
|
|
44
|
+
|
|
45
|
+
assert_eq!(setup.sac_client.balance(&blacklisted_user), user_balance_before - 400);
|
|
46
|
+
assert_eq!(setup.sac_client.balance(&escrow), escrow_balance_before + 400);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// =========================================================================
|
|
50
|
+
// Error — target is not blacklisted
|
|
51
|
+
// =========================================================================
|
|
52
|
+
|
|
53
|
+
#[test]
|
|
54
|
+
fn test_redistribute_funds_fails_if_not_blacklisted() {
|
|
55
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().with_escrow_enabled().build();
|
|
56
|
+
let user = setup.generate_address();
|
|
57
|
+
|
|
58
|
+
mock_redistribute_funds_auth(&setup, &setup.owner, &user, 100_i128);
|
|
59
|
+
let result = setup.sac_manager_client.try_redistribute_blacklisted_funds(&user, &100, &setup.owner);
|
|
60
|
+
assert_eq!(result.err().unwrap().unwrap(), SACManagerError::NotBlacklisted.into());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// =========================================================================
|
|
64
|
+
// Error — escrow not configured (redistribution feature disabled)
|
|
65
|
+
// =========================================================================
|
|
66
|
+
|
|
67
|
+
#[test]
|
|
68
|
+
fn test_redistribute_funds_fails_if_escrow_not_set() {
|
|
69
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
70
|
+
let user = setup.generate_address();
|
|
71
|
+
|
|
72
|
+
mock_redistribute_funds_auth(&setup, &setup.owner, &user, 100_i128);
|
|
73
|
+
let result = setup.sac_manager_client.try_redistribute_blacklisted_funds(&user, &100, &setup.owner);
|
|
74
|
+
assert_eq!(result.err().unwrap().unwrap(), SACManagerError::EscrowNotSet.into());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// =========================================================================
|
|
78
|
+
// Role — operator without REDISTRIBUTOR_ROLE fails
|
|
79
|
+
// =========================================================================
|
|
80
|
+
|
|
81
|
+
#[test]
|
|
82
|
+
fn test_redistribute_funds_fails_without_role() {
|
|
83
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().with_escrow_enabled().build();
|
|
84
|
+
let random = setup.generate_address();
|
|
85
|
+
let user = setup.generate_address();
|
|
86
|
+
|
|
87
|
+
mock_redistribute_funds_auth(&setup, &random, &user, 100_i128);
|
|
88
|
+
let result = setup.sac_manager_client.try_redistribute_blacklisted_funds(&user, &100, &random);
|
|
89
|
+
assert_eq!(result.err().unwrap().unwrap(), RbacError::Unauthorized.into());
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// =========================================================================
|
|
93
|
+
// Auth — must be authorized by the operator
|
|
94
|
+
// =========================================================================
|
|
95
|
+
|
|
96
|
+
#[test]
|
|
97
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
98
|
+
fn test_redistribute_funds_fails_without_operator_auth() {
|
|
99
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().with_escrow_enabled().build();
|
|
100
|
+
let user = setup.generate_address();
|
|
101
|
+
|
|
102
|
+
setup.sac_manager_client.redistribute_blacklisted_funds(&user, &100, &setup.owner);
|
|
103
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//! set_admin Integration Tests
|
|
2
|
+
//!
|
|
3
|
+
//! Tests that set_admin requires ADMIN_MANAGER_ROLE on the operator.
|
|
4
|
+
|
|
5
|
+
use super::test_helper::mock_set_admin_auth;
|
|
6
|
+
use crate::tests::test_helper::TestSetup;
|
|
7
|
+
use utils::errors::RbacError;
|
|
8
|
+
|
|
9
|
+
// =========================================================================
|
|
10
|
+
// set_admin Tests
|
|
11
|
+
// =========================================================================
|
|
12
|
+
|
|
13
|
+
#[test]
|
|
14
|
+
fn test_set_admin_by_owner() {
|
|
15
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
16
|
+
let new_admin = setup.generate_address();
|
|
17
|
+
|
|
18
|
+
assert_eq!(setup.sac_client.admin(), setup.sac_manager);
|
|
19
|
+
|
|
20
|
+
mock_set_admin_auth(&setup, &setup.owner, &new_admin);
|
|
21
|
+
setup.sac_manager_client.set_admin(&new_admin, &setup.owner);
|
|
22
|
+
assert_eq!(setup.sac_client.admin(), new_admin);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// =========================================================================
|
|
26
|
+
// set_admin Role Auth Tests
|
|
27
|
+
// =========================================================================
|
|
28
|
+
|
|
29
|
+
#[test]
|
|
30
|
+
fn test_set_admin_operator_without_role_fails() {
|
|
31
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
32
|
+
let new_admin = setup.generate_address();
|
|
33
|
+
let random = setup.generate_address();
|
|
34
|
+
|
|
35
|
+
mock_set_admin_auth(&setup, &random, &new_admin);
|
|
36
|
+
let result = setup.sac_manager_client.try_set_admin(&new_admin, &random);
|
|
37
|
+
assert_eq!(result.err().unwrap().unwrap(), RbacError::Unauthorized.into());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[test]
|
|
41
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
42
|
+
fn test_set_admin_without_auth() {
|
|
43
|
+
let setup = TestSetup::new().build();
|
|
44
|
+
let new_admin = setup.generate_address();
|
|
45
|
+
|
|
46
|
+
setup.sac_manager_client.set_admin(&new_admin, &setup.owner);
|
|
47
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//! set_authorized Integration Tests
|
|
2
|
+
//!
|
|
3
|
+
//! Requires revocable issuer flag on the SAC. Operator must hold BLACKLISTER_ROLE.
|
|
4
|
+
//! When the redistribution feature is enabled (escrow set), the address
|
|
5
|
+
//! currently configured as the escrow may not be blacklisted.
|
|
6
|
+
|
|
7
|
+
use super::test_helper::mock_set_authorized_auth;
|
|
8
|
+
use crate::errors::SACManagerError;
|
|
9
|
+
use crate::tests::test_helper::TestSetup;
|
|
10
|
+
use soroban_sdk::testutils::IssuerFlags;
|
|
11
|
+
use utils::errors::RbacError;
|
|
12
|
+
|
|
13
|
+
// =========================================================================
|
|
14
|
+
// set_authorized Tests
|
|
15
|
+
// =========================================================================
|
|
16
|
+
|
|
17
|
+
#[test]
|
|
18
|
+
fn test_set_authorized_by_owner() {
|
|
19
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
20
|
+
let user = setup.generate_address();
|
|
21
|
+
|
|
22
|
+
setup.sac_contract.issuer().set_flag(IssuerFlags::RevocableFlag);
|
|
23
|
+
assert!(setup.sac_client.authorized(&user));
|
|
24
|
+
|
|
25
|
+
mock_set_authorized_auth(&setup, &setup.owner, &user, false);
|
|
26
|
+
setup.sac_manager_client.set_authorized(&user, &false, &setup.owner);
|
|
27
|
+
assert!(!setup.sac_client.authorized(&user));
|
|
28
|
+
|
|
29
|
+
mock_set_authorized_auth(&setup, &setup.owner, &user, true);
|
|
30
|
+
setup.sac_manager_client.set_authorized(&user, &true, &setup.owner);
|
|
31
|
+
assert!(setup.sac_client.authorized(&user));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// =========================================================================
|
|
35
|
+
// set_authorized Role Auth Tests
|
|
36
|
+
// =========================================================================
|
|
37
|
+
|
|
38
|
+
#[test]
|
|
39
|
+
fn test_set_authorized_operator_without_role_fails() {
|
|
40
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
41
|
+
let user = setup.generate_address();
|
|
42
|
+
let random = setup.generate_address();
|
|
43
|
+
|
|
44
|
+
setup.sac_contract.issuer().set_flag(IssuerFlags::RevocableFlag);
|
|
45
|
+
mock_set_authorized_auth(&setup, &random, &user, false);
|
|
46
|
+
let result = setup.sac_manager_client.try_set_authorized(&user, &false, &random);
|
|
47
|
+
assert_eq!(result.err().unwrap().unwrap(), RbacError::Unauthorized.into());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[test]
|
|
51
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
52
|
+
fn test_set_authorized_without_auth() {
|
|
53
|
+
let setup = TestSetup::new().build();
|
|
54
|
+
let user = setup.generate_address();
|
|
55
|
+
|
|
56
|
+
setup.sac_manager_client.set_authorized(&user, &false, &setup.owner);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// =========================================================================
|
|
60
|
+
// Escrow guard — cannot blacklist the address currently configured as escrow
|
|
61
|
+
// =========================================================================
|
|
62
|
+
|
|
63
|
+
#[test]
|
|
64
|
+
fn test_set_authorized_cannot_blacklist_current_escrow() {
|
|
65
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().with_escrow_enabled().build();
|
|
66
|
+
let escrow = setup.escrow.clone().unwrap();
|
|
67
|
+
|
|
68
|
+
setup.sac_contract.issuer().set_flag(IssuerFlags::RevocableFlag);
|
|
69
|
+
|
|
70
|
+
mock_set_authorized_auth(&setup, &setup.owner, &escrow, false);
|
|
71
|
+
let result = setup.sac_manager_client.try_set_authorized(&escrow, &false, &setup.owner);
|
|
72
|
+
assert_eq!(result.err().unwrap().unwrap(), SACManagerError::CannotBlockEscrow.into());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[test]
|
|
76
|
+
fn test_set_authorized_can_reauthorize_current_escrow() {
|
|
77
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().with_escrow_enabled().build();
|
|
78
|
+
let escrow = setup.escrow.clone().unwrap();
|
|
79
|
+
|
|
80
|
+
setup.sac_contract.issuer().set_flag(IssuerFlags::RevocableFlag);
|
|
81
|
+
|
|
82
|
+
mock_set_authorized_auth(&setup, &setup.owner, &escrow, true);
|
|
83
|
+
setup.sac_manager_client.set_authorized(&escrow, &true, &setup.owner);
|
|
84
|
+
assert!(setup.sac_client.authorized(&escrow));
|
|
85
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//! set_escrow Integration Tests
|
|
2
|
+
//!
|
|
3
|
+
//! Verifies that the escrow can be set / cleared by the owner only, that a
|
|
4
|
+
//! `REDISTRIBUTOR_ROLE` holder can no longer change it, that the new escrow
|
|
5
|
+
//! must not be currently blacklisted on the SAC, and that an `EscrowSet` event
|
|
6
|
+
//! is emitted on every change.
|
|
7
|
+
//!
|
|
8
|
+
//! `set_escrow` is owner-gated (`#[only_auth]`) so that choosing the
|
|
9
|
+
//! redistribution destination and executing the seizure
|
|
10
|
+
//! (`redistribute_blacklisted_funds`, `REDISTRIBUTOR_ROLE`) require two
|
|
11
|
+
//! distinct keys.
|
|
12
|
+
//!
|
|
13
|
+
//! The constraint that the escrow MUST NOT be the underlying classic asset
|
|
14
|
+
//! issuer account is documented in `sac_manager.rs` and is the owner's
|
|
15
|
+
//! off-chain responsibility — the classic issuer is not queryable on-chain
|
|
16
|
+
//! once SAC admin has been transferred to this contract, so it cannot be
|
|
17
|
+
//! enforced in the contract.
|
|
18
|
+
|
|
19
|
+
use super::test_helper::{mock_set_authorized_auth, mock_set_escrow_auth};
|
|
20
|
+
use crate::errors::SACManagerError;
|
|
21
|
+
use crate::sac_manager::EscrowSet;
|
|
22
|
+
use crate::tests::test_helper::{mock_auth, TestSetup};
|
|
23
|
+
use soroban_sdk::{testutils::IssuerFlags, Symbol};
|
|
24
|
+
use utils::testing_utils::assert_contains_event;
|
|
25
|
+
|
|
26
|
+
// =========================================================================
|
|
27
|
+
// set_escrow Happy Paths
|
|
28
|
+
// =========================================================================
|
|
29
|
+
|
|
30
|
+
#[test]
|
|
31
|
+
fn test_set_escrow_enables_and_disables_redistribution() {
|
|
32
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
33
|
+
let escrow = setup.generate_address();
|
|
34
|
+
assert!(setup.sac_manager_client.escrow().is_none());
|
|
35
|
+
|
|
36
|
+
mock_set_escrow_auth(&setup, &setup.owner, &Some(escrow.clone()));
|
|
37
|
+
setup.sac_manager_client.set_escrow(&Some(escrow.clone()));
|
|
38
|
+
assert_eq!(setup.sac_manager_client.escrow(), Some(escrow.clone()));
|
|
39
|
+
|
|
40
|
+
mock_set_escrow_auth(&setup, &setup.owner, &None);
|
|
41
|
+
setup.sac_manager_client.set_escrow(&None);
|
|
42
|
+
assert!(setup.sac_manager_client.escrow().is_none());
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#[test]
|
|
46
|
+
fn test_set_escrow_emits_event_with_previous_and_new() {
|
|
47
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
48
|
+
let first = setup.generate_address();
|
|
49
|
+
let second = setup.generate_address();
|
|
50
|
+
|
|
51
|
+
mock_set_escrow_auth(&setup, &setup.owner, &Some(first.clone()));
|
|
52
|
+
setup.sac_manager_client.set_escrow(&Some(first.clone()));
|
|
53
|
+
assert_contains_event(
|
|
54
|
+
&setup.env,
|
|
55
|
+
&setup.sac_manager,
|
|
56
|
+
EscrowSet { previous_escrow: None, new_escrow: Some(first.clone()) },
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
mock_set_escrow_auth(&setup, &setup.owner, &Some(second.clone()));
|
|
60
|
+
setup.sac_manager_client.set_escrow(&Some(second.clone()));
|
|
61
|
+
assert_contains_event(
|
|
62
|
+
&setup.env,
|
|
63
|
+
&setup.sac_manager,
|
|
64
|
+
EscrowSet { previous_escrow: Some(first.clone()), new_escrow: Some(second.clone()) },
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
mock_set_escrow_auth(&setup, &setup.owner, &None);
|
|
68
|
+
setup.sac_manager_client.set_escrow(&None);
|
|
69
|
+
assert_contains_event(
|
|
70
|
+
&setup.env,
|
|
71
|
+
&setup.sac_manager,
|
|
72
|
+
EscrowSet { previous_escrow: Some(second), new_escrow: None },
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// =========================================================================
|
|
77
|
+
// set_escrow Guard — cannot set a currently-blacklisted address as escrow
|
|
78
|
+
// =========================================================================
|
|
79
|
+
|
|
80
|
+
#[test]
|
|
81
|
+
fn test_set_escrow_fails_when_candidate_is_blacklisted() {
|
|
82
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
83
|
+
let escrow_candidate = setup.generate_address();
|
|
84
|
+
|
|
85
|
+
setup.sac_contract.issuer().set_flag(IssuerFlags::RevocableFlag);
|
|
86
|
+
|
|
87
|
+
mock_set_authorized_auth(&setup, &setup.owner, &escrow_candidate, false);
|
|
88
|
+
setup.sac_manager_client.set_authorized(&escrow_candidate, &false, &setup.owner);
|
|
89
|
+
assert!(!setup.sac_client.authorized(&escrow_candidate));
|
|
90
|
+
|
|
91
|
+
mock_set_escrow_auth(&setup, &setup.owner, &Some(escrow_candidate.clone()));
|
|
92
|
+
let result = setup.sac_manager_client.try_set_escrow(&Some(escrow_candidate));
|
|
93
|
+
assert_eq!(result.err().unwrap().unwrap(), SACManagerError::EscrowIsBlocked.into());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// =========================================================================
|
|
97
|
+
// set_escrow Owner-gating — a REDISTRIBUTOR_ROLE holder cannot set the escrow
|
|
98
|
+
// =========================================================================
|
|
99
|
+
|
|
100
|
+
#[test]
|
|
101
|
+
fn test_set_escrow_fails_for_redistributor_role_holder() {
|
|
102
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
103
|
+
let redistributor = setup.generate_address();
|
|
104
|
+
let escrow = setup.generate_address();
|
|
105
|
+
|
|
106
|
+
// Grant REDISTRIBUTOR_ROLE to a non-owner address.
|
|
107
|
+
let role = Symbol::new(&setup.env, "REDISTRIBUTOR_ROLE");
|
|
108
|
+
mock_auth(
|
|
109
|
+
&setup.env,
|
|
110
|
+
&setup.sac_manager,
|
|
111
|
+
&setup.owner,
|
|
112
|
+
"grant_role",
|
|
113
|
+
(redistributor.clone(), role.clone(), setup.owner.clone()),
|
|
114
|
+
);
|
|
115
|
+
setup.sac_manager_client.grant_role(&redistributor, &role, &setup.owner);
|
|
116
|
+
|
|
117
|
+
// Holding REDISTRIBUTOR_ROLE and authorizing the call is not enough:
|
|
118
|
+
// set_escrow is owner-gated, so the owner's authorization is required.
|
|
119
|
+
mock_set_escrow_auth(&setup, &redistributor, &Some(escrow.clone()));
|
|
120
|
+
let result = setup.sac_manager_client.try_set_escrow(&Some(escrow));
|
|
121
|
+
assert!(result.is_err());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// =========================================================================
|
|
125
|
+
// set_escrow Auth — the owner must authorize
|
|
126
|
+
// =========================================================================
|
|
127
|
+
|
|
128
|
+
#[test]
|
|
129
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
130
|
+
fn test_set_escrow_fails_without_owner_auth() {
|
|
131
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
132
|
+
let escrow = setup.generate_address();
|
|
133
|
+
|
|
134
|
+
setup.sac_manager_client.set_escrow(&Some(escrow));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#[test]
|
|
138
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
139
|
+
fn test_set_escrow_fails_when_non_owner_authorizes() {
|
|
140
|
+
let setup = TestSetup::new().with_manager_as_sac_admin().build();
|
|
141
|
+
let random = setup.generate_address();
|
|
142
|
+
let escrow = setup.generate_address();
|
|
143
|
+
|
|
144
|
+
mock_auth(&setup.env, &setup.sac_manager, &random, "set_escrow", (Some(escrow.clone()),));
|
|
145
|
+
setup.sac_manager_client.set_escrow(&Some(escrow));
|
|
146
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
use crate::tests::test_helper::{mock_auth, TestSetup};
|
|
2
|
+
use soroban_sdk::Address;
|
|
3
|
+
|
|
4
|
+
/// Mock auth for `clawback(from, amount, operator)` — operator must hold CLAWBACK_ROLE.
|
|
5
|
+
pub fn mock_clawback_auth(setup: &TestSetup, operator: &Address, from: &Address, amount: i128) {
|
|
6
|
+
mock_auth(&setup.env, &setup.sac_manager, operator, "clawback", (from, amount, operator));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/// Mock auth for `set_authorized(id, authorize, operator)` — operator must hold BLACKLISTER_ROLE.
|
|
10
|
+
pub fn mock_set_authorized_auth(setup: &TestSetup, operator: &Address, user: &Address, authorize: bool) {
|
|
11
|
+
mock_auth(&setup.env, &setup.sac_manager, operator, "set_authorized", (user, authorize, operator));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// Mock auth for `set_admin(new_admin, operator)` — operator must hold ADMIN_MANAGER_ROLE.
|
|
15
|
+
pub fn mock_set_admin_auth(setup: &TestSetup, operator: &Address, new_admin: &Address) {
|
|
16
|
+
mock_auth(&setup.env, &setup.sac_manager, operator, "set_admin", (new_admin, operator));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Mock auth for `set_escrow(escrow)` — owner-gated (`#[only_auth]`), so
|
|
20
|
+
/// `authorizer` is the contract owner.
|
|
21
|
+
pub fn mock_set_escrow_auth(setup: &TestSetup, authorizer: &Address, escrow: &Option<Address>) {
|
|
22
|
+
mock_auth(&setup.env, &setup.sac_manager, authorizer, "set_escrow", (escrow.clone(),));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Mock auth for `redistribute_blacklisted_funds(from, amount, operator)` —
|
|
26
|
+
/// operator must hold REDISTRIBUTOR_ROLE.
|
|
27
|
+
pub fn mock_redistribute_funds_auth(setup: &TestSetup, operator: &Address, from: &Address, amount: i128) {
|
|
28
|
+
mock_auth(&setup.env, &setup.sac_manager, operator, "redistribute_blacklisted_funds", (from, amount, operator));
|
|
29
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//! View Functions Integration Tests
|
|
2
|
+
|
|
3
|
+
use crate::tests::test_helper::TestSetup;
|
|
4
|
+
|
|
5
|
+
// =========================================================================
|
|
6
|
+
// Core View Functions
|
|
7
|
+
// =========================================================================
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn test_underlying_sac() {
|
|
11
|
+
let setup = TestSetup::new().build();
|
|
12
|
+
assert_eq!(setup.sac_manager_client.underlying_sac(), setup.sac);
|
|
13
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
//! Test helper utilities for SAC manager tests.
|
|
2
|
+
//!
|
|
3
|
+
//! Provides authentication mocking helpers and common setup utilities.
|
|
4
|
+
|
|
5
|
+
extern crate std;
|
|
6
|
+
|
|
7
|
+
use crate::{SACManager, SACManagerClient};
|
|
8
|
+
use soroban_sdk::{
|
|
9
|
+
testutils::{Address as _, MockAuth, MockAuthInvoke, StellarAssetContract},
|
|
10
|
+
token::StellarAssetClient,
|
|
11
|
+
Address, Env, IntoVal, Symbol, Val,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// =========================================================================
|
|
15
|
+
// TestSetup Builder
|
|
16
|
+
// =========================================================================
|
|
17
|
+
|
|
18
|
+
/// Builder for creating test setups with chainable configuration.
|
|
19
|
+
///
|
|
20
|
+
/// # Example
|
|
21
|
+
/// ```ignore
|
|
22
|
+
/// let setup = TestSetup::new()
|
|
23
|
+
/// .with_manager_as_sac_admin()
|
|
24
|
+
/// .with_escrow_enabled()
|
|
25
|
+
/// .build();
|
|
26
|
+
/// ```
|
|
27
|
+
pub struct TestSetupBuilder {
|
|
28
|
+
manager_as_sac_admin: bool,
|
|
29
|
+
enable_escrow: bool,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl TestSetupBuilder {
|
|
33
|
+
fn new() -> Self {
|
|
34
|
+
Self { manager_as_sac_admin: false, enable_escrow: false }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Set the SACManager as SAC admin during setup.
|
|
38
|
+
pub fn with_manager_as_sac_admin(mut self) -> Self {
|
|
39
|
+
self.manager_as_sac_admin = true;
|
|
40
|
+
self
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Configure a fresh, randomly-generated escrow address on the manager so
|
|
44
|
+
/// the redistribution feature is enabled at the end of setup.
|
|
45
|
+
pub fn with_escrow_enabled(mut self) -> Self {
|
|
46
|
+
self.enable_escrow = true;
|
|
47
|
+
self
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Build the TestSetup with the configured options.
|
|
51
|
+
pub fn build<'a>(self) -> TestSetup<'a> {
|
|
52
|
+
let env = Env::default();
|
|
53
|
+
|
|
54
|
+
let owner = Address::generate(&env);
|
|
55
|
+
let oft = Address::generate(&env);
|
|
56
|
+
let sac_contract = env.register_stellar_asset_contract_v2(owner.clone());
|
|
57
|
+
let sac = sac_contract.address();
|
|
58
|
+
|
|
59
|
+
let sac_manager = env.register(SACManager, (&sac, &owner));
|
|
60
|
+
let sac_manager_client = SACManagerClient::new(&env, &sac_manager);
|
|
61
|
+
let sac_client = StellarAssetClient::new(&env, &sac);
|
|
62
|
+
|
|
63
|
+
// Grant all roles to owner (owner is the authorizer so can grant any role)
|
|
64
|
+
for role_str in ["ADMIN_MANAGER_ROLE", "MINTER_ROLE", "BLACKLISTER_ROLE", "CLAWBACK_ROLE", "REDISTRIBUTOR_ROLE"]
|
|
65
|
+
{
|
|
66
|
+
let role = Symbol::new(&env, role_str);
|
|
67
|
+
mock_auth(&env, &sac_manager, &owner, "grant_role", (owner.clone(), role.clone(), owner.clone()));
|
|
68
|
+
sac_manager_client.grant_role(&owner, &role, &owner);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Grant MINTER_ROLE to oft so it can call mint in tests
|
|
72
|
+
let minter_role = Symbol::new(&env, "MINTER_ROLE");
|
|
73
|
+
mock_auth(&env, &sac_manager, &owner, "grant_role", (oft.clone(), minter_role.clone(), owner.clone()));
|
|
74
|
+
sac_manager_client.grant_role(&oft, &minter_role, &owner);
|
|
75
|
+
|
|
76
|
+
if self.manager_as_sac_admin {
|
|
77
|
+
env.mock_auths(&[MockAuth {
|
|
78
|
+
address: &owner,
|
|
79
|
+
invoke: &MockAuthInvoke {
|
|
80
|
+
contract: &sac,
|
|
81
|
+
fn_name: "set_admin",
|
|
82
|
+
args: (&sac_manager,).into_val(&env),
|
|
83
|
+
sub_invokes: &[],
|
|
84
|
+
},
|
|
85
|
+
}]);
|
|
86
|
+
sac_client.set_admin(&sac_manager);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let escrow = if self.enable_escrow {
|
|
90
|
+
let escrow = Address::generate(&env);
|
|
91
|
+
mock_auth(&env, &sac_manager, &owner, "set_escrow", (Some(escrow.clone()),));
|
|
92
|
+
sac_manager_client.set_escrow(&Some(escrow.clone()));
|
|
93
|
+
Some(escrow)
|
|
94
|
+
} else {
|
|
95
|
+
None
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Clear mock auths so test starts with clean auth state
|
|
99
|
+
env.mock_auths(&[]);
|
|
100
|
+
|
|
101
|
+
TestSetup { env, owner, minter: oft, sac, sac_contract, sac_manager, sac_manager_client, sac_client, escrow }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// =========================================================================
|
|
106
|
+
// TestSetup
|
|
107
|
+
// =========================================================================
|
|
108
|
+
|
|
109
|
+
/// Common test setup that creates a SAC and SACManager.
|
|
110
|
+
pub struct TestSetup<'a> {
|
|
111
|
+
pub env: Env,
|
|
112
|
+
pub owner: Address,
|
|
113
|
+
/// Address that has MINTER_ROLE in default setup (used as operator for mint in tests).
|
|
114
|
+
pub minter: Address,
|
|
115
|
+
pub sac: Address,
|
|
116
|
+
pub sac_contract: StellarAssetContract,
|
|
117
|
+
pub sac_manager: Address,
|
|
118
|
+
pub sac_manager_client: SACManagerClient<'a>,
|
|
119
|
+
pub sac_client: StellarAssetClient<'a>,
|
|
120
|
+
/// Escrow address configured during build if `with_escrow_enabled()` was used.
|
|
121
|
+
pub escrow: Option<Address>,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
impl TestSetup<'_> {
|
|
125
|
+
/// Start building a new test setup.
|
|
126
|
+
#[allow(clippy::new_ret_no_self)]
|
|
127
|
+
pub fn new() -> TestSetupBuilder {
|
|
128
|
+
TestSetupBuilder::new()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Generate a new random address in this test environment.
|
|
132
|
+
pub fn generate_address(&self) -> Address {
|
|
133
|
+
Address::generate(&self.env)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// =========================================================================
|
|
138
|
+
// Auth Helpers
|
|
139
|
+
// =========================================================================
|
|
140
|
+
|
|
141
|
+
/// Test helper to mock a single contract invocation auth.
|
|
142
|
+
///
|
|
143
|
+
/// This keeps test files from repeating `env.mock_auths(&[MockAuth { ... }])` blocks.
|
|
144
|
+
pub fn mock_auth<A: IntoVal<Env, soroban_sdk::Vec<Val>>>(
|
|
145
|
+
env: &Env,
|
|
146
|
+
contract_id: &Address,
|
|
147
|
+
address: &Address,
|
|
148
|
+
fn_name: &'static str,
|
|
149
|
+
args: A,
|
|
150
|
+
) {
|
|
151
|
+
env.mock_auths(&[MockAuth {
|
|
152
|
+
address,
|
|
153
|
+
invoke: &MockAuthInvoke { contract: contract_id, args: args.into_val(env), fn_name, sub_invokes: &[] },
|
|
154
|
+
}]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// Mock auth for minter-originated `mint(to, amount, operator)`.
|
|
158
|
+
pub fn mock_oft_mint_auth(setup: &TestSetup, recipient: &Address, amount: i128) {
|
|
159
|
+
mock_auth(&setup.env, &setup.sac_manager, &setup.minter, "mint", (recipient, amount, &setup.minter));
|
|
160
|
+
}
|