Rust SDK - Building Transactions
Transactions allow you to change on-chain data or trigger events. Generally, transactions follow 5 steps from building to executing on chain: building, simulating, signing, submitting, and waiting.
-
Build
Building a transaction is how you specify:
- The sender account.
This account normally pays the gas fees for this transaction. See Sponsoring Transactions to learn how to have another account pay for transaction fees. - The function being called on-chain.
This is the identifier for the smart contract entry function on-chain that will trigger when you execute this transaction. - Type arguments and arguments.
This is any data the function needs to run.
The SDK can fetch sequence number, gas price, and chain ID for you:
use aptos_sdk::transaction::EntryFunction;let payload = EntryFunction::apt_transfer(bob.address(), 1_000)?;let raw_txn = aptos.build_transaction(&alice, payload.into()).await?;Or build the payload and transaction yourself:
use aptos_sdk::transaction::{EntryFunction, TransactionBuilder};let payload = EntryFunction::apt_transfer(bob.address(), 1_000)?;let seq_num = aptos.get_sequence_number(alice.address()).await?;let raw_txn = TransactionBuilder::new().sender(alice.address()).sequence_number(seq_num).payload(payload.into()).chain_id(aptos.chain_id()).max_gas_amount(100_000).gas_unit_price(100).expiration_from_now(600).build()?;Building Options
Section titled “Building Options”You can customize the way your transaction executes by setting builder fields. Some of the most commonly used options are:
max_gas_amount- This caps the amount of gas you are willing to pay to execute this transaction. The SDK default is2_000_000. The network reservesmax_gas_amount * gas_unit_pricefrom the sender (or fee payer), so faucet at least a few APT before submitting.gas_unit_price- You can specify a higher than minimum price per gas to be executed with higher priority by the Aptos network.expiration_from_now- This gives a concrete time the transaction must execute by or it will be canceled.
aptos.build_transactionprovides sensible defaults for these values if they are not specified explicitly. - The sender account.
-
Simulate (Optional)
Every transaction on the Aptos chain has a gas fee associated with how much work the network machines have to do when executing the transaction. In order to estimate the cost associated with that, you can simulate transactions before committing them.
You can execute the simulation by using
aptos.simulatelike so:let payload = EntryFunction::apt_transfer(bob.address(), 1_000)?;let simulation_result = aptos.simulate(&alice, payload.into()).await?;println!("Gas used: {}", simulation_result.gas_used());println!("Status: {}", simulation_result.vm_status()); -
Sign
Once the transaction is built and the fees seem reasonable, you can sign the transaction. The signature must come from the sender account.
use aptos_sdk::transaction::builder::sign_transaction;let signed_txn = sign_transaction(&raw_txn, &alice)?; -
Submit
Now that the transaction is signed, you can submit it to the network using
aptos.submit_transactionlike so:let pending = aptos.submit_transaction(&signed_txn).await?; -
Wait
Finally, you can wait for the result of the transaction.
submit_and_waitcombines submit and wait:let result = aptos.submit_and_wait(&signed_txn, None).await?;
For APT transfers, aptos.transfer_apt wraps build, sign, submit, and wait:
let result = aptos.transfer_apt(&alice, bob.address(), 1_000).await?;You can also combine simulate-then-submit, or sign-submit-and-wait after a manual payload:
let payload = EntryFunction::apt_transfer(bob.address(), 1_000)?;let result = aptos.sign_submit_and_wait(&alice, payload.into(), None).await?;Full Rust Example
Section titled “Full Rust Example”use aptos_sdk::{Aptos, AptosConfig, account::Ed25519Account, transaction::EntryFunction};
// Default max_gas_amount is 2_000_000. At ~100 octas per gas unit that// reserves 0.2 APT, so fund well above the transfer amount.const FUND_AMOUNT: u64 = 1_000_000_000;const TRANSFER_AMOUNT: u64 = 1_000;
#[tokio::main]async fn main() -> anyhow::Result<()> { let aptos = Aptos::new(AptosConfig::devnet())?;
let alice = Ed25519Account::generate(); let bob = Ed25519Account::generate();
println!("=== Addresses ==="); println!("Alice: {}", alice.address()); println!("Bob: {}", bob.address());
aptos.fund_account(alice.address(), FUND_AMOUNT).await?;
println!("=== Initial Balances ==="); println!("Alice: {}", aptos.get_balance(alice.address()).await?); println!("Bob: {}", aptos.get_balance(bob.address()).await?);
let payload = EntryFunction::apt_transfer(bob.address(), TRANSFER_AMOUNT)?; let simulation_result = aptos.simulate(&alice, payload.clone().into()).await?; println!("=== Simulation ==="); println!("Gas used: {}", simulation_result.gas_used()); println!("Status: {}", simulation_result.vm_status());
let result = aptos .sign_submit_and_wait(&alice, payload.into(), None) .await?; println!("Submitted: {:?}", result.data.get("hash"));
println!("=== Final Balances ==="); println!("Alice: {}", aptos.get_balance(alice.address()).await?); println!("Bob: {}", aptos.get_balance(bob.address()).await?);
Ok(())}Summary
Section titled “Summary”Building and sending transactions on-chain involves the following 5 steps:
- Build the transaction.
- Simulate the cost. (Optional)
- Sign the transaction (if the simulated cost seems ok).
- Submit the transaction to the network.
- Wait for the chain to validate and update.
Explore Advanced Transaction Features
Section titled “Explore Advanced Transaction Features”Transactions have a couple of additional features which let them adapt to your needs which you can learn about here:
- Multi-Agent Signatures - Allowing multiple accounts to be used for a single contract.
- Sponsoring Transactions - Have another account pay gas fees for this transaction.
- Batch Submit Transactions - How to send multiple transactions quickly from a single account.
- Binary Canonical Serialization (BCS) - The format used to serialize data for Aptos transactions.