2026-01-21 15:22:38 +01:00
|
|
|
//! Token transfer functions.
|
|
|
|
|
|
2026-01-26 10:45:51 +01:00
|
|
|
use std::{ffi::CString, ptr};
|
2026-01-21 15:22:38 +01:00
|
|
|
|
|
|
|
|
use common::error::ExecutionFailureKind;
|
|
|
|
|
use nssa::AccountId;
|
|
|
|
|
use wallet::program_facades::native_token_transfer::NativeTokenTransfer;
|
|
|
|
|
|
2026-01-26 10:45:51 +01:00
|
|
|
use crate::{
|
|
|
|
|
block_on,
|
|
|
|
|
error::{print_error, WalletFfiError},
|
|
|
|
|
types::{FfiBytes32, FfiTransferResult, WalletHandle},
|
|
|
|
|
wallet::get_wallet,
|
2026-02-11 17:45:35 -03:00
|
|
|
FfiPrivateAccountKeys,
|
2026-01-26 10:45:51 +01:00
|
|
|
};
|
2026-01-21 15:22:38 +01:00
|
|
|
|
|
|
|
|
/// Send a public token transfer.
|
|
|
|
|
///
|
|
|
|
|
/// Transfers tokens from one public account to another on the network.
|
|
|
|
|
///
|
|
|
|
|
/// # Parameters
|
|
|
|
|
/// - `handle`: Valid wallet handle
|
|
|
|
|
/// - `from`: Source account ID (must be owned by this wallet)
|
|
|
|
|
/// - `to`: Destination account ID
|
2026-01-26 10:15:23 +01:00
|
|
|
/// - `amount`: Amount to transfer as little-endian [u8; 16]
|
2026-01-21 15:22:38 +01:00
|
|
|
/// - `out_result`: Output pointer for transfer result
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// - `Success` if the transfer was submitted successfully
|
|
|
|
|
/// - `InsufficientFunds` if the source account doesn't have enough balance
|
|
|
|
|
/// - `KeyNotFound` if the source account's signing key is not in this wallet
|
|
|
|
|
/// - Error code on other failures
|
|
|
|
|
///
|
|
|
|
|
/// # Memory
|
|
|
|
|
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
|
2026-01-26 10:29:37 +01:00
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
|
|
|
/// - `from` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `to` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `amount` must be a valid pointer to a `[u8; 16]` array
|
|
|
|
|
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
|
2026-01-21 15:22:38 +01:00
|
|
|
#[no_mangle]
|
2026-01-26 10:29:37 +01:00
|
|
|
pub unsafe extern "C" fn wallet_ffi_transfer_public(
|
2026-01-21 15:22:38 +01:00
|
|
|
handle: *mut WalletHandle,
|
|
|
|
|
from: *const FfiBytes32,
|
|
|
|
|
to: *const FfiBytes32,
|
2026-01-26 10:15:23 +01:00
|
|
|
amount: *const [u8; 16],
|
2026-01-21 15:22:38 +01:00
|
|
|
out_result: *mut FfiTransferResult,
|
|
|
|
|
) -> WalletFfiError {
|
|
|
|
|
let wrapper = match get_wallet(handle) {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => return e,
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-26 10:15:23 +01:00
|
|
|
if from.is_null() || to.is_null() || amount.is_null() || out_result.is_null() {
|
2026-01-26 10:29:37 +01:00
|
|
|
print_error("Null pointer argument");
|
2026-01-21 15:22:38 +01:00
|
|
|
return WalletFfiError::NullPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => {
|
2026-01-26 10:29:37 +01:00
|
|
|
print_error(format!("Failed to lock wallet: {}", e));
|
2026-01-21 15:22:38 +01:00
|
|
|
return WalletFfiError::InternalError;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let from_id = AccountId::new(unsafe { (*from).data });
|
|
|
|
|
let to_id = AccountId::new(unsafe { (*to).data });
|
2026-01-26 10:15:23 +01:00
|
|
|
let amount = u128::from_le_bytes(unsafe { *amount });
|
2026-01-21 15:22:38 +01:00
|
|
|
|
|
|
|
|
let transfer = NativeTokenTransfer(&wallet);
|
|
|
|
|
|
|
|
|
|
match block_on(transfer.send_public_transfer(from_id, to_id, amount)) {
|
|
|
|
|
Ok(Ok(response)) => {
|
2026-01-29 22:20:42 +03:00
|
|
|
let tx_hash = CString::new(response.tx_hash.to_string())
|
2026-01-21 15:22:38 +01:00
|
|
|
.map(|s| s.into_raw())
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = tx_hash;
|
|
|
|
|
(*out_result).success = true;
|
|
|
|
|
}
|
|
|
|
|
WalletFfiError::Success
|
|
|
|
|
}
|
|
|
|
|
Ok(Err(e)) => {
|
2026-01-26 10:29:37 +01:00
|
|
|
print_error(format!("Transfer failed: {:?}", e));
|
2026-01-21 15:22:38 +01:00
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = ptr::null_mut();
|
|
|
|
|
(*out_result).success = false;
|
|
|
|
|
}
|
2026-02-12 12:38:34 -03:00
|
|
|
map_execution_error(e)
|
2026-01-21 15:22:38 +01:00
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-11 18:06:20 -03:00
|
|
|
/// Send a shielded token transfer.
|
2026-02-11 17:45:35 -03:00
|
|
|
///
|
2026-02-11 18:34:45 -03:00
|
|
|
/// Transfers tokens from a public account to a private account.
|
2026-02-11 17:45:35 -03:00
|
|
|
///
|
|
|
|
|
/// # Parameters
|
|
|
|
|
/// - `handle`: Valid wallet handle
|
|
|
|
|
/// - `from`: Source account ID (must be owned by this wallet)
|
|
|
|
|
/// - `to_keys`: Destination account keys
|
|
|
|
|
/// - `amount`: Amount to transfer as little-endian [u8; 16]
|
|
|
|
|
/// - `out_result`: Output pointer for transfer result
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// - `Success` if the transfer was submitted successfully
|
|
|
|
|
/// - `InsufficientFunds` if the source account doesn't have enough balance
|
|
|
|
|
/// - `KeyNotFound` if the source account's signing key is not in this wallet
|
|
|
|
|
/// - Error code on other failures
|
|
|
|
|
///
|
|
|
|
|
/// # Memory
|
|
|
|
|
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
|
|
|
/// - `from` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `to_keys` must be a valid pointer to a `FfiPrivateAccountKeys` struct
|
|
|
|
|
/// - `amount` must be a valid pointer to a `[u8; 16]` array
|
|
|
|
|
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
|
|
|
|
|
handle: *mut WalletHandle,
|
|
|
|
|
from: *const FfiBytes32,
|
|
|
|
|
to_keys: *const FfiPrivateAccountKeys,
|
|
|
|
|
amount: *const [u8; 16],
|
|
|
|
|
out_result: *mut FfiTransferResult,
|
|
|
|
|
) -> WalletFfiError {
|
|
|
|
|
let wrapper = match get_wallet(handle) {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => return e,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if from.is_null() || to_keys.is_null() || amount.is_null() || out_result.is_null() {
|
|
|
|
|
print_error("Null pointer argument");
|
|
|
|
|
return WalletFfiError::NullPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
print_error(format!("Failed to lock wallet: {}", e));
|
|
|
|
|
return WalletFfiError::InternalError;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let from_id = AccountId::new(unsafe { (*from).data });
|
|
|
|
|
let to_npk = (*to_keys).npk();
|
2026-02-16 20:39:00 -05:00
|
|
|
let to_vpk = match (*to_keys).vpk() {
|
|
|
|
|
Ok(vpk) => vpk,
|
2026-02-11 17:45:35 -03:00
|
|
|
Err(e) => {
|
|
|
|
|
print_error("Invalid viewing key");
|
|
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let amount = u128::from_le_bytes(unsafe { *amount });
|
|
|
|
|
|
|
|
|
|
let transfer = NativeTokenTransfer(&wallet);
|
|
|
|
|
|
|
|
|
|
match block_on(
|
2026-02-16 20:39:00 -05:00
|
|
|
transfer.send_shielded_transfer_to_outer_account(from_id, to_npk, to_vpk, amount),
|
2026-02-11 17:45:35 -03:00
|
|
|
) {
|
|
|
|
|
Ok(Ok((response, _shared_key))) => {
|
|
|
|
|
let tx_hash = CString::new(response.tx_hash)
|
|
|
|
|
.map(|s| s.into_raw())
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = tx_hash;
|
|
|
|
|
(*out_result).success = true;
|
|
|
|
|
}
|
|
|
|
|
WalletFfiError::Success
|
|
|
|
|
}
|
|
|
|
|
Ok(Err(e)) => {
|
|
|
|
|
print_error(format!("Transfer failed: {:?}", e));
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = ptr::null_mut();
|
|
|
|
|
(*out_result).success = false;
|
|
|
|
|
}
|
2026-02-12 12:38:34 -03:00
|
|
|
map_execution_error(e)
|
2026-02-11 18:06:20 -03:00
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Send a deshielded token transfer.
|
|
|
|
|
///
|
2026-02-11 18:34:45 -03:00
|
|
|
/// Transfers tokens from a private account to a public account.
|
2026-02-11 18:06:20 -03:00
|
|
|
///
|
|
|
|
|
/// # Parameters
|
|
|
|
|
/// - `handle`: Valid wallet handle
|
|
|
|
|
/// - `from`: Source account ID (must be owned by this wallet)
|
|
|
|
|
/// - `to`: Destination account ID
|
|
|
|
|
/// - `amount`: Amount to transfer as little-endian [u8; 16]
|
|
|
|
|
/// - `out_result`: Output pointer for transfer result
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// - `Success` if the transfer was submitted successfully
|
|
|
|
|
/// - `InsufficientFunds` if the source account doesn't have enough balance
|
|
|
|
|
/// - `KeyNotFound` if the source account's signing key is not in this wallet
|
|
|
|
|
/// - Error code on other failures
|
|
|
|
|
///
|
|
|
|
|
/// # Memory
|
|
|
|
|
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
|
|
|
/// - `from` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `to` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `amount` must be a valid pointer to a `[u8; 16]` array
|
|
|
|
|
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn wallet_ffi_transfer_deshielded(
|
|
|
|
|
handle: *mut WalletHandle,
|
|
|
|
|
from: *const FfiBytes32,
|
|
|
|
|
to: *const FfiBytes32,
|
|
|
|
|
amount: *const [u8; 16],
|
|
|
|
|
out_result: *mut FfiTransferResult,
|
|
|
|
|
) -> WalletFfiError {
|
|
|
|
|
let wrapper = match get_wallet(handle) {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => return e,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if from.is_null() || to.is_null() || amount.is_null() || out_result.is_null() {
|
|
|
|
|
print_error("Null pointer argument");
|
|
|
|
|
return WalletFfiError::NullPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
print_error(format!("Failed to lock wallet: {}", e));
|
|
|
|
|
return WalletFfiError::InternalError;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let from_id = AccountId::new(unsafe { (*from).data });
|
|
|
|
|
let to_id = AccountId::new(unsafe { (*to).data });
|
|
|
|
|
let amount = u128::from_le_bytes(unsafe { *amount });
|
|
|
|
|
|
|
|
|
|
let transfer = NativeTokenTransfer(&wallet);
|
|
|
|
|
|
|
|
|
|
match block_on(transfer.send_deshielded_transfer(from_id, to_id, amount)) {
|
|
|
|
|
Ok(Ok((response, _shared_key))) => {
|
|
|
|
|
let tx_hash = CString::new(response.tx_hash)
|
|
|
|
|
.map(|s| s.into_raw())
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = tx_hash;
|
|
|
|
|
(*out_result).success = true;
|
|
|
|
|
}
|
|
|
|
|
WalletFfiError::Success
|
|
|
|
|
}
|
|
|
|
|
Ok(Err(e)) => {
|
|
|
|
|
print_error(format!("Transfer failed: {:?}", e));
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = ptr::null_mut();
|
|
|
|
|
(*out_result).success = false;
|
|
|
|
|
}
|
2026-02-12 12:38:34 -03:00
|
|
|
map_execution_error(e)
|
2026-02-11 18:34:45 -03:00
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Send a private token transfer.
|
|
|
|
|
///
|
|
|
|
|
/// Transfers tokens from a private account to another private account.
|
|
|
|
|
///
|
|
|
|
|
/// # Parameters
|
|
|
|
|
/// - `handle`: Valid wallet handle
|
|
|
|
|
/// - `from`: Source account ID (must be owned by this wallet)
|
|
|
|
|
/// - `to_keys`: Destination account keys
|
|
|
|
|
/// - `amount`: Amount to transfer as little-endian [u8; 16]
|
|
|
|
|
/// - `out_result`: Output pointer for transfer result
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// - `Success` if the transfer was submitted successfully
|
|
|
|
|
/// - `InsufficientFunds` if the source account doesn't have enough balance
|
|
|
|
|
/// - `KeyNotFound` if the source account's signing key is not in this wallet
|
|
|
|
|
/// - Error code on other failures
|
|
|
|
|
///
|
|
|
|
|
/// # Memory
|
|
|
|
|
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
|
|
|
/// - `from` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `to_keys` must be a valid pointer to a `FfiPrivateAccountKeys` struct
|
|
|
|
|
/// - `amount` must be a valid pointer to a `[u8; 16]` array
|
|
|
|
|
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn wallet_ffi_transfer_private(
|
|
|
|
|
handle: *mut WalletHandle,
|
|
|
|
|
from: *const FfiBytes32,
|
|
|
|
|
to_keys: *const FfiPrivateAccountKeys,
|
|
|
|
|
amount: *const [u8; 16],
|
|
|
|
|
out_result: *mut FfiTransferResult,
|
|
|
|
|
) -> WalletFfiError {
|
|
|
|
|
let wrapper = match get_wallet(handle) {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => return e,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if from.is_null() || to_keys.is_null() || amount.is_null() || out_result.is_null() {
|
|
|
|
|
print_error("Null pointer argument");
|
|
|
|
|
return WalletFfiError::NullPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
print_error(format!("Failed to lock wallet: {}", e));
|
|
|
|
|
return WalletFfiError::InternalError;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let from_id = AccountId::new(unsafe { (*from).data });
|
|
|
|
|
let to_npk = (*to_keys).npk();
|
2026-02-16 20:39:00 -05:00
|
|
|
let to_vpk = match (*to_keys).vpk() {
|
|
|
|
|
Ok(vpk) => vpk,
|
2026-02-11 18:34:45 -03:00
|
|
|
Err(e) => {
|
|
|
|
|
print_error("Invalid viewing key");
|
|
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let amount = u128::from_le_bytes(unsafe { *amount });
|
|
|
|
|
|
|
|
|
|
let transfer = NativeTokenTransfer(&wallet);
|
|
|
|
|
|
2026-02-16 20:39:00 -05:00
|
|
|
match block_on(transfer.send_private_transfer_to_outer_account(from_id, to_npk, to_vpk, amount))
|
2026-02-11 18:34:45 -03:00
|
|
|
{
|
|
|
|
|
Ok(Ok((response, _shared_key))) => {
|
|
|
|
|
let tx_hash = CString::new(response.tx_hash)
|
|
|
|
|
.map(|s| s.into_raw())
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = tx_hash;
|
|
|
|
|
(*out_result).success = true;
|
|
|
|
|
}
|
|
|
|
|
WalletFfiError::Success
|
|
|
|
|
}
|
|
|
|
|
Ok(Err(e)) => {
|
|
|
|
|
print_error(format!("Transfer failed: {:?}", e));
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = ptr::null_mut();
|
|
|
|
|
(*out_result).success = false;
|
|
|
|
|
}
|
2026-02-12 12:38:34 -03:00
|
|
|
map_execution_error(e)
|
2026-02-11 17:45:35 -03:00
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 15:22:38 +01:00
|
|
|
/// Register a public account on the network.
|
|
|
|
|
///
|
|
|
|
|
/// This initializes a public account on the blockchain. The account must be
|
|
|
|
|
/// owned by this wallet.
|
|
|
|
|
///
|
|
|
|
|
/// # Parameters
|
|
|
|
|
/// - `handle`: Valid wallet handle
|
|
|
|
|
/// - `account_id`: Account ID to register
|
|
|
|
|
/// - `out_result`: Output pointer for registration result
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// - `Success` if the registration was submitted successfully
|
|
|
|
|
/// - Error code on failure
|
|
|
|
|
///
|
|
|
|
|
/// # Memory
|
|
|
|
|
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
|
2026-01-26 10:29:37 +01:00
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
|
|
|
/// - `account_id` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
|
2026-01-21 15:22:38 +01:00
|
|
|
#[no_mangle]
|
2026-01-26 10:29:37 +01:00
|
|
|
pub unsafe extern "C" fn wallet_ffi_register_public_account(
|
2026-01-21 15:22:38 +01:00
|
|
|
handle: *mut WalletHandle,
|
|
|
|
|
account_id: *const FfiBytes32,
|
|
|
|
|
out_result: *mut FfiTransferResult,
|
|
|
|
|
) -> WalletFfiError {
|
|
|
|
|
let wrapper = match get_wallet(handle) {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => return e,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if account_id.is_null() || out_result.is_null() {
|
2026-01-26 10:29:37 +01:00
|
|
|
print_error("Null pointer argument");
|
2026-01-21 15:22:38 +01:00
|
|
|
return WalletFfiError::NullPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => {
|
2026-01-26 10:29:37 +01:00
|
|
|
print_error(format!("Failed to lock wallet: {}", e));
|
2026-01-21 15:22:38 +01:00
|
|
|
return WalletFfiError::InternalError;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let account_id = AccountId::new(unsafe { (*account_id).data });
|
|
|
|
|
|
|
|
|
|
let transfer = NativeTokenTransfer(&wallet);
|
|
|
|
|
|
|
|
|
|
match block_on(transfer.register_account(account_id)) {
|
|
|
|
|
Ok(Ok(response)) => {
|
2026-01-29 22:20:42 +03:00
|
|
|
let tx_hash = CString::new(response.tx_hash.to_string())
|
2026-01-21 15:22:38 +01:00
|
|
|
.map(|s| s.into_raw())
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = tx_hash;
|
|
|
|
|
(*out_result).success = true;
|
|
|
|
|
}
|
|
|
|
|
WalletFfiError::Success
|
|
|
|
|
}
|
|
|
|
|
Ok(Err(e)) => {
|
2026-01-26 10:29:37 +01:00
|
|
|
print_error(format!("Registration failed: {:?}", e));
|
2026-01-21 15:22:38 +01:00
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = ptr::null_mut();
|
2026-02-11 16:28:24 -03:00
|
|
|
(*out_result).success = false;
|
|
|
|
|
}
|
2026-02-12 12:38:34 -03:00
|
|
|
map_execution_error(e)
|
2026-02-11 16:28:24 -03:00
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Register a private account on the network.
|
|
|
|
|
///
|
|
|
|
|
/// This initializes a private account. The account must be
|
|
|
|
|
/// owned by this wallet.
|
|
|
|
|
///
|
|
|
|
|
/// # Parameters
|
|
|
|
|
/// - `handle`: Valid wallet handle
|
|
|
|
|
/// - `account_id`: Account ID to register
|
|
|
|
|
/// - `out_result`: Output pointer for registration result
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// - `Success` if the registration was submitted successfully
|
|
|
|
|
/// - Error code on failure
|
|
|
|
|
///
|
|
|
|
|
/// # Memory
|
|
|
|
|
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
|
|
|
|
/// - `account_id` must be a valid pointer to a `FfiBytes32` struct
|
|
|
|
|
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn wallet_ffi_register_private_account(
|
|
|
|
|
handle: *mut WalletHandle,
|
|
|
|
|
account_id: *const FfiBytes32,
|
|
|
|
|
out_result: *mut FfiTransferResult,
|
|
|
|
|
) -> WalletFfiError {
|
|
|
|
|
let wrapper = match get_wallet(handle) {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => return e,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if account_id.is_null() || out_result.is_null() {
|
|
|
|
|
print_error("Null pointer argument");
|
|
|
|
|
return WalletFfiError::NullPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let wallet = match wrapper.core.lock() {
|
|
|
|
|
Ok(w) => w,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
print_error(format!("Failed to lock wallet: {}", e));
|
|
|
|
|
return WalletFfiError::InternalError;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let account_id = AccountId::new(unsafe { (*account_id).data });
|
|
|
|
|
|
|
|
|
|
let transfer = NativeTokenTransfer(&wallet);
|
|
|
|
|
|
|
|
|
|
match block_on(transfer.register_account_private(account_id)) {
|
|
|
|
|
Ok(Ok((res, _secret))) => {
|
|
|
|
|
let tx_hash = CString::new(res.tx_hash)
|
|
|
|
|
.map(|s| s.into_raw())
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = tx_hash;
|
|
|
|
|
(*out_result).success = true;
|
|
|
|
|
}
|
|
|
|
|
WalletFfiError::Success
|
|
|
|
|
}
|
|
|
|
|
Ok(Err(e)) => {
|
|
|
|
|
print_error(format!("Registration failed: {:?}", e));
|
|
|
|
|
unsafe {
|
|
|
|
|
(*out_result).tx_hash = ptr::null_mut();
|
2026-01-21 15:22:38 +01:00
|
|
|
(*out_result).success = false;
|
|
|
|
|
}
|
2026-02-12 12:38:34 -03:00
|
|
|
map_execution_error(e)
|
2026-01-21 15:22:38 +01:00
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Free a transfer result returned by `wallet_ffi_transfer_public` or
|
|
|
|
|
/// `wallet_ffi_register_public_account`.
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// The result must be either null or a valid result from a transfer function.
|
|
|
|
|
#[no_mangle]
|
2026-01-26 10:29:37 +01:00
|
|
|
pub unsafe extern "C" fn wallet_ffi_free_transfer_result(result: *mut FfiTransferResult) {
|
2026-01-21 15:22:38 +01:00
|
|
|
if result.is_null() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
let result = &*result;
|
|
|
|
|
if !result.tx_hash.is_null() {
|
|
|
|
|
drop(CString::from_raw(result.tx_hash));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-12 12:38:34 -03:00
|
|
|
|
|
|
|
|
fn map_execution_error(e: ExecutionFailureKind) -> WalletFfiError {
|
|
|
|
|
match e {
|
|
|
|
|
ExecutionFailureKind::InsufficientFundsError => WalletFfiError::InsufficientFunds,
|
|
|
|
|
ExecutionFailureKind::KeyNotFoundError => WalletFfiError::KeyNotFound,
|
|
|
|
|
ExecutionFailureKind::SequencerError => WalletFfiError::NetworkError,
|
|
|
|
|
ExecutionFailureKind::SequencerClientError(_) => WalletFfiError::NetworkError,
|
|
|
|
|
_ => WalletFfiError::InternalError,
|
|
|
|
|
}
|
|
|
|
|
}
|