Skip to content

Python 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.

  1. Build

    Building a transaction is how you specify:

    1. 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.
    2. 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.
    3. Type arguments and arguments.
      This is any data the function needs to run.

    This can be made for a single account like so:

    from aptos_sdk.v2.bcs import Serializer
    from aptos_sdk.v2.transactions import EntryFunction, TransactionArgument, TransactionPayload
    from aptos_sdk.v2.types import StructTag, TypeTag
    payload = EntryFunction.natural(
    "0x1::aptos_account",
    "transfer_coins",
    [TypeTag(StructTag.from_str("0x1::aptos_coin::AptosCoin"))],
    [
    TransactionArgument(bob.address, Serializer.struct),
    TransactionArgument(1_000, Serializer.u64),
    ],
    )
    raw_txn = await aptos.transaction.build(
    sender=alice.address,
    payload=TransactionPayload(payload),
    )

    You can customize the way your transaction executes by passing keyword arguments to aptos.transaction.build. Some of the most commonly used options are:

    1. max_gas_amount - This caps the amount of gas you are willing to pay to execute this transaction.
    2. gas_unit_price - You can specify a higher than minimum price per gas to be executed with higher priority by the Aptos network.
    3. expiration_timestamps_secs - This gives a concrete time the transaction must execute by or it will be canceled.
    4. replay_protection_nonce - When set, the SDK builds an orderless transaction that does not consume a sequence number.

    The SDK provides sensible defaults for these values if they are not specified explicitly. You can also set defaults on AptosConfig.

  2. 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.transaction.simulate like so:

    simulation_result = await aptos.transaction.simulate(raw_txn, alice.public_key)
  3. Sign

    Once the transaction is built and the fees seem reasonable, you can sign the transaction. The signature must come from the sender account.

    signed_txn = aptos.transaction.sign(raw_txn, alice)
  4. Submit

    Now that the transaction is signed, you can submit it to the network using aptos.transaction.submit like so:

    txn_hash = await aptos.transaction.submit(signed_txn)
  5. Wait

    Finally, you can wait for the result of the transaction by using aptos.transaction.wait_for_transaction and specifying the hash of the transaction you just submitted like so:

    result = await aptos.transaction.wait_for_transaction(txn_hash)

For coin transfers, aptos.coin.transfer wraps build, sign, and submit:

txn_hash = await aptos.coin.transfer(alice, bob.address, 1_000)
await aptos.transaction.wait_for_transaction(txn_hash)

You can also combine sign, submit, and wait after a manual build:

result = await aptos.transaction.sign_submit_and_wait(raw_txn, alice)
import asyncio
from aptos_sdk.v2 import Account, Aptos, AptosConfig, Network
from aptos_sdk.v2.bcs import Serializer
from aptos_sdk.v2.transactions import EntryFunction, TransactionArgument, TransactionPayload
from aptos_sdk.v2.types import StructTag, TypeTag
FUND_AMOUNT = 100_000_000
TRANSFER_AMOUNT = 1_000
async def main():
async with Aptos(AptosConfig(network=Network.DEVNET)) as aptos:
alice = Account.generate()
bob = Account.generate()
print("=== Addresses ===")
print(f"Alice: {alice.address}")
print(f"Bob: {bob.address}")
await aptos.faucet.fund_account(alice.address, FUND_AMOUNT)
await aptos.faucet.fund_account(bob.address, 10_000_000)
print("=== Initial Balances ===")
print(f"Alice: {await aptos.coin.balance(alice.address)}")
print(f"Bob: {await aptos.coin.balance(bob.address)}")
payload = EntryFunction.natural(
"0x1::aptos_account",
"transfer_coins",
[TypeTag(StructTag.from_str("0x1::aptos_coin::AptosCoin"))],
[
TransactionArgument(bob.address, Serializer.struct),
TransactionArgument(TRANSFER_AMOUNT, Serializer.u64),
],
)
raw_txn = await aptos.transaction.build(
sender=alice.address,
payload=TransactionPayload(payload),
)
simulation_result = await aptos.transaction.simulate(raw_txn, alice.public_key)
print("=== Simulation ===")
print(f"Gas used: {simulation_result[0]['gas_used']}")
print(f"Status: {simulation_result[0]['vm_status']}")
result = await aptos.transaction.sign_submit_and_wait(raw_txn, alice)
print(f"Success: {result['success']}")
print("=== Final Balances ===")
print(f"Alice: {await aptos.coin.balance(alice.address)}")
print(f"Bob: {await aptos.coin.balance(bob.address)}")
asyncio.run(main())

Building and sending transactions on-chain involves the following 5 steps:

  1. Build the transaction.
  2. Simulate the cost. (Optional)
  3. Sign the transaction (if the simulated cost seems ok).
  4. Submit the transaction to the network.
  5. Wait for the chain to validate and update.

Transactions have a couple of additional features which let them adapt to your needs which you can learn about here:

  1. Multi-Agent Signatures - Allowing multiple accounts to be used for a single contract.
  2. Sponsoring Transactions - Have another account pay gas fees for this transaction.
  3. Batch Submit Transactions - How to send multiple transactions quickly from a single account.
  4. Binary Canonical Serialization (BCS) - The format used to serialize data for Aptos transactions.