use alloc::vec::Vec;
use core::{fmt::Debug, mem::MaybeUninit};
use crate::{
bytesrepr::deserialize,
contract_api::{
self,
error::{self, Error},
runtime, ContractRef,
},
ext_ffi,
system_contracts::SystemContract,
unwrap_or_revert::UnwrapOrRevert,
uref::{URef, UREF_SERIALIZED_LENGTH},
value::{
account::{PublicKey, PurseId, PURSE_ID_SERIALIZED_LENGTH},
U512,
},
};
pub type TransferResult = Result<TransferredTo, Error>;
pub const MINT_NAME: &str = "mint";
pub const POS_NAME: &str = "pos";
fn get_system_contract(system_contract: SystemContract) -> ContractRef {
let system_contract_index = system_contract.into();
let uref: URef = {
let result = {
let mut uref_data_raw = [0u8; UREF_SERIALIZED_LENGTH];
let value = unsafe {
ext_ffi::get_system_contract(
system_contract_index,
uref_data_raw.as_mut_ptr(),
uref_data_raw.len(),
)
};
error::result_from(value).map(|_| uref_data_raw)
};
let uref_bytes = result.unwrap_or_else(|e| runtime::revert(e));
deserialize(uref_bytes.to_vec()).unwrap_or_revert()
};
if uref.access_rights().is_none() {
runtime::revert(Error::NoAccessRights);
}
ContractRef::URef(uref)
}
pub fn get_mint() -> ContractRef {
get_system_contract(SystemContract::Mint)
}
pub fn get_proof_of_stake() -> ContractRef {
get_system_contract(SystemContract::ProofOfStake)
}
pub fn create_purse() -> PurseId {
let purse_id_ptr = contract_api::alloc_bytes(PURSE_ID_SERIALIZED_LENGTH);
unsafe {
let ret = ext_ffi::create_purse(purse_id_ptr, PURSE_ID_SERIALIZED_LENGTH);
if ret == 0 {
let bytes = Vec::from_raw_parts(
purse_id_ptr,
PURSE_ID_SERIALIZED_LENGTH,
PURSE_ID_SERIALIZED_LENGTH,
);
deserialize(bytes).unwrap_or_revert()
} else {
runtime::revert(Error::PurseNotCreated)
}
}
}
pub fn get_balance(purse_id: PurseId) -> Option<U512> {
let (purse_id_ptr, purse_id_size, _bytes) = contract_api::to_ptr(purse_id);
let value_size = {
let mut output_size = MaybeUninit::uninit();
let ret =
unsafe { ext_ffi::get_balance(purse_id_ptr, purse_id_size, output_size.as_mut_ptr()) };
match error::result_from(ret) {
Ok(_) => unsafe { output_size.assume_init() },
Err(Error::InvalidPurse) => return None,
Err(error) => runtime::revert(error),
}
};
let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
let value: U512 = deserialize(value_bytes).unwrap_or_revert();
Some(value)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum TransferredTo {
ExistingAccount = 0,
NewAccount = 1,
}
impl TransferredTo {
fn result_from(value: i32) -> TransferResult {
match value {
x if x == TransferredTo::ExistingAccount as i32 => Ok(TransferredTo::ExistingAccount),
x if x == TransferredTo::NewAccount as i32 => Ok(TransferredTo::NewAccount),
_ => Err(Error::Transfer),
}
}
pub fn i32_from(result: TransferResult) -> i32 {
match result {
Ok(transferred_to) => transferred_to as i32,
Err(_) => 2,
}
}
}
pub fn transfer_to_account(target: PublicKey, amount: U512) -> TransferResult {
let (target_ptr, target_size, _bytes1) = contract_api::to_ptr(target);
let (amount_ptr, amount_size, _bytes2) = contract_api::to_ptr(amount);
let return_code =
unsafe { ext_ffi::transfer_to_account(target_ptr, target_size, amount_ptr, amount_size) };
TransferredTo::result_from(return_code)
}
pub fn transfer_from_purse_to_account(
source: PurseId,
target: PublicKey,
amount: U512,
) -> TransferResult {
let (source_ptr, source_size, _bytes1) = contract_api::to_ptr(source);
let (target_ptr, target_size, _bytes2) = contract_api::to_ptr(target);
let (amount_ptr, amount_size, _bytes3) = contract_api::to_ptr(amount);
let return_code = unsafe {
ext_ffi::transfer_from_purse_to_account(
source_ptr,
source_size,
target_ptr,
target_size,
amount_ptr,
amount_size,
)
};
TransferredTo::result_from(return_code)
}
pub fn transfer_from_purse_to_purse(
source: PurseId,
target: PurseId,
amount: U512,
) -> Result<(), Error> {
let (source_ptr, source_size, _bytes1) = contract_api::to_ptr(source);
let (target_ptr, target_size, _bytes2) = contract_api::to_ptr(target);
let (amount_ptr, amount_size, _bytes3) = contract_api::to_ptr(amount);
let result = unsafe {
ext_ffi::transfer_from_purse_to_purse(
source_ptr,
source_size,
target_ptr,
target_size,
amount_ptr,
amount_size,
)
};
if result == 0 {
Ok(())
} else {
Err(Error::Transfer)
}
}