Skip to content

feat: estimate transaction size in the FFI crate #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/algokit_transact/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub use constants::{
ALGORAND_PUBLIC_KEY_BYTE_LENGTH, HASH_BYTES_LENGTH,
};
pub use error::AlgoKitTransactError;
pub use traits::{AlgorandMsgpack, TransactionId};
pub use traits::{AlgorandMsgpack, EstimateTransactionSize, TransactionId};
pub use transactions::{
AssetTransferTransactionBuilder, AssetTransferTransactionFields, PaymentTransactionBuilder,
PaymentTransactionFields, SignedTransaction, Transaction, TransactionHeader,
Expand Down
8 changes: 6 additions & 2 deletions crates/algokit_transact/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub trait AlgorandMsgpack: Serialize + for<'de> Deserialize<'de> {
/// * `bytes` - The MessagePack encoded bytes
///
/// # Returns
/// The decoded instance or an AlgoKitTransactError if the input is empty or
/// The decoded instance or an AlgoKitTransactError if the input is empty or
/// deserialization fails.
fn decode(bytes: &[u8]) -> Result<Self, AlgoKitTransactError> {
if bytes.is_empty() {
Expand All @@ -84,7 +84,7 @@ pub trait AlgorandMsgpack: Serialize + for<'de> Deserialize<'de> {
///
/// This method performs canonical encoding and prepends the domain separation
/// prefix defined by the PREFIX constant.
///
///
/// Use `encode_raw()` if you want to encode without the prefix.
///
/// # Returns
Expand Down Expand Up @@ -140,3 +140,7 @@ pub trait TransactionId: AlgorandMsgpack {
))
}
}

pub trait EstimateTransactionSize: AlgorandMsgpack {
fn estimate_size(&self) -> Result<usize, AlgoKitTransactError>;
}
21 changes: 20 additions & 1 deletion crates/algokit_transact/src/transactions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub use payment::{PaymentTransactionBuilder, PaymentTransactionFields};

use crate::constants::HASH_BYTES_LENGTH;
use crate::error::AlgoKitTransactError;
use crate::traits::{AlgorandMsgpack, TransactionId};
use crate::traits::{AlgorandMsgpack, EstimateTransactionSize, TransactionId};
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, Bytes};
use std::any::Any;
Expand Down Expand Up @@ -59,6 +59,19 @@ impl AssetTransferTransactionBuilder {
impl AlgorandMsgpack for Transaction {}
impl TransactionId for Transaction {}

impl EstimateTransactionSize for Transaction {
fn estimate_size(&self) -> Result<usize, AlgoKitTransactError> {
// We are simulating a signature on this transaction in order to encode it, and get the size
// with which we will be able to estimate the fee.
let signed_tx = SignedTransaction {
transaction: self.clone(),
// Avoiding a zero signature just in case it's compressed or removed in the encoding.
signature: [1; 64],
};
return Ok(signed_tx.encode()?.len());
}
}

/// A signed transaction.
#[serde_as]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
Expand Down Expand Up @@ -127,3 +140,9 @@ impl TransactionId for SignedTransaction {
self.transaction.raw_id()
}
}

impl EstimateTransactionSize for SignedTransaction {
fn estimate_size(&self) -> Result<usize, AlgoKitTransactError> {
return Ok(self.encode()?.len());
}
}
21 changes: 20 additions & 1 deletion crates/algokit_transact_ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use algokit_transact::{AlgorandMsgpack, Byte32, TransactionId};
use algokit_transact::{AlgorandMsgpack, Byte32, EstimateTransactionSize, TransactionId};
use ffi_macros::{ffi_func, ffi_record};
use serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;
Expand Down Expand Up @@ -406,6 +406,25 @@ pub fn attach_signature(
Ok(signed_tx.encode()?)
}

#[ffi_func]
/// Return the size of the transaction in bytes as if it was already signed and encoded.
/// This is useful for estimating the fee for the transaction.
pub fn estimate_transaction_size(transaction: &Transaction) -> Result<u64, AlgoKitTransactError> {
let core_tx: algokit_transact::Transaction = transaction.clone().try_into()?;
return core_tx
.estimate_size()
.map_err(|e| {
AlgoKitTransactError::EncodingError(format!(
"Failed to estimate transaction size: {}",
e
))
})?
.try_into()
.map_err(|_| {
AlgoKitTransactError::EncodingError("Failed to convert size to u64".to_string())
});
}

#[ffi_func]
pub fn address_from_pub_key(pub_key: &[u8]) -> Result<Address, AlgoKitTransactError> {
Ok(
Expand Down
Loading