Skip to content

Commit 8dc5c22

Browse files
authored
Merge pull request Emmyt24#336 from Christopherdominic/feature/address-freeze-317
feat: Implement address freeze/unfreeze for compliance (Emmyt24#317)
2 parents 6c2d12a + cfabb2e commit 8dc5c22

4 files changed

Lines changed: 518 additions & 0 deletions

File tree

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
use crate::{storage, types::Error};
2+
use soroban_sdk::{symbol_short, Address, Env};
3+
4+
/// Freeze an address for a specific token
5+
///
6+
/// Prevents the frozen address from participating in transfers, burns, or mints.
7+
/// Only the token creator can freeze addresses, and freeze must be enabled for the token.
8+
///
9+
/// Implements #317
10+
///
11+
/// # Arguments
12+
/// * `env` - The contract environment
13+
/// * `token_address` - The token contract address
14+
/// * `admin` - The token admin address (must be token creator)
15+
/// * `address_to_freeze` - The address to freeze
16+
///
17+
/// # Security
18+
/// - Requires admin authorization
19+
/// - Verifies admin is the token creator
20+
/// - Checks freeze is enabled for the token
21+
/// - Prevents freezing already frozen addresses
22+
///
23+
/// # Errors
24+
/// * `ContractPaused` - If contract is paused
25+
/// * `Unauthorized` - If caller is not the token creator
26+
/// * `TokenNotFound` - If token doesn't exist
27+
/// * `FreezeNotEnabled` - If freeze is not enabled for this token
28+
/// * `AddressFrozen` - If address is already frozen
29+
pub fn freeze_address(
30+
env: &Env,
31+
token_address: &Address,
32+
admin: &Address,
33+
address_to_freeze: &Address,
34+
) -> Result<(), Error> {
35+
// Check if contract is paused
36+
if storage::is_paused(env) {
37+
return Err(Error::ContractPaused);
38+
}
39+
40+
// Require admin authorization
41+
admin.require_auth();
42+
43+
// Verify token exists and get info
44+
let token_info = storage::get_token_info_by_address(env, token_address)
45+
.ok_or(Error::TokenNotFound)?;
46+
47+
// Verify admin is the token creator
48+
if token_info.creator != *admin {
49+
return Err(Error::Unauthorized);
50+
}
51+
52+
// Verify freeze is enabled for this token
53+
if !token_info.freeze_enabled {
54+
return Err(Error::FreezeNotEnabled);
55+
}
56+
57+
// Check if address is already frozen
58+
if storage::is_address_frozen(env, token_address, address_to_freeze) {
59+
return Err(Error::AddressFrozen);
60+
}
61+
62+
// Freeze the address
63+
storage::set_address_frozen(env, token_address, address_to_freeze, true);
64+
65+
// Emit freeze event
66+
env.events().publish(
67+
(symbol_short!("freeze"), token_address.clone()),
68+
(
69+
admin.clone(),
70+
address_to_freeze.clone(),
71+
env.ledger().timestamp(),
72+
),
73+
);
74+
75+
Ok(())
76+
}
77+
78+
/// Unfreeze an address for a specific token
79+
///
80+
/// Restores normal functionality for a previously frozen address.
81+
/// Only the token creator can unfreeze addresses.
82+
///
83+
/// Implements #317
84+
///
85+
/// # Arguments
86+
/// * `env` - The contract environment
87+
/// * `token_address` - The token contract address
88+
/// * `admin` - The token admin address (must be token creator)
89+
/// * `address_to_unfreeze` - The address to unfreeze
90+
///
91+
/// # Security
92+
/// - Requires admin authorization
93+
/// - Verifies admin is the token creator
94+
/// - Checks freeze is enabled for the token
95+
/// - Prevents unfreezing non-frozen addresses
96+
///
97+
/// # Errors
98+
/// * `ContractPaused` - If contract is paused
99+
/// * `Unauthorized` - If caller is not the token creator
100+
/// * `TokenNotFound` - If token doesn't exist
101+
/// * `FreezeNotEnabled` - If freeze is not enabled for this token
102+
/// * `AddressNotFrozen` - If address is not frozen
103+
pub fn unfreeze_address(
104+
env: &Env,
105+
token_address: &Address,
106+
admin: &Address,
107+
address_to_unfreeze: &Address,
108+
) -> Result<(), Error> {
109+
// Check if contract is paused
110+
if storage::is_paused(env) {
111+
return Err(Error::ContractPaused);
112+
}
113+
114+
// Require admin authorization
115+
admin.require_auth();
116+
117+
// Verify token exists and get info
118+
let token_info = storage::get_token_info_by_address(env, token_address)
119+
.ok_or(Error::TokenNotFound)?;
120+
121+
// Verify admin is the token creator
122+
if token_info.creator != *admin {
123+
return Err(Error::Unauthorized);
124+
}
125+
126+
// Verify freeze is enabled for this token
127+
if !token_info.freeze_enabled {
128+
return Err(Error::FreezeNotEnabled);
129+
}
130+
131+
// Check if address is actually frozen
132+
if !storage::is_address_frozen(env, token_address, address_to_unfreeze) {
133+
return Err(Error::AddressNotFrozen);
134+
}
135+
136+
// Unfreeze the address
137+
storage::set_address_frozen(env, token_address, address_to_unfreeze, false);
138+
139+
// Emit unfreeze event
140+
env.events().publish(
141+
(symbol_short!("unfreeze"), token_address.clone()),
142+
(
143+
admin.clone(),
144+
address_to_unfreeze.clone(),
145+
env.ledger().timestamp(),
146+
),
147+
);
148+
149+
Ok(())
150+
}
151+
152+
/// Check if an address is frozen for a specific token
153+
///
154+
/// # Arguments
155+
/// * `env` - The contract environment
156+
/// * `token_address` - The token contract address
157+
/// * `address` - The address to check
158+
///
159+
/// # Returns
160+
/// `true` if the address is frozen, `false` otherwise
161+
pub fn is_frozen(env: &Env, token_address: &Address, address: &Address) -> bool {
162+
storage::is_address_frozen(env, token_address, address)
163+
}
164+
165+
/// Toggle freeze capability for a token (creator only)
166+
///
167+
/// Allows token creator to enable or disable freeze functionality.
168+
/// Once disabled, it can be re-enabled by the creator.
169+
///
170+
/// # Arguments
171+
/// * `env` - The contract environment
172+
/// * `token_address` - The token contract address
173+
/// * `admin` - The token admin address (must be token creator)
174+
/// * `enabled` - Whether to enable or disable freeze
175+
///
176+
/// # Errors
177+
/// * `ContractPaused` - If contract is paused
178+
/// * `Unauthorized` - If caller is not the token creator
179+
/// * `TokenNotFound` - If token doesn't exist
180+
pub fn set_freeze_enabled(
181+
env: &Env,
182+
token_address: &Address,
183+
admin: &Address,
184+
enabled: bool,
185+
) -> Result<(), Error> {
186+
// Check if contract is paused
187+
if storage::is_paused(env) {
188+
return Err(Error::ContractPaused);
189+
}
190+
191+
// Require admin authorization
192+
admin.require_auth();
193+
194+
// Get token info
195+
let mut token_info = storage::get_token_info_by_address(env, token_address)
196+
.ok_or(Error::TokenNotFound)?;
197+
198+
// Verify admin is the token creator
199+
if token_info.creator != *admin {
200+
return Err(Error::Unauthorized);
201+
}
202+
203+
// Update freeze setting
204+
token_info.freeze_enabled = enabled;
205+
storage::set_token_info_by_address(env, token_address, &token_info);
206+
207+
// Emit event
208+
env.events().publish(
209+
(symbol_short!("frz_set"), token_address.clone()),
210+
(admin.clone(), enabled, env.ledger().timestamp()),
211+
);
212+
213+
Ok(())
214+
}

0 commit comments

Comments
 (0)