use aptos_sdk::{Aptos, AptosConfig};
use std::env;
fn get_test_config() -> AptosConfig {
if let Ok(node_url) = env::var("APTOS_LOCAL_NODE_URL") {
AptosConfig::custom(&node_url)
.unwrap()
.with_faucet_url(
&env::var("APTOS_LOCAL_FAUCET_URL")
.unwrap_or_else(|_| "http://127.0.0.1:8081".to_string()),
)
.unwrap()
} else {
AptosConfig::local()
}
}
async fn wait_for_finality() {
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod account_tests {
use super::*;
use aptos_sdk::account::Ed25519Account;
#[tokio::test]
#[ignore]
async fn e2e_create_and_fund_account() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = Ed25519Account::generate();
println!("Created account: {}", account.address());
let txn_hashes = aptos
.fund_account(account.address(), 100_000_000)
.await
.expect("failed to fund account");
println!("Funded with txns: {:?}", txn_hashes);
wait_for_finality().await;
let balance = aptos
.get_balance(account.address())
.await
.expect("failed to get balance");
assert!(balance > 0, "balance should be > 0");
println!("Balance: {} octas", balance);
}
#[tokio::test]
#[ignore]
async fn e2e_create_funded_account_helper() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(50_000_000)
.await
.expect("failed to create funded account");
println!("Created funded account: {}", account.address());
wait_for_finality().await;
let balance = aptos
.get_balance(account.address())
.await
.expect("failed to get balance");
assert!(balance > 0, "balance should be > 0");
}
#[tokio::test]
#[ignore]
async fn e2e_get_sequence_number() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
let seq_num = aptos
.get_sequence_number(account.address())
.await
.expect("failed to get sequence number");
println!("Sequence number: {}", seq_num);
assert_eq!(seq_num, 0);
}
#[tokio::test]
#[ignore]
async fn e2e_account_not_found() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = Ed25519Account::generate();
let result = aptos.get_sequence_number(account.address()).await;
assert!(result.is_err() || result.unwrap() == 0);
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod transfer_tests {
use super::*;
use aptos_sdk::account::Ed25519Account;
#[tokio::test]
#[ignore]
async fn e2e_transfer_apt() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(200_000_000)
.await
.expect("failed to create sender");
println!("Sender: {}", sender.address());
let recipient = Ed25519Account::generate();
println!("Recipient: {}", recipient.address());
let result = aptos
.transfer_apt(&sender, recipient.address(), 10_000_000)
.await
.expect("failed to transfer");
let success = result.data.get("success").and_then(|v| v.as_bool());
assert_eq!(success, Some(true), "transfer should succeed");
println!("Transfer successful!");
wait_for_finality().await;
let balance = aptos
.get_balance(recipient.address())
.await
.expect("failed to get balance");
assert_eq!(
balance, 10_000_000,
"recipient should have transferred amount"
);
}
#[tokio::test]
#[ignore]
async fn e2e_multiple_transfers() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(500_000_000)
.await
.expect("failed to create sender");
let recipients: Vec<_> = (0..3).map(|_| Ed25519Account::generate()).collect();
for (i, recipient) in recipients.iter().enumerate() {
let result = aptos
.transfer_apt(&sender, recipient.address(), 1_000_000 * (i as u64 + 1))
.await
.expect("failed to transfer");
let success = result.data.get("success").and_then(|v| v.as_bool());
assert_eq!(success, Some(true));
println!("Transfer {} to {} successful", i + 1, recipient.address());
}
wait_for_finality().await;
for (i, recipient) in recipients.iter().enumerate() {
let balance = aptos.get_balance(recipient.address()).await.unwrap_or(0);
let expected = 1_000_000 * (i as u64 + 1);
assert_eq!(balance, expected, "recipient {} balance mismatch", i);
}
}
#[tokio::test]
#[ignore]
async fn e2e_transfer_insufficient_balance() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(1_000_000)
.await
.expect("failed to create sender");
let recipient = Ed25519Account::generate();
let result = aptos
.transfer_apt(&sender, recipient.address(), 999_999_999_999)
.await;
assert!(
result.is_err() || {
let r = result.unwrap();
r.data.get("success").and_then(|v| v.as_bool()) == Some(false)
}
);
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod view_tests {
use super::*;
use aptos_sdk::account::Ed25519Account;
#[tokio::test]
#[ignore]
async fn e2e_view_timestamp() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let result = aptos
.view("0x1::timestamp::now_seconds", vec![], vec![])
.await
.expect("failed to call view function");
assert!(!result.is_empty(), "should return a value");
println!("Current timestamp: {:?}", result);
if let Some(timestamp) = result[0].as_str() {
let ts: u64 = timestamp.parse().expect("should be a number");
assert!(ts > 0, "timestamp should be > 0");
}
}
#[tokio::test]
#[ignore]
async fn e2e_view_coin_balance() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
wait_for_finality().await;
let result = aptos
.view(
"0x1::coin::balance",
vec!["0x1::aptos_coin::AptosCoin".to_string()],
vec![serde_json::json!(account.address().to_string())],
)
.await
.expect("failed to call view function");
assert!(!result.is_empty());
println!("Balance via view: {:?}", result);
}
#[tokio::test]
#[ignore]
async fn e2e_view_account_exists() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
wait_for_finality().await;
let result = aptos
.view(
"0x1::account::exists_at",
vec![],
vec![serde_json::json!(account.address().to_string())],
)
.await
.expect("failed to call view function");
assert_eq!(result[0], serde_json::json!(true));
}
#[tokio::test]
#[ignore]
async fn e2e_view_nonexistent_account() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let random_address = Ed25519Account::generate().address();
println!("Random address: {}", random_address);
let result = aptos
.view(
"0x1::account::exists_at",
vec![],
vec![serde_json::json!(random_address.to_string())],
)
.await
.expect("failed to call view function");
println!("Result: {:?}", result);
assert_eq!(result[0], serde_json::json!(true));
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod transaction_tests {
use super::*;
use aptos_sdk::account::Ed25519Account;
use aptos_sdk::transaction::{EntryFunction, builder::sign_transaction};
#[tokio::test]
#[ignore]
async fn e2e_build_sign_submit_transaction() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
let recipient = Ed25519Account::generate();
let payload = EntryFunction::apt_transfer(recipient.address(), 1000).unwrap();
let raw_txn = aptos
.build_transaction(&sender, payload.into())
.await
.expect("failed to build transaction");
let signed = sign_transaction(&raw_txn, &sender).expect("failed to sign");
let bcs_bytes = signed.to_bcs().expect("failed to serialize");
println!("BCS bytes ({} total):", bcs_bytes.len());
println!(
"First 100 bytes: {}",
const_hex::encode(&bcs_bytes[..100.min(bcs_bytes.len())])
);
println!(
"Last 100 bytes: {}",
const_hex::encode(&bcs_bytes[bcs_bytes.len().saturating_sub(100)..])
);
println!(
"Authenticator variant (byte at offset {}): {}",
bcs_bytes.len() - 97,
bcs_bytes.get(bcs_bytes.len() - 97).unwrap_or(&0)
);
let result = aptos
.submit_and_wait(&signed, None)
.await
.expect("failed to submit");
println!("Transaction result: {:?}", result.data);
}
#[tokio::test]
#[ignore]
async fn e2e_simulate_transaction() {
use aptos_sdk::account::Account;
use aptos_sdk::transaction::authenticator::{Ed25519PublicKey, Ed25519Signature};
use aptos_sdk::transaction::{SignedTransaction, TransactionAuthenticator};
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
let payload =
EntryFunction::apt_transfer(Ed25519Account::generate().address(), 1000).unwrap();
let raw_txn = aptos
.build_transaction(&account, payload.into())
.await
.expect("failed to build transaction");
let auth = TransactionAuthenticator::Ed25519 {
public_key: Ed25519PublicKey(account.public_key_bytes().try_into().unwrap()),
signature: Ed25519Signature([0u8; 64]),
};
let signed = SignedTransaction::new(raw_txn, auth);
let result = aptos
.simulate_transaction(&signed)
.await
.expect("failed to simulate");
assert!(!result.data.is_empty(), "simulation should return results");
let success = result.data[0].get("success").and_then(|v| v.as_bool());
assert_eq!(success, Some(true), "simulation should succeed");
let gas_used = result.data[0].get("gas_used").and_then(|v| v.as_str());
println!("Simulated gas used: {:?}", gas_used);
}
#[tokio::test]
#[ignore]
async fn e2e_get_transaction_by_hash() {
use aptos_sdk::types::HashValue;
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
let result = aptos
.transfer_apt(&sender, Ed25519Account::generate().address(), 1000)
.await
.expect("failed to transfer");
let hash_str = result.data.get("hash").and_then(|v| v.as_str()).unwrap();
println!("Transaction hash: {}", hash_str);
wait_for_finality().await;
let hash = HashValue::from_hex(hash_str).expect("invalid hash");
let txn = aptos.fullnode().get_transaction_by_hash(&hash).await;
assert!(txn.is_ok(), "should be able to get transaction by hash");
}
#[tokio::test]
#[ignore]
async fn e2e_transaction_expiration() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
let payload =
EntryFunction::apt_transfer(Ed25519Account::generate().address(), 1000).unwrap();
let chain_id = aptos
.ensure_chain_id()
.await
.expect("failed to resolve chain id");
let raw_txn = aptos_sdk::transaction::TransactionBuilder::new()
.sender(account.address())
.sequence_number(0)
.payload(payload.into())
.chain_id(chain_id)
.expiration_timestamp_secs(1) .build()
.expect("failed to build");
let signed = sign_transaction(&raw_txn, &account).expect("failed to sign");
let result = aptos.submit_and_wait(&signed, None).await;
assert!(result.is_err(), "expired transaction should fail");
}
}
#[cfg(feature = "ed25519")]
mod ledger_tests {
use super::*;
#[tokio::test]
#[ignore]
async fn e2e_get_ledger_info() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let ledger_info = aptos
.ledger_info()
.await
.expect("failed to get ledger info");
println!(
"Ledger version: {}",
ledger_info.version().expect("failed to parse version")
);
println!(
"Block height: {}",
ledger_info.height().expect("failed to parse height")
);
println!(
"Epoch: {}",
ledger_info.epoch_num().expect("failed to parse epoch")
);
assert!(
ledger_info.version().expect("failed to parse version") > 0,
"ledger version should be > 0"
);
}
#[tokio::test]
#[ignore]
async fn e2e_chain_id_from_ledger() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let _ledger_info = aptos
.ledger_info()
.await
.expect("failed to get ledger info");
assert!(aptos.chain_id().id() > 0);
println!("Client chain ID: {}", aptos.chain_id().id());
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod multi_signer_tests {
use super::*;
use aptos_sdk::account::{Account, Ed25519Account};
use aptos_sdk::transaction::{
EntryFunction, TransactionBuilder,
builder::{sign_fee_payer_transaction, sign_multi_agent_transaction},
types::{FeePayerRawTransaction, MultiAgentRawTransaction},
};
#[tokio::test]
#[ignore]
async fn e2e_fee_payer_transaction() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(1_000)
.await
.expect("failed to create sender");
let fee_payer = aptos
.create_funded_account(500_000_000)
.await
.expect("failed to create fee payer");
let recipient = Ed25519Account::generate();
println!("Sender: {}", sender.address());
println!("Fee payer: {}", fee_payer.address());
println!("Recipient: {}", recipient.address());
let payload = EntryFunction::apt_transfer(recipient.address(), 500).unwrap();
let sender_seq = aptos
.get_sequence_number(sender.address())
.await
.unwrap_or(0);
let chain_id = aptos
.ensure_chain_id()
.await
.expect("failed to resolve chain id");
let raw_txn = TransactionBuilder::new()
.sender(sender.address())
.sequence_number(sender_seq)
.payload(payload.into())
.chain_id(chain_id)
.max_gas_amount(100_000)
.gas_unit_price(100)
.build()
.expect("failed to build");
let fee_payer_txn = FeePayerRawTransaction {
raw_txn,
secondary_signer_addresses: vec![],
fee_payer_address: fee_payer.address(),
};
let signed = sign_fee_payer_transaction(&fee_payer_txn, &sender, &[], &fee_payer)
.expect("failed to sign");
let result = aptos.submit_and_wait(&signed, None).await;
match result {
Ok(r) => {
println!("Fee payer transaction result: {:?}", r.data);
}
Err(e) => {
println!("Fee payer transaction failed (may not be supported): {}", e);
}
}
}
#[tokio::test]
#[ignore]
async fn e2e_multi_agent_transaction() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(200_000_000)
.await
.expect("failed to create sender");
let secondary = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create secondary");
println!("Sender: {}", sender.address());
println!("Secondary: {}", secondary.address());
let payload =
EntryFunction::apt_transfer(Ed25519Account::generate().address(), 1000).unwrap();
let sender_seq = aptos
.get_sequence_number(sender.address())
.await
.unwrap_or(0);
let chain_id = aptos
.ensure_chain_id()
.await
.expect("failed to resolve chain id");
let raw_txn = TransactionBuilder::new()
.sender(sender.address())
.sequence_number(sender_seq)
.payload(payload.into())
.chain_id(chain_id)
.max_gas_amount(100_000)
.gas_unit_price(100)
.build()
.expect("failed to build");
let multi_agent_txn = MultiAgentRawTransaction {
raw_txn,
secondary_signer_addresses: vec![secondary.address()],
};
let secondary_ref: &dyn Account = &secondary;
let signed = sign_multi_agent_transaction(&multi_agent_txn, &sender, &[secondary_ref])
.expect("failed to sign");
let result = aptos.submit_and_wait(&signed, None).await;
match result {
Ok(r) => {
println!("Multi-agent transaction result: {:?}", r.data);
}
Err(e) => {
println!("Multi-agent transaction error: {}", e);
}
}
}
}
#[cfg(all(feature = "ed25519", feature = "secp256k1", feature = "faucet"))]
mod multi_key_e2e_tests {
use super::*;
use aptos_sdk::account::{AnyPrivateKey, MultiKeyAccount};
use aptos_sdk::crypto::{Ed25519PrivateKey, Secp256k1PrivateKey};
use aptos_sdk::transaction::{EntryFunction, TransactionBuilder, builder::sign_transaction};
#[tokio::test]
#[ignore]
async fn e2e_multi_key_account_transfer() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let ed_key1 = Ed25519PrivateKey::generate();
let secp_key = Secp256k1PrivateKey::generate();
let ed_key2 = Ed25519PrivateKey::generate();
let keys = vec![
AnyPrivateKey::ed25519(ed_key1),
AnyPrivateKey::secp256k1(secp_key),
AnyPrivateKey::ed25519(ed_key2),
];
let multi_key_account = MultiKeyAccount::new(keys, 2).unwrap();
println!("Multi-key account: {}", multi_key_account.address());
aptos
.fund_account(multi_key_account.address(), 100_000_000)
.await
.expect("failed to fund");
wait_for_finality().await;
let balance = aptos
.get_balance(multi_key_account.address())
.await
.unwrap_or(0);
println!("Multi-key balance: {}", balance);
let recipient = aptos_sdk::account::Ed25519Account::generate();
let payload = EntryFunction::apt_transfer(recipient.address(), 1_000_000).unwrap();
let seq = aptos
.get_sequence_number(multi_key_account.address())
.await
.unwrap_or(0);
let chain_id = aptos
.ensure_chain_id()
.await
.expect("failed to resolve chain id");
let raw_txn = TransactionBuilder::new()
.sender(multi_key_account.address())
.sequence_number(seq)
.payload(payload.into())
.chain_id(chain_id)
.max_gas_amount(100_000)
.gas_unit_price(100)
.build()
.expect("failed to build");
let signed =
sign_transaction(&raw_txn, &multi_key_account).expect("failed to sign with multi-key");
let result = aptos.submit_and_wait(&signed, None).await;
match result {
Ok(r) => {
let success = r.data.get("success").and_then(|v| v.as_bool());
println!("Multi-key transaction success: {:?}", success);
}
Err(e) => {
println!("Multi-key transaction error (may not be supported): {}", e);
}
}
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod state_tests {
use super::*;
#[tokio::test]
#[ignore]
async fn e2e_get_account_resource() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
wait_for_finality().await;
let resource = aptos
.fullnode()
.get_account_resource(account.address(), "0x1::account::Account")
.await;
match resource {
Ok(r) => {
println!("Account resource: {:?}", r.data);
}
Err(e) => {
println!("Failed to get resource: {}", e);
}
}
}
#[tokio::test]
#[ignore]
async fn e2e_get_coin_store_resource() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let account = aptos
.create_funded_account(100_000_000)
.await
.expect("failed to create account");
wait_for_finality().await;
let resource = aptos
.fullnode()
.get_account_resource(
account.address(),
"0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
)
.await;
match resource {
Ok(r) => {
println!("CoinStore resource: {:?}", r.data);
}
Err(e) => {
println!("Failed to get CoinStore: {}", e);
}
}
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod single_key_tests {
use aptos_sdk::account::Ed25519SingleKeyAccount;
#[tokio::test]
#[ignore]
async fn e2e_single_key_account_address_derivation() {
let account = Ed25519SingleKeyAccount::generate();
assert!(!account.address().is_zero());
let address1 = account.address();
let address2 = account.address();
assert_eq!(address1, address2);
println!("SingleKey account address: {}", account.address());
}
}
#[cfg(all(feature = "secp256k1", feature = "faucet"))]
mod secp256k1_tests {
use aptos_sdk::account::Secp256k1Account;
#[tokio::test]
#[ignore]
async fn e2e_secp256k1_account_address_derivation() {
let account = Secp256k1Account::generate();
assert!(!account.address().is_zero());
let address1 = account.address();
let address2 = account.address();
assert_eq!(address1, address2);
println!("Secp256k1 account address: {}", account.address());
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod batch_tests {
use super::*;
use aptos_sdk::account::Ed25519Account;
use aptos_sdk::transaction::{InputEntryFunctionData, TransactionBatchBuilder};
#[tokio::test]
#[ignore]
async fn e2e_batch_build() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let sender = aptos
.create_funded_account(500_000_000)
.await
.expect("failed to create sender");
let recipient1 = Ed25519Account::generate();
let recipient2 = Ed25519Account::generate();
wait_for_finality().await;
let seq_num = aptos
.fullnode()
.get_sequence_number(sender.address())
.await
.expect("failed to get seq num");
let chain_id = aptos
.ensure_chain_id()
.await
.expect("failed to resolve chain id");
let payload1 = InputEntryFunctionData::transfer_apt(recipient1.address(), 10_000_000)
.expect("failed to build payload 1");
let payload2 = InputEntryFunctionData::transfer_apt(recipient2.address(), 10_000_000)
.expect("failed to build payload 2");
let batch = TransactionBatchBuilder::new()
.sender(sender.address())
.starting_sequence_number(seq_num)
.chain_id(chain_id)
.add_payload(payload1)
.add_payload(payload2)
.build_and_sign(&sender)
.expect("failed to build batch");
println!("Created batch of {} transactions", batch.len());
assert_eq!(batch.len(), 2);
}
}
#[cfg(all(feature = "ed25519", feature = "faucet"))]
mod balance_tests {
use super::*;
use aptos_sdk::account::Ed25519Account;
#[tokio::test]
#[ignore]
async fn e2e_balance_multiple_accounts() {
let config = get_test_config();
let aptos = Aptos::new(config).expect("failed to create client");
let accounts: Vec<_> = (0..3).map(|_| Ed25519Account::generate()).collect();
for account in &accounts {
aptos
.fund_account(account.address(), 50_000_000)
.await
.expect("failed to fund account");
}
wait_for_finality().await;
for (i, account) in accounts.iter().enumerate() {
let balance = aptos
.get_balance(account.address())
.await
.expect("failed to get balance");
assert!(
balance >= 50_000_000,
"Account {} should have at least 50M octas",
i
);
println!("Account {}: {} octas", i, balance);
}
}
}