|
1 | 1 | use soroban_sdk::{contracttype, panic_with_error, Address, Env, Symbol};
|
2 |
| -use stellar_constants::{ |
3 |
| - ADMIN_TRANSFER_THRESHOLD, ADMIN_TRANSFER_TTL, ROLE_EXTEND_AMOUNT, ROLE_TTL_THRESHOLD, |
4 |
| -}; |
| 2 | +use stellar_constants::{ROLE_EXTEND_AMOUNT, ROLE_TTL_THRESHOLD}; |
5 | 3 |
|
6 | 4 | use crate::{
|
7 |
| - emit_admin_transfer_cancelled, emit_admin_transfer_completed, emit_admin_transfer_started, |
8 |
| - emit_role_admin_changed, emit_role_granted, emit_role_revoked, AccessControlError, |
| 5 | + emit_admin_transfer, emit_admin_transfer_completed, emit_role_admin_changed, emit_role_granted, |
| 6 | + emit_role_revoked, AccessControlError, |
9 | 7 | };
|
10 | 8 |
|
11 | 9 | #[contracttype]
|
@@ -223,56 +221,54 @@ pub fn renounce_role(e: &Env, caller: &Address, role: &Symbol) {
|
223 | 221 | /// * `e` - Access to Soroban environment.
|
224 | 222 | /// * `caller` - The address of the caller, must be the admin.
|
225 | 223 | /// * `new_admin` - The account to transfer the admin privileges to.
|
| 224 | +/// * `live_until_ledger` - The ledger number at which the pending transfer |
| 225 | +/// expires. If `live_until_ledger` is `0`, the pending transfer is cancelled. |
| 226 | +/// `live_until_ledger` argument is implicitly bounded by the maximum allowed |
| 227 | +/// TTL extension for a temporary storage entry and specifying a higher value |
| 228 | +/// will cause the code to panic. |
226 | 229 | ///
|
227 | 230 | /// # Errors
|
228 | 231 | ///
|
229 | 232 | /// * `AccessControlError::Unauthorized` - If the `caller` is not the admin.
|
| 233 | +/// * `AccessControlError::NoPendingAdminTransfer` - If tried to cancel the |
| 234 | +/// pending admin transfer when there is no pending admin transfer. |
230 | 235 | ///
|
231 | 236 | /// # Events
|
232 | 237 | ///
|
233 | 238 | /// * topics - `["admin_transfer_started", current_admin: Address]`
|
234 |
| -/// * data - `[new_admin: Address]` |
235 |
| -pub fn transfer_admin_role(e: &Env, caller: &Address, new_admin: &Address) { |
| 239 | +/// * data - `[new_admin: Address, live_until_ledger: u32]` |
| 240 | +pub fn transfer_admin_role(e: &Env, caller: &Address, new_admin: &Address, live_until_ledger: u32) { |
236 | 241 | if caller != &get_admin(e) {
|
237 | 242 | panic_with_error!(e, AccessControlError::Unauthorized);
|
238 | 243 | }
|
239 | 244 |
|
240 |
| - // Store the new admin address in temporary storage |
241 |
| - e.storage().temporary().set(&AccessControlStorageKey::PendingAdmin, new_admin); |
242 |
| - e.storage().temporary().extend_ttl( |
243 |
| - &AccessControlStorageKey::PendingAdmin, |
244 |
| - ADMIN_TRANSFER_THRESHOLD, |
245 |
| - ADMIN_TRANSFER_TTL, |
246 |
| - ); |
247 |
| - |
248 |
| - emit_admin_transfer_started(e, caller, new_admin); |
249 |
| -} |
| 245 | + let key = AccessControlStorageKey::PendingAdmin; |
250 | 246 |
|
251 |
| -/// Cancels a pending admin role transfer if it is not accepted yet. |
252 |
| -/// This can only be called by the current admin. |
253 |
| -/// |
254 |
| -/// # Arguments |
255 |
| -/// |
256 |
| -/// * `e` - Access to Soroban environment. |
257 |
| -/// * `caller` - The address of the caller, must be the admin. |
258 |
| -/// |
259 |
| -/// # Errors |
260 |
| -/// |
261 |
| -/// * `AccessControlError::Unauthorized` - If the `caller` is not the admin. |
262 |
| -/// |
263 |
| -/// # Events |
264 |
| -/// |
265 |
| -/// * topics - `["admin_transfer_cancelled", admin: Address]` |
266 |
| -/// * data - `[]` (empty data) |
267 |
| -pub fn cancel_admin_transfer(e: &Env, caller: &Address) { |
268 |
| - if caller != &get_admin(e) { |
269 |
| - panic_with_error!(e, AccessControlError::Unauthorized); |
| 247 | + if live_until_ledger == 0 { |
| 248 | + let Some(pending_new_admin) = e.storage().temporary().get::<_, Address>(&key) else { |
| 249 | + panic_with_error!(e, AccessControlError::NoPendingAdminTransfer) |
| 250 | + }; |
| 251 | + e.storage().temporary().remove(&key); |
| 252 | + |
| 253 | + emit_admin_transfer(e, caller, &pending_new_admin, live_until_ledger); |
| 254 | + return; |
270 | 255 | }
|
271 | 256 |
|
272 |
| - e.storage().temporary().remove(&AccessControlStorageKey::PendingAdmin); |
| 257 | + let current_ledger = e.ledger().sequence(); |
| 258 | + |
| 259 | + if live_until_ledger < current_ledger { |
| 260 | + panic_with_error!(e, AccessControlError::InvalidLiveUntilLedger); |
| 261 | + } |
| 262 | + |
| 263 | + let live_for = live_until_ledger - current_ledger; |
| 264 | + |
| 265 | + // Store the new admin address in temporary storage |
| 266 | + e.storage().temporary().set(&key, new_admin); |
| 267 | + e.storage().temporary().extend_ttl(&key, live_for, live_for); |
273 | 268 |
|
274 |
| - emit_admin_transfer_cancelled(e, caller); |
| 269 | + emit_admin_transfer(e, caller, new_admin, live_until_ledger); |
275 | 270 | }
|
| 271 | +// TODO: test for live_until_ledger = 0 when there is no pending admin |
276 | 272 |
|
277 | 273 | /// Completes the 2-step admin transfer.
|
278 | 274 | ///
|
|
0 commit comments