use alloc::vec::Vec;
use core::mem::MaybeUninit;
use casper_types::{
account::AccountHash,
api_error, bytesrepr,
system::{
auction::{self, EraInfo},
SystemContractType,
},
ApiError, ContractHash, EraId, HashAddr, PublicKey, TransferResult, TransferredTo, URef, U512,
UREF_SERIALIZED_LENGTH,
};
use crate::{
contract_api::{self, account, runtime},
ext_ffi,
unwrap_or_revert::UnwrapOrRevert,
};
fn get_system_contract(system_contract: SystemContractType) -> ContractHash {
let system_contract_index = system_contract.into();
let contract_hash: ContractHash = {
let result = {
let mut hash_data_raw: HashAddr = ContractHash::default().value();
let value = unsafe {
ext_ffi::casper_get_system_contract(
system_contract_index,
hash_data_raw.as_mut_ptr(),
hash_data_raw.len(),
)
};
api_error::result_from(value).map(|_| hash_data_raw)
};
#[allow(clippy::redundant_closure)] let contract_hash_bytes = result.unwrap_or_else(|e| runtime::revert(e));
bytesrepr::deserialize(contract_hash_bytes.to_vec()).unwrap_or_revert()
};
contract_hash
}
pub fn get_mint() -> ContractHash {
get_system_contract(SystemContractType::Mint)
}
pub fn get_handle_payment() -> ContractHash {
get_system_contract(SystemContractType::HandlePayment)
}
pub fn get_standard_payment() -> ContractHash {
get_system_contract(SystemContractType::StandardPayment)
}
pub fn get_auction() -> ContractHash {
get_system_contract(SystemContractType::Auction)
}
pub fn create_purse() -> URef {
let purse_non_null_ptr = contract_api::alloc_bytes(UREF_SERIALIZED_LENGTH);
let ret = unsafe {
ext_ffi::casper_create_purse(purse_non_null_ptr.as_ptr(), UREF_SERIALIZED_LENGTH)
};
api_error::result_from(ret).unwrap_or_revert();
let bytes = unsafe {
Vec::from_raw_parts(
purse_non_null_ptr.as_ptr(),
UREF_SERIALIZED_LENGTH,
UREF_SERIALIZED_LENGTH,
)
};
bytesrepr::deserialize(bytes).unwrap_or_revert()
}
pub fn get_purse_balance(purse: URef) -> Option<U512> {
let (purse_ptr, purse_size, _bytes) = contract_api::to_ptr(purse);
let value_size = {
let mut output_size = MaybeUninit::uninit();
let ret =
unsafe { ext_ffi::casper_get_balance(purse_ptr, purse_size, output_size.as_mut_ptr()) };
match api_error::result_from(ret) {
Ok(_) => unsafe { output_size.assume_init() },
Err(ApiError::InvalidPurse) => return None,
Err(error) => runtime::revert(error),
}
};
let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
let value: U512 = bytesrepr::deserialize(value_bytes).unwrap_or_revert();
Some(value)
}
pub fn get_balance() -> Option<U512> {
get_purse_balance(account::get_main_purse())
}
pub fn transfer_to_account(target: AccountHash, amount: U512, id: Option<u64>) -> TransferResult {
let (target_ptr, target_size, _bytes1) = contract_api::to_ptr(target);
let (amount_ptr, amount_size, _bytes2) = contract_api::to_ptr(amount);
let (id_ptr, id_size, _bytes3) = contract_api::to_ptr(id);
let mut maybe_result_value = MaybeUninit::uninit();
let return_code = unsafe {
ext_ffi::casper_transfer_to_account(
target_ptr,
target_size,
amount_ptr,
amount_size,
id_ptr,
id_size,
maybe_result_value.as_mut_ptr(),
)
};
api_error::result_from(return_code)?;
let transferred_to_value = unsafe { maybe_result_value.assume_init() };
TransferredTo::result_from(transferred_to_value)
}
pub fn transfer_to_public_key(target: PublicKey, amount: U512, id: Option<u64>) -> TransferResult {
let target = AccountHash::from(&target);
transfer_to_account(target, amount, id)
}
pub fn transfer_from_purse_to_account(
source: URef,
target: AccountHash,
amount: U512,
id: Option<u64>,
) -> 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 (id_ptr, id_size, _bytes4) = contract_api::to_ptr(id);
let mut maybe_result_value = MaybeUninit::uninit();
let return_code = unsafe {
ext_ffi::casper_transfer_from_purse_to_account(
source_ptr,
source_size,
target_ptr,
target_size,
amount_ptr,
amount_size,
id_ptr,
id_size,
maybe_result_value.as_mut_ptr(),
)
};
api_error::result_from(return_code)?;
let transferred_to_value = unsafe { maybe_result_value.assume_init() };
TransferredTo::result_from(transferred_to_value)
}
pub fn transfer_from_purse_to_public_key(
source: URef,
target: PublicKey,
amount: U512,
id: Option<u64>,
) -> TransferResult {
let target = AccountHash::from(&target);
transfer_from_purse_to_account(source, target, amount, id)
}
pub fn transfer_from_purse_to_purse(
source: URef,
target: URef,
amount: U512,
id: Option<u64>,
) -> Result<(), ApiError> {
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 (id_ptr, id_size, _bytes4) = contract_api::to_ptr(id);
let result = unsafe {
ext_ffi::casper_transfer_from_purse_to_purse(
source_ptr,
source_size,
target_ptr,
target_size,
amount_ptr,
amount_size,
id_ptr,
id_size,
)
};
api_error::result_from(result)
}
#[doc(hidden)]
pub fn record_transfer(
maybe_to: Option<AccountHash>,
source: URef,
target: URef,
amount: U512,
id: Option<u64>,
) -> Result<(), ApiError> {
let (maybe_to_ptr, maybe_to_size, _bytes1) = contract_api::to_ptr(maybe_to);
let (source_ptr, source_size, _bytes2) = contract_api::to_ptr(source);
let (target_ptr, target_size, _bytes3) = contract_api::to_ptr(target);
let (amount_ptr, amount_size, _bytes4) = contract_api::to_ptr(amount);
let (id_ptr, id_size, _bytes5) = contract_api::to_ptr(id);
let result = unsafe {
ext_ffi::casper_record_transfer(
maybe_to_ptr,
maybe_to_size,
source_ptr,
source_size,
target_ptr,
target_size,
amount_ptr,
amount_size,
id_ptr,
id_size,
)
};
if result == 0 {
Ok(())
} else {
Err(ApiError::Transfer)
}
}
#[doc(hidden)]
pub fn record_era_info(era_id: EraId, era_info: EraInfo) -> Result<(), ApiError> {
let (era_id_ptr, era_id_size, _bytes1) = contract_api::to_ptr(era_id);
let (era_info_ptr, era_info_size, _bytes2) = contract_api::to_ptr(era_info);
let result = unsafe {
ext_ffi::casper_record_era_info(era_id_ptr, era_id_size, era_info_ptr, era_info_size)
};
if result == 0 {
Ok(())
} else {
Err(auction::Error::RecordEraInfo.into())
}
}