feat: add withdraw ffi

This commit is contained in:
Sergio Chouhy 2026-06-17 23:32:04 -03:00
parent 62d9ba10f8
commit 32c6b9e5fc
6 changed files with 209 additions and 17 deletions

View File

@ -60,7 +60,7 @@ allow-git = [
"https://github.com/logos-blockchain/logos-blockchain.git",
"https://github.com/logos-blockchain/logos-blockchain-circuits.git",
"https://github.com/logos-blockchain/logos-blockchain-rust-rapidsnark.git",
"https://github.com/arkworks-rs/spongefish.git"
"https://github.com/arkworks-rs/spongefish.git",
]
unknown-git = "deny"
unknown-registry = "deny"

4
Cargo.lock generated
View File

@ -975,9 +975,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitcoin_hashes"
version = "0.14.100"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f"
checksum = "4ed83caece3afc59919481b33b472e1432d1abc4641ed9100be142ef5110b406"
dependencies = [
"hex-conservative",
]

View File

@ -165,6 +165,14 @@ unsafe extern "C" {
fn wallet_ffi_free_transfer_result(result: *mut FfiTransferResult);
fn wallet_ffi_bridge_withdraw(
handle: *mut WalletHandle,
from: *const FfiBytes32,
amount: u64,
bedrock_account_pk: *const FfiBytes32,
out_result: *mut FfiTransferResult,
) -> error::WalletFfiError;
fn wallet_ffi_register_public_account(
handle: *mut WalletHandle,
account_id: *const FfiBytes32,
@ -1110,6 +1118,66 @@ fn test_wallet_ffi_transfer_private() -> Result<()> {
Ok(())
}
#[test]
fn test_wallet_ffi_bridge_withdraw() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
let from: FfiBytes32 = ctx.ctx().existing_public_accounts()[0].into();
let bridge_account: FfiBytes32 = lee::system_bridge_account_id().into();
let bedrock_account_pk = FfiBytes32::from_bytes([0x42; 32]);
let amount = 100_u64;
let mut transfer_result = FfiTransferResult::default();
unsafe {
wallet_ffi_bridge_withdraw(
wallet_ffi_handle,
&raw const from,
amount,
&raw const bedrock_account_pk,
&raw mut transfer_result,
)
.unwrap();
}
info!("Waiting for next block creation");
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
let from_balance = unsafe {
let mut out_balance: [u8; 16] = [0; 16];
wallet_ffi_get_balance(
wallet_ffi_handle,
&raw const from,
true,
&raw mut out_balance,
)
.unwrap();
u128::from_le_bytes(out_balance)
};
let bridge_balance = unsafe {
let mut out_balance: [u8; 16] = [0; 16];
wallet_ffi_get_balance(
wallet_ffi_handle,
&raw const bridge_account,
true,
&raw mut out_balance,
)
.unwrap();
u128::from_le_bytes(out_balance)
};
assert_eq!(from_balance, 9900);
assert_eq!(bridge_balance, 1_000_100);
unsafe {
wallet_ffi_free_transfer_result(&raw mut transfer_result);
wallet_ffi_destroy(wallet_ffi_handle);
}
Ok(())
}
#[test]
fn test_wallet_ffi_transfer_generic_public() -> Result<()> {
let ctx = BlockingTestContext::new()?;

View File

@ -0,0 +1,91 @@
//! Bridge program functions (deposit/withdraw between L1 Bedrock and L2).
use std::{ffi::CString, ptr};
use lee::AccountId;
use wallet::program_facades::bridge::Bridge;
use crate::{
block_on,
error::{print_error, WalletFfiError},
map_execution_error,
types::{FfiBytes32, FfiTransferResult, WalletHandle},
wallet::get_wallet,
};
/// Withdraw native tokens from a public account to Bedrock (L1) through the bridge.
///
/// # Parameters
/// - `handle`: Valid wallet handle
/// - `from`: Source public account ID (must be owned by this wallet). Bridge withdrawals only
/// support public sender accounts.
/// - `amount`: Amount of native tokens to withdraw
/// - `bedrock_account_pk`: Recipient's Bedrock (L1) public key, 32 bytes
/// - `out_result`: Output pointer for the withdraw result
///
/// # Returns
/// - `Success` if the withdraw transaction 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
/// - `bedrock_account_pk` 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_bridge_withdraw(
handle: *mut WalletHandle,
from: *const FfiBytes32,
amount: u64,
bedrock_account_pk: *const FfiBytes32,
out_result: *mut FfiTransferResult,
) -> WalletFfiError {
let wrapper = match get_wallet(handle) {
Ok(w) => w,
Err(e) => return e,
};
if from.is_null() || bedrock_account_pk.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 bedrock_account_pk = unsafe { (*bedrock_account_pk).data };
let bridge = Bridge(&wallet);
match block_on(bridge.send_withdraw(from_id, amount, bedrock_account_pk)) {
Ok(tx_hash) => {
let tx_hash = CString::new(tx_hash.to_string())
.map_or(ptr::null_mut(), std::ffi::CString::into_raw);
unsafe {
(*out_result).tx_hash = tx_hash;
(*out_result).success = true;
}
WalletFfiError::Success
}
Err(e) => {
print_error(format!("Bridge withdraw failed: {e:?}"));
unsafe {
(*out_result).tx_hash = ptr::null_mut();
(*out_result).success = false;
}
map_execution_error(e)
}
}
}

View File

@ -42,6 +42,7 @@ pub use types::*;
use crate::error::print_error;
pub mod account;
pub mod bridge;
pub mod error;
pub mod generic_transaction;
pub mod keys;

View File

@ -219,6 +219,20 @@ typedef struct FfiAccount {
struct FfiU128 nonce;
} FfiAccount;
/**
* Result of a transfer operation.
*/
typedef struct FfiTransferResult {
/**
* Transaction hash (null-terminated string, or null on failure).
*/
char *tx_hash;
/**
* Whether the transfer succeeded.
*/
bool success;
} FfiTransferResult;
typedef struct FfiInstructionWords {
uint32_t *instruction_words;
uintptr_t instruction_words_size;
@ -285,20 +299,6 @@ typedef struct FfiPublicAccountKey {
struct FfiBytes32 public_key;
} FfiPublicAccountKey;
/**
* Result of a transfer operation.
*/
typedef struct FfiTransferResult {
/**
* Transaction hash (null-terminated string, or null on failure).
*/
char *tx_hash;
/**
* Whether the transfer succeeded.
*/
bool success;
} FfiTransferResult;
/**
* Create a new public account.
*
@ -532,6 +532,38 @@ enum WalletFfiError wallet_ffi_import_private_account(struct WalletHandle *handl
const struct FfiU128 *identifier,
const char *account_state_json);
/**
* Withdraw native tokens from a public account to Bedrock (L1) through the bridge.
*
* # Parameters
* - `handle`: Valid wallet handle
* - `from`: Source public account ID (must be owned by this wallet). Bridge withdrawals only
* support public sender accounts.
* - `amount`: Amount of native tokens to withdraw
* - `bedrock_account_pk`: Recipient's Bedrock (L1) public key, 32 bytes
* - `out_result`: Output pointer for the withdraw result
*
* # Returns
* - `Success` if the withdraw transaction 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
* - `bedrock_account_pk` must be a valid pointer to a `FfiBytes32` struct
* - `out_result` must be a valid pointer to a `FfiTransferResult` struct
*/
enum WalletFfiError wallet_ffi_bridge_withdraw(struct WalletHandle *handle,
const struct FfiBytes32 *from,
uint64_t amount,
const struct FfiBytes32 *bedrock_account_pk,
struct FfiTransferResult *out_result);
/**
* Serialize sequence of bytes into RISC0 readable words.
*