use soroban_sdk::{contracttype, panic_with_error, Address, Env, IntoVal, Val};
use crate::role_transfer::RoleTransferError;
#[contracttype]
pub struct PendingTransfer {
pub address: Address,
pub live_until_ledger: u32,
}
pub fn transfer_role<T>(e: &Env, new: &Address, pending_key: &T, live_until_ledger: u32)
where
T: IntoVal<Env, Val>,
{
if live_until_ledger == 0 {
let Some(pending) = e.storage().temporary().get::<T, PendingTransfer>(pending_key) else {
panic_with_error!(e, RoleTransferError::NoPendingTransfer);
};
if pending.address != *new {
panic_with_error!(e, RoleTransferError::InvalidPendingAccount);
}
e.storage().temporary().remove(pending_key);
return;
}
let current_ledger = e.ledger().sequence();
if live_until_ledger > e.ledger().max_live_until_ledger() || live_until_ledger < current_ledger
{
panic_with_error!(e, RoleTransferError::InvalidLiveUntilLedger);
}
let live_for = live_until_ledger - current_ledger;
let pending = PendingTransfer { address: new.clone(), live_until_ledger };
e.storage().temporary().set(pending_key, &pending);
e.storage().temporary().extend_ttl(pending_key, live_for, live_for);
}
pub fn has_active_pending_transfer<T>(e: &Env, pending_key: &T) -> bool
where
T: IntoVal<Env, Val>,
{
match e.storage().temporary().get::<T, PendingTransfer>(pending_key) {
Some(pending) if e.ledger().sequence() <= pending.live_until_ledger => true,
Some(_) => {
e.storage().temporary().remove(pending_key);
false
}
None => false,
}
}
pub fn accept_transfer<T, U>(e: &Env, active_key: &T, pending_key: &U) -> Address
where
T: IntoVal<Env, Val>,
U: IntoVal<Env, Val>,
{
let pending = e
.storage()
.temporary()
.get::<U, PendingTransfer>(pending_key)
.unwrap_or_else(|| panic_with_error!(e, RoleTransferError::NoPendingTransfer));
if e.ledger().sequence() > pending.live_until_ledger {
panic_with_error!(e, RoleTransferError::TransferExpired);
}
pending.address.require_auth();
e.storage().temporary().remove(pending_key);
e.storage().instance().set(active_key, &pending.address);
pending.address
}