Skip to content
Merged
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
61 changes: 27 additions & 34 deletions crates/algokit_transact/src/transactions/asset_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,14 +479,7 @@ impl Validate for AssetConfigTransactionFields {
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::TransactionHeaderMother;

fn create_test_address() -> Address {
// Use a valid Algorand address for testing
"JB3K6HTAXODO4THESLNYTSG6GQUFNEVIQG7A6ZYVDACR6WA3ZF52TKU5NA"
.parse()
.unwrap()
}
use crate::test_utils::{AccountMother, TransactionHeaderMother};

#[test]
fn test_validate_asset_creation_multiple_errors() {
Expand All @@ -503,7 +496,7 @@ mod tests {
unit_name: Some("VERYLONGUNITNAME".to_string()), // More than 8 bytes - ERROR 4
url: Some(long_url), // More than 96 bytes - ERROR 5
metadata_hash: None,
manager: Some(create_test_address()),
manager: Some(AccountMother::neil().address()),
reserve: None,
freeze: None,
clawback: None,
Expand Down Expand Up @@ -535,10 +528,10 @@ mod tests {
unit_name: Some("TA".to_string()),
url: Some("https://example.com".to_string()),
metadata_hash: Some([1; 32]),
manager: Some(create_test_address()),
reserve: Some(create_test_address()),
freeze: Some(create_test_address()),
clawback: Some(create_test_address()),
manager: Some(AccountMother::neil().address()),
reserve: Some(AccountMother::neil().address()),
freeze: Some(AccountMother::neil().address()),
clawback: Some(AccountMother::neil().address()),
};

let result = asset_config.validate();
Expand All @@ -551,18 +544,18 @@ mod tests {
fn test_validate_valid_asset_reconfigure() {
let asset_config = AssetConfigTransactionFields {
header: TransactionHeaderMother::example().build().unwrap(),
asset_id: 123, // Existing asset
total: None, // Can't modify
decimals: None, // Can't modify
default_frozen: None, // Can't modify
asset_name: None, // Can't modify
unit_name: None, // Can't modify
url: None, // Can't modify
metadata_hash: None, // Can't modify
manager: Some(create_test_address()), // Can modify
reserve: Some(create_test_address()), // Can modify
freeze: Some(create_test_address()), // Can modify
clawback: Some(create_test_address()), // Can modify
asset_id: 123, // Existing asset
total: None, // Can't modify
decimals: None, // Can't modify
default_frozen: None, // Can't modify
asset_name: None, // Can't modify
unit_name: None, // Can't modify
url: None, // Can't modify
metadata_hash: None, // Can't modify
manager: Some(AccountMother::neil().address()), // Can modify
reserve: Some(AccountMother::neil().address()), // Can modify
freeze: Some(AccountMother::neil().address()), // Can modify
clawback: Some(AccountMother::neil().address()), // Can modify
};

let result = asset_config.validate();
Expand Down Expand Up @@ -595,15 +588,15 @@ mod tests {
fn test_validate_asset_reconfigure_multiple_immutable_field_errors() {
let asset_config = AssetConfigTransactionFields {
header: TransactionHeaderMother::example().build().unwrap(),
asset_id: 123, // Existing asset
total: Some(2000), // Can't modify total - ERROR 1
decimals: Some(3), // Can't modify decimals - ERROR 2
default_frozen: Some(true), // Can't modify default_frozen - ERROR 3
asset_name: Some("NewName".to_string()), // Can't modify asset_name - ERROR 4
unit_name: Some("NEW".to_string()), // Can't modify unit_name - ERROR 5
url: Some("https://newurl.com".to_string()), // Can't modify url - ERROR 6
metadata_hash: Some([2; 32]), // Can't modify metadata_hash - ERROR 7
manager: Some(create_test_address()), // This is allowed
asset_id: 123, // Existing asset
total: Some(2000), // Can't modify total - ERROR 1
decimals: Some(3), // Can't modify decimals - ERROR 2
default_frozen: Some(true), // Can't modify default_frozen - ERROR 3
asset_name: Some("NewName".to_string()), // Can't modify asset_name - ERROR 4
unit_name: Some("NEW".to_string()), // Can't modify unit_name - ERROR 5
url: Some("https://newurl.com".to_string()), // Can't modify url - ERROR 6
metadata_hash: Some([2; 32]), // Can't modify metadata_hash - ERROR 7
manager: Some(AccountMother::neil().address()), // This is allowed
reserve: None,
freeze: None,
clawback: None,
Expand Down
87 changes: 86 additions & 1 deletion crates/algokit_transact/src/transactions/asset_freeze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use crate::Transaction;
use crate::address::Address;
use crate::traits::Validate;
use crate::transactions::common::TransactionHeader;
use crate::utils::{is_zero, is_zero_addr};
use derive_builder::Builder;
Expand Down Expand Up @@ -53,6 +54,90 @@ pub struct AssetFreezeTransactionFields {

impl AssetFreezeTransactionBuilder {
pub fn build(&self) -> Result<Transaction, AssetFreezeTransactionBuilderError> {
self.build_fields().map(Transaction::AssetFreeze)
let d = self.build_fields()?;
d.validate().map_err(|errors| {
AssetFreezeTransactionBuilderError::ValidationError(format!(
"Asset freeze validation failed: {}",
errors.join("\n")
))
})?;
Ok(Transaction::AssetFreeze(d))
}
}

impl Validate for AssetFreezeTransactionFields {
fn validate(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();

if self.asset_id == 0 {
errors.push("Asset ID must not be 0".to_string());
}

match errors.is_empty() {
true => Ok(()),
false => Err(errors),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{AccountMother, TransactionHeaderMother};

#[test]
fn test_validate_asset_freeze_zero_asset_id() {
let asset_freeze = AssetFreezeTransactionFields {
header: TransactionHeaderMother::example().build().unwrap(),
asset_id: 0, // Invalid asset ID
freeze_target: AccountMother::neil().address(),
frozen: true,
};

let result = asset_freeze.validate();
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 1);
assert_eq!(errors[0], "Asset ID must not be 0");
}

#[test]
fn test_validate_valid_asset_freeze() {
let asset_freeze = AssetFreezeTransactionFields {
header: TransactionHeaderMother::example().build().unwrap(),
asset_id: 123, // Valid asset ID
freeze_target: AccountMother::neil().address(),
frozen: true,
};

let result = asset_freeze.validate();
assert!(result.is_ok());
}

#[test]
fn test_build_with_invalid_asset_id() {
let result = AssetFreezeTransactionBuilder::default()
.header(TransactionHeaderMother::example().build().unwrap())
.asset_id(0) // Invalid asset ID
.freeze_target(AccountMother::neil().address())
.frozen(true)
.build();

assert!(result.is_err());
let error_message = result.unwrap_err().to_string();
assert!(error_message.contains("Asset freeze validation failed"));
assert!(error_message.contains("Asset ID must not be 0"));
}

#[test]
fn test_build_with_valid_asset_id() {
let result = AssetFreezeTransactionBuilder::default()
.header(TransactionHeaderMother::example().build().unwrap())
.asset_id(123) // Valid asset ID
.freeze_target(AccountMother::neil().address())
.frozen(true)
.build();

assert!(result.is_ok());
}
}
91 changes: 90 additions & 1 deletion crates/algokit_transact/src/transactions/asset_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//!
//! This module provides functionality for creating and managing asset transfer transactions.

use crate::traits::Validate;
use crate::transactions::common::TransactionHeader;
use crate::utils::{is_zero, is_zero_addr, is_zero_addr_opt};
use crate::{Address, Transaction};
Expand Down Expand Up @@ -77,6 +78,94 @@ pub struct AssetTransferTransactionFields {

impl AssetTransferTransactionBuilder {
pub fn build(&self) -> Result<Transaction, AssetTransferTransactionBuilderError> {
self.build_fields().map(Transaction::AssetTransfer)
let d = self.build_fields()?;
d.validate().map_err(|errors| {
AssetTransferTransactionBuilderError::ValidationError(format!(
"Asset transfer validation failed: {}",
errors.join("\n")
))
})?;
Ok(Transaction::AssetTransfer(d))
}
}

impl Validate for AssetTransferTransactionFields {
fn validate(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();

if self.asset_id == 0 {
errors.push("Asset ID must not be 0".to_string());
}

match errors.is_empty() {
true => Ok(()),
false => Err(errors),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{AccountMother, TransactionHeaderMother};

#[test]
fn test_validate_asset_transfer_zero_asset_id() {
let asset_transfer = AssetTransferTransactionFields {
header: TransactionHeaderMother::example().build().unwrap(),
asset_id: 0, // Invalid asset ID
amount: 1000,
receiver: AccountMother::neil().address(),
asset_sender: None,
close_remainder_to: None,
};

let result = asset_transfer.validate();
assert!(result.is_err());
let errors = result.unwrap_err();
assert_eq!(errors.len(), 1);
assert_eq!(errors[0], "Asset ID must not be 0");
}

#[test]
fn test_validate_valid_asset_transfer() {
let asset_transfer = AssetTransferTransactionFields {
header: TransactionHeaderMother::example().build().unwrap(),
asset_id: 123, // Valid asset ID
amount: 1000,
receiver: AccountMother::neil().address(),
asset_sender: None,
close_remainder_to: None,
};

let result = asset_transfer.validate();
assert!(result.is_ok());
}

#[test]
fn test_build_with_invalid_asset_id() {
let result = AssetTransferTransactionBuilder::default()
.header(TransactionHeaderMother::example().build().unwrap())
.asset_id(0) // Invalid asset ID
.amount(1000)
.receiver(AccountMother::neil().address())
.build();

assert!(result.is_err());
let error_message = result.unwrap_err().to_string();
assert!(error_message.contains("Asset transfer validation failed"));
assert!(error_message.contains("Asset ID must not be 0"));
}

#[test]
fn test_build_with_valid_asset_id() {
let result = AssetTransferTransactionBuilder::default()
.header(TransactionHeaderMother::example().build().unwrap())
.asset_id(123) // Valid asset ID
.amount(1000)
.receiver(AccountMother::neil().address())
.build();

assert!(result.is_ok());
}
}
11 changes: 3 additions & 8 deletions crates/algokit_utils/src/transactions/asset_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,13 @@ pub fn build_asset_clawback(
#[cfg(test)]
mod tests {
use super::*;
use algokit_transact::{Address, TransactionHeader};
use std::str::FromStr;
use algokit_transact::{TransactionHeader, test_utils::AccountMother};

#[test]
fn test_asset_opt_out_with_optional_close_remainder_to() {
// Use valid test addresses
let sender =
Address::from_str("JB3K6HTAXODO4THESLNYTSG6GQUFNEVIQG7A6ZYVDACR6WA3ZF52TKU5NA")
.unwrap();
let creator =
Address::from_str("JB3K6HTAXODO4THESLNYTSG6GQUFNEVIQG7A6ZYVDACR6WA3ZF52TKU5NA")
.unwrap();
let sender = AccountMother::neil().address();
let creator = AccountMother::neil().address();

// Test with Some(creator) - explicit close_remainder_to
let params_with_creator = AssetOptOutParams {
Expand Down
Loading