diff --git a/unit_tests/tests/test_add_invoke_transaction.rs b/unit_tests/tests/test_add_invoke_transaction.rs new file mode 100644 index 0000000..1446581 --- /dev/null +++ b/unit_tests/tests/test_add_invoke_transaction.rs @@ -0,0 +1,151 @@ +#![feature(assert_matches)] + +mod common; +use common::*; +use starknet_core::types::{BroadcastedInvokeTransaction, FieldElement, StarknetError, TransactionStatus}; +use starknet_providers::{ + jsonrpc::{HttpTransport, JsonRpcClient}, + Provider, ProviderError, +}; +use std::assert_matches::assert_matches; +use std::thread; +use std::time::Duration; + +/// Test for the `add_invoke_transaction` Deoxys RPC method +/// Submit a new transaction to be added to the chain +/// +/// # Arguments +/// * `invoke_transaction` - An invoke transaction, +/// with following fields: +/// * `type` - INVOKE +/// * `sender_address` - The address of the sender +/// * `calldata` - The calldata to send +/// * `max_fee` - The maximum fees sender is willing to pay +/// * `version` - The version of the transaction +/// * `signature` - The transaction signature +/// * `nonce` - The nonce of the transaction +/// +/// # Returns +/// * `result` - The result of the transaction submission, with the transaction hash that has been submitted +/// +/// # Errors +/// * `invalid_transaction_nonce` - If the transaction nonce is invalid +/// * `insufficient_account_balance` - If the account balance is insufficient +/// * `insufficient_max_fee` - If the max fee is insufficient +/// * `invalid_transaction_nonce` - If the transaction nonce is invalid +/// * `validation_failure` - If the transaction validation fails +/// * `non_account` - If the sender address is not a valid account +/// * `duplicate_transaction` - If a transaction with same params already exists +/// * `unsupported_transaction_version` - If the transaction version is not supported +/// * `unexpected_error` - If an unexpected error occurs + +/// Following tests runs using V1 Invoke Transaction (params follow starknet-rs implementation) +#[rstest] +#[tokio::test] +async fn fail_if_param_(deoxys: JsonRpcClient) { + + let invalid_invoke_transaction = BroadcastedInvokeTransaction { + sender_address: FieldElement::from_hex_be("valid_address").unwrap(), + calldata: vec![FieldElement::from_hex_be("calldata_array").unwrap()], + max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(), + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x000000").unwrap(), //here nonce is invalid + is_query: false, + }; + + let response_deoxys = deoxys + .add_invoke_transaction(invalid_invoke_transaction) + .await; + + assert_matches!( + response_deoxys, + Err(ProviderError::StarknetError( + StarknetError::InvalidTransactionNonce + )) + ); +} + +#[rstest] +#[tokio::test] +async fn fail_if_insufficient_max_fee(deoxys: JsonRpcClient) { + + let invalid_invoke_transaction = BroadcastedInvokeTransaction { + sender_address: FieldElement::from_hex_be("valid_address").unwrap(), + calldata: vec![FieldElement::from_hex_be("calldata_array").unwrap()], + max_fee: FieldElement::from_hex_be("0x000000").unwrap(), //here max_fee is insufficient + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x01").unwrap(), + is_query: false, + }; + + let response_deoxys = deoxys + .add_invoke_transaction(invalid_invoke_transaction) + .await; + + assert_matches!( + response_deoxys, + Err(ProviderError::StarknetError( + StarknetError::InsufficientMaxFee + )) + ); +} + +#[rstest] +#[tokio::test] +async fn fail_if_bad_calldata(deoxys: JsonRpcClient) { + + let invalid_invoke_transaction = BroadcastedInvokeTransaction { + sender_address: FieldElement::from_hex_be("valid_address").unwrap(), + calldata: vec![FieldElement::from_hex_be("0x000000").unwrap()], //here calldata is invalid + max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(), + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x01").unwrap(), + is_query: false, + }; + + let response_deoxys = deoxys + .add_invoke_transaction(invalid_invoke_transaction) + .await; + + assert_matches!( + response_deoxys, + Err(ProviderError::StarknetError( + StarknetError::ValidationFailure + )) + ); +} + +#[rstest] +#[tokio::test] +async fn works_ok_with_valid_params(deoxys: JsonRpcClient) { + + let valid_invoke_transaction = BroadcastedInvokeTransaction { + sender_address: FieldElement::from_hex_be("valid_address").unwrap(), + calldata: vec![FieldElement::from_hex_be("calldata_array").unwrap()], + max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(), + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x01").unwrap(), + is_query: false, + }; + + //Here we added a valid transaction + let response_deoxys = deoxys + .add_invoke_transaction(valid_invoke_transaction) + .await; + + //Now, if the transaction is valid, the rpc call response contain the transaction hash + let transaction_submitted_hash = response_deoxys.expect("Transaction submition failed").transaction_hash; + + //Wait for the transaction to be added to the chain + thread::sleep(Duration::from_secs(15)); + + //Let's check the transaction status + let transaction_status = deoxys + .get_transaction_status(transaction_submitted_hash) + .await; + + assert_matches!( + transaction_status.unwrap(), + TransactionStatus::Received + ); +} \ No newline at end of file diff --git a/unit_tests/tests/test_deploy_account_transaction.rs b/unit_tests/tests/test_deploy_account_transaction.rs new file mode 100644 index 0000000..951ccb0 --- /dev/null +++ b/unit_tests/tests/test_deploy_account_transaction.rs @@ -0,0 +1,156 @@ +#![feature(assert_matches)] + +mod common; +use common::*; +use starknet_core::types::{BroadcastedDeployAccountTransaction, FieldElement, StarknetError, TransactionStatus}; +use starknet_providers::{ + jsonrpc::{HttpTransport, JsonRpcClient}, + Provider, ProviderError, +}; +use std::assert_matches::assert_matches; +use std::thread; +use std::time::Duration; + +/// Test for the `deploy_account_transaction` Deoxys RPC method +/// Submit a new deploy account transaction +/// +/// There is two type of DeployAccountTransaction: V1 and V3 +/// +/// # Arguments +/// * `deploy_account_transaction` - A deploy account transaction +/// with following fields (V1): +/// * `type` - DEPLOY_ACCOUNT +/// * `max_fee` - The maximal fee willing to be paid +/// * `signature` - The transaction signature +/// * `nonce` - The nonce of the transaction +/// * `contract_address_salt` - The salt for the address of the deployed contract +/// * `constructor_calldata` - The parameters passed to the constructor +/// * `class_hash` - The hash of the deployed contract's class +/// * `is_query` - If set to `true`, uses a query-only transaction version that's invalid for execution +/// +/// # Returns +/// * `result` - The result of the transaction submission +/// with following fields: +/// * `transaction_hash` - The hash of the transaction +/// * `contract_address` - The address of the deployed contract +/// +/// # Errors +/// * `invalid_transaction_nonce` - If the transaction nonce is invalid +/// * `insufficient_account_balance` - If the account balance is insufficient +/// * `insufficient_max_fee` - If the max fee is insufficient +/// * `invalid_transaction_nonce` - If the transaction nonce is invalid +/// * `validation_failure` - If the transaction validation fails +/// * `non_account` - If the sender address is not a valid account +/// * `duplicate_transaction` - If a transaction with same params already exists +/// * `unsupported_transaction_version` - If the transaction version is not supported +/// * `unexpected_error` - If an unexpected error occurs + +#[rstest] +#[tokio::test] +async fn fail_if_param_(deoxys: JsonRpcClient) { + + let invalid_deploy_account_transaction = BroadcastedDeployAccountTransaction { + max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(), + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x000000").unwrap(), //here nonce is invalid + contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(), + constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()], + class_hash: FieldElement::from_hex_be("0x000000").unwrap(), + is_query: false, + }; + + let response_deoxys = deoxys + .add_deploy_account_transaction(invalid_deploy_account_transaction) + .await; + + assert_matches!( + response_deoxys, + Err(ProviderError::StarknetError(StarknetError::InvalidTransactionNonce)) + ); +} + +#[rstest] +#[tokio::test] +async fn fail_if_insufficient_max_fee(deoxys: JsonRpcClient) { + + let invalid_deploy_account_transaction = BroadcastedDeployAccountTransaction { + max_fee: FieldElement::from_hex_be("0x000000").unwrap(), //here max_fee is insufficient + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x000000").unwrap(), + contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(), + constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()], + class_hash: FieldElement::from_hex_be("0x000000").unwrap(), + is_query: false, + }; + + let response_deoxys = deoxys + .add_deploy_account_transaction(invalid_deploy_account_transaction) + .await; + + assert_matches!( + response_deoxys, + Err(ProviderError::StarknetError(StarknetError::InsufficientMaxFee)) + ); +} + +#[rstest] +#[tokio::test] +async fn fail_if_invalid_transaction_nonce(deoxys: JsonRpcClient) { + + let invalid_deploy_account_transaction = BroadcastedDeployAccountTransaction { + max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(), + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x000000").unwrap(), //here nonce is invalid + contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(), + constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()], + class_hash: FieldElement::from_hex_be("0x000000").unwrap(), + is_query: false, + }; + + let response_deoxys = deoxys + .add_deploy_account_transaction(invalid_deploy_account_transaction) + .await; + + assert_matches!( + response_deoxys, + Err(ProviderError::StarknetError(StarknetError::InvalidTransactionNonce)) + ); +} + +#[rstest] +#[tokio::test] +async fn works_ok(deoxys: JsonRpcClient) { + + let valid_deploy_account_transaction = BroadcastedDeployAccountTransaction { + max_fee: FieldElement::from_hex_be("0x0ffffffff").unwrap(), + signature: vec![FieldElement::from_hex_be("signature_array").unwrap()], + nonce: FieldElement::from_hex_be("0x000000").unwrap(), + contract_address_salt: FieldElement::from_hex_be("0x000000").unwrap(), + constructor_calldata: vec![FieldElement::from_hex_be("constructor_calldata_array").unwrap()], + class_hash: FieldElement::from_hex_be("0x000000").unwrap(), + is_query: false, + }; + + let response_deoxys = deoxys + .add_deploy_account_transaction(valid_deploy_account_transaction) + .await; + + //Here, as response we got the transaction hash and the contract address deployed + let result = response_deoxys.unwrap(); + + //Now, if the transaction is valid, the rpc call response contain the transaction hash + let transaction_submitted_hash = response_deoxys.expect("Transaction submition failed").transaction_hash; + + //Wait for the transaction to be added to the chain + thread::sleep(Duration::from_secs(15)); + + //Let's check the transaction status + let transaction_status = deoxys + .get_transaction_status(transaction_submitted_hash) + .await; + + assert_matches!( + transaction_status.unwrap(), + TransactionStatus::Received + ); +} \ No newline at end of file